Rest API
Disbursements are instructions for Stitch to pay directly to destination accounts. Stitch provides an alternative REST interface to initiate Disbursement requests.
Please note the following about the Disbursements product:
- Disbursements are currently only available to South African customers.
- A float account is required.
Please contact support@stitch.money for more information on these requirements.
Use the Stitch REST API URL https://api.stitch.money/v2 for all requests on test and live clients. Standard HTTP status conventions are used for all API responses.
Disbursement Creation
A client token is required in order to create a disbursement to a particular account. You'll need to follow the steps described
in the client token guide to obtain a client token with the client_disbursement
scope.
To create the disbursement, the POST
request body includes the required amount,
destination account details, nonce to enforce uniqueness, and a beneficiary statement reference.
Initiate a POST
request to create a disbursement with the following request body:
curl -X POST 'https://api.stitch.money/v2/disbursements' \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"amount": {
"currency": "ZAR",
"quantity": "1"
},
"nonce": "5d29a396-5e6c-419e-9279-d26a01923815",
"beneficiaryReference": "TestReference",
"beneficiary": {
"name": "Lilo",
"accountNumber": "123456789",
"bank": "absa"
},
"type": "instant"
}'
Field | Type | Description | |
---|---|---|---|
amount.currency | string | The disbursement currency. Possible values: ZAR | |
amount.quantity | numeric | The disbursement value. | |
nonce | string | A random value to enforce uniqueness. | |
beneficiaryReference | string | A reference used for the beneficiary's statement | |
beneficiary.name | string | The beneficiary name | |
beneficiary.accountNumber | string | The beneficiary account number. | |
beneficiary.bank | string | The beneficiary bank. Must be a DisbursementBankBeneficiaryBankId value. | |
type | string (optional) | The type of disbursement. Possible values: instant or default |
The response includes the disbursement id
, status, and reason with status code 201 Created
if the disbursement is created. Standard HTTP response codes are returned for non-success scenarios. See HTTP Status Codes for a description of each possible response code.
{
"id": "ZGlzYnVyc2VtZW50L2MwNDBiOTI0LWFiYTItNDhhZS1hMzlmLTYxZmFhMGNkYTJiMw==",
"amount": {
"currency": "ZAR",
"quantity": "1"
},
"nonce": "5d29a396-5e6c-419e-9279-d26a01923815",
"beneficiaryReference": "TestReference",
"beneficiary": {
"name": "Lilo",
"accountNumber": "123456789",
"bankId": "absa"
},
"type": "instant",
"status": "pending",
"createdAt": "2025-12-01T00:00:00Z"
}
Field | Type | Description | |
---|---|---|---|
id | string | The unique Stitch ID | |
amount.currency | string | The disbursement currency. | |
amount.quantity | numeric | The disbursement value. | |
nonce | string | A random GUID to enforce uniqueness. | |
beneficiaryReference | string | A reference used for the beneficiary's statement | |
beneficiary.name | string | The beneficiary name | |
beneficiary.accountNumber | string | The beneficiary account number. | |
beneficiary.bank | string | The beneficiary bank. | |
type | string | The type of disbursement. | |
status | string | The disbursement status. | |
statusReason | string (optional) | The disbursement status reason. | |
description | string (optional) | The disbursement description if applicable. | |
createdAt | string | The creation timestamp of the disbursement. |
Disbursement Types
Type | Description |
---|---|
instant | Attempt to clear this payment with the bank immediately. If submitted after bank cutoff, it will rollover to the next business day. This method can incur an additional cost. Note: za_olympus_mobile , za_citibank and grindrod_bank do not support the INSTANT type. |
default | Attempt to clear this payment with the bank on the same day. If submitted after the bank's cutoff time, it will rollover to the next business day. |
In order to look up the status of the request in the API you will need the disbursement id
from the response. The disbursement id
will also be returned with the subscription payload. Examples of these are below.
If there is insufficient balance in your account to issue the disbursement, then the instruction will be accepted but remain on hold for 7
days until the balance is available. If the account balance is still insufficient after 7 days the disbursement will be marked as errored
with insufficient_funds
as the reason.
Please note that verification of the recipient's bank account is not done as part of this request. If an invalid beneficiary is specified as part of this request, the disbursement will fail with an appropriate error (see error reasons below).
To avoid this, you can separately query the bank account verification status, before making the disbursement request. Additionally, this query provides useful identity-related information, such as name of the account holder, and whether a given identity number matches the one associated with the account.
Implementing Retry Mechanisms
To handle the rare instance of a timeout or error response received on a disbursement request, we recommend implementing a retry mechanism to ensure successful processing. By resending the request with the same nonce
, you can safely retry without risking duplicate disbursements. Our platform will reject requests with duplicate nonce
values, ensuring that if a request was successfully initiated, another request with the same nonce value will not create an additional disbursement.
Best Practices for Retrying Requests
-
Retry with the same
nonce
: When retrying a request, ensure the originalnonce
is reused. This guarantees idempotency, allowing our platform to recognize and safely reject duplicate disbursements. -
Implement a backoff strategy: Use an exponential backoff or other delay mechanism between retries to avoid overwhelming your systems or our platform during transient issues.
-
Log and alert: Capture error responses and timeouts in your system logs, and consider setting up alerts for unresolved retries or frequent failures to help identify and resolve any underlying issues.
Retrieving Disbursement Status
Initiate a GET
request to retrieve a created disbursement by ID:
curl -X GET 'https://api.stitch.money/v2/disbursements/ZGlzYnVyc2VtZW50L2MwNDBiOTI0LWFiYTItNDhhZS1hMzlmLTYxZmFhMGNkYTJiMw==' \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
The response object is identical in structure to the one returned by the POST
request used to create a disbursement.
The response includes the disbursement id
, status, and reason if applicable.
{
"id": "ZGlzYnVyc2VtZW50L2MwNDBiOTI0LWFiYTItNDhhZS1hMzlmLTYxZmFhMGNkYTJiMw==",
"amount": {
"currency": "ZAR",
"quantity": "1"
},
"nonce": "5d29a396-5e6c-419e-9279-d26a01923815",
"beneficiaryReference": "TestReference",
"beneficiary": {
"name": "Lilo",
"accountNumber": "123456789",
"bankId": "absa"
},
"type": "instant",
"status": "completed"
}
The completed
status is not always a final state for a disbursement because in rare cases the disbursement may be reversed by the bank after it has been marked as completed.
For example, this can happen when the destination account is recently closed. If this happens, a disbursement will have a new status reversed
which is a final state.
Disbursement Paused Reasons
The paused
status indicates that we can no longer continue processing the disbursement until certain conditions are met.
These conditions are outlined in the table below.
Reason | Description |
---|---|
insufficient_funds | There are insufficient funds in the account. |
Disbursement Cancellation
Disbursements are processed on a First-In-First-Out basis. A disbursement that exceeds the total available float will pause more recent disbursements.
Disbursements that are paused can be cancelled and results in recently paused disbursements being resumed
.
Cancelled disbursements will never be processed and have a cancelled
status.
Initiate a POST
request to cancel a disbursement with the following request body:
curl -X POST 'https://api.stitch.money/v2/disbursements/cancel' \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"id": "ZGlzYnVyc2VtZW50L2MwNDBiOTI0LWFiYTItNDhhZS1hMzlmLTYxZmFhMGNkYTJiMw==",
"reason": "incorrect_amount"
}'
Field | Type | Description | |
---|---|---|---|
id | string | The unique Stitch ID | |
reason | string | The cancellation reason |
The response includes the id
of the disbursement and provided reason with status code 200 OK
if cancellation is successful. See HTTP Status Codes for a description of each possible response code.
{
"id": "ZGlzYnVyc2VtZW50L2MwNDBiOTI0LWFiYTItNDhhZS1hMzlmLTYxZmFhMGNkYTJiMw==",
"reason": "incorrect_amount"
}
Disbursement Error Handling
On requesting a disbursement, check-digit verification (CDV) is performed to ensure the validity of the specified account number. Should this check fail, you will receive an 400 Bad Request response. No disbursement is created at Stitch.
Disbursement Error Reasons
The error
status indicates that the disbursement has been attempted to be processed at Stitch, but has ultimately failed.
An associated status reason is returned in the reason
field. Possible failure reasons are detailed below:
Reason | Description |
---|---|
bank_error | There was a problem communicating with the processing bank. |
bank_processing_error | An internal error occurred inside the bank while trying to process the transaction. |
insufficient_funds | The account from which the payout was to be made had insufficient_funds for longer than the allowed duration |
restricted_account | There was a restriction on the account. This could be anything from FICA compliance to the account being manually frozen. |
inactive_account | The account is inactive. This can be remedied by reactivating it. |
exceeded_limit | A limit was exceeded. |
invalid_account | The account was not found. It may have been closed or cannot process this type of transaction. |
beneficiary_bank_processing_error | The recipient bank did not accept the transaction. Common causes are the beneficiary bank not responding to an RTC payment within the required 60 seconds, or not being enrolled for RTC. |
invalid_transaction_details | The details (e.g. account number or branch code) were rejected by the bank. |
payment_not_received | The payment associated with the refund has not yet been received. |
invalid_destination | (Deprecated) The provided beneficiary account is invalid. |
internal_error | (Deprecated) An unknown error has occurred processing the payout into the provided beneficiary account. |
HTTP Status Codes
Status Code | Meaning | Description |
---|---|---|
200 | OK | The request was successful. The response body contains the disbursement resource. |
201 | Created | The disbursement was successfully created. The response body contains the new disbursement resource. |
400 | Bad Request | The request was invalid. For example, a required field is missing or malformed. |
401 | Unauthorized | Authentication failed or no valid authentication credentials provided. |
403 | Forbidden | The authenticated user does not have permission to perform this action. |
404 | Not Found | The requested resource (e.g., disbursement) was not found. |
409 | Conflict | There is a duplicate nonce or other conflict with the request. |
429 | Too Many Requests | Too many requests have been made in a short period (rate limiting). |
500 | Internal Server Error | An unexpected error occurred on the Stitch platform. |
503 | Service Unavailable | The service is temporarily unavailable. |
Disbursement Status Flow
A disbursement will go through various state transitions once the request is submitted. The below diagram illustrates this status flow

Subscribing to Disbursement Webhooks
To receive a webhook upon payment completion or failure you will need to create a subscription for your client. Please note that this will always send a signed webhook for your disbursement requests. You can read more about how to verify the signature within the webhook event in our more detailed guide here.
If the subscription is successfully created, the body returned by the request will look like the sample in the Example Response
tab in widget above. The value in the secret
field will be used to verify the webhook event signature, and should be kept
in a secure place like an environment variable.
For more information on receiving webhook events, listing active webhook subscriptions, unsubscribing from webhooks and validating signed webhook subscriptions, please visit the Webhooks page.
Webhook Statuses
The disbursement
webhook will be dispatched for each of the following status updates:
submitted
completed
error
paused
cancelled
reversed
Simulating Disbursement Scenarios
To simulate some common scenarios when using your test client, see the table below with requirements to simulate each scenario.
Simulation Scenario | Simulated Status | Simulation Requirements |
---|---|---|
A successful disbursement | completed | -> beneficiary.accountNumber must end in 0 -> amount.quantity must be less than 400 -> Wait approximately 2 minutes before querying the disbursement status |
A failed disbursement due to a bank processing error | error , with reason bank_processing_error | -> amount.quantity = 400 -> Wait approximately 2 minutes before querying the disbursement status |
A failed disbursement due to an inactive account | error , with reason inactive_account | -> amount.quantity = 401 -> Wait approximately 2 minutes before querying the disbursement status |
A failed disbursement due to an invalid account | error , with reason invalid_account | -> amount.quantity = 402 -> Wait approximately 2 minutes before querying the disbursement status |
A paused disbursement | paused , with reason insufficient_funds | -> amount.quantity > 404 |
Top up of a float account, to allow a paused disbursement to continue to be successfully processed | paused initially, which becomes completed | -> amount.quantity = 404 -> Wait approximately 3 minutes before querying the disbursement status |
A failed disbursement due to a paused disbursement exceeding the expiry period | error , with reason insufficient_funds | -> amount.quantity > 404 -> Wait approximately 3 minutes before querying the disbursement status |