Affreta TMS ← Retour au site

Documentation API

Affreta expose une API REST, un serveur MCP pour les assistants IA et des webhooks signés pour intégrer vos appels d'offres transport à votre ERP, WMS ou vos scripts. Cette documentation est publique. Pour appeler l'API, générez une clé dans l'application → Intégrations API.

Base & authentification

URL de base : https://app.affreta.com

Chaque requête s'authentifie par une clé d'organisation, dans l'en-tête Authorization :

Authorization: Bearer aff_live_xxxxxxxxxxxxxxxxxxxxxxxx

La clé se génère dans l'app (Intégrations API), est affichée une seule fois, stockée hachée, et révocable à tout moment. Une clé peut porter le scope write (nécessaire pour créer). Sans clé valide, l'API répond 401.

Endpoints

GET /api/v1/requests

Liste les demandes de l'organisation, les plus récentes d'abord. Paramètres : limit (défaut 50, max 200), statut (filtre optionnel).

curl -H "Authorization: Bearer aff_live_xxx" \
  "https://app.affreta.com/api/v1/requests?statut=cloture&limit=50"

Réponse : { "data": [ … ] }. Chaque demande contient notamment : id, reference, statut, type_transport, date_enlevement, date_livraison, adresse_enlevement, adresse_livraison, partenaire, nature_marchandise, nb_colis, poids_kg, volume_m3, offers_count, replies_count, best_offer ({ transporteur, price_eur, delai_jours }) et le détail offers[].

POST /api/v1/requests

Crée une demande (en brouillon) depuis un système tiers. Nécessite le scope write.

ChampTypeRequis
date_enlevementstring (YYYY-MM-DD)oui
type_transportdepart | receptionnon
date_livraisonstring (YYYY-MM-DD)non
reference, partenairestringnon
adresse_enlevement, adresse_livraisonstringnon
nb_colis, poids_kg, volume_m3numbernon
nature_marchandise, notesstringnon
curl -X POST "https://app.affreta.com/api/v1/requests" \
  -H "Authorization: Bearer aff_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "date_enlevement": "2026-10-02",
    "adresse_enlevement": "Lyon 69007",
    "adresse_livraison": "Nantes 44000",
    "nb_colis": 12, "poids_kg": 4200,
    "reference": "CMD-10482"
  }'

Réponse : { "id": "…", "statut": "brouillon" } (HTTP 201).

GET /api/v1/requests/:id

Détail d'une demande et de ses offres (mêmes champs que la liste, dont offers[] avec l'id de chaque offre — utile pour l'attribution).

curl -H "Authorization: Bearer aff_live_xxx" \
  "https://app.affreta.com/api/v1/requests/REQUEST_ID"

POST /api/v1/requests/:id/send

Diffuse un brouillon à ses transporteurs (envoie les emails de consultation, calcule le trajet, passe la demande en envoye). Nécessite le scope write. Ajoutez d'abord les transporteurs à la demande.

curl -X POST "https://app.affreta.com/api/v1/requests/REQUEST_ID/send" \
  -H "Authorization: Bearer aff_live_xxx"

Réponse : { "id": "…", "statut": "envoye", "emailed": { "sent": n, "skipped": n, "failed": n } }.

POST /api/v1/requests/:id/award

Attribue la demande à une offre : offre retenue confirmée, écartés notifiés, ligne de coût créée, email de confirmation envoyé. Nécessite le scope write. Fournissez soit une offre précise, soit une stratégie automatique.

ChampTypeDescription
offer_idstringOffre à retenir (voir offers[].id)
strategycheapest · fastestRetient automatiquement l'offre chiffrée la moins chère, ou celle au délai le plus court
curl -X POST "https://app.affreta.com/api/v1/requests/REQUEST_ID/award" \
  -H "Authorization: Bearer aff_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "strategy": "cheapest" }'

Réponse : { "id": "…", "statut": "confirme", "offer_id": "…" }.

GET /api/v1/offers

Liste les offres de l'organisation (toutes demandes). Filtres : statut (en_attente | accepte | refuse), request_id, limit (défaut 100, max 500).

curl -H "Authorization: Bearer aff_live_xxx" \
  "https://app.affreta.com/api/v1/offers?statut=accepte"

GET /api/v1/carriers

Liste les transporteurs (panel) de l'organisation.

curl -H "Authorization: Bearer aff_live_xxx" \
  "https://app.affreta.com/api/v1/carriers"

Serveur MCP (assistants IA)

Affreta parle le Model Context Protocol : branchez votre compte dans Claude Code, Claude Desktop, Cursor ou VS Code (Copilot) et pilotez vos appels d'offres en langage naturel : « crée une demande Lyon → Marseille, 12 palettes, enlèvement mardi, et envoie-la à mon panel », « qui est le moins cher sur l'AO-2418 ? attribue-la ».

URL : https://app.affreta.com/api/mcp (transport HTTP « Streamable »). Authentification par la même clé API que l'API REST : l'assistant ne voit que votre organisation, et une clé read l'empêche d'envoyer ou d'attribuer.

Claude Code

claude mcp add --transport http affreta https://app.affreta.com/api/mcp \
  --header "Authorization: Bearer aff_live_xxx"

Cursor (.cursor/mcp.json) · VS Code (.vscode/mcp.json, clé servers)

{
  "mcpServers": {
    "affreta": {
      "url": "https://app.affreta.com/api/mcp",
      "headers": { "Authorization": "Bearer aff_live_xxx" }
    }
  }
}

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "affreta": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://app.affreta.com/api/mcp",
               "--header", "Authorization: Bearer ${AFFRETA_KEY}"],
      "env": { "AFFRETA_KEY": "aff_live_xxx" }
    }
  }
}

Outils exposés

OutilRôle
get_organizationvotre organisation et la date du jour (pour « mardi prochain »)
list_carrierstransporteurs du panel
list_requests · get_requestdemandes et leurs offres (par id ou référence)
list_offersoffres reçues
create_requestcrée un brouillon (rien n'est envoyé)
send_requestenvoie la demande aux transporteurs — après votre accord
award_requestattribue (offre précise, la moins chère ou la plus rapide) — après votre accord

Les actions qui envoient des emails à des transporteurs sont signalées comme telles à l'assistant, qui vous demande confirmation avant de les exécuter. Limite : 120 appels par minute et par clé.

Scopes des clés

Chaque clé porte un scope choisi à sa création : read (lecture seule) ou read + write (lecture et écriture). Les endpoints POST exigent le scope write, sinon l'API répond 403.

Webhooks

Dans Intégrations API, renseignez une URL et un secret. Affreta envoie alors un POST JSON à chaque événement :

ÉvénementDéclencheur
offer.receivedun transporteur a déposé un prix
offer.declinedun transporteur a décliné
pingtest manuel depuis l'app

Corps :

{
  "event": "offer.received",
  "created_at": "2026-09-18T09:12:40.000Z",
  "data": { "request_id": "…", "transporteur": "…", "price_eur": 512, "delai_jours": 2 }
}

Vérifier la signature

Chaque requête porte les en-têtes x-affreta-event et x-affreta-signature. La signature est un HMAC-SHA256 du corps brut avec votre secret, préfixé de sha256=. Vérifiez-la avant de traiter l'événement :

// Node.js
import crypto from "crypto";
const sig = "sha256=" + crypto
  .createHmac("sha256", WEBHOOK_SECRET)
  .update(rawBody)
  .digest("hex");
if (sig !== req.headers["x-affreta-signature"]) return res.status(401).end();

Email → demande (entrant)

Chaque organisation dispose d'une URL d'ingestion (visible dans l'application → Intégrations API → « Email → demande »). Tout email qui lui est transmis en POST devient un brouillon de demande pré-rempli : référence, date d'enlèvement, adresses, colis, poids et volume sont extraits du sujet et du corps ; le texte intégral est conservé dans les notes. Compatible Resend Receiving (webhook email.received), Zapier (Email Parser), Make (Mailhook), n8n (Email Trigger) ou tout appel HTTP.

curl -X POST "https://app.affreta.com/api/public/inbound/VOTRE_JETON" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Client Démo <achats@client.fr>",
    "subject": "Transport palettes réf CMD-10482",
    "text": "Enlèvement : 12/11/2026 à Lyon 69007. Livraison : Nantes 44000. 12 palettes, 3200 kg."
  }'

Réponse (HTTP 201) : { "ok": true, "id": "…", "statut": "brouillon", "extracted": { … } }. Sans date détectée, l'enlèvement est posé à J+2 ouvrés et signalé « à confirmer ». Un GET sur l'URL vérifie la validité du jeton.

Erreurs

Les erreurs renvoient un JSON { "error": "…" } avec un statut HTTP : 400 (requête invalide), 401 (clé absente/invalide), 403 (scope insuffisant), 500 (erreur serveur).

Intégration ERP / WMS

Il n'existe pas de connecteur ERP « clé en main » : l'intégration se fait via cette API REST et ces webhooks, câblés à votre système par votre intégrateur (directement, ou via un middleware type Make/n8n). Des connecteurs dédiés peuvent être développés au cas par cas — écrivez-nous à hello@valkode.fr.

Créer un compte & générer une clé
© 2026 Valkode — Affreta TMS. · Mentions légales · CGV · Qui édite Affreta