/developpeurs · API partenaire

L'API Billies — devis et factures conformes dans ton logiciel.

Ton produit génère des devis signables en ligne et des factures légales françaises — numérotation séquentielle, TVA, mentions obligatoires, PDF, e-facturation via une plateforme de dématérialisation — sans développer un moteur de facturation. En marque grise : tes utilisateurs restent dans ton interface, les documents portent la mention « propulsé par Billies ». Ou en mode connecté : ton utilisateur branche son propre compte Billies, ton logiciel y crée et émet ses documents avec son accord.

Deux offres, la même API

Tous les endpoints, sans engagement ni frais de mise en service dans les deux cas. Seule la façon de payer change.

Intégré

99 € par mois · 10 sous-comptes actifs inclus ·puis 5 € par sous-compte actif

Le prix par sous-compte actif est dégressif, par tranche : 5 € par mois du 11ᵉ au 100ᵉ, 4 € du 101ᵉ au 500ᵉ, 3 € au-delà. Un sous-compte est « actif » un mois donné s'il a émis au moins un document ce mois-là. Un sous-compte qui n'émet rien ne coûte rien : tu peux créer un sous-compte pour chacun de tes utilisateurs sans regarder le compteur.

À l'acte

0 € par mois · 0,50 € par document émis

Dégressif par tranche, remis à zéro chaque mois : 0,50 € par document jusqu'à 999 par mois, puis 0,30 € de 1 000 à 9 999, puis 0,20 € au-delà. Pas d'abonnement : un mois sans émission est un mois à 0 €. Une carte est demandée à l'activation.

Dans les deux offres, les documents émis sur des comptes connectés (voir mode connecté) ne te sont jamais facturés — c'est l'utilisateur qui paie son propre abonnement Billies. Le détail de ta consommation est disponible à tout moment via GET /usage.

Gros volumes ou partenariat ? Une offre sur mesure est possible — écris-nous à contact@billies.fr.

Démarrage rapide

Du compte vide au devis signable : cinq étapes, cinq appels.

  1. 1.

    Active l'API dans tes réglages

    Depuis ton compte Billies, active l'offre API (99 €/mois). Ton compte devient un compte partenaire.

  2. 2.

    Crée ta clé

    Toujours dans les réglages : génère une clé bil_live_…. Elle n'est affichée qu'une fois — stocke-la dans ton gestionnaire de secrets.

  3. 3.

    Crée un sous-compte

    Un sous-compte par entreprise cliente de ton logiciel. C'est lui qui porte l'identité légale des documents.

  4. 4.

    Crée un devis

    Un client final + des lignes en centimes. Le document naît en brouillon, les totaux sont calculés pour toi.

  5. 5.

    Émets

    L'émission attribue le numéro légal et génère le PDF. Tu récupères l'URL du PDF et, si tu veux, un lien de signature en ligne.

Bout en bout, en curl
# Ta clé API — créée dans Réglages → API (affichée une seule fois)
export BILLIES_API_KEY="bil_live_…"
BASE="https://billies.fr/api/partner/v1"

# 1. Crée un sous-compte pour ton client final
curl -s -X POST "$BASE/companies" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Idempotency-Key: onboarding-martin-001" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Plomberie Martin", "siret": "73282932000074", "postalCode": "69003", "city": "Lyon" }'
# → 201 { "company": { "id": "COMPANY_ID", … } }

# 2. Ajoute le client à facturer
curl -s -X POST "$BASE/companies/COMPANY_ID/clients" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "business", "displayName": "SCI Les Tilleuls", "email": "compta@lestilleuls.example" }'
# → 201 { "client": { "id": "CLIENT_ID", … } }

# 3. Crée un devis (brouillon)
curl -s -X POST "$BASE/companies/COMPANY_ID/documents" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Idempotency-Key: devis-sdb-tilleuls-01" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "quote",
    "clientId": "CLIENT_ID",
    "title": "Rénovation salle de bain",
    "lines": [
      { "description": "Pose faïence murale", "quantity": 12, "unitPriceCents": 4500, "vatRate": 10, "unit": "m²" }
    ]
  }'
# → 201 { "document": { "id": "DOCUMENT_ID", "status": "draft", "totalTtcCents": 59400, … } }

# 4. Émets : numéro légal + PDF, le document devient immuable
curl -s -X POST "$BASE/companies/COMPANY_ID/documents/DOCUMENT_ID/issue" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Idempotency-Key: emission-devis-sdb-01"
# → 200 { "number": "D-20260709-0001", "status": "sent", "pdfUrl": "https://…" }

# 5. Lien de signature en ligne, à afficher dans TON interface
curl -s -X POST "$BASE/companies/COMPANY_ID/documents/DOCUMENT_ID/signature-link" \
  -H "Authorization: Bearer $BILLIES_API_KEY"
# → 200 { "url": "https://billies.fr/q/…" }

Les fondamentaux

Authentification

Chaque requête porte ta clé API en en-tête Authorization: Bearer bil_live_…. Tu crées tes clés dans tes réglages Billies ; la clé complète n'est affichée qu'une seule fois (on n'en stocke qu'une empreinte). Plusieurs clés peuvent être actives en parallèle, pour une rotation sans coupure : crée la nouvelle, bascule ton code, révoque l'ancienne.

curl "https://billies.fr/api/partner/v1/companies" \
  -H "Authorization: Bearer bil_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Format des erreurs

Toute erreur renvoie le même objet JSON, avec un code stable sur lequel brancher ton code — le message est là pour les humains, il peut changer.

{
  "error": {
    "code": "conflict",
    "message": "Ce document est déjà émis : il ne peut plus être modifié."
  }
}
CodeHTTPQuand
unauthorized401Clé absente, révoquée ou inconnue.
forbidden403La clé n'a pas le scope requis.
not_found404Ressource inexistante — ou rattachée à un autre partenaire.
invalid_request400Corps invalide, champ manquant, Idempotency-Key absent.
conflict409L'état du document interdit l'action (ex. modifier un document émis) — ou une requête identique (même Idempotency-Key) est encore en cours de traitement.
rate_limited429Trop de requêtes sur la fenêtre en cours — réessaie après une pause.
internal500Erreur côté Billies. Rejoue avec la même Idempotency-Key, sans risque.

Idempotence : jamais deux factures pour un retry

Un timeout réseau ne te dit pas si l'appel a abouti. Sans protection, le retry naturel de ton code créerait un deuxième document — et deux numéros légaux. C'est pourquoi l'en-tête Idempotency-Key est obligatoire (sinon 400) sur les trois POST qui créent quelque chose d'irréversible ou de numéroté : POST /companies, POST …/documents et POST …/documents/{id}/issue. Sur POST …/payments et POST …/credit-note, l'en-tête est facultatif — mais recommandé.

Choisis une clé qui identifie l'opération côté chez toi (ex. facture-cmd-8842) : rejouer la même requête avec la même clé renvoie la réponse d'origine, sans rien ré-exécuter. Deux requêtes simultanées avec la même clé ne s'exécutent jamais deux fois : la seconde reçoit un 409 conflict tant que la première est en vol — réessaie quelques secondes plus tard pour obtenir la réponse stockée. Seules les réponses définitives sont stockées : après un 500, la clé reste rejouable.

Montants en centimes

Tous les montants sont des entiers en centimes d'euro : "unitPriceCents": 4500= 45,00 €. Jamais de flottants, donc jamais d'erreur d'arrondi. Les taux de TVA sont des pourcentages (20, 10, 5.5, 2.1, 0) et les totaux sont toujours calculés côté Billies.

Mode connecté : ton utilisateur garde son compte Billies

La marque grise (sous-comptes) convient quand tes utilisateurs n'ont pas de compte Billies : tu portes tout. Le mode connecté couvre l'autre cas : ton utilisateur a — ou prend — son propre compte Billies, et il autorise ton logiciel à créer et émettre des documents dessus. Le cas type : un back-office de vente qui délègue devis, factures et e-facturation à Billies.

Le flux, en cinq temps

  1. 1.

    « Connecter Billies », chez toi

    Tu génères un state opaque (un nonce, stocké côté serveur), puis tu rediriges l'utilisateur vers https://billies.fr/connect/<ton-slug> avec redirect_uri et state en paramètres.

  2. 2.

    Il se connecte — ou s'inscrit

    Écran Billies co-brandé à tes couleurs (ton nom, ton logo). S'il n'a pas encore de compte Billies, il en crée un au passage : c'est le sien, pas un sous-compte à toi.

  3. 3.

    Il autorise

    L'écran de consentement liste précisément ce que ton logiciel pourra faire sur son compte — et ce qu'il ne pourra pas. S'il refuse, il revient chez toi avec ?error=access_denied.

  4. 4.

    Retour chez toi

    Billies le renvoie sur ton redirect_uri avec ?state=…&billies_company_id=…. Ces paramètres ne sont que de l'UX : ne t'y fie pas pour ouvrir un accès.

  5. 5.

    Ton serveur vérifie

    GET /api/partner/v1/links?state=… est la source de vérité : il renvoie le companyId et les scopes consentis. Stocke le companyId — tous tes appels /companies/{companyId}/… passent ensuite par lui.

Le flux, bout en bout
# 1. Chez toi : bouton « Connecter Billies » → redirection de l'utilisateur
https://billies.fr/connect/ton-slug?redirect_uri=https://ton-app.example/billies/callback&state=n-4f7a2b9c

# 2-3. Chez Billies : il se connecte (ou crée son compte), puis autorise
#      — écrans co-brandés à tes couleurs.

# 4. Retour chez toi :
#    autorisé → https://ton-app.example/billies/callback?state=n-4f7a2b9c&billies_company_id=1f6f9c2e-…
#    refusé   → https://ton-app.example/billies/callback?state=n-4f7a2b9c&error=access_denied

# 5. Côté SERVEUR, vérifie avec ton state — c'est la source de vérité :
curl "https://billies.fr/api/partner/v1/links?state=n-4f7a2b9c" \
  -H "Authorization: Bearer $BILLIES_API_KEY"
# → 200 { "link": { "companyId": "1f6f9c2e-…", "scopes": […], "revokedAt": null } }

Le redirect_uridoit figurer dans la liste d'URLs de retour déclarée pour ton compte partenaire (protection contre la redirection ouverte) — sinon le flux répond 400avant même l'écran de connexion. Pour déclarer tes URLs : écris à contact@billies.fr.

Ce que ça change

  • Il paie son abonnement Billies, rien pour toi.Les documents émis sur un compte connecté ne comptent ni dans tes sous-comptes actifs, ni dans tes documents à l'acte — ils ne te sont jamais facturés.
  • Il gère lui-même sa connexion e-facturationdans ses réglages Billies (plateforme de dématérialisation, Factur-X). Tu n'as rien à configurer : le même POST …/issue suit sa configuration.
  • Ton accès est limité à ce qu'il a consenti : documents, clients, paiements — jamais ses réglages (PATCH /companies/{id} répond 403) ni son IBAN. Et il peut révoquer l'accès à tout moment, depuis ses réglages.
  • Les documents vivent aussi chez lui.Un devis créé via ton logiciel apparaît dans son app Billies, web et mobile — il le retrouve, le suit, l'archive comme les autres.
  • L'habillage suit son plan Billies: sur un compte gratuit, les documents gardent la mention « Fait avec billies.fr », comme ceux qu'il crée lui-même.

Une fois le companyIdrécupéré, tout le reste de l'API fonctionne à l'identique. Les deux endpoints du mode connecté sont détaillés dans la référence.

Référence des endpoints

Base : https://billies.fr/api/partner/v1. Tous les endpoints, famille par famille : sous-comptes, clients finaux, documents, émission, mode connecté, e-reporting, factures reçues, consommation.

Sous-comptes

Un sous-compte représente une entreprise cliente de ton logiciel : son identité légale, ses coordonnées bancaires, son habillage PDF. Chaque devis et chaque facture appartient à un sous-compte.

Créer un sous-compte

POST /api/partner/v1/companies

Scope companies:write · en-tête Idempotency-Key obligatoire

Crée l'entreprise cliente finale. Seul name est requis : tu peux compléter le SIRET, l'adresse, le régime de TVA ou les infos RCS plus tard via PATCH, avant la première émission. vatRegime vaut franchise_base (défaut), reel_simplifie ou reel_normal ; legalForm vaut auto_entrepreneur, ei, eurl, sarl, sas ou sasu. logoUrl (URL https publique) s'affiche en tête des PDF de facture. vatNumber est optionnel : à défaut, il est dérivé automatiquement du SIREN dès que le sous-compte est assujetti (régime ≠ franchise en base). Pour une société, tu peux poser rcsNumber, rcsCity et shareCapitalCents (capital en centimes) dès la création.

Corps de la requête
{
  "name": "Plomberie Martin",
  "logoUrl": "https://cdn.monlogiciel.fr/logos/plomberie-martin.png",
  "siret": "73282932000074",
  "legalForm": "ei",
  "addressLine1": "12 rue des Ateliers",
  "postalCode": "69003",
  "city": "Lyon",
  "country": "FR",
  "vatRegime": "reel_normal"
}
Réponse — 201
{
  "company": {
    "id": "1f6f9c2e-8d1a-4b6e-9f3d-2a7c5e8b1d40",
    "name": "Plomberie Martin",
    "logoUrl": "https://cdn.monlogiciel.fr/logos/plomberie-martin.png",
    "legalForm": "ei",
    "siret": "73282932000074",
    "vatNumber": "FR44732829320",
    "vatRegime": "reel_normal",
    "addressLine1": "12 rue des Ateliers",
    "addressLine2": null,
    "postalCode": "69003",
    "city": "Lyon",
    "country": "FR",
    "ibanLast4": null,
    "einvoiceEnabled": false,
    "createdAt": "2026-07-01T09:12:00.000Z"
  }
}
Exemple
curl -X POST "https://billies.fr/api/partner/v1/companies" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Idempotency-Key: onboarding-martin-001" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Plomberie Martin", "siret": "73282932000074", "vatRegime": "reel_normal", "postalCode": "69003", "city": "Lyon" }'
  • Un sous-compte ne coûte rien tant qu'il n'émet pas de document dans le mois.
  • logoUrl doit être une URL http(s):// publique (500 caractères max), rendue telle quelle en tête des PDF émis — héberge le fichier chez toi et passe le lien. Les URL data: ou javascript: sont refusées.
  • vatNumber est facultatif : fourni, il est normalisé (FR + clé + SIREN) ; omis, il est dérivé automatiquement du SIREN dès que le sous-compte est assujetti (reel_simplifie / reel_normal). En franchise_base aucun numéro n'est posé (art. 293 B du CGI).
  • Piège à connaître : sans vatRegime, le sous-compte naît en franchise_base (TVA non applicable, art. 293 B du CGI) — ses documents sortent sans TVA, quel que soit le vatRate des lignes. Pour une entreprise qui facture la TVA, envoie reel_normal ou reel_simplifie.
  • Sociétés (sarl, sas, sasu, eurl) : renseigne rcsNumber, rcsCity et shareCapitalCents (capital social en centimes) avant la première émission — sinon l'émission renvoie une erreur de conformité (art. R.123-237 Code commerce).
  • Idempotence : seule une réponse de succès (2xx) est mémorisée puis rejouée à l'identique. Une erreur (ex. 400 SIRET invalide) n'est PAS mémorisée — corrige la donnée et rejoue la MÊME Idempotency-Key, la création aboutit.

Lister tes sous-comptes

GET /api/partner/v1/companies

Scope companies:read

Liste paginée de tous les sous-comptes rattachés à ta clé. Paramètres : limit (défaut 50) et offset.

Réponse — 200
{
  "companies": [
    {
      "id": "1f6f9c2e-8d1a-4b6e-9f3d-2a7c5e8b1d40",
      "name": "Plomberie Martin",
      "siret": "73282932000074",
      "city": "Lyon",
      "createdAt": "2026-07-01T09:12:00.000Z"
    }
  ],
  "total": 1
}
Exemple
curl "https://billies.fr/api/partner/v1/companies?limit=20&offset=0" \
  -H "Authorization: Bearer $BILLIES_API_KEY"

Détail d'un sous-compte

GET /api/partner/v1/companies/{companyId}

Scope companies:read

Renvoie la fiche complète du sous-compte. 404 si l'identifiant n'existe pas ou n'appartient pas à ta clé.

Réponse — 200
{
  "company": {
    "id": "1f6f9c2e-8d1a-4b6e-9f3d-2a7c5e8b1d40",
    "name": "Plomberie Martin",
    "logoUrl": "https://cdn.monlogiciel.fr/logos/plomberie-martin.png",
    "legalForm": "ei",
    "siret": "73282932000074",
    "vatNumber": "FR44732829320",
    "vatRegime": "reel_normal",
    "addressLine1": "12 rue des Ateliers",
    "postalCode": "69003",
    "city": "Lyon",
    "country": "FR",
    "ibanLast4": "0189",
    "einvoiceEnabled": false,
    "createdAt": "2026-07-01T09:12:00.000Z"
  }
}
Exemple
curl "https://billies.fr/api/partner/v1/companies/1f6f9c2e-8d1a-4b6e-9f3d-2a7c5e8b1d40" \
  -H "Authorization: Bearer $BILLIES_API_KEY"

Modifier un sous-compte

PATCH /api/partner/v1/companies/{companyId}

Scope companies:write

Mise à jour partielle : n'envoie que les champs à changer. Éditables : name, logoUrl, siret (une seule fois), vatNumber, vatRegime, legalForm, rcsCity, rcsNumber, shareCapitalCents, addressLine1, addressLine2, postalCode, city, country, iban, bic, bankName, pdfTemplateKey, pdfBrandColor, pdfFooterText, legalMentionsCustom, einvoiceEnabled, einvoiceProfile (en16931 | extended), ereportingEnabled, hasVatOnDebits, vatFrequency (monthly | quarterly | null).

Corps de la requête
{
  "logoUrl": "https://cdn.monlogiciel.fr/logos/plomberie-martin.png",
  "vatRegime": "reel_normal",
  "iban": "FR7630006000011234567890189",
  "bic": "AGRIFRPP",
  "bankName": "Crédit Agricole",
  "pdfBrandColor": "#1d3a6b",
  "pdfFooterText": "Merci pour votre confiance."
}
Réponse — 200
{
  "company": {
    "id": "1f6f9c2e-8d1a-4b6e-9f3d-2a7c5e8b1d40",
    "name": "Plomberie Martin",
    "vatRegime": "reel_normal",
    "ibanLast4": "0189",
    "bankName": "Crédit Agricole",
    "pdfBrandColor": "#1d3a6b",
    "pdfFooterText": "Merci pour votre confiance."
  }
}
Exemple
curl -X PATCH "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "vatRegime": "reel_normal", "pdfBrandColor": "#1d3a6b" }'
  • L'IBAN est chiffré au repos : il n'est jamais renvoyé en clair par l'API (ibanLast4 seulement).
  • Le SIRET se pose une seule fois : le changer ensuite renvoie 409 (verrou de numérotation légale).
  • Changer vatRegime n'affecte que les documents émis après le changement — les documents émis sont immuables.
  • Sociétés (sarl, sas, sasu, eurl) : renseigne rcsNumber, rcsCity et shareCapitalCents (capital social en centimes) avant la première émission — sinon l'émission renvoie une erreur de conformité (art. R.123-237 Code commerce).
  • logoUrl, pdfTemplateKey, pdfBrandColor et pdfFooterText pilotent l'habillage des PDF émis — c'est là que se joue la marque grise. Le logo se met à jour autant de fois que voulu (contrairement au SIRET) ; sans logo, le PDF affiche le nom de la société en texte. Attention : pdfBrandColor n'est appliquée que par les templates bold et elegant. bw, classic et modern ont une palette figée et l'ignorent. pdfBrandColor à null = chaque template reprend sa couleur d'origine.
  • vatNumber : le poser explicitement écrase la valeur courante ; le laisser vide déclenche, sur un sous-compte assujetti, une dérivation automatique depuis le SIREN — et cette dérivation ne s'applique qu'une fois (elle n'écrase jamais un numéro déjà présent).
  • Facturation électronique : einvoiceEnabled active le Factur-X sur les factures B2B, ereportingEnabled la déclaration e-reporting B2C. hasVatOnDebits et vatFrequency décrivent le régime de TVA — un changement est répercuté à la plateforme de dématérialisation (best-effort). L'objet company renvoie aussi pdpConnected et pdpMode (delegated | platform | none).

Lire la numérotation

GET /api/partner/v1/companies/{companyId}/numbering

Scope companies:read

Renvoie les préfixes de numérotation (devis / facture / avoir) et l'état des compteurs de séquence (par type et par année).

Réponse — 200
{
  "numbering": {
    "quotePrefix": "D",
    "invoicePrefix": "F",
    "creditNotePrefix": "AV",
    "sequences": [
      { "type": "invoice", "year": 2026, "lastNumber": 42 },
      { "type": "credit_note", "year": 2026, "lastNumber": 3 }
    ]
  }
}
Exemple
curl "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/numbering" \
  -H "Authorization: Bearer $BILLIES_API_KEY"

Configurer la numérotation

PUT /api/partner/v1/companies/{companyId}/numbering

Scope companies:write

Utilise le verbe PUT. Configure les préfixes et, surtout, AMORCE les compteurs pour continuer une série existante quand tu migres un client depuis un autre logiciel. Préfixes : 1 à 10 caractères A-Z, chiffres et tiret. seedCounters pose le dernier numéro atteint pour une année donnée : la prochaine facture repart à la valeur + 1.

Corps de la requête
{
  "invoicePrefix": "F",
  "creditNotePrefix": "AV",
  "seedCounters": { "invoice": 42, "creditNote": 3, "year": 2026 }
}
Réponse — 200
{
  "numbering": {
    "quotePrefix": "D",
    "invoicePrefix": "F",
    "creditNotePrefix": "AV",
    "sequences": [
      { "type": "invoice", "year": 2026, "lastNumber": 42 },
      { "type": "credit_note", "year": 2026, "lastNumber": 3 }
    ]
  }
}
Exemple
curl -X PUT "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/numbering" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "invoicePrefix": "F", "creditNotePrefix": "AV", "seedCounters": { "invoice": 42, "year": 2026 } }'
  • Amorcer un compteur bascule le sous-compte en numérotation annuelle (F-2026-0043) pour que l'année serve bien de clé de série.
  • Un compteur peut AVANCER (re-semer plus haut est toujours accepté), jamais RECULER : amorcer sous un numéro déjà émis renvoie 409, avec le dernier numéro émis dans le message.
  • Chaque valeur de seedCounters est le DERNIER numéro atteint : pose 42 pour que la prochaine facture soit la 43.

Clients finaux

Le carnet d'adresses d'un sous-compte : les particuliers et entreprises que ton utilisateur facture. Un document référence toujours un client de ce carnet.

Créer un client

POST /api/partner/v1/companies/{companyId}/clients

Scope clients:write

Seul displayName est requis. type vaut individual (particulier) ou business (professionnel). Pour un professionnel, renseigne siret et legalName : ils apparaissent sur les factures et servent à la e-facturation.

Corps de la requête
{
  "type": "business",
  "displayName": "SCI Les Tilleuls",
  "legalName": "SCI Les Tilleuls",
  "siret": "90123456700013",
  "email": "compta@lestilleuls.example",
  "phone": "0612345678",
  "addressLine1": "8 avenue des Platanes",
  "postalCode": "69006",
  "city": "Lyon"
}
Réponse — 201
{
  "client": {
    "id": "b8e64c1d-5a3f-4e2b-9c7d-1f0a2b3c4d5e",
    "type": "business",
    "displayName": "SCI Les Tilleuls",
    "legalName": "SCI Les Tilleuls",
    "siret": "90123456700013",
    "email": "compta@lestilleuls.example",
    "addressLine1": "8 avenue des Platanes",
    "postalCode": "69006",
    "city": "Lyon",
    "country": "FR"
  }
}
Exemple
curl -X POST "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/clients" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "business", "displayName": "SCI Les Tilleuls", "email": "compta@lestilleuls.example" }'

Lister les clients

GET /api/partner/v1/companies/{companyId}/clients

Scope clients:read

Liste paginée du carnet du sous-compte. q filtre sur le nom (recherche plein texte simple), limit et offset paginent.

Réponse — 200
{
  "clients": [
    {
      "id": "b8e64c1d-5a3f-4e2b-9c7d-1f0a2b3c4d5e",
      "type": "business",
      "displayName": "SCI Les Tilleuls",
      "email": "compta@lestilleuls.example",
      "city": "Lyon"
    }
  ],
  "total": 1
}
Exemple
curl "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/clients?q=tilleuls" \
  -H "Authorization: Bearer $BILLIES_API_KEY"

Détail d'un client

GET /api/partner/v1/companies/{companyId}/clients/{clientId}

Scope clients:read

Renvoie la fiche complète du client.

Réponse — 200
{
  "client": {
    "id": "b8e64c1d-5a3f-4e2b-9c7d-1f0a2b3c4d5e",
    "type": "business",
    "displayName": "SCI Les Tilleuls",
    "legalName": "SCI Les Tilleuls",
    "siret": "90123456700013",
    "email": "compta@lestilleuls.example",
    "phone": "0612345678",
    "addressLine1": "8 avenue des Platanes",
    "postalCode": "69006",
    "city": "Lyon",
    "country": "FR"
  }
}
Exemple
curl "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/clients/b8e64c1d-…" \
  -H "Authorization: Bearer $BILLIES_API_KEY"

Modifier un client

PATCH /api/partner/v1/companies/{companyId}/clients/{clientId}

Scope clients:write

Mise à jour partielle, mêmes champs qu'à la création. Les documents déjà émis ne sont pas retouchés : ils gardent les coordonnées du client au moment de l'émission.

Corps de la requête
{
  "email": "facturation@lestilleuls.example",
  "addressLine1": "10 avenue des Platanes"
}
Réponse — 200
{
  "client": {
    "id": "b8e64c1d-5a3f-4e2b-9c7d-1f0a2b3c4d5e",
    "displayName": "SCI Les Tilleuls",
    "email": "facturation@lestilleuls.example",
    "addressLine1": "10 avenue des Platanes"
  }
}
Exemple
curl -X PATCH "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/clients/b8e64c1d-…" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "facturation@lestilleuls.example" }'

Documents — brouillons

Un document naît toujours en brouillon (status draft) : sans numéro, modifiable, supprimable. Les totaux (HT, TVA, TTC) sont recalculés côté Billies à partir des lignes — tu n'envoies jamais de total.

Créer un devis ou une facture (brouillon)

POST /api/partner/v1/companies/{companyId}/documents

Scope documents:write · en-tête Idempotency-Key obligatoire

type vaut quote (devis) ou invoice (facture). Chaque ligne porte sa quantité, son prix unitaire HT en centimes et son taux de TVA (en pourcentage : 20, 10, 5.5, 2.1 ou 0). Le title est optionnel : il n'existe pas de champ titre sur le document — il est converti en ligne d'en-tête (kind section) en position 0, et c'est là qu'il ressort dans la réponse (pas de champ title). La réponse contient le document complet avec les totaux calculés.

Corps de la requête
{
  "type": "quote",
  "clientId": "b8e64c1d-5a3f-4e2b-9c7d-1f0a2b3c4d5e",
  "title": "Rénovation salle de bain",
  "notes": "Acompte de 30 % à la signature.",
  "attachCgv": true,
  "lines": [
    {
      "description": "Pose faïence murale",
      "quantity": 12,
      "unitPriceCents": 4500,
      "vatRate": 10,
      "unit": "m²"
    },
    {
      "description": "Remplacement mitigeur thermostatique",
      "quantity": 1,
      "unitPriceCents": 18900,
      "vatRate": 10,
      "unit": "forfait"
    }
  ]
}
Réponse — 201
{
  "document": {
    "id": "7c9e4b2a-6d5f-4a1b-8e3c-9f0d1a2b3c4d",
    "type": "quote",
    "status": "draft",
    "number": null,
    "clientId": "b8e64c1d-5a3f-4e2b-9c7d-1f0a2b3c4d5e",
    "issuedAt": "2026-07-09",
    "dueAt": null,
    "expiresAt": "2026-08-08",
    "totalHtCents": 72900,
    "totalVatCents": 7290,
    "totalTtcCents": 80190,
    "globalDiscountPct": null,
    "creditNoteOfId": null,
    "createdAt": "2026-07-09T10:04:00.000Z",
    "updatedAt": "2026-07-09T10:04:00.000Z",
    "notes": "Acompte de 30 % à la signature.",
    "paymentTermsText": "Paiement sous 30 jours",
    "pdpStatus": null,
    "attachCgv": true,
    "lines": [
      { "id": "1a2b3c4d-…", "position": 0, "kind": "section", "description": "Rénovation salle de bain", "quantity": 0, "unit": "u", "unitPriceCents": 0, "vatRate": 0, "discountPct": 0, "totalHtCents": 0, "totalVatCents": 0, "totalTtcCents": 0 },
      { "id": "2b3c4d5e-…", "position": 1, "kind": "item", "description": "Pose faïence murale", "quantity": 12, "unit": "m²", "unitPriceCents": 4500, "vatRate": 10, "discountPct": 0, "totalHtCents": 54000, "totalVatCents": 5400, "totalTtcCents": 59400 },
      { "id": "3c4d5e6f-…", "position": 2, "kind": "item", "description": "Remplacement mitigeur thermostatique", "quantity": 1, "unit": "forfait", "unitPriceCents": 18900, "vatRate": 10, "discountPct": 0, "totalHtCents": 18900, "totalVatCents": 1890, "totalTtcCents": 20790 }
    ],
    "pdfUrl": null
  }
}
Exemple
curl -X POST "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/documents" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Idempotency-Key: devis-sdb-tilleuls-01" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "quote",
    "clientId": "b8e64c1d-5a3f-4e2b-9c7d-1f0a2b3c4d5e",
    "lines": [
      { "description": "Pose faïence murale", "quantity": 12, "unitPriceCents": 4500, "vatRate": 10, "unit": "m²" }
    ]
  }'
  • Remise globale : globalDiscountPct, un pourcentage entre 0 et 100. C'est le seul format accepté — pas de remise en montant. Chaque ligne accepte aussi son propre discountPct (0-100).
  • title n'est PAS stocké comme champ du document : il devient une ligne kind section en position 0. La réponse ne contient donc pas de champ title — il ressort dans lines.
  • Chaque ligne accepte un kind optionnel : item (défaut, chiffrée), section (en-tête) ou comment (note) — les lignes section et comment n'ont aucun montant. Au moins une ligne item est requise.
  • Mode TTC « en dedans » (facturation B2C) : une ligne item peut porter totalTtcCents (montant TTC exact en centimes, entier ≠ 0) + vatRate au lieu de quantity/unitPriceCents — la TVA est alors extraite du TTC, si bien que le TTC facturé est au centime égal à ce que tu encaisses. Un totalTtcCents négatif est accepté pour déduire un acompte déjà facturé, tant que le total du document reste positif et comporte au moins une ligne positive. La ligne renvoie enteredTtcCents (null pour une ligne HT classique).
  • Conditions générales de vente : attachCgv est un booléen optionnel qui décide si les CGV de l'entreprise sont annexées en pages supplémentaires du PDF. Omis (ou null), le défaut dépend du type — un devis part AVEC les CGV, une facture et un avoir SANS. Envoie true pour joindre les CGV à une facture, false pour les retirer d'un devis. Le champ ressort tel quel (attachCgv, null quand tu n'as rien tranché) dans la réponse de GET /documents/{documentId}. Les mentions légales obligatoires (pénalités de retard, indemnité de 40 €, TVA) ne dépendent PAS de ce flag : elles sont générées à part et figurent toujours sur le document.
  • Un PDF de CGV importé par l'entreprise (au lieu d'un texte) n'est annexé qu'aux documents adressés à un client professionnel (client.type = business).
  • Le brouillon ne consomme pas de numéro et ne compte pas dans ta facturation : seul un document émis rend le sous-compte actif.

Lister les documents

GET /api/partner/v1/companies/{companyId}/documents

Scope documents:read

Liste paginée des documents du sous-compte. Filtres : type (quote, invoice ou credit_note), status (draft, sent, accepted, rejected, expired, partially_paid, paid, late ou cancelled), limit (1-100, défaut 50), offset.

Réponse — 200
{
  "documents": [
    {
      "id": "7c9e4b2a-6d5f-4a1b-8e3c-9f0d1a2b3c4d",
      "type": "quote",
      "status": "sent",
      "number": "D-20260709-0001",
      "clientId": "b8e64c1d-5a3f-4e2b-9c7d-1f0a2b3c4d5e",
      "issuedAt": "2026-07-09",
      "dueAt": null,
      "expiresAt": "2026-08-08",
      "totalHtCents": 72900,
      "totalVatCents": 7290,
      "totalTtcCents": 80190,
      "globalDiscountPct": null,
      "creditNoteOfId": null,
      "createdAt": "2026-07-09T10:04:00.000Z",
      "updatedAt": "2026-07-09T10:12:00.000Z"
    }
  ],
  "total": 1
}
Exemple
curl "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/documents?type=quote&status=sent" \
  -H "Authorization: Bearer $BILLIES_API_KEY"
  • Il n'existe pas de statut signed : un devis signé passe en accepted.
  • Le format du numéro suit les réglages de numérotation du sous-compte (par défaut : préfixe D pour les devis, F pour les factures, A pour les avoirs, date du jour puis compteur — ex. D-20260709-0001). number vaut null tant que le document est en brouillon.

Détail d'un document

GET /api/partner/v1/companies/{companyId}/documents/{documentId}

Scope documents:read

Document complet : lignes, totaux, statut, numéro. Si le document est émis, pdfUrl contient une URL signée temporaire vers le PDF (sinon null).

Réponse — 200
{
  "document": {
    "id": "7c9e4b2a-6d5f-4a1b-8e3c-9f0d1a2b3c4d",
    "type": "quote",
    "status": "sent",
    "number": "D-20260709-0001",
    "clientId": "b8e64c1d-5a3f-4e2b-9c7d-1f0a2b3c4d5e",
    "issuedAt": "2026-07-09",
    "dueAt": null,
    "expiresAt": "2026-08-08",
    "totalHtCents": 72900,
    "totalVatCents": 7290,
    "totalTtcCents": 80190,
    "globalDiscountPct": null,
    "creditNoteOfId": null,
    "createdAt": "2026-07-09T10:04:00.000Z",
    "updatedAt": "2026-07-09T10:12:00.000Z",
    "notes": "Acompte de 30 % à la signature.",
    "paymentTermsText": "Paiement sous 30 jours",
    "pdpStatus": null,
    "attachCgv": true,
    "lines": [
      { "id": "1a2b3c4d-…", "position": 0, "kind": "section", "description": "Rénovation salle de bain", "quantity": 0, "unit": "u", "unitPriceCents": 0, "vatRate": 0, "discountPct": 0, "totalHtCents": 0, "totalVatCents": 0, "totalTtcCents": 0 },
      { "id": "2b3c4d5e-…", "position": 1, "kind": "item", "description": "Pose faïence murale", "quantity": 12, "unit": "m²", "unitPriceCents": 4500, "vatRate": 10, "discountPct": 0, "totalHtCents": 54000, "totalVatCents": 5400, "totalTtcCents": 59400 },
      { "id": "3c4d5e6f-…", "position": 2, "kind": "item", "description": "Remplacement mitigeur thermostatique", "quantity": 1, "unit": "forfait", "unitPriceCents": 18900, "vatRate": 10, "discountPct": 0, "totalHtCents": 18900, "totalVatCents": 1890, "totalTtcCents": 20790 }
    ],
    "pdfUrl": "https://…/D-20260709-0001.pdf?token=…"
  }
}
Exemple
curl "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/documents/7c9e4b2a-…" \
  -H "Authorization: Bearer $BILLIES_API_KEY"

Supprimer un brouillon

DELETE /api/partner/v1/companies/{companyId}/documents/{documentId}

Scope documents:write

Supprime un document en brouillon. Un document émis n'est jamais supprimable (409 conflict) : c'est une exigence légale française — passe par un avoir.

Réponse — 204 (brouillon supprimé)
HTTP/1.1 204 No Content
Exemple
curl -X DELETE "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/documents/7c9e4b2a-…" \
  -H "Authorization: Bearer $BILLIES_API_KEY"

Documents — émission et suites

L'émission est le point de bascule : le document reçoit son numéro légal séquentiel, son PDF est généré, et il devient immuable. Tout ce qui suit — signature, paiement, avoir — s'applique à un document émis.

Émettre un document

POST /api/partner/v1/companies/{companyId}/documents/{documentId}/issue

Scope documents:issue · en-tête Idempotency-Key obligatoire

Attribue le numéro légal, génère le PDF et passe le document en sent. Aucun email n'est envoyé : c'est ton logiciel qui présente le document à l'utilisateur final. C'est cet appel qui rend le sous-compte actif pour le mois en cours.

Réponse — 200
{
  "id": "7c9e4b2a-6d5f-4a1b-8e3c-9f0d1a2b3c4d",
  "number": "DEV-2026-0042",
  "status": "sent",
  "pdfUrl": "https://billies.fr/…/DEV-2026-0042.pdf?token=…"
}
Exemple
curl -X POST "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/documents/7c9e4b2a-…/issue" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Idempotency-Key: emission-devis-sdb-01"
  • Rejouer l'appel avec la même Idempotency-Key renvoie la même réponse : jamais deux numéros pour un retry réseau. Seuls les succès (2xx) sont mémorisés — après une erreur, corrige et rejoue la même clé.
  • Si la e-facturation est activée sur le sous-compte, la facture part aussi via la plateforme de dématérialisation raccordée.

Récupérer le PDF

GET /api/partner/v1/companies/{companyId}/documents/{documentId}/pdf

Scope documents:read

Renvoie une URL signée valable environ 1 heure vers le PDF du document émis. 409 si le document est encore en brouillon. Regénère une URL à chaque besoin plutôt que de la stocker.

Réponse — 200
{
  "url": "https://billies.fr/…/DEV-2026-0042.pdf?token=…"
}
Exemple
curl "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/documents/7c9e4b2a-…/pdf" \
  -H "Authorization: Bearer $BILLIES_API_KEY"

Créer un lien de signature

POST /api/partner/v1/companies/{companyId}/documents/{documentId}/signature-link

Scope documents:write

Pour un devis émis, génère une page publique de signature en ligne (lecture du devis, acceptation, signature tracée avec horodatage). Tu présentes ce lien dans ton interface ou tu l'envoies toi-même au client final. 409 si le document n'est pas un devis émis.

Réponse — 200
{
  "url": "https://billies.fr/q/9f8e7d6c5b4a3f2e1d0c"
}
Exemple
curl -X POST "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/documents/7c9e4b2a-…/signature-link" \
  -H "Authorization: Bearer $BILLIES_API_KEY"
  • Client avec co-titulaires (coHolders) : la réponse contient en plus un tableau signatories [{ name, url }] — un lien nominatif PAR signataire, et le devis ne passe en accepted qu'à la dernière signature. Distribue chaque lien à la bonne personne ; url reste celui du titulaire principal. Champ absent pour un client mono-titulaire.

Enregistrer un paiement

POST /api/partner/v1/companies/{companyId}/documents/{documentId}/payments

Scope payments:write

Enregistre un règlement reçu sur une facture émise. Quand le cumul des paiements atteint le TTC, la facture passe en paid. Les paiements partiels sont acceptés. method accepte transfer, check, card ou other — cash est refusé (Billies est un logiciel de facturation, il n'enregistre pas d'encaissements en espèces).

Corps de la requête
{
  "amountCents": 80190,
  "method": "transfer",
  "paidAt": "2026-07-15",
  "reference": "VIR-2026-4821"
}
Réponse — 200
{
  "document": {
    "id": "3a2b1c0d-9e8f-4a5b-8c7d-6e5f4a3b2c1d",
    "type": "invoice",
    "status": "paid",
    "number": "FAC-2026-0117",
    "totalTtcCents": 80190
  }
}
Exemple
curl -X POST "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/documents/3a2b1c0d-…/payments" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amountCents": 80190, "method": "transfer", "paidAt": "2026-07-15" }'

Créer un avoir

POST /api/partner/v1/companies/{companyId}/documents/{documentId}/credit-note

Scope documents:write

Le seul verbe correctif sur une facture émise : l'avoir annule comptablement la facture d'origine, avec son propre numéro légal et des montants négatifs (il crédite ce que la facture avait débité). Sans corps, il crédite la totalité. Avec un corps { amountCents } (> 0), il crée un avoir PARTIEL : le montant TTC est ventilé au prorata des taux de TVA de la facture, plafonné à ce qu'il reste à créditer. L'avoir est créé et émis en un seul appel.

Corps de la requête
{
  "amountCents": 40000
}
Réponse — 201
{
  "creditNote": {
    "id": "e4d3c2b1-a0f9-4e8d-9c7b-6a5f4e3d2c1b",
    "type": "credit_note",
    "status": "sent",
    "number": "AV-2026-0007",
    "creditNoteOfId": "3a2b1c0d-9e8f-4a5b-8c7d-6e5f4a3b2c1d",
    "totalTtcCents": -40000,
    "pdfUrl": "https://billies.fr/…/AV-2026-0007.pdf?token=…"
  }
}
Exemple
curl -X POST "https://billies.fr/api/partner/v1/companies/1f6f9c2e-…/documents/3a2b1c0d-…/credit-note" \
  -H "Authorization: Bearer $BILLIES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amountCents": 40000 }'
  • amountCents absent → avoir total (montants exactement opposés à la facture). amountCents présent → avoir partiel : une ligne par taux de TVA, TVA « en dedans ».
  • Plafond : la somme des avoirs d'une facture ne peut pas dépasser son TTC. Un amountCents qui dépasse le restant créditable renvoie 409.
  • Idempotency-Key optionnelle mais recommandée : elle évite un double avoir sur un retry réseau.

Mode connecté

Les connexions accordées par des utilisateurs Billies à ton logiciel via le flux /connect (voir la section Mode connecté plus haut). Chaque lien donne accès à une seule company — celle de l'utilisateur — avec les scopes qu'il a consentis. Une fois le companyId récupéré, tous les endpoints /companies/{companyId}/… fonctionnent à l'identique.

E-reporting (ventes aux particuliers)

Journal des données de transaction et de paiement B2C transmises à l'administration (réforme e-facturation, obligatoire TPE/PME au 1ᵉʳ septembre 2027). La mise en file est automatique dès l'émission ou l'encaissement d'une facture à un particulier — aucun appel à faire de ton côté. Inclus, sans surcoût par déclaration.

Journal e-reporting d'un sous-compte

GET /api/partner/v1/companies/{companyId}/ereporting

Scope documents:read

Lecture seule : l'état d'activation et les déclarations du sous-compte, avec leur statut de transmission (pending → sending → sent, ou error). Une entrée transaction est créée à l'émission d'une facture ou d'un avoir à un particulier français (montants HT/TVA agrégés par taux — jamais le nom du client) ; une entrée payment à chaque encaissement (TTC ventilé). La transmission part toutes les heures, sous l'identité fiscale du sous-compte (jeton délégué). Filtres : status (pending, sending, sent, error) et kind (transaction, payment) ; pagination limit (≤ 100) et offset.

Réponse — 200
{
  "mode": "live",
  "ereportingEnabled": true,
  "pdpConnected": true,
  "pdpMode": "platform",
  "entries": [
    {
      "id": "a1b2c3d4-…",
      "documentId": "d4c3b2a1-…",
      "documentNumber": "F-20260710-0012",
      "kind": "transaction",
      "status": "sent",
      "providerId": "1593",
      "errorMessage": null,
      "date": "2026-07-10",
      "categoryCode": "TPS1",
      "amounts": { "htCents": 90000, "vatCents": 18000, "ttcCents": 108000 },
      "createdAt": "2026-07-10T14:02:11.000Z",
      "sentAt": "2026-07-10T14:30:04.000Z"
    },
    {
      "id": "e5f6a7b8-…",
      "documentId": "d4c3b2a1-…",
      "documentNumber": "F-20260710-0012",
      "kind": "payment",
      "status": "sent",
      "providerId": "668",
      "errorMessage": null,
      "date": "2026-07-10",
      "categoryCode": null,
      "amounts": { "htCents": null, "vatCents": null, "ttcCents": 50000 },
      "createdAt": "2026-07-10T15:11:40.000Z",
      "sentAt": "2026-07-10T15:30:02.000Z"
    }
  ],
  "limit": 50,
  "offset": 0
}
Exemple
curl "https://billies.fr/api/partner/v1/companies/1f6f9c2e-8a4b-4c5d-9e0f-2a3b4c5d6e7f/ereporting?status=sent&kind=transaction" \
  -H "Authorization: Bearer $BILLIES_API_KEY"
  • Rien à déclencher : l'émission d'une facture B2C (POST /documents + issue) et l'enregistrement d'un paiement (POST /payments) alimentent la file automatiquement. Cet endpoint sert à VÉRIFIER, pas à déclarer.
  • Périmètre : factures et avoirs aux particuliers français uniquement. Les factures B2B (client avec SIRET) passent par le flux Factur-X, jamais par l'e-reporting — aucun double comptage possible.
  • ereportingEnabled=false ou pdpConnected=false → aucune donnée ne part. pdpMode indique le raccordement effectif : delegated (l'artisan a connecté son propre accès PDP), platform (sous-compte marque grise déclaré via l'identité de la plateforme) ou none (non raccordé).
  • categoryCode (transactions) : TPS1 prestations de services, TNT1 non taxable (TVA 0), norme AFNOR XP Z12-013. Les montants sont en centimes, négatifs pour un avoir ou une annulation d'encaissement.
  • status=error : la déclaration a été refusée (définitif) — le détail est dans errorMessage. status=sending : transmission en cours de vérification, ne pas rejouer.

Factures reçues (réception e-facturation)

Les factures électroniques que le sous-compte REÇOIT de ses fournisseurs via le réseau de dématérialisation (réception obligatoire pour toutes les entreprises au 1ᵉʳ septembre 2026). Billies relève l'inbox de chaque sous-compte raccordé — rien à déclencher de ton côté. Ces deux endpoints te permettent de les afficher et de les servir directement dans ton back-office.

Factures reçues d'un sous-compte

GET /api/partner/v1/companies/{companyId}/received-invoices

Scope received-invoices:read

Liste des factures reçues, plus récentes d'abord. read indique si l'utilisateur l'a déjà ouverte sur billies.fr — cet appel ne modifie JAMAIS cet état (lecture non destructive, le badge de l'utilisateur reste intact). Filtre : unread=true|false (autre valeur → 400) ; pagination limit (≤ 100) et offset.

Réponse — 200
{
  "einvoiceEnabled": true,
  "pdpConnected": true,
  "invoices": [
    {
      "id": "9c1e4f7a-5b2d-4e8f-a1c3-7d6e9f0b2a41",
      "status": "delivered",
      "senderName": "SARL Fournitures Pro",
      "senderSiret": "88991234500017",
      "receivedAt": "2026-07-09T08:14:52.000Z",
      "read": false,
      "createdAt": "2026-07-09T09:00:03.000Z"
    }
  ],
  "limit": 50,
  "offset": 0
}
Exemple
curl "https://billies.fr/api/partner/v1/companies/1f6f9c2e-8a4b-4c5d-9e0f-2a3b4c5d6e7f/received-invoices?unread=true" \
  -H "Authorization: Bearer $BILLIES_API_KEY"
  • Scope dédié received-invoices:read : les connexions (mode connecté) autorisées AVANT son introduction ne l'ont pas — l'appel répond 403 « non consenti » tant que l'utilisateur n'a pas ré-autorisé ton application via /connect (l'écran de consentement mentionne désormais les factures reçues). Même chose pour une clé API créée avant : recrée une clé.
  • Prérequis côté sous-compte : facturation électronique activée (einvoiceEnabled) et compte raccordé à la plateforme (pdpConnected) — les deux champs sont renvoyés pour que tu puisses guider l'utilisateur. Sans raccordement, rien n'est relevé ; les factures déjà reçues avant une déconnexion restent listées.
  • L'inbox est relevée périodiquement par Billies : une facture apparaît ici quelques minutes à quelques heures après son dépôt sur le réseau, pas en temps réel.
  • senderName / senderSiret peuvent être null si la plateforme ne les expose pas pour ce dépôt.

Télécharger une facture reçue

GET /api/partner/v1/companies/{companyId}/received-invoices/{receivedInvoiceId}/download

Scope received-invoices:read

Renvoie le document lui-même en binaire (Content-Type: application/pdf pour un Factur-X, application/xml ou text/xml pour un CII pur — selon ce que le fournisseur a déposé), avec un Content-Disposition: attachment. Contrairement à …/documents/{id}/pdf qui renvoie une URL signée, la réponse est ici le flux binaire : le document vit chez la plateforme de dématérialisation, pas dans le Storage Billies.

Réponse — 200
— binaire —
Content-Type: application/pdf
Content-Disposition: attachment; filename="facture-recue-9c1e4f7a-….pdf"
Exemple
curl -OJ "https://billies.fr/api/partner/v1/companies/1f6f9c2e-8a4b-4c5d-9e0f-2a3b4c5d6e7f/received-invoices/9c1e4f7a-5b2d-4e8f-a1c3-7d6e9f0b2a41/download" \
  -H "Authorization: Bearer $BILLIES_API_KEY"
  • Le téléchargement est fait côté serveur Billies avec l'accès de la société — son jeton plateforme ne transite jamais par toi. Ne mets pas cette URL en cache : sers le flux à ton utilisateur ou re-télécharge à la demande.
  • 404 : l'id n'existe pas ou n'appartient pas à ce sous-compte (isolation stricte par société). 500 : erreur temporaire côté plateforme (panne, connexion du sous-compte à renouveler) — retente plus tard, ne conclus pas que la facture n'existe pas.
  • 409 si la plateforme du sous-compte ne permet pas le téléchargement — cas limite, n'arrive pas avec le raccordement standard.

Consommation

Suis en temps réel ce que ton abonnement va facturer : sous-comptes actifs du mois et documents émis. Un sous-compte est actif dès qu'il a émis au moins un document dans le mois.

Consommation du mois

GET /api/partner/v1/usage

Scope usage:read

Paramètre month au format YYYY-MM (défaut : mois en cours). bill.mode reflète ton offre (flat, per_document ou custom) et bill.amountCents est l'estimation totale du mois. En offre à l'acte, la facturation porte sur billableDocuments (factures ET avoirs — un avoir consomme un numéro légal et un PDF, il compte comme un document émis). documentsIssued/creditNotesIssued détaillent les deux. Les documents des comptes connectés (linkedDocuments) ne te sont jamais facturés.

Réponse — 200
{
  "month": "2026-07",
  "activeCompanies": 14,
  "documentsIssued": 82,
  "ereportingDeclarationsSent": 31,
  "creditNotesIssued": 5,
  "billableDocuments": 87,
  "linkedDocuments": 0,
  "pricingMode": "flat",
  "includedCompanies": 10,
  "pricePerExtraCompanyCents": 500,
  "bill": {
    "mode": "flat",
    "baseCents": 9900,
    "extraCompanies": 4,
    "extraAmountCents": 2000,
    "amountCents": 11900
  }
}
Exemple
curl "https://billies.fr/api/partner/v1/usage?month=2026-07" \
  -H "Authorization: Bearer $BILLIES_API_KEY"
  • Offre Intégrée (mode flat) : le barème est dégressif par tranche de sous-comptes actifs — 5 € du 11ᵉ au 100ᵉ, 4 € du 101ᵉ au 500ᵉ, 3 € au-delà. includedCompanies (10) et pricePerExtraCompanyCents ne sont renseignés qu'en flat.
  • Offre à l'acte (mode per_document) : bill.amountCents est calculé sur billableDocuments (factures + avoirs), pas sur documentsIssued seul. Réconcilie ta facturation sur billableDocuments.
  • ereportingDeclarationsSent est purement INFORMATIF : les déclarations e-reporting (ventes aux particuliers) ne sont jamais facturées — ni au sous-compte actif, ni à l'acte. Le compteur porte sur les déclarations transmises dans le mois pour tes sous-comptes.

Règles métier à connaître

L'API applique le droit français de la facturation. Quatre règles structurent tout le reste — les connaître t'évite la plupart des 409.

Une facture émise est immuable

C'est une obligation légale française, pas un choix d'API : une facture émise ne se modifie pas et ne se supprime pas. Le seul verbe correctif est l'avoir (POST …/credit-note), qui annule la facture d'origine avec son propre numéro — puis tu refactures proprement. Toute mutation sur un document émis renvoie 409 conflict.

Cycle de vie : brouillon → émis → accepté / payé

Un document naît en draft: modifiable et supprimable à volonté, sans numéro. L'émission le fige en sent. Ensuite, un devis signé via le lien de signature passe accepted(il n'y a pas de statut signed), une facture passe partially_paid puis paid quand les paiements enregistrés couvrent le TTC.

La numérotation est séquentielle et légale

Le numéro est attribué au moment de l'émission, dans une séquence chronologique continue et sans trou, propre à chaque sous-compte. Tu ne peux ni choisir un numéro, ni en réserver un à l'avance — c'est ce qui rend les documents opposables en cas de contrôle.

E-facturation via plateforme de dématérialisation

Quand la e-facturation est activée sur un sous-compte, ses factures B2B partent au format Factur-X et sont transmises via la plateforme de dématérialisation à laquelle Billies est raccordé — sans rien changer à tes appels : c'est le même POST …/issue.

À voir aussi

Prêt à facturer depuis ton logiciel ?

Active l'API dans tes réglages, crée ta clé, émets ton premier devis dans l'heure. À l'acte (0 € par mois, 0,50 € par document) ou intégré (99 €/mois, 10 sous-comptes actifs inclus) — sans engagement.

Un sous-compte qui n'émet rien ne coûte rien