Skip to main content

Disbursements

Disbursements are instructions for Stitch to pay directly to destination accounts.

info

Please note the following about our disbursement 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.

Creating a Disbursement

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, a GraphQL mutation is used to specify the requested amount, the destination account details, a nonce to enforce uniqueness, and a reference that the beneficiary will see on their statement. The GraphQL API URL https://api.stitch.money/graphql can be used for all disbursement requests (whether on test or live clients).

An example of this can be found below:

info

NOTE: DisbursementCompleted 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 DisbursementReversed which is a final state.

The disbursement type allows you to specify either "INSTANT" or "DEFAULT". Leaving it empty will default to the latter. See the table below for more information.

Disbursement Types

TypeDescription
INSTANTAttempt 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_bank_zero and za_olympus_mobile do not support the INSTANT type.
DEFAULTAttempt 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 be 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.

Recipient Bank Account Verification

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.

Retrieving Disbursement Status

After handling the response from the disbursement mutation, you will likely want to check the status of the disbursement. Using the query below you can retrieve the status of a given disbursement by id, provided you have a client token with the client_disbursement scope.

Retrieve Disbursement Status by Nonce

As an alternative to query a disbursement by its id, it is possible to query by the nonce value that was provided when creating the disbursement. This is done by specifying the nonce as a filter, as shown in the example below:

Retrieve All Disbursements

Should you want to list all disbursements attempted on your client, you can run the following query, provided you have a client token with the client_disbursement scope.

You can also further refine the above query to only show disbursements in a certain status. For example, the below query will list all disbursements having the status DisbursementError or DisbursementReversed. For more options, you can filter at the top level on the disbursements node, just like in the example below.

Disbursement Paused Reasons

The DisbursementPaused status indicates that we can no longer continue processing the disbursement until certain conditions are met. These conditions are outlined in the table below.

ReasonDescription
insufficient_fundsThere are insufficient funds in the account.

Cancelling a disbursement

Due to disbursements being processed on a First-In-First-Out basis a disbursement that exceeds the total available float will cause newer disbursements to also be paused. Disbursements that are paused can be cancelled by executing the mutation below, which cause all recently paused disbursements to be resumed. Cancelled disbursements will never be processed and will have a status DisbursementCancelled.

info

NOTE: Only disbursements in the DisbursementPaused status are eligible for cancellation.

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 API error containing message: account_verification_failed_cdv and extensions.code: BAD_USER_INPUT, and no disbursement will be created at Stitch.

Disbursement Error Reasons

The DisbursementError status indicates that the disbursement has been attempted to be processed at Stitch, but has ultimately failed. This includes an associated disbursementErrorReason. Possible failure reasons are detailed below:

ReasonDescription
bank_errorThere was a problem communicating with the processing bank.
bank_processing_errorAn internal error occurred inside the bank while trying to process the transaction.
insufficient_fundsThe account from which the payout was to be made had insufficient_funds for longer than the allowed duration
restricted_accountThere was a restriction on the account. This could be anything from FICA compliance to the account being manually frozen.
inactive_accountThe account is inactive. This can be remedied by reactivating it.
exceeded_limitA limit was exceeded.
invalid_accountThe account was not found. It may have been closed or cannot process this type of transaction.
beneficiary_bank_processing_errorThe 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_detailsThe details (e.g. account number or branch code) were rejected by the bank.
payment_not_receivedThe 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.

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:

  • DisbursementSubmitted
  • DisbursementCompleted
  • DisbursementError
  • DisbursementPaused
  • DisbursementCancelled
  • DisbursementReversed
Example Payload
{
"data": {
"client": {
"disbursements": {
"node": {
"__typename": "Disbursement",
"amount": {
"currency": "ZAR",
"quantity": "1"
},
"bankBeneficiary": {
"__typename": "DisbursementBankBeneficiary",
"accountHolder": "example-name",
"accountNumber": "62741016950",
"accountType": "savings",
"bankId": "nedbank"
},
"beneficiaryReference": "example-ref",
"created": "2022-10-06T15:03:08.436Z",
"id": "ZGlzYnVyc2VtZW50Lzc2YTlhMjIwLWVjMGItNDJmOS04NGMzLTcyOGU4MjQ4MWVlNQ==",
"linkedAccountId": null,
"nonce": "bc70425a-9fa3-4db7-8e8b-93edb0b2aa23",
"status": {
"__typename": "DisbursementCompleted",
"date": "2022-10-06T15:03:08.694Z",
"expectedSettlement": "2022-10-08T15:03:08.694Z"
}
}
}
}
}
}

Simulating Disbursement Scenarios

To simulate some common scenarios when using your test client, see the table below with requirements to simulate each scenario.

Simulation ScenarioSimulated StatusSimulation Requirements
A successful, disbursementDisbursementCompleted-> bankBeneficiary.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 errorDisbursementError, with reason bank_processing_error-> amount.quantity = 400
-> Wait approximately 2 minutes before querying the disbursement status
A failed disbursement due to an inactive accountDisbursementError, with reason inactive_account-> amount.quantity = 401
-> Wait approximately 2 minutes before querying the disbursement status
A failed disbursement due to an invalid accountDisbursementError, with reason invalid_account-> amount.quantity = 402
-> Wait approximately 2 minutes before querying the disbursement status
A paused disbursementDisbursementPaused, with reason insufficient_funds-> amount.quantity > 404
Top up of a float account, to allow a paused disbursement to continue to be successfully processedDisbursementPaused initially, which becomes DisbursementCompleted-> amount.quantity = 404
-> Wait approximately 3 minutes before querying the disbursement status
A failed disbursement due to a paused disbursement exceeding the expiry periodDisbursementError, with reason insufficient_funds-> amount.quantity > 404
-> Wait approximately 3 minutes before querying the disbursement status

Postman Collection

Disbursement requests can be created and tested using the Postman collection available here. These can be used by specifying your client credentials and supplying the custom request variables where required.