API de génération de factures

Générez des factures PDF professionnelles par programme depuis votre serveur, pour vos systèmes internes, marketplaces ou produits SaaS.

1. Authentification#

Créez une API key sur app.invosend.com/settings/api-keys. Un workspace payant et un rôle owner sont requis.

Envoyez la clé comme Bearer token dans le header Authorization à chaque requête :

Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx
Les clés absentes, mal formées, invalides ou révoquées sont rejetées avec 403. Elles sont aussi rejetées si le workspace est gratuit, suspendu ou expiré.

2. Endpoint#

POST/api/invoices/generate-external

Base URL: https://app.invosend.com

Description de l'API lisible par machine : OpenAPI 3.1 et Postman collection. Importez l'une ou l'autre dans Postman, Insomnia ou un générateur de SDK.

3. Request Body#

ChampTypeRequisDescription
customer_namestringOuiNom du client ou destinataire de la facture.
customer_emailstringNonAdresse e-mail valide. La facture créée par cet appel n'est pas envoyée par e-mail (cette API n'envoie pas d'e-mails). Obligatoire pour une facture récurrente : chaque facture générée ensuite est envoyée à cette adresse.
customer_tax_idstringNonNuméro d'identification fiscale du client (NPWP en Indonésie, 40 caractères maximum). Imprimé sous l'adresse du client sur la facture.
itemsarrayOuiAu moins 1 ligne valide avec description, quantity > 0 et price >= 0.
items[].descriptionstringNonNom du produit ou du service. Si vide, Item est utilisé.
items[].quantitynumberOuiNombre d'unités, avec jusqu'à 2 décimales (par exemple 1.5 heure), de 0.01 à 999 999.
items[].pricenumberOuiPrix unitaire.
items[].discountnumberNonMontant de remise par ligne. Défaut 0.
currencystringNonCode devise comme EUR, USD ou IDR.
tax_ratenumberNonPourcentage de taxe calculé sur le sous-total après remise. Défaut 0.
due_datestringNonFormat YYYY-MM-DD. Défaut : 14 jours après génération.
notesstringNonNotes additionnelles affichées sur la facture.
items[].unitstringNonLibellé d'unité à côté de la quantité, par ex. pcs ou heure.
items[].tax_ratenumberNonTaux de taxe spécifique à la ligne (0-100). Omettez-le pour utiliser le tax_rate de la facture.
discount_totalnumberNonRemise au niveau de la facture. Elle est déduite avant la taxe : la taxe est calculée sur le montant remisé (réparti entre les lignes proportionnellement à leur valeur). Défaut 0.
templatestringNonID du modèle de facture à utiliser. Listez les modèles disponibles avec GET /api/invoices/external/templates. Sans ce champ, le modèle par défaut choisi dans les Paramètres est utilisé, puis celui d'Invosend.
document_languagestringNonLangue des mots du document de la facture (PDF et e-mail) : en (par défaut), id, es, fr, de, nl, it, pt, zh, ja, hi, ar (indonésien, espagnol, français, allemand, néerlandais, italien, portugais du Brésil, chinois simplifié, japonais, hindi, arabe). Toute autre valeur est refusée avec une erreur. La mise en page intégrée simple, utilisée uniquement lorsqu'aucun modèle n'existe, est toujours en anglais.
custom_payment_notesstringNonInstructions de paiement supplémentaires affichées sur le PDF.
footer_textstringNonTexte court affiché en bas du PDF.
is_recurringbooleanNonCrée une série récurrente. Cette facture est la première ; les suivantes sont générées et envoyées automatiquement à customer_email selon le calendrier, chacune utilisant 1 crédit de facture. Nécessite un forfait avec la fonctionnalité de factures récurrentes, ainsi que recurring_interval et customer_email.
recurring_intervalstringNonFréquence de répétition de la série : l'une des valeurs weekly, fortnightly, monthly, quarterly, semi-annually, yearly. Obligatoire lorsque is_recurring vaut true.
recurring_countintegerNonNombre total de factures de la série, cette première facture comprise : un entier de 2 à 121. Par défaut 4. Utilisez ce champ ou recurring_end_date, pas les deux.
recurring_end_datestringNonDate après laquelle plus aucune facture n'est générée, YYYY-MM-DD, dans le futur. Utilisez ce champ ou recurring_count, pas les deux.
payment_gatewaystringNonL'une des valeurs midtrans, xendit, stripe, paypal, razorpay, mollie. La passerelle doit déjà être connectée dans le workspace.
payment_accountstringNonID d'un compte de paiement existant (coordonnées bancaires) à associer à la facture.

Exemple complet de requête :

curl -X POST https://app.invosend.com/api/invoices/generate-external \
  -H "Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Acme Corp",
    "customer_email": "billing@acme.com",
    "items": [
      { "description": "Consulting Services", "quantity": 2, "price": 500, "tax_rate": 11 }
    ],
    "discount_total": 50,
    "currency": "EUR",
    "notes": "Thank you for your business."
  }'

Exemple de facture récurrente (la première maintenant, puis une par mois, 6 au total) :

curl -X POST https://app.invosend.com/api/invoices/generate-external \
  -H "Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Acme Corp",
    "customer_email": "billing@acme.com",
    "items": [
      { "description": "Monthly retainer", "quantity": 1, "price": 2500 }
    ],
    "currency": "EUR",
    "recurring_interval": "monthly",
    "recurring_count": 6
  }'
Pour réessayer en toute sécurité après un délai dépassé ou une erreur réseau, ajoutez un en-tête Idempotency-Key (1 à 255 caractères, par exemple un UUID généré pour chaque facture). Si la première requête a réussi, la nouvelle tentative renvoie la même réponse avec l'en-tête Idempotent-Replayed: true, sans créer une autre facture ni consommer un autre crédit (credits_remaining est la valeur du premier appel). Les clés sont conservées 24 heures par espace de travail. Seules les réponses réussies sont enregistrées : une requête en échec peut donc être réessayée avec la même clé.

4. Réponse#

200 OK signifie que la facture a été créée et que le PDF a été généré :

{
  "data": {
    "id": "cjld2cjxh0000qzrmn831i7rn",
    "invoice_number": "INV-1-202609-0016",
    "pdf_url": "https://cdn.invosend.com/company/1/pdf/Invoice_INV_1_202609_0016_UNPAID_0da2beeabb.pdf",
    "credits_remaining": 24,
    "is_recurring": false,
    "template": {
      "id": "k3p9x2m7c0000abcd1234efgh",
      "name": "Classic",
      "source": "system_default"
    }
  }
}
credits_remaining vaut null lorsque le workspace dispose d'un quota illimité. id sert à appeler les Endpoints supplémentaires ci-dessous. Les factures créées via l'API apparaissent aussi dans la liste du workspace et utilisent la même séquence de numéros. Les numéros de facture ressemblent à INV-{id du workspace}-{AAAAMM}-{séquence}. Pour une facture récurrente, la réponse contient aussi un objet recurring : schedule_id, interval, occurrences, next_issue_date, end_date. recurring.schedule_id est l'identifiant à utiliser avec les endpoints récurrents ci-dessous.

5. Codes d'erreur#

StatutCause
400Champs obligatoires manquants ou invalides : customer_name, items vide, aucun élément valide, un payment_gateway non reconnu, un payment_gateway non connecté pour ce workspace ou, pour une facture récurrente, un recurring_interval ou customer_email manquant, ou un recurring_count / recurring_end_date invalide. Ce statut est aussi renvoyé si le PDF n'a pas pu être généré : relancez la requête (aucun crédit n'est débité).
402Le solde invoice_credits du workspace est vide.
403Le header Authorization manque ou est mal formé, l'API key est invalide/révoquée, ou l'offre n'inclut pas l'accès API.
403 (récurrente)is_recurring a été envoyé mais l'offre du workspace n'inclut pas les factures récurrentes.
409Une requête avec la même Idempotency-Key est encore en cours de traitement. Réessayez dans un instant.
422La Idempotency-Key a déjà été utilisée avec un corps de requête différent. Utilisez une nouvelle clé pour une nouvelle facture.
429Limite de débit dépassée. Voir la section Rate Limit.

6. Rate Limit#

PortéeLimite
Par workspace (toutes les requêtes API cumulées)Professional 10/min · Specialist 20/min · Business 30/min · Enterprise 60/min
Par adresse IP (toutes les requêtes API cumulées)30 requêtes/minute
Clés API actives par workspaceProfessional 3 · Specialist 5 · Business 10 · Enterprise 20
La génération de factures et les endpoints de lecture/gestion partagent la même limite. Les réponses réussies incluent les en-têtes X-RateLimit-Limit et X-RateLimit-Remaining. Les quotas d'API du workspace sont configurés par forfait d'abonnement dans Invosend. Besoin d'un volume de production plus élevé ? Contactez-nous depuis votre compte workspace. Les clés révoquées ne comptent pas dans la limite du nombre de clés.

7. Endpoints supplémentaires#

La même API key peut aussi lire et gérer les factures qu'elle a créées via generate-external :

MéthodeCheminDescription
GET/api/invoices/externalListe les factures de ce workspace. Prend en charge ?status= et ?page=/?pageSize=.
GET/api/invoices/external/templatesListe les modèles de facture que vous pouvez indiquer dans template, si votre offre les autorise et lequel est le modèle par défaut.
GET/api/invoices/external/:idRécupère une facture par son id (la valeur renvoyée par generate-external).
PUT/api/invoices/external/:id/statusMet à jour le statut de la facture. Body : { "status": "paid" } (l'une de unpaid, paid, overdue, cancelled).
PUT/api/invoices/external/:id/archiveArchive ou désarchive une facture. Body : { "is_archived": true }.
POST/api/invoices/external/:id/pdfRécupère à nouveau l'URL du PDF, en le régénérant si nécessaire. Body : { "force": true } pour forcer la régénération.
GET/api/invoices/external/recurringListe les séries récurrentes. Prend en charge ?status= (active, paused, completed) et ?page=/?pageSize=.
GET/api/invoices/external/recurring/:idRécupère une série (le recurring.schedule_id renvoyé par generate-external) avec toutes ses occurrences : date, statut et id de la facture produite par chacune.
PUT/api/invoices/external/recurring/:id/statusMet en pause ou reprend une série. Body : { "status": "paused" } ou { "status": "active" }. La reprise nécessite une offre avec factures récurrentes.
PUT/api/invoices/external/recurring/:id/extendAjoute des occurrences après la dernière. Body : { "count": 3 } ou { "end_date": "2027-12-31" }.
PUT/api/invoices/external/recurring/:id/frequencyChange la fréquence de la série ; les occurrences à venir non encore générées sont remplacées. Body : { "frequency": "monthly", "start_date": "2027-01-01", "count": 6 } (vous pouvez utiliser end_date au lieu de count ; start_date doit être dans le futur).
POST/api/invoices/external/recurring/:id/skipSupprime une occurrence à venir. Body : { "date": "2027-01-01" }. Les occurrences ayant déjà produit une facture ne peuvent pas être supprimées.
Pour encaisser un paiement en ligne, renseignez payment_gateway (et éventuellement payment_account) dans generate-external, puis envoyez à votre client la page de paiement publique de la facture : https://app.invosend.com/p/invoice/{id}, avec l'id renvoyé par l'API. Il y paie via la passerelle connectée et la facture est marquée comme payée automatiquement. Le paiement est géré par cette page ; il n'existe pas d'endpoint de paiement public distinct à appeler.

8. Webhooks#

Plutôt que d'interroger l'API en boucle, enregistrez une URL HTTPS : Invosend y envoie une requête POST avec un corps JSON chaque fois qu'une facture change. Ajoutez des endpoints dans Paramètres > Webhooks (propriétaire de l'espace de travail, offre avec accès à l'API, jusqu'à 5 endpoints). Chaque endpoint possède son propre secret de signature, affiché une seule fois.

Les événements sont envoyés pour les factures créées dans le tableau de bord comme pour celles créées via cette API. Les modèles de séries récurrentes ne sont jamais envoyés.

ÉvénementEnvoyé lorsque
invoice.createdUne facture est créée. Le PDF peut ne pas être encore prêt : pdf_url peut donc valoir null.
invoice.paidLe statut passe à paid (par vous, via l'API ou par un paiement en ligne).
invoice.overdueLe statut passe à overdue (automatiquement après l'échéance, ou manuellement).
invoice.cancelledLe statut passe à cancelled.

Tous les événements ont la même enveloppe. data.object est la facture, avec la même forme que GET /api/invoices/external/:id ; les événements de statut incluent aussi data.previous_status :

{
  "id": "evt_9f2c1b7a4e3d5c6b8a1f0e2d",
  "object": "event",
  "type": "invoice.paid",
  "created": "2026-10-01T09:30:12.000Z",
  "data": {
    "object": {
      "id": "cjld2cjxh0000qzrmn831i7rn",
      "invoice_number": "INV-1-202610-0007",
      "customer_name": "Acme Corp",
      "customer_email": "billing@acme.com",
      "currency": "USD",
      "subtotal": 500,
      "discount_total": 0,
      "tax_rate": 0,
      "tax_amount": 0,
      "amount": 500,
      "amount_paid": 500,
      "amount_credited": 0,
      "amount_received": 500,
      "invoice_status": "paid",
      "is_archived": false,
      "is_recurring": false,
      "recurring_interval": "none",
      "issue_date": "2026-10-01T02:15:40.000Z",
      "due_date": "2026-10-15",
      "paid_at": "2026-10-01T09:30:11.000Z",
      "pdf_url": "https://cdn.invosend.com/company/1/pdf/Invoice_INV_1_202610_0007_PAID.pdf",
      "pdf_status": "ready"
    },
    "previous_status": "unpaid"
  }
}

Chaque requête porte ces en-têtes. Pour la vérifier, calculez un HMAC-SHA256 de {t}.{corps brut} avec le secret de l'endpoint et comparez-le à v1 ; rejetez-la si t date de plus de 5 minutes :

POST /your/webhook/url
Content-Type: application/json
Invosend-Event: invoice.paid
Invosend-Delivery: evt_9f2c1b7a4e3d5c6b8a1f0e2d
Invosend-Signature: t=1790847012,v1=5b1d...e9a2

// Node.js
import crypto from "crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(parts.t + "." + rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
Répondez avec n'importe quel code 2xx en moins de 10 secondes. Sinon, l'envoi est retenté jusqu'à 7 fois (après 1 minute, 5 minutes, 30 minutes, 2 heures, 6 heures, 12 heures et 24 heures). Un endpoint qui échoue en continu est désactivé et vous êtes notifié. Un événement peut arriver plusieurs fois ou dans le désordre : utilisez l'id de l'événement pour ignorer les doublons et considérez invoice_status comme la référence. Les journaux de livraison sont conservés 30 jours et tout événement peut être renvoyé depuis Paramètres > Webhooks.

9. Limitations#

Cette API est volontairement limitée à la génération de factures et à une gestion légère. Ce qui n'est pas encore disponible :

  • Les webhooks couvrent uniquement la création, le paiement, le retard et l'annulation d'une facture. Les autres changements, comme les modifications ou l'archivage, ne sont pas envoyés : lisez l'état actuel avec GET /invoices/external/:id.
  • La facture créée par chaque requête n'est pas envoyée par e-mail : cette API ne fait que générer le PDF, la transmettre à votre client vous incombe (ou utilisez le tableau de bord). Exception : les séries récurrentes, où chaque facture suivante générée par le planificateur est envoyée automatiquement à customer_email.
  • Une facture ne peut pas être modifiée après sa création — seuls son statut et son statut d'archivage peuvent être changés via cette API.
  • La prise en charge d'une passerelle de paiement suppose qu'elle soit déjà connectée dans le tableau de bord ; cette API se contente de l'associer à la facture, elle ne connecte pas la passerelle elle-même.
  • Une série récurrente peut être mise en pause, reprise, prolongée, reprogrammée et voir ses occurrences à venir ignorées via l'API, mais son contenu (articles, client, montants) ne peut pas être modifié ni la série supprimée depuis l'API ; faites-le dans le tableau de bord.

Questions frequentes#

Comment obtenir une API key Invosend ?

Créez un compte sur app.invosend.com, passez le workspace sur une offre payante, puis ouvrez Settings > API Keys et cliquez sur Generate key. La clé brute n'est affichée qu'une seule fois.

Combien coûte chaque appel API de génération ?

Chaque requête réussie consomme 1 crédit de facture du solde du workspace. Le même pool est utilisé que pour les factures créées depuis le tableau de bord.

Quelle est la limite de débit de cette API ?

La limite de débit du workspace suit la configuration de votre forfait payant. La section Rate Limit ci-dessus indique la limite actuelle de chaque forfait.