Payment sessions
Fix an amount on your server, then let your checkout page collect it.
| Endpoint | What it does |
|---|---|
POST /v1/payment_sessions |
Create a payment session |
GET /v1/payment_sessions/{id} |
Retrieve a payment session |
POST /v1/payment_sessions/{id}/cancel |
Cancel a payment session |
Try any of them in the API tester.
Create a payment session
Section titled “Create a payment session”POST /v1/payment_sessions
Opens a hosted-checkout session and returns a short-lived client token for your page.
THE AMOUNT IS FIXED HERE. amount, currency, fee_bearer and channels are recorded on the session by this call, with your secret key, and nothing the customer’s browser sends can change any of them. Confirming reads them back out of the session.
client_token is returned ONCE and is not recoverable: we store only a digest. Pass it to your page and do not log it or put it in a URL, where it would end up in browser history and in every referrer header the page emits.
The session and its token live for 15 minutes by default and 30 at the most. That covers a customer reading the page, choosing a network, typing a number and approving a handset prompt, with one retry. A longer window is not available: create a second session instead.
No Idempotency-Key is required. A duplicate session moves no money; use the token the successful call returned and let the other session expire.
Authentication: a secret key (server-side only) with the scope payment_session.create.
Request body
Section titled “Request body”| Field | Type | Required | Allowed values and limits | What it is |
|---|---|---|---|---|
amount |
integer | yes | min 1; e.g. 1250 |
Amount in minor units — 1250 is GHS 12.50. FIXED once the session is created; the customer’s browser cannot change it. |
callback_url |
string (uri) | no | HTTPS only. | |
channels |
array | no | Channels this session may be paid with. Omit for every channel your account is enabled for. A channel you are not enabled for is refused here rather than at checkout. | |
currency |
string | yes | exactly 3 characters; e.g. GHS |
The currency of amount, as an ISO 4217 code such as GHS. Supported currencies are listed at /v1/reference/currencies. |
customer |
object | no | ||
customer.email |
string (email) | no | at most 255 characters; e.g. [email protected] |
Where the receipt goes. Takes precedence over an email the customer types at checkout, and is needed for a card payment unless the customer supplies one. |
customer.id |
string (uuid) | no | An existing PaySigna customer id. | |
customer.name |
string | no | at most 150 characters; e.g. Ama Mensah |
Pre-fills the payer’s name. A name the customer types at checkout takes precedence. |
description |
string | no | at most 500 characters | Shown to the customer on the checkout page. |
expires_in_seconds |
integer | no | min 60; max 1800 | How long the session and its client token live. Default 900 (15 minutes), maximum 1800. Clamped, not refused. |
fee_bearer |
string | no | One of: merchant, customer |
Only honoured if your account permits per-transaction choice. |
metadata |
object | no | Returned on your own reads. NEVER returned to the browser. | |
reference |
string | no | at most 100 characters | Your own identifier for this payment. Never shown to the customer. |
return_url |
string (uri) | no | Where to send the customer when the flow ends. HTTPS only. |
curl -X POST https://sandbox.paysigna.com/v1/payment_sessions \ -H "Authorization: Bearer sk_test_…" \ -H "Content-Type: application/json" \ -d '{ "amount": 1250, "currency": "GHS"}'Response 201
Section titled “Response 201”| Field | Type | What it is |
|---|---|---|
amount |
integer | In minor units. Fixed; nothing can change it. |
cancelled_at |
string (date-time) | When you cancelled the session. |
channels |
array | The channels the customer may pay with: mobile_money, bank or card. |
client_token |
string | Shown once, on creation. Give it to your checkout page. Not recoverable. |
completed_at |
string (date-time) | When the customer confirmed and a charge was created. |
confirm_attempts |
integer | How many times the customer has tried to confirm. A session allows at most 5. |
created_at |
string (date-time) | When the session was created. |
currency |
string | ISO 4217 code of amount. |
description |
string | Shown to the customer on the checkout page. |
display_name |
string | The payee name shown to the customer. |
expired_at |
string (date-time) | When the session expired unpaid. |
expires_at |
string (date-time) | After this the session cannot be paid. |
fee_bearer |
string | Who pays the fees: merchant (deducted from what you receive) or customer (added to what they pay). Absent means your account’s default. One of: merchant, customer |
id |
string | The session’s id. Pass it to your checkout page with the client token. |
metadata |
object | The metadata you sent, unchanged. |
object |
string | |
return_url |
string | Where the customer is sent when the flow ends. |
status |
string | The SESSION’s state, not the payment’s. Once it is completed a charge exists; read the charge for whether the money moved. One of: created, confirming, completed, cancelled, expired |
transaction_id |
string | The charge this session produced. Read it at /v1/charges/{id} for the payment’s state. |
url |
string | Shown once, on creation: PaySigna’s hosted payment page for this session, with your branding. Send the customer here if you are not building your own checkout page. It carries the client token after #, which browsers never send to a server. |
your_reference |
string | The reference you sent when creating the session. |
Errors
Section titled “Errors”400: the request is malformed401: the key is missing, wrong or revoked403: the key lacks the scope, or your account may not do this409: it conflicts with the current state, or the idempotency key was used for a different request422: a field is invalid;errorsnames it429: too many requests; wait forRetry-After500: Internal Server Error503: the payment partner is unreachable; safe to retry
Every error is a JSON problem with a stable code. See Errors.
Retrieve a payment session
Section titled “Retrieve a payment session”GET /v1/payment_sessions/{id}
Reads a session’s own state: whether it has been confirmed, cancelled or expired, and which charge it produced.
status describes the SESSION, not the payment. Once it is completed a charge exists at transaction_id, and that charge is the authority on whether the money moved — this endpoint deliberately carries no copy of its status, because two records of one fact drift. Read /v1/charges/{id} for the payment.
Authentication: a secret key (server-side only) with the scope payment_session.read.
Parameters
Section titled “Parameters”| Field | Type | Required | Allowed values and limits | What it is |
|---|---|---|---|---|
{id} |
string (uuid) | yes | The payment session’s id, as returned when it was created. |
Response 200
Section titled “Response 200”| Field | Type | What it is |
|---|---|---|
amount |
integer | In minor units. Fixed; nothing can change it. |
cancelled_at |
string (date-time) | When you cancelled the session. |
channels |
array | The channels the customer may pay with: mobile_money, bank or card. |
client_token |
string | Shown once, on creation. Give it to your checkout page. Not recoverable. |
completed_at |
string (date-time) | When the customer confirmed and a charge was created. |
confirm_attempts |
integer | How many times the customer has tried to confirm. A session allows at most 5. |
created_at |
string (date-time) | When the session was created. |
currency |
string | ISO 4217 code of amount. |
description |
string | Shown to the customer on the checkout page. |
display_name |
string | The payee name shown to the customer. |
expired_at |
string (date-time) | When the session expired unpaid. |
expires_at |
string (date-time) | After this the session cannot be paid. |
fee_bearer |
string | Who pays the fees: merchant (deducted from what you receive) or customer (added to what they pay). Absent means your account’s default. One of: merchant, customer |
id |
string | The session’s id. Pass it to your checkout page with the client token. |
metadata |
object | The metadata you sent, unchanged. |
object |
string | |
return_url |
string | Where the customer is sent when the flow ends. |
status |
string | The SESSION’s state, not the payment’s. Once it is completed a charge exists; read the charge for whether the money moved. One of: created, confirming, completed, cancelled, expired |
transaction_id |
string | The charge this session produced. Read it at /v1/charges/{id} for the payment’s state. |
url |
string | Shown once, on creation: PaySigna’s hosted payment page for this session, with your branding. Send the customer here if you are not building your own checkout page. It carries the client token after #, which browsers never send to a server. |
your_reference |
string | The reference you sent when creating the session. |
Errors
Section titled “Errors”400: the request is malformed401: the key is missing, wrong or revoked403: the key lacks the scope, or your account may not do this404: no such resource on this account422: a field is invalid;errorsnames it429: too many requests; wait forRetry-After500: Internal Server Error503: the payment partner is unreachable; safe to retry
Every error is a JSON problem with a stable code. See Errors.
Cancel a payment session
Section titled “Cancel a payment session”POST /v1/payment_sessions/{id}/cancel
Withdraws a session nobody has paid yet. The client token stops working immediately — there is no denylist and no cache to wait for, because the token is a lookup key and this is its row.
Only a session in created can be cancelled. One that is mid-confirmation would race the charge, and one that is completed has already been sent to a partner: the answer there is a refund, not a cancellation.
Authentication: a secret key (server-side only) with the scope payment_session.create.
Parameters
Section titled “Parameters”| Field | Type | Required | Allowed values and limits | What it is |
|---|---|---|---|---|
{id} |
string (uuid) | yes | The payment session to cancel. Only a session still in created status can be cancelled. |
Request body
Section titled “Request body”| Field | Type | Required | Allowed values and limits | What it is |
|---|---|---|---|---|
reason |
string | no | at most 200 characters | Recorded for your own audit. Never shown to the customer. |
curl -X POST https://sandbox.paysigna.com/v1/payment_sessions/{id}/cancel \ -H "Authorization: Bearer sk_test_…" \ -H "Content-Type: application/json" \ -d '{}'Response 200
Section titled “Response 200”| Field | Type | What it is |
|---|---|---|
amount |
integer | In minor units. Fixed; nothing can change it. |
cancelled_at |
string (date-time) | When you cancelled the session. |
channels |
array | The channels the customer may pay with: mobile_money, bank or card. |
client_token |
string | Shown once, on creation. Give it to your checkout page. Not recoverable. |
completed_at |
string (date-time) | When the customer confirmed and a charge was created. |
confirm_attempts |
integer | How many times the customer has tried to confirm. A session allows at most 5. |
created_at |
string (date-time) | When the session was created. |
currency |
string | ISO 4217 code of amount. |
description |
string | Shown to the customer on the checkout page. |
display_name |
string | The payee name shown to the customer. |
expired_at |
string (date-time) | When the session expired unpaid. |
expires_at |
string (date-time) | After this the session cannot be paid. |
fee_bearer |
string | Who pays the fees: merchant (deducted from what you receive) or customer (added to what they pay). Absent means your account’s default. One of: merchant, customer |
id |
string | The session’s id. Pass it to your checkout page with the client token. |
metadata |
object | The metadata you sent, unchanged. |
object |
string | |
return_url |
string | Where the customer is sent when the flow ends. |
status |
string | The SESSION’s state, not the payment’s. Once it is completed a charge exists; read the charge for whether the money moved. One of: created, confirming, completed, cancelled, expired |
transaction_id |
string | The charge this session produced. Read it at /v1/charges/{id} for the payment’s state. |
url |
string | Shown once, on creation: PaySigna’s hosted payment page for this session, with your branding. Send the customer here if you are not building your own checkout page. It carries the client token after #, which browsers never send to a server. |
your_reference |
string | The reference you sent when creating the session. |
Errors
Section titled “Errors”400: the request is malformed401: the key is missing, wrong or revoked403: the key lacks the scope, or your account may not do this404: no such resource on this account409: it conflicts with the current state, or the idempotency key was used for a different request422: a field is invalid;errorsnames it429: too many requests; wait forRetry-After500: Internal Server Error503: the payment partner is unreachable; safe to retry
Every error is a JSON problem with a stable code. See Errors.

