Transaction flow
- Introduction
- Creating transactions
- Transaction object
- Funding transactions
- Checking the state of the transaction
- Receiving error messages
- Cancelling recipients and transactions
Introduction
Transactions are the main objects in the AZA Finance API, so it’s important to understand how to create and manage them. Transactions facilitate money movement from one Sender in a specific currency to one or multiple Recipients in another currency.
The main flow of a successful transaction flow is the following:
- Transaction is created linking the Sender to the Recipient(s) with the requested amounts.
- Once the sender is KYC’d and approved the transaction can be funded.
- Once the transaction is funded, we will initiate the payout to the recipient(s).
- After the recipient (or all recipients) has received the money, the transaction is finished.
Unfortunately not all transactions are successful. The main causes of issues are the following:
- Transactions are cancelled automatically if they are not funded within the first hour of creation.
- The recipient details might be wrong (for example the destination bank account number is invalid), or there are other issues blocking the successful payout. In these cases the transaction has to be cancelled.
- Once the transaction is cancelled, we will refund the money to the sender. Funds from account balances are always refunded automatically to the account balance. Funds from other types of payins might require manual processing however.
Flow Diagram
Creating transactions
Transactions can be created by calling the POST /v1/transactions
endpoint. The template of transaction requests is the following:
Note that the external_id
field is optional but we prefer if you use it. Please see external ID for further information.
A full example transaction creation request with sender creation from USD
to NGN
would look like the following:
Input currency
The input currency describes what currency the transaction will be paid in. For example if you wish to create an EUR
to NGN
transaction then input currency should be set to EUR
.
Sender
This section contains the details of the sender. The first time a specific sender is used the full details should be provided, example:
When a sender is created you will receive a response which contains the sender’s status. Possible states for a sender are:
initial
- When a sender is created and has not been through any KYC checking (cannot transact)verified
- A sender has passed sanction list checks (cannot transact)approved
- The sender has passed both KYC and sanction checks (can transact)banned
- An admin has banned the sender (cannot transact)rejected
- The sender has failed sanction list checks (cannot transact)disabled
- A sender is put into this state as a result of a delete request via the API (cannot transact)
In order to transact with TransferZero we need to have an approved
sender record. The flow for approving senders depend on whether KYC requirements are waived for your integration or not. In case the KYC requirements are waived then all created senders will be in the approved
state immediately, and can be immediately used for transactions.
Please see our KYC documentation on more details about the KYC sender processes.
ID and External ID
The external_id
field is optional, allowing you to add a custom ID for the sender as with the external ID available for transactions. The ID/External ID can be included with the sender in four ways:
- Only an
id
is provided - we will search for the corresponding sender and use this reference. - An
id
is provided along with additional fields - we will update the corresponding sender with the information contained as parameters if the id exists on our system. Otherwise an error will be returned if theid
does not exist. - Only an
external_id
is provided - we will search for the corresponding sender and use this reference. - An
external_id
is provided along with additional fields - we will create a new sender with this reference. This process is subject to duplicate validation, and an error will be returned with the corresponding sender if a duplicateexternal_id
is found to already exist on our system.
An exception to this is if the external_id
is provided along with additional fields when a transaction is being created. In this case, any details sent along with the external ID are used to update the sender.
Please note that sending both an id
and external_id
at once is invalid and will result in an error.
If a sender has been assigned an external_id
, this value can be used to find senders using the GET /v1/senders
endpoint, with external_id
parameter included as a string. For example: GET /v1/senders?external_id=76f69f5e
Sender type
The sender type indicates if the sender is a person or business.
Please note the sender details vary depending on the sender typology.
Please refer to the example above (for personal senders) and to Business payments (for business senders) for more details.
Metadata
The metadata
field can store any information you wish to store with the sender. If you don’t wish to store anything simply specify {}
.
Phone number
The sender’s phone number is required to be in E. 164 international format.
Documents
The documents
should contain all documents necessary to KYC the sender. It is either an empty array "documents": [ ]
in case you don’t need to send us the sender documents, or it should contain all of the proof of ID documents that are required. See our KYC documentation on more details.
WTR2
We support sender validation based on the simplified WTR2 rules. If WTR2 is enabled, all you need in order to identify a sender
is to provide one of the set of details defined below:
- There are three different set of details:
identification_number
andidentification_type
street
,city
andpostal_code
birth_date
- The sender only needs to contain one of the set of details from above.
E.g.: if the
sender
contains the birth date, then it does not need to contain anything from the first or the second set of the details.
Examples:
- When using Identification Document:
- Identification number: Identification number of document used with a 4-character minimum length.
- Identification type:
DL
: Driving LicensePP
: International PassportID
: National IDOT
: Other
- When using Address:
- Street: Street 17-3
- City: London
- Postal Code: NW123ET
- When using Birthdate:
- Birthdate: 1989-01-31
If WTR2 is not enabled, the sender
will need to be provided with all of the following:
phone_number
email
street
,city
,postal_code
birth_date
Note! You’ll have to contact us if you wish to use the relaxed WTR2 mode, so we can set this up for you. This is only available if you are doing a full KYC on your senders.
Warning! Due to regulatory requirements the WTR2 mode is not compatible with the MAD::Cash
and XOF::Cash
corridors. Plese check the documentation on these corridors on what extra detail you need to provide to be compliant.
Re-using senders
For both KYC and auditing purposes, it’s crucial to maintain consistency in your sender records. Avoid creating multiple senders for the same person/business. This aspect will also be checked during the onboarding process. To facilitate the reuse of sender information, you can utilise the external_id
field. This field allows you to assign a unique and custom ID to each sender, ensuring they are easily identifiable.
Additionally, providing the full sender details along with the external_id
serves two purposes: it helps in sender identification and ensures the accuracy of sender details. Should there be any changes in your sender data, we will cross-reference these details with the external ID and update the sender information as needed.
Warning! For your application to get approved you MUST support using external IDs for sender reusability across transactions.
Note! Although Senders can also be created separately using the sender creation API call, if you are doing full KYC then in order to decrease the amount of distinct calls to the system we prefer if new senders are created with the first transaction they appear in. If you are not doing a full KYC however then you always need to use the separate sender creation API call, as you need to wait for the sender to get approved before they can transact in the system.
Recipient
The recipient describes the amount, the currency and the destination where the money should be sent. Although transactions can support paying out multiple recipients, usually only one should be provided.
The template for the recipient is the following:
Recipient type
The recipient type indicates if the recipient is a person or business.
Please note the payout details vary depending on the recipient typology.
The name
field is mandatory for business recipients.
Please refer to the Individual payments and Business payments sections for more details.
Payout type
The payout type contains where the money should be sent to and to what currency. You can find a complete list of supported types at the API reference documentation.
You can find the commonly used payout types at the payout documentation.
Unless you hold an internal balance with us, the input currency and payout currency cannot be the same. If you wish to do same-currency transactions please contact our team for further details.
Requested amount and currency
This will be the amount that you would wish to pay to the recipient. The value here can be in any particular supported currency, usually either the input or the output one.
In the transaction create response we will return how much this is in the input currency, and will also return how much we would pay out to the recipient in the output currency.
A few common examples:
-
Input currency is
EUR
Payout type isNGN::Bank
. Requested amount is100 EUR
.In this case we will calculate how much
100 EUR
is inNGN
(as of writing around 44000), and will return: input amount is100 EUR
and output amount is44000 NGN
-
Input currency is
USD
Payout type isNGN::Bank
. Requested amount is10000 NGN
.In this case we will calculate how much
10000 NGN
is inUSD
(as of writing around 25), and will return: input amount is25 USD
and output amount is10000 NGN
-
Input currency is
NGN
Payout type isNGN::Bank
. Requested amount is10000 NGN
.In this case we will simply pay out
10000 NGN
to the recipient. Note that same input and output currencies are only supported if you hold an internal balance with us. If you’re interested in that please contact our team. -
Input currency is
USD
Payout type isNGN::Bank
. Requested amount is100 EUR
.Although the requested currency is neither the input nor the output one, we will calculate how much
100 EUR
is inUSD
and inNGN
and will return those values in the input and output amounts. We will afterwards do the currency exchange directly, and will not involve the requested currency at all.
The requested amount is rounded to a specific number of decimal places and this depends on the currency. The decimal place information can be obtained from our currencies API endpoint - /info/currencies/out.
For some currencies however, we are not able to pay out subunits and they will always be rounded up. These currencies are XOF, XAF, KES, TZS, UGX and NGN.
The current list of currencies and associated decimal places is below -
AED
,CAD
,CHF
,CNY
,EUR
,GHS
,GBP
,MAD
,USD
,ZAR
: 2JPY
,KES
,KRW
,NGN
,TZS
,UGX
,XOF
,XAF
: 0
Also note for XOF
and XAF
some mobile operators will only pay out in multiples of 5, so the paid amount might be rounded up to the nearest five. Example if you wish to pay out 102 XOF
, the recipient will actually get 105 XOF
Payout details
The payout details depend on the chosen payout type. Please check the Payout documentation for more details, and you can also find example calls at the API reference documentation.
Metadata
Similarly to the sender, you can store any kind of information to the transaction. If you don’t wish to store anything leave the field empty: {}
External ID
External ID is an optional field that allows you to add a custom ID for the transaction, should you wish to link it to a transaction within your own local system. This internal reference value will be displayed in your transaction reports.
If an external_id
is present when transactions are created, we will validate whether it is a duplicate in our system or not. This functionality provides a safeguard against transactions being assigned the same external_id
. If a duplicate is found, an error will be returned along with the corresponding transaction.
Once an external_id
has been set, it can be used to find transactions using the GET /v1/transactions
endpoint, with external_id
parameter included as a string. For example: GET /v1/transactions?external_id=806ec63a
Transaction object
Once the transaction is created successfully, you will receive back a transaction object. This object will always contain all details about the sender, the transaction and the recipients.
In case the transaction couldn’t be created, you will get back a 422
response and inside the body you should find all of the vaildation messages which should be fixed.
A transaction object looks like the following:
As shown, the response contains a lot of information, the most important are the following:
id
The ID of the transaction
state
The state of the transaction, which can be one of the following:
initial
: Transaction is created, but not yet ready to receive payments (waiting for Sender to be KYC’d and approved).approved
: Transaction is created and the sender is approved. Payment can be receivedpending
: Transaction has received a payin, and it’s waiting for the funds to clear.processing
: Transaction is under compliance review. Please expect feedback in 24 hours or less.received
: Transaction has received the correct payin amount and will start processing the payouts.mispaid
: Transaction received funds, but not the requested amount. The transaction will be resized, and will start payout based on the received funds.manual
: Some of the payments to the recipients have run into issues. Please check the recipient statuses for more information.paid
: Transaction has received correct payins and has performed payouts to all recipients. No further steps requiredcanceled
: The transaction has been cancelled. Transactions are cancelled automatically after one hour if there was no funds received. Once transactions are funded they can be cancelled by the API user unless the recipients have been paid out.refunded
: The transaction has been cancelled after it has been funded, but the funds have been returned now to the sender either partially or fully.exception
: An exception happened during the processing of the transaction. Please contact TransferZero
state_reason
If there is an error with the transaction you can find the error message in this field.
input_amount and input_currency
This is the amount that has to be collected from the sender, or funded from the internal balance.
payin_methods
When you are processing a collection this object will be used to specify the details of where you expect the money to come from.
details when collecting money from a customer
Please Check the Collection page to see more examples.
state
The state of the payin method, which can be one of the following:
incomplete
: Some fields need to be filled inin_details
before we can initiate the collection request.initial
: All required fields inin_details
are present and collection process with the sender will start.pending
: Collection process has been started, waiting for sender to send funds.success
: Collection succeeded.processing
: Collection succeeded but waiting for final confirmation of receipt.error
: Collection failed. No funds received from sender. You can update or retry the PayinMethod.mispaid
: Collection succeeded but sender sent the wrong amount.canceled
: The transaction has been canceled and we will refund the sender soon.refunded
: The sender has been refunded the amount they sent in.exception
: An exception happened during processing of the collection. Please contact support.
state_reason_details
If there is a specific error for the collection, you can find the details here. It contains the following fields:
code
Status code of failed collections.
There are six different categories of errors based on the following error codes:
- 0 - category:
paid
- funds are collected - 1x - category:
pending
- Awaiting funds from the sender - 2x - category:
sender_action_required
- This collection requires an action by the sender - 3x - category:
temporary_error
- The payment provider is not accepting transactions at the moment. We will retry the collection at a later time. You can also edit the payin method or retry the payin
category
Category of the error
messages
Tiered messages. There are three tiers, each providing additional details of the error.
description
Public, human readable, detailed error message.
sender
The full details of the sender. If this is a new sender, please make a note of the id
field, as that MUST be used in subsequent transaction creation calls that are from the same sender. If an external_id
is present on the sender, this will also be included. Please see Sender for more details on how external_id
functions.
recipients
The following fields are useful on the recipients:
id
The id of the recipient
output_amount and output_currency
The amount of money the recipient will receive if the transaction is funded.
state
The state of the recipient. Can be one of the following:
initial
: We haven’t initiated the payout yet, you can still cancel the transactionpending
: Payout has been initiated and we’re waiting from a response from the provider. The transaction cannot be cancelledsuccess
: Payout is done, and the recipient has been paid.error
: There was an error from the provider, you can find more details in thestate_reason
attribute. We will usually retry the transaction at a later date. You can either wait, edit the recipient or cancel the transaction.refunded
: You asked us to cancel the transaction and we refunded the money.manual
: There were too many errors on this transaction, and we stopped retrying. Please edit the recipient, contact us or cancel the transaction.stuck
: We didn’t receive a response from the provider in time, and we don’t know whether it has been paid our not. Please contact us for further details.overpaid
: The recipient was paid out more than was requested (not applicable for most of the payout providers)canceled
: The transaction has been cancelled, and we will refund the money soonexception
: Some exception has happened; please contact TransferZero
state_reason
If there is a specific error for the recipient, you can find a description of the error here.
state_reason_details
If there is a specific error for the recipient, you can find details here. It contains following fields:
code
Status code of failed transaction.
There are six different categories of errors based on error codes:
- 0 - category:
paid
- transaction is paid - 1x - category:
unknown
- transaction is awaiting - 2x - category:
pickupable
- recipient action required - 3xx - category:
temporary_error
- we will retry the transaction at a later date - 4xx - category:
recipient_error
- update recipient details or cancel this transaction - 5xx - category:
sender_error
- transaction cannot be processed
category
Category of the error
messages
Tiered messages. There are three tiers, each providing additional details of the error.
description
Public, human readable, detailed error message.
The list of possible error codes can be found here.
editable
Describes whether the recipient can still be edited or not. If it’s editable, and the error message describes thet the account number or phone number was invalid, the recipient can be edited to contain the approriate values. Once the recipient is updated we will retry the payouts with the new details.
You can find more details on how to update recipients inside the API reference documentation.
may_cancel
Shows whether the payout to the recipient can be cancelled at this state or not.
payout_method.fields
In case there were validation errors, you can find all of the fields and their valid values for the specified payout method. This can also include the available bank codes as well for bank payout providers.
external_id
The external ID of a transaction, if present.
Funding transactions
When using our system to send funds to customers then usually you’ll be using your internal account to fund these transactions. Please contact us so we can set up this internal account with us. If this is the case you’ll need to explicitly fund transactions once they are created so we know that you are happy with the transaction to go forward.
Note! You can also use our system to do the collection from the senders. For more information on how to handle some collections, please visit Collections. You can also check the API reference documentation
Funding transactions can be done using the POST /v1/accounts/debits
endpoint, with the following body:
You can also supply the currency
and amount
parameters, in which case we’ll verify if they match the amount on the transaction and only fund it if they do
To successfully fund a transaction:
- The
to_id
is theid
of the transaction - You need to have enough balance of the appropriate currency inside your wallet.
If you choose to include the optional currency and/or amount params:
- The
currency
needs to be the same as theinput_currency
on the transaction. - The
amount
has to be the same as theinput_amount
on the transaction
Once the transaction is funded, we will immediately start trying to pay out the recipient(s).
Warning! Funding call will return a HTTP 200
in case of successful funding or a HTTP 422
in case of unsuccessful funding (for example because you are out of funds). In any other case it is best to double check the state of the transaction to see whether the funding call had been successful or not.
Creating and funding a transaction simultaneously
If you wish to create a transaction and fund it immediately, it is possible to do so by using the POST /v1/transactions/create_and_fund
endpoint. This functions in the same way as creating a transaction, except that the external_id
field is required in this case.
In order to use this endpoint, you must first establish an account with us in the input currency of the transactions you wish to create, and ensure that this account is funded appropriately. Also note that by using this endpoint you will miss the two step approval process, as this will be implicitly assumed.
Warning! Calling create_and_fund
might take a long time to finish, sometimes more than a minute. Make sure your client settings allow longer timeouts when calling this endpoint, and also make sure that in case you do receive a timeout-like error (which can be either a connection timeout, a read timeout, or gateway errors like HTTP 502, 503 or 504) you verify that the transaction was not created by calling the GET /v1/transactions?external_id=[YOUR_TRANSACTION_ID]
endpoint.
A good practice is that any time you don’t receive a HTTP 422
error but something else you double check that the transaction was clearly not created. Only in case you receive a HTTP 422
can you be certain that the transaction was not created on our end.
Checking the state of the transaction
To manually check the state of the transaction, use the GET /v1/transactions/[TRANSFERZERO_TRANSACTION_ID]
endpoint, where the TRANSFERZERO_TRANSACTION_ID
is the id of the transaction (and not the external id). Using the external id the transactions can be retrieved external_id
, as documented here.
However to get real-time information on when a transaction’s state changes please create webhooks for transaction state changes using the developer portal, or the API, where we will send a response every time the transaction’s state is changed.
Warning! For your application to get approved it MUST use primarily the webhook functionality to determine the state of the transaction. Using the GET
endpoint should only be done occasionally as a fall-back mechanism.
Receiving error messages
Because payouts happen on the recipient level inside our system, any kind of issues with the payouts will appear on the recipient. To get real-time information on issues with payouts, please create webhooks for transaction and recipient state changes using the developer portal, or the API, where we will send a response every time the recipient’s state changes to error.
The error message can be found inside the state_reason
field on the recipient. More information about error can be found inside the state_reason_details
Note that as the errors are sent on the recipient, you will receive a recipient object in the webhook and not a full transaction. You can find the transaction id inside the transaction_id
property of the recipient.
For example, on an error you will receive a webhook like this:
Warning! For your application to get approved, it MUST support obtaining the error message from the recipient. It MUST also primarily use the webhook functionality to be notified of any errors, and only fall-back to using GET
calls against the transaction occasionally.
You can read more about problems during payments at our error handling documentation.
Cancelling recipients and transactions
In case there are errors with the payout, or you want to revoke the cash pickup reference number for a cash transaction you can initiate a cancellation request. To do that call the DELETE /v1/recipients/[TRANSFERZERO_RECIPIENT_ID]
endpoint, where TRANSFERZERO_RECIPIENT_ID
is the id of the recipient (and NOT the transaction). If the recipient can be cancelled, this request, once processed, will cancel it. If the transaction was funded from an internal balance, it will then also be refunded.
Note! You can cancel either cash transactions, or transactions where the may_cancel
field is on the recipient true
. If may_cancel
field is false
you can still try to cancel it, however it will only gets cancelled once we have confirmation from our partner that the payment have failed.
Warning! For your application to get approved it MUST support the cancellation of recipients.
Warning! Any transaction that is not cancelled - even ones that seemingly have a fatal error in their description could potentially pay out in the future. If you don’t wish a transaction to pay out and you’d like to recover the debited funds you HAVE TO cancel the transaction.
Warning! Cancellation is an asynchoronous operation. You should wait for the transaction to change state to refunded
before updating your system.
Note that auto_refund
trait is enabled by default on the transaction, this means your transactions will automatically be cancelled and refunded if they can’t be paid out. For more information, please check the auto cancellation documentation.
You can read more about how cancellation works in our error handling documentation.