Skip to content

Billing

Products, plans, subscriptions, invoices, coupons and credit.

Endpoint What it does
POST /v1/coupons Create a coupon
GET /v1/coupons List coupons
POST /v1/coupons/{id} Activate or deactivate a coupon
GET /v1/coupons/{id} Retrieve a coupon
POST /v1/credit_notes Issue a credit note
GET /v1/credit_notes List credit notes
POST /v1/customers/{id}/credit Give a customer credit
GET /v1/customers/{id}/credit Retrieve a customer’s credit
POST /v1/invoices Issue a one-off invoice
GET /v1/invoices List invoices
GET /v1/invoices/{id} Retrieve an invoice
POST /v1/invoices/{id}/void Void an invoice
POST /v1/plans Create a plan
GET /v1/plans List plans
POST /v1/plans/{id} Activate or deactivate a plan
GET /v1/plans/{id} Retrieve a plan
POST /v1/products Create a product
GET /v1/products List products
POST /v1/products/{id} Update a product
GET /v1/products/{id} Retrieve a product
POST /v1/subscriptions Create a subscription
GET /v1/subscriptions List subscriptions
GET /v1/subscriptions/{id} Retrieve a subscription
POST /v1/subscriptions/{id}/cancel Cancel a subscription
POST /v1/subscriptions/{id}/discount Apply a coupon to a subscription
DELETE /v1/subscriptions/{id}/discount Remove a subscription’s discount
POST /v1/subscriptions/{id}/pause Pause a subscription
POST /v1/subscriptions/{id}/resume Resume a subscription

Try any of them in the API tester.

POST /v1/coupons

A percentage or a fixed amount off a subscription’s bills: the next one (once), a number of them (repeating), or all of them (forever). With a code, customers can enter it when subscribing.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
amount_off integer no min 1 Minor units. With currency.
code string no at most 40 characters; e.g. LAUNCH20 Optional. Case does not matter.
currency string no exactly 3 characters; e.g. GHS The currency of amount_off, as an ISO 4217 code. Required with amount_off; the coupon then applies only to plans in this currency.
duration string yes One of: once, repeating, forever How many bills the discount applies to: once (the next bill only), repeating (the next duration_periods bills) or forever.
duration_periods integer no min 1; max 60 How many bills, for repeating.
max_redemptions integer no min 1 How many subscriptions may use the coupon in total. Omit for no limit.
name string yes at least 1 characters; at most 120 characters; e.g. Launch offer Shown on the invoice’s discount line.
percent_off number no min 0.01; max 100; e.g. 20 A percentage taken off each discounted bill, up to two decimal places. Send percent_off or amount_off, not both.
products array no Restrict to these products.
redeem_by string (date-time) no The last moment the coupon can be applied, as an RFC 3339 timestamp in the future. Subscriptions that already have it keep it. Omit for no deadline.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/coupons \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"code": "LAUNCH20",
"currency": "GHS",
"duration": "once",
"name": "Launch offer",
"percent_off": 20
}'
Field Type What it is
active boolean
amount_off integer
code string What a customer types. Absent on a coupon only you apply.
created_at string (date-time)
currency string
duration string One of: once, repeating, forever
duration_periods integer
id string
max_redemptions integer
name string Shown on the invoice’s discount line.
object string
percent_off number
products array Empty means any product.
redeem_by string (date-time)
times_redeemed integer
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/coupons

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type What it is
data array
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/coupons/{id}

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The coupon to update.
Field Type Required Allowed values and limits What it is
active boolean yes An inactive coupon cannot be applied. Subscriptions already carrying it keep it.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/coupons/{id} \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"active": false
}'
Field Type What it is
active boolean
amount_off integer
code string What a customer types. Absent on a coupon only you apply.
created_at string (date-time)
currency string
duration string One of: once, repeating, forever
duration_periods integer
id string
max_redemptions integer
name string Shown on the invoice’s discount line.
object string
percent_off number
products array Empty means any product.
redeem_by string (date-time)
times_redeemed integer
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/coupons/{id}

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The id of the object named in the path. For /v1/customers/{id}/credit it is the customer’s id.
Field Type What it is
active boolean
amount_off integer
code string What a customer types. Absent on a coupon only you apply.
created_at string (date-time)
currency string
duration string One of: once, repeating, forever
duration_periods integer
id string
max_redemptions integer
name string Shown on the invoice’s discount line.
object string
percent_off number
products array Empty means any product.
redeem_by string (date-time)
times_redeemed integer
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/credit_notes

Reduces what an invoice is worth.

On an OPEN invoice, reduce_due takes the amount off what is collected; brought to zero, the invoice is paid.

On a PAID invoice, customer_credit adds the amount to the customer’s credit balance, which their next invoices spend; refund returns it to the payment that paid the invoice, and also needs refund.create.

Requires an Idempotency-Key header.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
amount integer yes min 1; e.g. 2500 How much to credit, in minor units, in the invoice’s currency. At most what is still due on an open invoice, or what is left to credit on a paid one.
invoice string (uuid) yes The invoice to credit. It must be open or paid.
memo string no at most 500 characters A note for your own records, stored on the credit note.
outcome string yes One of: customer_credit, refund, reduce_due What happens to the amount. reduce_due: takes it off what an open invoice still asks for. customer_credit: on a paid invoice, adds it to the customer’s credit balance, which their next invoices spend. refund: on a paid invoice, refunds it to the payment that paid it (your key needs the refund.create scope).
reason string yes One of: duplicate, fraudulent, order_change, product_unsatisfactory, other Why the invoice is being credited. Recorded on the note and in the credit_note.created event.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/credit_notes \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"amount": 2500,
"invoice": "01a1…",
"outcome": "customer_credit",
"reason": "duplicate"
}'
Field Type What it is
amount integer
created_at string (date-time)
currency string
id string
invoice string
memo string
number string
object string
outcome string One of: customer_credit, refund, reduce_due
reason string One of: duplicate, fraudulent, order_change, product_unsatisfactory, other
refund string The refund, for the refund outcome. Follow it on GET /v1/refunds/{id}.
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/credit_notes

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type Required Allowed values and limits What it is
invoice string (uuid) no Only this invoice’s credit notes. Omit for all of them.
Field Type What it is
data array
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/customers/{id}/credit

Goodwill or a correction. Their next invoices in that currency spend it.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The customer to give credit to.
Field Type Required Allowed values and limits What it is
amount integer yes min 1; e.g. 1000 How much credit to add, in minor units. Credit can only be added here; it is used up by the customer’s next invoices.
currency string yes exactly 3 characters; e.g. GHS The currency of the credit, as an ISO 4217 code. It is spent only on invoices in this currency.
note string no at most 500 characters Why the credit was given, such as a goodwill gesture. Shown on the credit entry.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/customers/{id}/credit \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"amount": 1000,
"currency": "GHS"
}'
Field Type What it is
balances array Spent automatically by the customer’s next invoices in that currency.
customer string
entries array
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/customers/{id}/credit

Balances per currency, and the latest entries: credit given and spent.

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The id of the object named in the path. For /v1/customers/{id}/credit it is the customer’s id.
Field Type What it is
balances array Spent automatically by the customer’s next invoices in that currency.
customer string
entries array
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/invoices

A bill you issue by hand to a customer you already have: consulting, a setup fee, a purchase order. Lines are added up; the customer’s credit balance is spent unless apply_credit is false.

It is collected like a subscription’s bill: a prompt goes to payment_method within a minute and is retried on your schedule. due_in_days sets the payment terms printed on it; it is closed uncollectible 30 days after it falls due.

Requires an Idempotency-Key header.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
apply_credit boolean no Spend the customer’s credit balance. Defaults to true.
currency string yes exactly 3 characters; e.g. GHS The currency of every line, as an ISO 4217 code.
customer string (uuid) yes The stored customer to bill. They must not be blocked.
due_in_days integer no min 0; max 90 Payment terms. 0, the default, is due on receipt.
lines array yes What is being billed: 1 to 50 lines. Each line’s amount is quantity times unit_amount.
memo string no at most 500 characters A note printed on the invoice.
payment_method object yes
payment_method.momo_network string no at most 30 characters; e.g. mtn The number’s network code, such as mtn, telecel or airteltigo. Required unless momo_network_id is sent instead. Codes are listed at /v1/reference/momo-networks.
payment_method.momo_network_id string (uuid) no The network’s id from /v1/reference/momo-networks. An alternative to momo_network; send one, not both.
payment_method.phone string no at most 20 characters; e.g. 0244123456 Defaults to the customer’s phone.
purchase_order string no at most 60 characters; e.g. PO-2026-118 The customer’s purchase order number, printed on the invoice.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/invoices \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"currency": "GHS",
"customer": "01a1…",
"lines": "string",
"payment_method": {
"momo_network": "mtn",
"phone": "0244123456"
},
"purchase_order": "PO-2026-118"
}'
Field Type What it is
amount integer What is collected: subtotal - discount - credit_applied.
attempt_count integer
attempts array On a single invoice read only.
collect_until string (date-time) If unpaid by then the invoice is uncollectible (and a subscription’s ends).
created_at string (date-time)
credit_applied integer Paid from the customer’s credit balance, and taken off by credit notes on the open invoice.
credited integer Given back on the paid invoice by credit notes.
currency string
customer string
discount integer Taken off by the subscription’s coupon.
due_on string (date) A one-off invoice’s due date.
id string
kind string One of: subscription, proration, one_off
lines array On a single invoice read only.
memo string
next_attempt_at string (date-time)
number string
object string
paid_at string (date-time)
payment_method string A one-off invoice’s number, masked.
period_end string (date-time)
period_start string (date-time)
purchase_order string
status string One of: open, paid, void, uncollectible
subscription string Absent on a one-off invoice.
subtotal integer The lines before discount and credit.
transaction string The charge that paid it. Absent when discount or credit left nothing to pay.
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/invoices

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type Required Allowed values and limits What it is
subscription string (uuid) no Only this subscription’s invoices.
status string no One of: open, paid, void, uncollectible Only invoices in this state. open: awaiting payment. paid: settled. void: cancelled, nothing is owed. uncollectible: collection stopped unpaid.
limit integer no min 1; max 100 Rows to return, newest first. Default 25, clamped to 100.
Field Type What it is
data array
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/invoices/{id}

With every attempt to collect it, and the charge each one created.

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The id of the object named in the path. For /v1/customers/{id}/credit it is the customer’s id.
Field Type What it is
amount integer What is collected: subtotal - discount - credit_applied.
attempt_count integer
attempts array On a single invoice read only.
collect_until string (date-time) If unpaid by then the invoice is uncollectible (and a subscription’s ends).
created_at string (date-time)
credit_applied integer Paid from the customer’s credit balance, and taken off by credit notes on the open invoice.
credited integer Given back on the paid invoice by credit notes.
currency string
customer string
discount integer Taken off by the subscription’s coupon.
due_on string (date) A one-off invoice’s due date.
id string
kind string One of: subscription, proration, one_off
lines array On a single invoice read only.
memo string
next_attempt_at string (date-time)
number string
object string
paid_at string (date-time)
payment_method string A one-off invoice’s number, masked.
period_end string (date-time)
period_start string (date-time)
purchase_order string
status string One of: open, paid, void, uncollectible
subscription string Absent on a one-off invoice.
subtotal integer The lines before discount and credit.
transaction string The charge that paid it. Absent when discount or credit left nothing to pay.
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/invoices/{id}/void

Withdraws an open one-off or proration invoice; collection stops. A subscription’s period bill is voided by cancelling or pausing the subscription. A paid invoice is not voided: issue a credit note.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The id of the object named in the path. For /v1/customers/{id}/credit it is the customer’s id.
Field Type What it is
amount integer What is collected: subtotal - discount - credit_applied.
attempt_count integer
attempts array On a single invoice read only.
collect_until string (date-time) If unpaid by then the invoice is uncollectible (and a subscription’s ends).
created_at string (date-time)
credit_applied integer Paid from the customer’s credit balance, and taken off by credit notes on the open invoice.
credited integer Given back on the paid invoice by credit notes.
currency string
customer string
discount integer Taken off by the subscription’s coupon.
due_on string (date) A one-off invoice’s due date.
id string
kind string One of: subscription, proration, one_off
lines array On a single invoice read only.
memo string
next_attempt_at string (date-time)
number string
object string
paid_at string (date-time)
payment_method string A one-off invoice’s number, masked.
period_end string (date-time)
period_start string (date-time)
purchase_order string
status string One of: open, paid, void, uncollectible
subscription string Absent on a one-off invoice.
subtotal integer The lines before discount and credit.
transaction string The charge that paid it. Absent when discount or credit left nothing to pay.
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/plans

A price for a product: an amount every interval_count intervals, with an optional free trial.

A PLAN’S PRICE NEVER CHANGES ONCE SOMEONE IS ON IT. To charge a new price, create a new plan and deactivate the old one; existing subscribers keep the price they agreed to.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
amount integer yes min 1; e.g. 5000 Per period, in minor units.
currency string yes exactly 3 characters; e.g. GHS The currency of amount, as an ISO 4217 code.
interval string yes One of: day, week, month, year The unit of the billing period. With interval_count it sets how often the customer is billed.
interval_count integer no min 1; max 365 Defaults to 1.
metadata object no Your own key-value data. Stored with the plan and returned on every read.
nickname string no at most 120 characters; e.g. Monthly A short label that tells this plan apart from the product’s others. Shown after the product name on invoices.
product string (uuid) yes The product this plan prices.
trial_days integer no min 0; max 365 Free days before the first bill. Omit or send 0 for no trial, so the first bill is due when the customer subscribes.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/plans \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "GHS",
"interval": "day",
"nickname": "Monthly",
"product": "01a1…"
}'
Field Type What it is
active boolean
amount integer Per period, in minor units.
created_at string (date-time)
currency string
id string
interval string One of: day, week, month, year
interval_count integer Bill every interval_count intervals: 3 with month is quarterly.
metadata object
nickname string
object string
product string
product_name string
subscribers integer Subscriptions on this plan that have not ended. Secret keys only.
trial_days integer
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/plans

With a secret key, every plan and its subscriber count. With a publishable key holding plan.read, for a pricing page: active plans only.

Authentication: a secret key (server-side only) with the scope billing.read, or a publishable key (safe in a browser) with the scope plan.read.

Field Type Required Allowed values and limits What it is
product string (uuid) no Only this product’s plans. Omit for every product.
active string no One of: true, false A publishable key always gets active plans only.
limit integer no min 1; max 100 Rows to return, newest first. Default 25, clamped to 100.
Field Type What it is
data array
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/plans/{id}

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The plan to update.
Field Type Required Allowed values and limits What it is
active boolean yes A plan’s price never changes once it has subscribers: create a new plan instead. Deactivating stops new subscriptions; existing ones keep renewing.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/plans/{id} \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"active": false
}'
Field Type What it is
active boolean
amount integer Per period, in minor units.
created_at string (date-time)
currency string
id string
interval string One of: day, week, month, year
interval_count integer Bill every interval_count intervals: 3 with month is quarterly.
metadata object
nickname string
object string
product string
product_name string
subscribers integer Subscriptions on this plan that have not ended. Secret keys only.
trial_days integer
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/plans/{id}

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The id of the object named in the path. For /v1/customers/{id}/credit it is the customer’s id.
Field Type What it is
active boolean
amount integer Per period, in minor units.
created_at string (date-time)
currency string
id string
interval string One of: day, week, month, year
interval_count integer Bill every interval_count intervals: 3 with month is quarterly.
metadata object
nickname string
object string
product string
product_name string
subscribers integer Subscriptions on this plan that have not ended. Secret keys only.
trial_days integer
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/products

Something you sell on a subscription. Its prices are plans.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
description string no at most 500 characters A longer description of the product, returned on every read.
metadata object no Your own key-value data. Stored with the product and returned on every read.
name string yes at least 1 characters; at most 120 characters; e.g. Premium membership The product’s name. Shown to customers on invoices, the checkout page and subscription emails.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/products \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium membership"
}'
Field Type What it is
active boolean Inactive products cannot be subscribed to. Existing subscribers are unaffected.
created_at string (date-time)
description string
id string
metadata object
name string
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/products

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type Required Allowed values and limits What it is
active string no One of: true, false Filter by whether the product is on sale.
limit integer no min 1; max 100 Rows to return, newest first. Default 25, clamped to 100.
Field Type What it is
data array
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/products/{id}

Name, description, and whether it is on sale. An inactive product cannot be subscribed to; existing subscribers keep renewing.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The product to update.
Field Type Required Allowed values and limits What it is
active boolean no false takes the product off sale: no new subscriptions to its plans. Existing subscribers are unaffected. Omit to leave it unchanged.
description string no at most 500 characters A new description. Omit to leave it unchanged.
name string no at least 1 characters; at most 120 characters A new name. Omit to leave it unchanged.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/products/{id} \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Field Type What it is
active boolean Inactive products cannot be subscribed to. Existing subscribers are unaffected.
created_at string (date-time)
description string
id string
metadata object
name string
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/products/{id}

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The id of the object named in the path. For /v1/customers/{id}/credit it is the customer’s id.
Field Type What it is
active boolean Inactive products cannot be subscribed to. Existing subscribers are unaffected.
created_at string (date-time)
description string
id string
metadata object
name string
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/subscriptions

Subscribes a customer to a plan.

WITHOUT A TRIAL the subscription is incomplete and its first invoice opens now. With payment_method, a prompt for it goes to that number within a minute; with checkout, the response carries a payment_session for your checkout page and the number the customer pays from becomes the renewal number. Either way it becomes active when the first invoice is paid.

WITH A TRIAL it is trialing, nothing is collected, and the first invoice opens when the trial ends, so payment_method is required.

RENEWAL IS A PROMPT. Each period we open an invoice and prompt the renewal number; the customer approves on their phone. We tell them by SMS and email, with a code for the PaySigna subscription page where they can pay a missed bill or cancel. If a bill is not paid we retry on your schedule (by default days 0, 1, 3 and 5) and end the subscription on day 7.

Requires an Idempotency-Key header.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
checkout object no
checkout.return_url string (uri) no at most 2048 characters Where to send the customer after they pay the first bill.
coupon string no at most 64 characters A coupon’s id, or the code the customer typed. Taken off from the first bill.
customer object yes
customer.email string (email) no at most 254 characters; e.g. [email protected] Where subscription emails, such as the welcome and renewal notices, are sent.
customer.id string (uuid) no A stored customer. Otherwise send phone or email.
customer.name string no at most 200 characters; e.g. Ama Mensah The customer’s name, used in their emails and on your dashboard.
customer.phone string no at most 20 characters; e.g. 0244123456 The customer’s mobile-money number. Also the number each bill is prompted on unless payment_method.phone is sent.
metadata object no Your own key-value data. Stored with the subscription and returned on every read.
payment_method object no
payment_method.momo_network string no at most 30 characters; e.g. mtn The number’s network code, such as mtn, telecel or airteltigo. Required with payment_method unless momo_network_id is sent instead. Codes are listed at /v1/reference/momo-networks.
payment_method.momo_network_id string (uuid) no The network’s id from /v1/reference/momo-networks. An alternative to momo_network; send one, not both.
payment_method.phone string no at most 20 characters The mobile-money number each bill is prompted on. Defaults to the customer’s phone.
plan string (uuid) yes The plan to subscribe the customer to. It and its product must be active.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/subscriptions \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"customer": {
"email": "[email protected]",
"name": "Ama Mensah",
"phone": "0244123456"
},
"plan": "01a1…"
}'
Field Type What it is
cancel_at_period_end boolean
cancel_reason string One of: merchant, customer, unpaid
canceled_at string (date-time)
created_at string (date-time)
current_period_end string (date-time)
current_period_start string (date-time)
customer object
customer.email string
customer.id string
customer.name string
id string
latest_invoice string
metadata object
object string
paused_at string (date-time)
payment_method object
payment_method.channel string
payment_method.masked string
payment_session object
payment_session.client_token string Hand this to your checkout page, as for any payment session. Shown once.
payment_session.expires_at string (date-time)
payment_session.id string
plan object
plan.amount integer
plan.currency string
plan.id string
plan.interval string
plan.interval_count integer
plan.nickname string
plan.product string
plan.product_name string
status string One of: incomplete, trialing, active, past_due, paused, canceled
trial_end string (date-time)
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/subscriptions

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type Required Allowed values and limits What it is
status string no One of: incomplete, trialing, active, past_due, paused, canceled Only subscriptions in this state. incomplete: the first bill is unpaid. trialing: in a free trial. active: paid up. past_due: a renewal bill is unpaid and being retried. paused: billing is on hold. canceled: ended.
customer string (uuid) no Only this customer’s subscriptions.
plan string (uuid) no Only subscriptions on this plan.
limit integer no min 1; max 100 Rows to return, newest first. Default 25, clamped to 100.
Field Type What it is
data array
object string
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


GET /v1/subscriptions/{id}

Authentication: a secret key (server-side only) with the scope billing.read.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The id of the object named in the path. For /v1/customers/{id}/credit it is the customer’s id.
Field Type What it is
cancel_at_period_end boolean
cancel_reason string One of: merchant, customer, unpaid
canceled_at string (date-time)
created_at string (date-time)
current_period_end string (date-time)
current_period_start string (date-time)
customer object
customer.email string
customer.id string
customer.name string
id string
latest_invoice string
metadata object
object string
paused_at string (date-time)
payment_method object
payment_method.channel string
payment_method.masked string
payment_session object
payment_session.client_token string Hand this to your checkout page, as for any payment session. Shown once.
payment_session.expires_at string (date-time)
payment_session.id string
plan object
plan.amount integer
plan.currency string
plan.id string
plan.interval string
plan.interval_count integer
plan.nickname string
plan.product string
plan.product_name string
status string One of: incomplete, trialing, active, past_due, paused, canceled
trial_end string (date-time)
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/subscriptions/{id}/cancel

Ends it now, voiding any open invoice, or with at_period_end when the paid period runs out. The customer is emailed when it ends.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The subscription to cancel.
Field Type Required Allowed values and limits What it is
at_period_end boolean no Keep it until the paid period ends, then end it. Otherwise it ends now and any open invoice is voided.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/subscriptions/{id}/cancel \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Field Type What it is
cancel_at_period_end boolean
cancel_reason string One of: merchant, customer, unpaid
canceled_at string (date-time)
created_at string (date-time)
current_period_end string (date-time)
current_period_start string (date-time)
customer object
customer.email string
customer.id string
customer.name string
id string
latest_invoice string
metadata object
object string
paused_at string (date-time)
payment_method object
payment_method.channel string
payment_method.masked string
payment_session object
payment_session.client_token string Hand this to your checkout page, as for any payment session. Shown once.
payment_session.expires_at string (date-time)
payment_session.id string
plan object
plan.amount integer
plan.currency string
plan.id string
plan.interval string
plan.interval_count integer
plan.nickname string
plan.product string
plan.product_name string
status string One of: incomplete, trialing, active, past_due, paused, canceled
trial_end string (date-time)
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/subscriptions/{id}/discount

Replaces any discount it had. Takes effect from the next bill opened.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The subscription to discount. It replaces any discount the subscription already has, from the next bill.
Field Type Required Allowed values and limits What it is
coupon string yes at least 1 characters; at most 64 characters A coupon’s id or code.
Terminal window
curl -X POST https://sandbox.paysigna.com/v1/subscriptions/{id}/discount \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"coupon": "string"
}'
Field Type What it is
cancel_at_period_end boolean
cancel_reason string One of: merchant, customer, unpaid
canceled_at string (date-time)
created_at string (date-time)
current_period_end string (date-time)
current_period_start string (date-time)
customer object
customer.email string
customer.id string
customer.name string
id string
latest_invoice string
metadata object
object string
paused_at string (date-time)
payment_method object
payment_method.channel string
payment_method.masked string
payment_session object
payment_session.client_token string Hand this to your checkout page, as for any payment session. Shown once.
payment_session.expires_at string (date-time)
payment_session.id string
plan object
plan.amount integer
plan.currency string
plan.id string
plan.interval string
plan.interval_count integer
plan.nickname string
plan.product string
plan.product_name string
status string One of: incomplete, trialing, active, past_due, paused, canceled
trial_end string (date-time)
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


DELETE /v1/subscriptions/{id}/discount

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The id of the object named in the path. For /v1/customers/{id}/credit it is the customer’s id.
Field Type What it is
cancel_at_period_end boolean
cancel_reason string One of: merchant, customer, unpaid
canceled_at string (date-time)
created_at string (date-time)
current_period_end string (date-time)
current_period_start string (date-time)
customer object
customer.email string
customer.id string
customer.name string
id string
latest_invoice string
metadata object
object string
paused_at string (date-time)
payment_method object
payment_method.channel string
payment_method.masked string
payment_session object
payment_session.client_token string Hand this to your checkout page, as for any payment session. Shown once.
payment_session.expires_at string (date-time)
payment_session.id string
plan object
plan.amount integer
plan.currency string
plan.id string
plan.interval string
plan.interval_count integer
plan.nickname string
plan.product string
plan.product_name string
status string One of: incomplete, trialing, active, past_due, paused, canceled
trial_end string (date-time)
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/subscriptions/{id}/pause

Stops billing. No invoice opens while it is paused.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The id of the object named in the path. For /v1/customers/{id}/credit it is the customer’s id.
Field Type What it is
cancel_at_period_end boolean
cancel_reason string One of: merchant, customer, unpaid
canceled_at string (date-time)
created_at string (date-time)
current_period_end string (date-time)
current_period_start string (date-time)
customer object
customer.email string
customer.id string
customer.name string
id string
latest_invoice string
metadata object
object string
paused_at string (date-time)
payment_method object
payment_method.channel string
payment_method.masked string
payment_session object
payment_session.client_token string Hand this to your checkout page, as for any payment session. Shown once.
payment_session.expires_at string (date-time)
payment_session.id string
plan object
plan.amount integer
plan.currency string
plan.id string
plan.interval string
plan.interval_count integer
plan.nickname string
plan.product string
plan.product_name string
status string One of: incomplete, trialing, active, past_due, paused, canceled
trial_end string (date-time)
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.


POST /v1/subscriptions/{id}/resume

Restarts billing. If the paid period ran out while it was paused, the next one starts now and is billed at once.

Authentication: a secret key (server-side only) with the scope billing.write.

Field Type Required Allowed values and limits What it is
{id} string (uuid) yes The id of the object named in the path. For /v1/customers/{id}/credit it is the customer’s id.
Field Type What it is
cancel_at_period_end boolean
cancel_reason string One of: merchant, customer, unpaid
canceled_at string (date-time)
created_at string (date-time)
current_period_end string (date-time)
current_period_start string (date-time)
customer object
customer.email string
customer.id string
customer.name string
id string
latest_invoice string
metadata object
object string
paused_at string (date-time)
payment_method object
payment_method.channel string
payment_method.masked string
payment_session object
payment_session.client_token string Hand this to your checkout page, as for any payment session. Shown once.
payment_session.expires_at string (date-time)
payment_session.id string
plan object
plan.amount integer
plan.currency string
plan.id string
plan.interval string
plan.interval_count integer
plan.nickname string
plan.product string
plan.product_name string
status string One of: incomplete, trialing, active, past_due, paused, canceled
trial_end string (date-time)
  • 400: the request is malformed
  • 401: the key is missing, wrong or revoked
  • 403: the key lacks the scope, or your account may not do this
  • 404: no such resource on this account
  • 409: it conflicts with the current state, or the idempotency key was used for a different request
  • 422: a field is invalid; errors names it
  • 429: too many requests; wait for Retry-After
  • 500: Internal Server Error
  • 503: the payment partner is unreachable; safe to retry

Every error is a JSON problem with a stable code. See Errors.