Aller au contenu

Factures

L'objet facture, les montants calculés et les huit appels qui permettent de le gérer.

Une facture est un document que vous émettez, remettez à un client, puis attendez de voir payé. Wajub stocke les lignes, calcule les totaux, héberge la page de paiement et enregistre les montants réglés. Wajub ne relance personne et ne débite rien. Le produit se résume à un document avec une URL publique et un solde actualisé. Facturation et abonnements présente ce cycle. Cette page sert de référence pour l'objet lui-même.

La facturation est un produit live dont l'accès dépend de votre plan. Une clé sandbox renvoie 403 This feature is only available in live mode.. Un plan sans cette fonctionnalité renvoie 403 Invoicing is not available on your plan.. Une fois le plafond total d'un plan atteint, la réponse est 403 You have reached the maximum number of invoices for your plan.. Seuls les documents de type invoice comptent dans ce plafond.

Les huit appels

Voici toutes les opérations proposées par l'API pour les factures. Il n'existe aucun endpoint pour les PDF, les e-mails ou le débit d'un client.

AppelFonction
GET /invoicesLister, filtrer et trier vos factures
POST /invoicesCréer une facture, toujours avec draft
GET /invoices/{id}Récupérer une facture
PUT /invoices/{id}Remplacer une facture tant qu'elle reste modifiable
DELETE /invoices/{id}Supprimer logiquement une facture tant qu'elle reste modifiable
POST /invoices/{id}/sendLa faire passer à sent et ouvrir sa page publique
POST /invoices/{id}/mark-paidEnregistrer un paiement encaissé ailleurs
POST /invoices/{id}/cancelFermer une facture

{id} correspond à l'id renvoyé par l'API, soit la chaîne inv_. L'UUID interne est également accepté, mais il ne vous est jamais présenté. Utilisez donc l'identifiant inv_.

L'objet facture

Tous les appels renvoient cet objet. items est présent chaque fois que la facture est chargée avec ses lignes, ce qui est le cas pour les huit appels.

Réponse · une facture avec une ligne taxée
{
"id": "inv_LRQqYvlhrgUOE225KMKU",
"invoice_number": "INV-202609-0001",
"document_type": "invoice",
"status": "sent",
"customer_id": "9d1f0c7a-4b2e-4f61-9d3a-7c8e5b21a940",
"customer_name": "Amina Traoré",
"customer_email": "amina@example.cm",
"customer_company_name": "Traoré & Fils",
"customer_phone": "+237670000000",
"customer_address": "BP 1204, Douala",
"invoice_date": "2026-09-12",
"due_date": "2026-10-12",
"currency": "XAF",
"subtotal": 300000,
"discount_amount": 0,
"tax_amount": 57750,
"total": 357750,
"amount_paid": 0,
"amount_due": 357750,
"payment_type": "full",
"is_recurring": false,
"recurring_interval": null,
"notes": null,
"terms": null,
"footer": null,
"sent_at": "2026-09-12T09:14:02+00:00",
"paid_at": null,
"cancelled_at": null,
"items": [
{
"id": "0199f3c2-8b41-7a6e-9d02-4c1e7f83ab55",
"name": "Consulting service",
"description": null,
"quantity": 2,
"unit": null,
"unit_price": 150000,
"tax_rate": 19.25,
"tax_amount": 57750,
"discount_type": null,
"discount_value": 0,
"discount_amount": 0,
"subtotal": 300000,
"total": 357750,
"sort_order": 0
}
],
"created_at": "2026-09-12T09:10:44+00:00",
"updated_at": "2026-09-12T09:14:02+00:00"
}

invoice_number est généré pour chaque équipe et redémarre tous les mois : INV-, suivi de l'année et du mois, puis d'un compteur à quatre chiffres. status peut valoir draft, sent, viewed, partial, paid, overdue, cancelled ou refunded. Facturation et abonnements présente les transitions entre ces statuts et les webhooks émis.

Deux champs sont calculés plutôt que stockés. amount_due vaut toujours total moins amount_paid, avec un minimum de zéro. Il ne devient donc jamais négatif en cas de trop-perçu. amount_paid ne change qu'avec mark-paid ou un paiement effectué sur la page hébergée.

Créer une facture

Les quatre champs obligatoires sont customer_name, items, invoice_date et currency. Tous les autres sont facultatifs. La facture est toujours créée avec draft, quelle que soit votre requête.

POSThttps://api.wajub.com/invoices
customer_namestringobligatoire
Le destinataire de la facture. Ce champ reste obligatoire même avec customer_id.
customer_idstring (uuid)facultatif
Un client existant. Son nom, son e-mail, son entreprise, son téléphone et son adresse sont copiés sur la facture et remplacent les champs customer_* envoyés.
customer_emailstringfacultatif
Adresse utilisée par le Dashboard pour envoyer la facture et ses relances. L'API n'envoie rien.
customer_company_namestringfacultatif
Imprimé sur le document à côté du nom.
customer_phonestringfacultatif
Imprimé sur le document.
customer_addressstringfacultatif
Imprimée sur le document.
itemsarrayobligatoire
Au moins une ligne. Consultez l'objet ligne ci-dessous.
invoice_datedateobligatoire
La date d'émission imprimée sur le document.
due_datedatefacultatif
La date de paiement attendue. Elle doit être égale ou postérieure à invoice_date.
payment_terms_daysintegerfacultatif
Définit due_date à ce nombre de jours après aujourd'hui, uniquement si due_date est absent. Le calcul part de la date du jour, pas de invoice_date.
currencystringobligatoire
Trois lettres. XAF, XOF, NGN et les autres devises prises en charge.
is_recurringbooleanfacultatifdéfaut : false
Marque la facture comme modèle. Consultez [Factures récurrentes](/billing/invoice/recurring).
recurring_intervalenumfacultatif
daily, weekly, monthly, quarterly ou yearly.
payment_typeenumfacultatifdéfaut : full
full, split ou milestone. Les paiements en plusieurs fois sont décrits ci-dessous.
payment_schedulesarrayfacultatif
Les échéances lorsque payment_type ne vaut pas full.
notesstringfacultatif
Texte libre imprimé sur le document, limité à 5 000 caractères.
termsstringfacultatif
Conditions générales, limitées à 5 000 caractères.
footerstringfacultatif
Texte de pied de page, limité à 500 caractères.
template_idstring (uuid)facultatif
Un modèle de facture créé dans le Dashboard. Il détermine uniquement la mise en page imprimée.
document_typeenumfacultatifdéfaut : invoice
invoice, quote ou estimate. Lisez l’avertissement ci-dessous avant de le modifier.

Chaque entrée de items constitue un objet distinct.

namestringobligatoire
Le libellé de la ligne.
descriptionstringfacultatif
Une seconde ligne sous le libellé.
quantitynumberobligatoire
La quantité.
unit_priceintegerobligatoire
Le prix unitaire, en unités entières de la devise.
unitstringfacultatif
Un libellé comme hour ou day. Il sert uniquement à l’affichage.
tax_ratenumberfacultatif
Un pourcentage de 0 à 100, ajouté à la ligne.
discount_typeenumfacultatif
percentage ou fixed.
discount_valuenumberfacultatif
Le pourcentage, ou le montant lorsque le type vaut fixed.
curl https://api.wajub.com/invoices   -H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…"   -H "Content-Type: application/json"   -d '{
    "customer_name": "Traoré & Fils",
    "customer_email": "amina@example.cm",
    "invoice_date": "2026-09-12",
    "payment_terms_days": 30,
    "currency": "XAF",
    "items": [
      { "name": "Consulting service", "quantity": 2, "unit_price": 150000, "tax_rate": 19.25 },
      { "name": "Onboarding", "quantity": 1, "unit_price": 100000, "discount_type": "percentage", "discount_value": 10 }
    ]
  }'

La création est le seul appel de facture qui respecte Idempotency-Key. Rejouer une création avec la même clé renvoie la première facture au lieu d'en émettre une seconde. Rejouer un appel send ou mark-paid l'exécute une nouvelle fois.

Les montants sont des nombres entiers

Tous les champs monétaires d'une facture et de ses lignes sont des entiers. unit_price, subtotal, discount_amount, tax_amount, total et amount_paid contiennent des unités entières de la devise. Aucune unité secondaire n'est utilisée. 150000 représente cent cinquante mille francs, et non mille cinq cents.

Calcul d'une ligne

Chaque ligne effectue ses propres calculs à chaque enregistrement, dans l'ordre suivant. Vous envoyez les quatre premières valeurs. Les quatre autres sont calculées et renvoyées.

subtotal        = quantity × unit_price
discount_amount = discount_type = percentage  →  subtotal × discount_value / 100
                  discount_type = fixed       →  discount_value
                  otherwise                   →  0
taxable         = subtotal − discount_amount
tax_amount      = taxable × tax_rate / 100
total           = taxable + tax_amount

La taxe est exclusive. Elle est ajoutée à la ligne et n'en est jamais extraite. Elle est aussi calculée après la remise. Une remise de 10 % sur une ligne taxée réduit donc également la taxe.

La facture additionne ensuite les lignes.

subtotal   = Σ (quantity × unit_price)
tax_amount = Σ line tax_amount
total      = subtotal + tax_amount
amount_due = max(0, total − amount_paid)

Aucune remise globale sur une facture

Les remises s'appliquent par ligne. L'appel de création accepte également has_global_discount, global_discount_type et global_discount_value. Ces trois champs ne font rien. Ils sont validés, puis supprimés avant l'écriture, car aucune colonne ne les stocke. Ils ne produisent aucune erreur. Une facture envoyée avec une remise globale de 15 % est donc enregistrée au prix total tout en donnant l'impression d'avoir fonctionné.

Devis et estimations

document_type accepte quote et estimate, puis crée le document.

Paiement en plusieurs fois

Définissez payment_type sur split ou milestone, puis transmettez le calendrier. La page hébergée débite alors une échéance à la fois au lieu du solde total.

amountintegerobligatoire
Le montant débité pour cette échéance.
labelstringfacultatif
Affiché sur la page de paiement. La valeur par défaut est Installment 1 ou Milestone 1.
due_datedatefacultatif
Affichée sur la page de paiement.

Le client ouvre la facture et voit la première échéance non payée. Son amount est débité dans la limite de l'amount_due restant. Après son règlement, Wajub écrit status: "paid", paid_at et transaction_uid sur cette échéance, ajoute le montant à amount_paid et fait passer la facture à partial. La visite suivante propose l'échéance suivante. La facture passe à paid lorsque amount_paid atteint le total. Si toutes les échéances sont réglées mais qu'un solde reste dû, la page propose le paiement du reste en une fois.

Lister les factures

GET /invoices renvoie vos factures live, de la plus récente à la plus ancienne, par groupes de vingt-cinq.

GEThttps://api.wajub.com/invoices
searchstringfacultatif
Recherche en texte intégral sur le numéro, le nom et l’e-mail du client.
statusenumfacultatif
Un statut, exactement comme il apparaît sur l’objet.
customer_idstring (uuid)facultatif
Un client.
date_fromdatefacultatif
Limite inférieure de invoice_date.
date_todatefacultatif
Limite supérieure de invoice_date.
due_date_fromdatefacultatif
Limite inférieure de due_date.
due_date_todatefacultatif
Limite supérieure de due_date.
amount_minintegerfacultatif
Limite inférieure de total.
amount_maxintegerfacultatif
Limite supérieure de total.
payment_statusenumfacultatif
unpaid, partially_paid ou fully_paid, déterminé en comparant amount_paid à total.
sort_byenumfacultatifdéfaut : created_at
invoice_date, due_date, total, amount_paid, status ou created_at.
sort_direnumfacultatifdéfaut : desc
asc ou desc.
per_pageintegerfacultatifdéfaut : 25
Entre 1 et 100.
curl -G https://api.wajub.com/invoices   -H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…"   -d status=overdue   -d customer_id=9d1f0c7a-4b2e-4f61-9d3a-7c8e5b21a940

La liste utilise une pagination par curseur avec le tri par défaut. Si vous demandez un sort_by ou un sort_dir personnalisé, elle revient aux numéros de page, car un curseur nécessite un ordre stable.

Corriger une facture

PUT /invoices/{id} remplace le document au lieu de le modifier partiellement. customer_name, items, invoice_date et currency sont obligatoires à chaque appel. Renvoyez donc toute la facture avec votre modification, pas uniquement le changement. Les lignes envoyées remplacent entièrement les précédentes. Les anciennes sont supprimées et recréées. Tous les items[].id changent donc.

Vous pouvez modifier recurring_interval pendant une mise à jour. L'alias recurring_frequency accepté à la création ne l'est pas. Utilisez donc toujours recurring_interval. La suppression est logique : la ligne reste présente, le numéro de facture reste utilisé et rien ne vous la renvoie.

Enregistrer un paiement encaissé ailleurs

mark-paid sert aux fonds reçus hors de Wajub, en espèces ou par virement bancaire. Il modifie le solde de la facture, mais ne crée aucune transaction et ne règle rien.

POSThttps://api.wajub.com/invoices/{id}/mark-paid
amountintegerfacultatif
Le montant reçu. Omettez-le pour enregistrer la totalité de total.
payment_datedatefacultatif
Utilisée comme paid_at si ce paiement règle le solde.
transaction_idstringfacultatif
Accepté, puis ignoré.
payment_methodstringfacultatif
Accepté, puis ignoré.
notesstringfacultatif
Accepté, puis ignoré.

Un amount inférieur au total est ajouté à amount_paid et fait passer le statut à partial. Rappelez l'endpoint pour l'échéance suivante. Les montants s'accumulent et la facture passe à paid avec un paid_at dès qu'ils atteignent le total.

curl https://api.wajub.com/invoices/inv_LRQqYvlhrgUOE225KMKU/mark-paid   -H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…"   -H "Content-Type: application/json"   -d '{ "amount": 178875, "payment_date": "2026-09-20" }'

Une facture cancelled ou refunded refuse l'appel avec 400 This invoice cannot be marked as paid.

Annuler une facture

POST /invoices/{id}/cancel définit le statut sur cancelled, renseigne cancelled_at et ferme la page publique. L'appel accepte un champ reason, mais ne le stocke pas. Une facture déjà paid, cancelled ou refunded renvoie 400 This invoice cannot be cancelled.

L'annulation est la seule sortie possible pour une facture partiellement payée, car elle ne peut plus être modifiée ou supprimée. Elle ne rembourse pas les fonds encaissés. Utilisez les remboursements sur le paiement sous-jacent.

Que pensez-vous de ce contenu ?