Every billing period produces one invoice per subscription. An invoice starts as a draft, gets finalized when amounts are locked, transitions to paid when the charge succeeds, and can be voided if something goes wrong. This guide covers each state and the transitions between them.
Status overview
┌──────────────────────────────────────────┐
│ │
generate → draft → finalize → finalized → paid │
│ │
└──────────► void ───────────┘
| Status | Description |
|---|
draft | Invoice exists but amounts are not locked. Can be regenerated. |
finalized | Amounts are locked. Payment collection is in progress. |
paid | Payment was collected successfully. |
void | The invoice was voided. No charge was collected. |
Draft
A draft invoice is created automatically at the end of each billing period, or manually via the API. In draft state:
- Line items reflect the latest aggregated usage
- The invoice can be regenerated (if usage was updated)
- No charge has been attempted
Generate a draft invoice manually:
curl -X POST https://api.monigo.co/v1/invoices/generate \
-H "Authorization: Bearer mk_live_..." \
-H "Content-Type: application/json" \
-d '{ "subscription_id": "<subscription_id>" }'
Inspect a draft invoice:
curl https://api.monigo.co/v1/invoices/<invoice_id> \
-H "Authorization: Bearer mk_live_..."
The response includes line_items — one entry per metric/price pair — so you can verify each charge before finalizing.
Finalized
Finalizing locks the invoice amounts. Once finalized:
- Line item amounts cannot be changed
- Payment collection is triggered automatically
- The
invoice.finalized webhook fires
Finalize an invoice:
curl -X POST https://api.monigo.co/v1/invoices/<invoice_id>/finalize \
-H "Authorization: Bearer mk_live_..."
In the automatic billing cycle, Monigo finalizes invoices for you. You only need to call finalize manually if you generated a draft invoice yourself and want to trigger collection.
Paid
When Monigo successfully collects payment from your configured provider, the invoice moves to paid. The invoice.paid webhook fires and paid_at is set.
If collection fails (insufficient funds, expired card, etc.), the invoice remains finalized and the invoice.payment_failed webhook fires. Monigo does not automatically retry — you should listen for this webhook and take action (notify the customer, pause the subscription, retry after they update their payment method).
List paid invoices for a customer:
curl "https://api.monigo.co/v1/invoices?customer_id=<customer_id>&status=paid" \
-H "Authorization: Bearer mk_live_..."
Void
Voiding cancels an invoice without collecting payment. Void a draft or finalized invoice when:
- The invoice was generated in error
- The customer should not be charged for that period
- You need to issue a corrected invoice (void → regenerate)
curl -X POST https://api.monigo.co/v1/invoices/<invoice_id>/void \
-H "Authorization: Bearer mk_live_..."
A paid invoice cannot be voided. If you need to reverse a collected payment, issue a refund directly through your payment provider (Paystack, Flutterwave, or Monnify).
Void and regenerate (correcting an invoice):
# 1. Void the incorrect invoice
curl -X POST https://api.monigo.co/v1/invoices/<invoice_id>/void \
-H "Authorization: Bearer mk_live_..."
# 2. Replay affected events if needed (see Idempotency guide)
curl -X POST https://api.monigo.co/v1/events/replay \
-H "Authorization: Bearer mk_live_..." \
-d '{ "from": "2026-02-01T00:00:00Z", "to": "2026-02-28T23:59:59Z" }'
# 3. Generate a fresh draft
curl -X POST https://api.monigo.co/v1/invoices/generate \
-H "Authorization: Bearer mk_live_..." \
-d '{ "subscription_id": "<subscription_id>" }'
Webhook events
| Event | Status transition | Fired when |
|---|
invoice.created | → draft | A draft invoice is generated |
invoice.finalized | → finalized | Invoice amounts are locked |
invoice.paid | → paid | Payment collected successfully |
invoice.payment_failed | stays finalized | Payment collection failed |
invoice.voided | → void | Invoice is voided |