Invoices and credit
Every bill PaySigna collects for you is an invoice: a subscription’s period bill (see Subscriptions), or a one-off invoice you issue yourself — consulting, a setup fee, a business customer’s purchase order. This page covers what an invoice is made of, one-off invoices, and the two ways to give money back on one: credit notes and customer credit.
Everything here needs a secret key: billing.read to read, billing.write to
change. It is under the Billing tag in the API tester.
What an invoice is made of
Section titled “What an invoice is made of”An invoice is the sum of its lines, less any discount and credit:
amount = subtotal − discount − credit_applied| Field | What it is |
|---|---|
kind |
subscription (a period’s bill), one_off (issued by you) or proration. |
subtotal |
The lines, before discount and credit. |
discount |
Taken off by the subscription’s coupon. Always 0 on a one-off invoice. |
credit_applied |
Paid from the customer’s credit balance, plus anything taken off by a credit note while the invoice was open. |
amount |
What is collected. Never below zero. |
credited |
Given back by credit notes after it was paid. |
transaction |
The charge that paid it. Absent when nothing had to be paid — a coupon or credit covered the whole bill. |
subscription, period_start, period_end |
Present on a subscription’s bill; absent on a one-off invoice. |
due_on, purchase_order, memo, payment_method |
A one-off invoice’s terms, and the number it is prompted on (masked). |
GET /v1/invoices/{id} adds lines. Each line has a kind (plan,
one_off, discount, credit and others), a description, quantity,
unit_amount and amount. Discount and credit lines are negative. The list,
GET /v1/invoices, leaves the lines out.
An invoice that comes to zero is paid as soon as it opens, with no charge and
no prompt. invoice.paid still fires, with amount: 0.
One-off invoices
Section titled “One-off invoices”POST /v1/invoices bills a customer you already have. The Idempotency-Key
header is required, so a retried request never bills them twice.
curl https://sandbox.paysigna.com/v1/invoices \ -H "Authorization: Bearer psg_test_…" \ -H "Idempotency-Key: 4c1d7e20-8a3b-4f55-9d0e-6b2a1c7f3e98" \ -H "Content-Type: application/json" \ -d '{ "customer": "<customer id>", "currency": "GHS", "lines": [ { "description": "Website build — phase 1", "unit_amount": 250000 }, { "description": "Hosting setup", "quantity": 2, "unit_amount": 15000 } ], "due_in_days": 14, "purchase_order": "PO-2026-118", "payment_method": { "momo_network": "mtn" } }'| Field | Notes |
|---|---|
customer |
A stored customer’s id. A customer blocked on your account cannot be invoiced. |
currency |
The invoice’s currency. |
lines |
1 to 50 lines, each a description, a quantity (default 1) and a unit_amount in minor units. |
due_in_days |
Payment terms, 0 to 90. 0, the default, is due on receipt. Sets due_on. |
purchase_order |
Optional. Your customer’s PO number, shown on the invoice and in the notice. |
memo |
Optional note. |
payment_method |
Required. phone defaults to the customer’s phone. Name its network with momo_network (a code such as mtn) or momo_network_id. |
apply_credit |
Spend the customer’s credit balance on it. Defaults to true. |
One-off invoices are collected by mobile-money prompt only. All fields: issue a one-off invoice.
How it is collected
Section titled “How it is collected”The invoice is created open and fires invoice.created and
invoice.finalized. The customer is sent the invoice by SMS and email at once.
When the first prompt goes out depends on the terms:
due_in_days |
First prompt | Retries counted from |
|---|---|---|
0 (due on receipt) |
Within a minute. | The day it was issued. |
1 or more |
09:00 UTC on due_on (09:00 in Ghana). Nothing is asked for before the due date, and the notice tells the customer the prompt will come that day. |
That first prompt. |
After that it is collected like a subscription’s bill, with these differences:
- Failed prompts are retried on the days in your retry schedule. When those run out, nobody is prompted again.
collect_untilis 30 days afterdue_on. If the invoice is still unpaid then, it becomesuncollectible. Your schedule’s “end on day” setting does not apply to one-off invoices.- A failed attempt fires
invoice.payment_failedto you, withattempt_countandfailure_message. An attempt refused before it reached a partner reads “The payment was refused before it was sent: {reason}”, where the reason is the same detail the API would return (a limit, a blocked number, a suspended account, a method your account can’t use). The customer is sent an SMS and email saying it was not collected, the date it is still due by, and when the next prompt will come — or that there will be no more prompts. - When it is paid, the customer is emailed a receipt.
If the customer’s credit covers the whole invoice, it is paid at once, nobody is prompted, and the customer is emailed a receipt for the amount paid from credit.
Each attempt is an ordinary charge, with the invoice number as its reference. It appears in your transactions and settles like any other payment.
Voiding an invoice
Section titled “Voiding an invoice”POST /v1/invoices/{id}/void withdraws an open one-off or proration invoice.
Collection stops, nobody is prompted again, and invoice.voided fires. It cannot
be undone. Any customer credit the invoice had spent is given back, as a credit
entry with reason adjustment and the note “Credit returned: invoice … was
voided.” This happens however the invoice is voided: by you, or by cancelling,
pausing or closing.
| You cannot void | Instead |
|---|---|
| A subscription’s period bill | Cancel or pause the subscription, which voids its open bill. Otherwise the next bill would simply open again. |
| A paid invoice | Issue a credit note. |
| An invoice the customer is being prompted for right now | Try again in a few minutes. |
Credit notes
Section titled “Credit notes”A credit note lowers what an invoice is worth. POST /v1/credit_notes, with an
Idempotency-Key:
{ "invoice": "<invoice id>", "amount": 5000, "reason": "order_change", "memo": "One hosting seat cancelled", "outcome": "customer_credit"}reason is duplicate, fraudulent, order_change, product_unsatisfactory
or other (all fields). What outcome you may use depends on the invoice:
| Invoice | outcome |
What happens |
|---|---|---|
| Open | reduce_due |
The amount due goes down. If it reaches zero, the invoice is paid. A subscription waiting on its first bill then becomes active. |
| Paid | customer_credit |
The amount goes on the customer’s credit balance. Their next invoice spends it. |
| Paid | refund |
The payment that paid the invoice is refunded by that amount. Also needs the refund.create scope. Follow the refund through refund on the credit note and GET /v1/refunds/{id}. |
Rules:
- On an open invoice, the amount cannot be more than the invoice still asks for.
- On a paid invoice, the total credited cannot be more than the invoice amount.
creditedshows how much has been given back so far. - A paid invoice that cost nothing (no
transaction) cannot be refunded. Give credit instead. - Void and uncollectible invoices cannot be credited.
- An open invoice cannot be reduced while the customer is being prompted for it, because they would be approving the old amount. Try again in a few minutes.
If the partner doesn’t answer the refund in time, the call fails with 504 partner_timeout, but the refund is kept against your Idempotency-Key. Retry with
the same key and you get the credit note for that refund; never send it with a
new key.
A refund credit note is refused before any money moves if it breaks a rule. Each
credit note fires credit_note.created. GET /v1/credit_notes?invoice=<id> lists
an invoice’s credit notes.
Customer credit
Section titled “Customer credit”A customer’s credit balance is held per currency. It is spent automatically by
their next invoice in that currency, whether a subscription bill or a one-off
invoice, until it runs out. The spend shows on the invoice as credit_applied and
a negative credit line. A one-off invoice can skip it with
"apply_credit": false.
Credit comes from credit notes with outcome: customer_credit, from a payment
that arrived after its bill closed (below), or from a grant:
curl https://sandbox.paysigna.com/v1/customers/<customer id>/credit \ -H "Authorization: Bearer psg_test_…" \ -H "Content-Type: application/json" \ -d '{ "amount": 2000, "currency": "GHS", "note": "Sorry about the outage on 3 Oct" }'Grants are positive only. Credit goes down only by being spent, so a balance can never fall below zero, and it cannot be paid out as money.
GET /v1/customers/{id}/credit returns balances (one per currency) and
entries, the history. Each entry is positive (credit given) or negative (credit
spent), with a reason: credit_note, adjustment (a grant),
applied_to_invoice, proration, overpayment, overpayment_refund (credit
used up by refunding the payment it came from), or overpayment_refund_failed
(given back because that refund failed).
A payment that arrives late
Section titled “A payment that arrives late”A prompt can expire, the attempt be marked failed, and the customer’s payment then be confirmed later by the partner (see late outcomes). The billing cycle picks these up:
- The bill is still open: that payment pays it, with
invoice.paidand the receipt or subscription activation as usual. - The bill is already closed (paid by another attempt, voided, or
uncollectible): the amount becomes the customer’s credit, reason
overpayment, with a note such as “Payment received after invoice INV-… was closed (paid).” Their next bill in that currency spends it, or you can refund the payment.
The same applies to a payment that completes after its bill was voided, for example a subscription cancelled while a charge was in flight. Each payment is handled once. It is never dropped.
Refunding the late payment uses up its credit. The refund spends the credit
kept from that payment in the same step, so the customer never gets both. You can
refund only what is still unspent: the smaller of what that payment left as credit
(less anything already refunded) and the customer’s whole credit balance, since
credit already spent on a later bill can’t be refunded again. Beyond that the
refund is refused with 409 conflict: “This payment arrived after its bill was
closed and was kept as the customer’s account credit. Refunding it spends that
credit, and {amount} of it is unspent, so at most that can be refunded.” If the
refund then fails, the credit is given back.
Webhooks
Section titled “Webhooks”| Event | When |
|---|---|
invoice.created |
Any invoice opened, including a one-off. |
invoice.finalized |
A one-off invoice was issued. |
invoice.paid |
An invoice was paid. amount is 0 when a coupon or credit covered it, and there is no transaction_id. |
invoice.payment_failed |
An attempt failed. More may follow. |
invoice.uncollectible |
Collection ended unpaid. For a one-off invoice nothing else happens. |
invoice.voided |
An open invoice was voided. |
credit_note.created |
A credit note was issued. Carries the outcome, and refund_id for a refund. |
Invoice events for a one-off invoice carry no subscription_id. See
Webhooks for signatures and retries.
What the customer receives
Section titled “What the customer receives”| When | How | |
|---|---|---|
| A one-off invoice is issued | SMS and email | The amount, invoice number, due date and any PO number. Due on receipt, a prompt follows at once; on terms, it says a prompt will come on the due date. |
| An attempt to collect it failed | SMS and email | The date it is still due by, and when the next prompt will come, or that no more will. |
| It is paid | A receipt. Also sent when credit covered the whole invoice. |
They are sent in your name. In the sandbox they are prefixed [TEST].

