SIPCO

Documentation de l'API — v1

Facturation électronique • Espace développeur

Objectif

Cette API centralise la réception des factures au format canonique (JSON) pour plusieurs applications et plusieurs clients (multi-tenant). Elle permet ensuite l'envoi vers une plateforme de facturation électronique (ex : B2Brouter), avec traçabilité, idempotence et suivi des statuts.

Cycle de vie d’une facture

  • DRAFT : facture créée, modifiable
  • SENDING : envoi en cours vers la plateforme
  • SENT : transmise à la plateforme
  • ACCEPTED : acceptée par la plateforme / destinataire
  • REJECTED : rejetée (erreur métier ou technique)
  • ERROR : erreur lors de l’envoi (ou validation plateforme)

Chaque transition est historisée dans un journal d’événements (audit).

Base URL

https://facturation.sipco.fr

En production, remplacer par l'URL de l'API (ex : https://facturation.sipco.fr).

Authentification

Toutes les routes sous /api/v1 nécessitent une clé API interne transmise dans l’en-tête :

Authorization: Bearer VOTRE_CLE_API

Sans cet en-tête, l’API renvoie 401 Unauthorized.

Exemple rapide

curl -X GET "https://facturation.sipco.fr/api/v1/secure/ping" \
  -H "Authorization: Bearer <VOTRE_CLE_API>"

GET /api/health/db

Endpoint de santé (non protégé) validant la connexion à la base.

curl -X GET "https://facturation.sipco.fr/api/health/db"

GET /api/v1/secure/ping

Endpoint protégé de test (valide l’auth API Key).

curl -X GET "https://facturation.sipco.fr/api/v1/secure/ping" \
-H "Authorization: Bearer <VOTRE_CLE_API>"

GET /api/v1/b2brouter/accounts

Liste les comptes B2Brouter accessibles (utile pour récupérer l’accountId à associer au tenant).

curl -X GET "https://facturation.sipco.fr/api/v1/b2brouter/accounts" \
-H "Authorization: Bearer <VOTRE_CLE_API>"

POST /api/v1/tenants

Création complète d’un tenant local. Cette route administrative utilise SUPERADMIN_API_KEY et n’appelle pas B2Brouter. Le body JSON n’est pas enveloppé dans account.

Body JSON (recommandé)

{
  "name": "ENTREPRISE EXEMPLE",
  "slug": "entreprise-exemple",
  "companyName": "ENTREPRISE EXEMPLE",
  "siren": "123456789",
  "siret": "12345678900010",
  "vatNumber": "FRXX123456789",
  "rcsCity": "VILLE EXEMPLE",
  "rcsNumber": "123456789",
  "legalRepresentativeName": "NOM EXEMPLE",
  "legalRepresentativeTitle": "Gérant",
  "street": "1 RUE EXEMPLE",
  "city": "VILLE EXEMPLE",
  "zipCode": "35000",
  "province": "Ille-et-Vilaine",
  "country": "FR",
  "phone": "+33 2 99 00 00 00",
  "electronicInvoicingEmail": "facturation@example.fr",
  "dgfipTypeOperation": "services",
  "dgfipEnterpriseSize": "pme",
  "dgfipNafCodePrefix": "62",
  "dgfipAnnuaireOnly": true,
  "dgfipStartDate": "2026-09-01"
}

Les champs métier facultatifs acceptent null ou une chaîne vide, normalisée en null. Le SIRET doit commencer par le SIREN. Avec dgfipAnnuaireOnly=false, la taille et le préfixe NAF sont obligatoires. isActive, b2brouterAccountId, les statuts et l’erreur d’onboarding ne sont pas librement modifiables par ces routes.

La province est obligatoire avant la création du compte B2Brouter. Le téléphone reste facultatif et nullable. La taille et le préfixe NAF à deux chiffres sont à collecter dès l’onboarding ; en mode Annuaire uniquement, ils restent stockés sans être envoyés au réglage DGFiP.

Les valeurs DGFiP officielles sont services, goods, mixed et ge, eti, pme, micro. Les anciennes formes en majuscules restent temporairement acceptées, puis sont normalisées et stockées en minuscules.

curl -X POST "https://facturation.sipco.fr/api/v1/tenants" \
      -H "Authorization: Bearer <SUPERADMIN_API_KEY>" \
      -H "Content-Type: application/json" \
      -d @tenant.json

GET /api/v1/tenants/:slug

Retourne la vue administrative complète du tenant local, ses données légales, DGFiP et son état d’onboarding, sans clé ni relation sensible.

curl -X GET "https://facturation.sipco.fr/api/v1/tenants/<TENANT_SLUG>" \
-H "Authorization: Bearer <SUPERADMIN_API_KEY>"

PATCH /api/v1/tenants/:slug

Modifie partiellement les champs métier du tenant. Le slug, isActive, b2brouterAccountId et les statuts techniques restent immuables via cette route.

curl -X PATCH "https://facturation.sipco.fr/api/v1/tenants/<TENANT_SLUG>" \
-H "Authorization: Bearer <SUPERADMIN_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
  "rcsCity": "VILLE EXEMPLE",
  "rcsNumber": "123456789",
  "legalRepresentativeName": "NOM EXEMPLE",
  "legalRepresentativeTitle": "Gérant"
}'

Le PATCH fusionne les champs fournis avec l’état existant avant de contrôler SIREN/SIRET et les exigences DGFiP. Le tenant local est distinct du compte B2Brouter : l’association et l’onboarding externe utilisent les routes dédiées ci-dessous. Aucune route d’import batch n’est exposée à ce stade.

POST /api/v1/tenants/:slug/b2brouter/account

Crée ou rattache le compte parent français B2Brouter d’un tenant existant. Route protégée par SUPERADMIN_API_KEY. Le tenant doit disposer d’une raison sociale, d’un SIREN, d’un numéro de TVA français et d’une adresse complète, sans compte B2Brouter déjà associé.

Le Hub recherche d’abord un compte compatible par SIREN (cin_scheme=0002). Un résultat unique est rattaché ; sinon le compte parent est créé au niveau SIREN. L’adresse e-mail provient exclusivement de electronicInvoicingEmail, enregistrée au préalable dans le tenant.

curl -X POST "https://facturation.sipco.fr/api/v1/tenants/<TENANT_SLUG>/b2brouter/account" \
-H "Authorization: Bearer <SUPERADMIN_API_KEY>" \
-H "Content-Type: application/json" \
-d '{}'

MVP strict : aucune unité organisationnelle SIRET, aucun transport, aucun utilisateur B2Brouter, aucune activation DGFiP et aucune TenantApiKey ne sont créés. Erreurs principales : 400 validation, 401 authentification, 404 tenant absent, 409 compte lié, ambigu, incompatible ou en conflit, 502 erreur B2Brouter, 503 configuration absente ou résultat de création incertain.

POST /api/v1/tenants/:slug/b2brouter/dgfip

Crée ou retrouve le DgfipTaxReportSetting du compte déjà lié. Cette étape distincte permet de reprendre un onboarding dont le compte existe mais dont l’inscription Annuaire manque. Route protégée par SUPERADMIN_API_KEY.

curl -X POST "https://facturation.sipco.fr/api/v1/tenants/<TENANT_SLUG>/b2brouter/dgfip" \
-H "Authorization: Bearer <SUPERADMIN_API_KEY>" \
-H "Content-Type: application/json" \
-d '{}'

Le réglage est créé avec code=dgfip, enabled=true et annuaire_only=true par défaut. En mode Annuaire uniquement, la taille d’entreprise et le préfixe NAF ne sont pas obligatoires. L’e-mail, le type d’opération et la date de début viennent du tenant.

Aucun transport Peppol n’est créé manuellement. Un 422 DGFIP_DIRECTORY_ALREADY_LINKED_TO_ANOTHER_PA signale une entreprise déjà rattachée à une autre PA ; les autres erreurs 422 sont retournées sous B2BROUTER_VALIDATION_ERROR. Un compte créé ne signifie pas que l’onboarding est actif : le statut devient ACTIVE après création ou détection d’un réglage DGFiP compatible.

Emission de factures

POST /api/v1/tenants/:slug/invoices

Création d’une facture au format canonique (JSON). Statut initial : DRAFT. Un événement INVOICE_CREATED est enregistré.

Déclenche automatiquement le service sendInvoice pour vérifier la conformité des champs et déclenche l'envoie si OK.

Body JSON (exemple “prêt à envoyer”)

{
  "type": "INVOICE",
  "externalRef": "SIPCO-INV-2026-0003",
  "issueDate": "2026-02-06",
  "currency": "EUR",

  "seller": {
    "companyName": "SIPCO",
    "siret": "32409458000051",
    "vatNumber": "FR81324094580",
    "address": { "street": "12 rue des artisans", "city": "Vitré", "zipCode": "35500", "country": "FR" }
  },

  "buyer": {
    "companyName": "CLIENT TEST SARL",
    "siret": "98765432100033",
    "vatNumber": "FR98765432100",
    "email": "contact@client.fr",
    "address": { "street": "10 Avenue du Client", "city": "Paris", "zipCode": "75001", "country": "FR" }
  },

  "lines": [
    { "id": "1", "description": "Abonnement maintenance – Février 2026", "quantity": 1, "unitPriceExclTax": "45.00", "vatRate": 20 }
  ]
}
curl -X POST "https://facturation.sipco.fr/api/v1/tenants/sipco/invoices" \
  -H "Authorization: Bearer <VOTRE_CLE_API>" \
  -H "Content-Type: application/json" \
  -d @invoice.json

GET /api/v1/tenants/:slug/invoices

Liste paginée des factures d’un tenant.

Paramètres (query)

limitnumberNombre d’éléments (défaut 50, max 200)
offsetnumberDécalage de pagination
statusstringFiltrer par statut (DRAFT, SENT, ERROR…)
searchstringRecherche sur externalRef (contains)
curl -X GET "https://facturation.sipco.fr/api/v1/tenants/sipco/invoices?limit=50&offset=0" \
     -H "Authorization: Bearer <VOTRE_CLE_API>"

GET /api/v1/tenants/:slug/invoices/:id

Détail complet d’une facture (canonique + événements + fichiers).

curl -X GET "https://facturation.sipco.fr/api/v1/tenants/sipco/invoices/{invoiceId}" \
     -H "Authorization: Bearer <VOTRE_CLE_API>"

PATCH /api/v1/tenants/:slug/invoices/:id

Mise à jour d’une facture uniquement si DRAFT. Ajoute un événement INVOICE_VALIDATED.

curl -X PATCH "https://facturation.sipco.fr/api/v1/tenants/sipco/invoices/{invoiceId}" \
     -H "Authorization: Bearer <VOTRE_CLE_API>" \
     -H "Content-Type: application/json" \
     -d '{ "buyer": { "email": "contact@client.fr" } }'

POST /api/v1/tenants/:slug/invoices/:id/send

Déclenche l’envoi vers B2Brouter. L’API assure l’idempotence pour éviter un double envoi. Si la facture est incomplète, l’API bloque avant B2Brouter.

Headers optionnels

Authorization: Bearer <VOTRE_CLE_API>
        Idempotency-Key: <clé_unique_côté_client>
curl -X POST "https://facturation.sipco.fr/api/v1/tenants/sipco/invoices/{invoiceId}/send" \
     -H "Authorization: Bearer <VOTRE_CLE_API>" \
     -H "Idempotency-Key: <clé_unique>"

Si déjà envoyé : 409 INVOICE_ALREADY_SENT. Si déjà en cours : 200 avec already: true.

Champs requis

1) Création / mise à jour (DRAFT)

À la création (POST) ou modification (PATCH), la facture doit respecter le schéma canonique.

  • externalRef (référence externe)
  • issueDate (YYYY-MM-DD) et currency (ISO3, ex: EUR)
  • seller.companyName + seller.address
  • buyer.companyName + buyer.address
  • lines[] (≥ 1) avec description, quantity, unitPriceExclTax

Note : certains identifiants (SIRET/TVA/email) peuvent être absents en DRAFT, mais requis à l’envoi.

2) Pré-requis à l’envoi (SEND)

Avant /send, validation renforcée pour éviter un appel fournisseur inutile.

Buyer (destinataire)
  • buyer.email (email valide) → requis par B2Brouter
  • buyer.siret ou buyer.siren
  • buyer.vatNumber (TVA intracom)
Tenant (émetteur côté B2Brouter)
  • Tenant lié à B2Brouter : tenant.b2brouterAccountId
  • Sinon : TENANT_B2BROUTER_ACCOUNT_MISSING

En cas d’échec : 400 INVOICE_NOT_READY_TO_SEND + liste des champs manquants.

Réception des factures (Incoming)

Centralise les factures reçues depuis une PDP (ex : B2Brouter) pour un tenant donné. Les factures sont synchronisées, stockées et exposées aux applications clientes.

POST /api/v1/tenants/:slug/received-invoices/sync

Synchronise les factures reçues : liste + détail JSON + document original + ACK.

POST /api/v1/tenants/:slug/received-invoices/sync
Authorization: Bearer <VOTRE_CLE_API>

GET /api/v1/tenants/:slug/received-invoices

Liste des factures reçues (paginée). Filtres : status, search.

GET /api/v1/tenants/:slug/received-invoices?limit=50&offset=0

GET /api/v1/tenants/:slug/received-invoices/:id

Détail d’une facture reçue (inclut events).

GET /api/v1/tenants/:slug/received-invoices/:id

GET /api/v1/tenants/:slug/received-invoices/:id/original

Proxy sécurisé vers la PDP : renvoie le binaire original sans exposer la clé PDP.

GET /api/v1/tenants/:slug/received-invoices/:id/original

POST /api/v1/tenants/:slug/received-invoices/:id/lifecycle

Met à jour le cycle de vie. Aucun e-mail B2Brouter n'est envoyé par défaut ; commit: "with_mail" doit être fourni explicitement pour le demander.

POST /api/v1/tenants/:slug/received-invoices/:id/lifecycle
Content-Type: application/json

{"action":"accept"}

Notification e-mail explicite uniquement :
{"action":"accept","commit":"with_mail"}

Codes de réponse

CodeSignificationDescription
200OKRequête réussie
201CreatedRessource créée
400Bad RequestValidation / JSON invalide
401UnauthorizedClé API absente ou incorrecte
404Not FoundTenant ou facture introuvable
409ConflictConflit
502Bad GatewayErreur fournisseur (B2Brouter)
500Server ErrorErreur interne