Skip to content

Billing & subscriptions

Invoices, how a customer pays one, and what recurring billing does today.

Payments and billing ask for money in two different ways. A payment is something your code starts: you call the API, the handset lights up, the customer confirms within the minute. An invoice is a document you hand over, and the customer settles it whenever they decide to.

This section is the second kind.

Your server creates the invoice, Wajub hosts the page it is paid on, and the cycle comes back round. Handing the address to the customer stays yours.

What the section contains

One product ships: Invoice. It produces the numbered document, hosts the page the customer pays on, and tracks what has come in against it. Subscriptions, in the sense of a plan a customer signs up to and a wallet charged every month without them acting, is not something you can call today.

What you needWhat to reach for
Bill a customer once and get paid onlineInvoice
Bill the same customer at a fixed intervalA recurring invoice, with the caveat below
Charge a saved wallet on a schedule, unattendedNothing yet, see Subscriptions
Take a payment your own checkout startsPayments, not billing

An invoice is a document, not a charge

Creating one moves no money and notifies nobody. It writes a numbered document with its line items, its totals and a status, and stops there. Sending it, paying it and chasing it are three separate things that happen afterwards.

POSThttps://api.wajub.com/invoices
customer_namestringrequired
Who the invoice is addressed to. A name is enough, a customer record is not required.
itemsarrayrequired
At least one line. Each takes name, quantity and unit_price.
invoice_datestring (date)required
The date printed on the document. Required: there is no default.
currencystringrequired
ISO 4217 code. Every amount on the invoice is in it.
customer_idstring (uuid)optional
Links the invoice to a [customer](/payments/customers). Their details are snapshotted onto the document at creation.
customer_emailstringoptional
Where the Dashboard sends the PDF, and what the payment page prefills.
due_datestring (date)optional
On or after invoice_date. What the overdue job compares against.
payment_terms_daysintegeroptional
Sets due_date that many days from today, and only when due_date is absent.
items[].tax_ratenumberoptional
Percentage on that line, 0 to 100. Applied per line, not per invoice.
items[].discount_typeenumoptional
percentage or fixed, paired with items[].discount_value.
items[].discount_valuenumberoptional
The amount taken off that line. Discounts are per line: the API sets none at invoice level.
document_typeenumoptionaldefault : invoice
invoice, quote or estimate. Only invoices are listed and retrieved by the API.
is_recurringbooleanoptional
Flags the document as recurring. Read the section on recurring invoices before you rely on it.
notesstringoptional
Free text under the lines, up to 5 000 characters. terms and footer sit alongside it.

Totals are computed for you from the lines, so you never send subtotal or total. Send the invoice with a live secret key.

curl https://api.wajub.com/invoices \
  -H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: invoice-amina-september" \
  -d '{
    "customer_name": "Amina Traoré",
    "customer_email": "amina@example.com",
    "invoice_date": "2026-09-11",
    "due_date": "2026-09-25",
    "currency": "XAF",
    "items": [
      { "name": "Consulting, September", "quantity": 3, "unit_price": 150000 }
    ]
  }'

The answer is 201 Created. The id is what every other call takes, and invoice_number is the human reference printed on the document: INV- plus the year and month, then a counter that restarts at 0001 each month, per account.

Response · 201 Created
{
"code": 201,
"status": "Created",
"message": "Invoice created",
"invoice": {
"id": "inv_LRQqYvlhrgUOE225KMKU",
"invoice_number": "INV-202609-0001",
"document_type": "invoice",
"status": "draft",
"customer_name": "Amina Traoré",
"customer_email": "amina@example.com",
"invoice_date": "2026-09-11",
"due_date": "2026-09-25",
"currency": "XAF",
"subtotal": 450000,
"discount_amount": 0,
"tax_amount": 0,
"total": 450000,
"amount_paid": 0,
"amount_due": 450000,
"is_recurring": false,
"sent_at": null,
"paid_at": null,
"created_at": "2026-09-11T14:00:00Z"
}
}

The life of an invoice

The repetition on those arrows is the point. There is no invoice.sent, no invoice.paid and no invoice.overdue: a transition that notifies you at all arrives as the same invoice.updated, and the status field on the payload is what tells you which one it was.

Two of those states move on their own. viewed is written the first time the customer opens the payment page, and overdue is written by a nightly job that compares due_date against today. Neither is something you set.

Sending an invoice does not send it

What the call does do is open the payment page. A draft invoice answers 403 This invoice is not available for payment. to anyone who opens its URL, and only four statuses are payable: sent, viewed, overdue and partial.

POSThttps://api.wajub.com/invoices/{id}/send

So an API-driven flow has three steps rather than two: create the invoice, mark it sent to unlock its page, then deliver the URL yourself over whatever channel you already use to talk to that customer. Nothing leaves Wajub on your behalf.

Where the customer pays

The payment page lives on its own host, with the invoice id as the whole path.

The customer's URL
https://invoice.wajub.com/inv_LRQqYvlhrgUOE225KMKU

That address is not in the API response. Build it from the id you got back, or copy it from the Dashboard, which is also the only place it changes: a merchant serving invoices from a verified custom domain gets that host instead of invoice.wajub.com.

The page itself is an ordinary checkout. It shows your branding, the invoice's lines, and the operators available for its currency, and a payment made there is an ordinary payment: it lands in your Payments list, fires payment.succeeded, settles into your balance and refunds like any other. On success the invoice moves to paid, or to partial when the invoice is split into installments and only one has been settled.

Recording money you took elsewhere

Plenty of invoices are paid in cash, or by a bank transfer that never touches Wajub. Marking one paid records that against the document without inventing a payment.

POSThttps://api.wajub.com/invoices/{id}/mark-paid

Pass no amount and the invoice is settled in full. Pass one and it is added to amount_paid, which moves the invoice to partial until the total is covered. A cancelled or refunded invoice refuses the call with 400, so a document its own flow has closed cannot be resurrected.

Recurring invoices, as they stand today

is_recurring and recurring_interval are accepted on creation, the interval being one of daily, weekly, monthly, quarterly or yearly. A job runs every night at 02:00, looks for recurring invoices whose next occurrence is due, and copies each one into a fresh document with new dates and the customer's current details.

Two things are worth knowing for when that gap closes, because they shape what recurrence will mean here. Each generated invoice is created as a draft, so even a working chain would produce documents rather than charges, and each one would still need to be sent and paid. And the feature is recurring_billing, which starts at Growth: the generator skips accounts that do not have it and leaves the parent invoice intact, so renewals resume on their own if a downgraded account upgrades again.

That gate is checked by the Dashboard and by the generator, but not by POST /invoices. Creating a recurring invoice through the API succeeds on any plan; it is the generation that would refuse.

What reaches your webhook

Three events, all of them about the document rather than the money.

EventFires when
invoice.createdAn invoice is created, in draft
invoice.updatedAny field or status changes through the API, plus viewed and a payment
invoice.deletedA draft invoice is deleted

The money arrives on a separate event. When a customer pays an invoice online you receive payment.succeeded for the payment itself and invoice.updated for the document, and the first is the one to fulfil on, since it carries the amount, the channel and the provider reference.

In the Dashboard

Billing → Invoices is the full surface, and it is wider than the API: it renders the PDF, emails it to the customer with the payment link, tracks reminders, and exports the list to CSV.

Two scheduled jobs run behind it. Statuses are refreshed nightly at 01:00, which is what flips an unpaid invoice to overdue, and reminders go out hourly to customers whose invoice is still open.

What did you think of this content?