Direct Credit Payout Workflow

📘

Please note

NPP and PayID apply to Australia only. For platforms operating in the UK, refer to the Bank-to-Bank Transfers guide.

Overview

This integration guide will run you through how to use Zai to do payouts to your users via direct credit (ACH / DE / BECS). To pay your users, you only need their Account Number and:

  • BSB for Australian platforms.
  • Sort code for UK platforms.

Getting Started

Before you can run your direct credit workflow, there are a few steps that need to be completed to get set up so that all of the pieces are available.

Prerequisites
You'll need to capture specific details from your users for you to be able to send direct credits.
The setup discussed in this guide makes use of a float to fund your payout. The float will require sufficient funds and periodic top-ups (​see inbound payments below​). For platforms operating in Australia, your funding bank account ​must allow PayID payments​, as the float is paid in by a customer reference number and PayID email address.

Before you start creating sub-wallets, ensure your platform is approved for use of stored value accounts and enabled for sub-wallets by contacting Zai's Account Management team(mailto:[email protected]). Or you can get in touch with us at [email protected].

📘

Please note

Stored value accounts along with the sub-wallet feature are optional and only offered to platforms operating in Australia at this time.

Terminology used
The following terminology is used throughout this guide:

  • Platform​ refers to you as a Zai customer, or your website, application, marketplace etc.
  • User​ refers to your customer who has a User record in Zai.
  • Item​ is the mechanism for moving funds between users in the API.
  • Payin​ is money being paid into the Zai ecosystem from a bank account.
  • Payout​ is money being paid out of the Zai ecosystem to a bank account.
  • Float user is a User you may create, if required, to temporarily hold the balance of funds (float) you wish to split and transfer to multiple users' wallet accounts before you perform the payouts.

API steps

Setup steps
We recommend that you create webhooks on the following objects to receive notifications from us of state and status changes.

  • Transactions
  • Items
  • Users
  • Batch Transactions
  • Accounts

To create a webhook, you'd call ​POST /webhooks​, more information on webhooks can be ​found here​.

Inbound payments (topping up your float - Pre-live only)
The following steps allow you to add funds to a user’s main wallet in our Pre-live environment. In the real world, you'd send us a payment and we'd match it to the wallet. For the purposes of testing, you can add funds to wallets as needed.

  1. Create a ​user​ for your float user.
    POST /users
  2. Get the ​digital wallet​ account ID for your float user.
    GET /users/:id/wallet_accounts
  3. Get the NPP reference details for the user (this is just so you can add money to the account in Pre-live).
    GET /wallet_accounts/:id/npp_details
  4. (Pre-live Only) Fund a user’s digital wallet with NPP.
    PATCH /testing/wallet_accounts/process_npp_payin
{
  "crn": "Customer reference number - reference from wallet", 
  "payid": "PayID - pay_id from wallet",
  "amount": 10000 // Amount in cents
}
  1. Get the digital wallet account ID and balance for your float user.
    GET /users/:id/wallet_accounts

Outbound payments (paying your users)
The following steps provide the end-to-end flow that includes how to fund your user's main wallet account as well as create and fund a sub-wallet.

  1. Create a user for your customer (who you want to pay) if they don’t exist already.
    POST /users
  2. Update the user for your customer if they already exist (if needed).
    PATCH /users/:id
  3. Add a ​bank account​ for your customer (you’ll need the bank account ID in step 10).
    POST /bank_accounts
  4. Set the bank account as the disbursement (or payout) account.
    PATCH /users/:id/disbursement_account
  5. Create an ​Item​ (use Type=2) to move money from your float user to your customer.
    POST /items
  6. Fund the Item with your float user’s wallet (use the ID you got in Step 5 from the inbound payments section)
    PATCH /items/:id/make_payment
  7. Check the webhook payload to see how your user’s wallet was funded.
    GET /webhooks/id:/responses
  8. (Sub-wallet account) Create sub-wallet(s) for the user's wallet account.
  9. (Sub-wallet account / Main wallet account) Fund transfers between the wallet account and its sub-wallet (or between any sub-wallets that belong to the same user's wallet account). The source and destination accounts must be active and use the same currency.
  10. (Main wallet account) Create a ​withdraw request​ on the wallet to pay to your user’s nominated bank account. (use the bank account ID from step 3)
    POST /wallet_accounts/:id/withdraw
    a. (Sub-wallet account) Create a withdraw request on the sub-wallet to pay to your user's nominated bank account. (use the bank account ID from step 3) POST /sub_wallets/:id/withdraw
{
	"account_id": abc 123456789,
  "custom_descriptor": "Custom Descriptor",
  "amount": 10000 // Amount in cents 
}
  1. As part of step 10, you'll receive a disbursements object. Please hold on to the id field as you'll need to pass it on to us to progress payment

  2. (Main wallet account) Check the wallet balance of your user to confirm funds have been sent.
    GET /wallet_accounts/:id
    a. (Sub-wallet account) Check the sub-wallet balance of your user to confirm funds have been sent. GET /sub_wallets/:id

  3. (Pre-live only) You can call the GET /batch_transactions/export_transactions API which moves all pending batch_transactions into batched state. As a result, this API will return all the batch_transactions that have moved from pending to exported. This will simulate the step of the flow where we batch payments and will result in you receiving a batch_transactions webhook. Please store the id in order to progress it in Pre-live.

📘

Note

When you simulate a payment journey in Pre-live, the response including the transaction id will be cleared after a day. If you need to retrieve this id, you can call the List Transactions API, input a limited number of results depending on how much time has passed, and then find it from that list of transaction

{
    "transactions": [
        {
            "id": "439970a2-e0a1-418e-aecf-6b519c115c55",
            "batch_id": null,
            "status": "batched",
            "type": "payment",
            "type_method": "direct_credit"
        }
    ]
}
  1. (Pre-Live only) You can call the following API which moves one or more batch_transactions into bank_processing state 12700. This will simulate the step of the flow where we send payments to our bank and will result in you receiving a batch_transactions webhook
    PATCH /batches/:id/transaction_states
{
	"exported_ids" : ["439970a2-e0a1-418e-aecf-6b519c115c55"],
	"state": 12700
}
{
    "aggregated_jobs_uuid": "c1cbc502-9754-42fd-9731-2330ddd7a41f",
    "msg": "1 jobs have been sent to the queue.",
    "errors": []
}
  1. (Pre-Live only) You can call the same API from the step above to move one or more batch_transactions to the final states of processing being either successful state 12000 or invalid_account_details state 12360. This will simulate the step of the flow where we complete the processing of the payment and will result in you receiving a batch_transactions webhook
    PATCH /batches/:id/transaction_states
{
	"exported_ids" : ["439970a2-e0a1-418e-aecf-6b519c115c55"],
	"state": 12000
}
{
    "aggregated_jobs_uuid": "c1cbc502-9754-42fd-9731-2330ddd7a41f",
    "msg": "1 jobs have been sent to the queue.",
    "errors": []
}

Alternatively to waiting for webhooks you can check the webhook payload to see that the payout was completed.
GET /webhooks/id:/responses

Design considerations

User data design
When designing how this workflow will interface with your platform, we suggest storing a map of specific object IDs on your side in order to reduce the number of API calls required throughout the API workflow.

your_customer_idzai_user_idzai_bank_account_idzai_digital_wallet_id or zai_sub_wallet_idzai_user_npp_crn (for payins)
100010015b8f59b6-32c1-44a3-8f5b-b9e2c77bc265fb35b028-76fb-44df-86ac-822dccf1cd7ddcdfdb78-27e7-485d-90c0-11ffbf950d721000023456
10001002efe6f8c1-0a30-484a-b085-9d9d0539dbfc4355de7b-18c2-4892-b800-b7bbb6f0a9b3054cd04e-19f1-4938-908b-8e9e5c212f4d1000098653

Custom descriptors
For direct credit payouts you have the option of customising the payment description seen by your customers.
It's an extra parameter on the ​withdraw call​ named ​custom_descriptor​. You can have a string of up to 13 alphanumeric characters. The batch ID will then prefix it. For example: "1234: Reference 123" or "ZAI:Reference 123".

/wallet_accounts/:id/withdraw?account_id=&amount=&custom_descriptor

Workflow summary
Once you’ve run through the API steps, and implemented something like the above in your platform, the sequence of calls to run the payout workflow in production will be reduced.

This flow assumes that you have a webhook service running, that your user exists, and that a bank account has been added, and set as a disbursement / payout account.

  1. Create an Item to move money from your float user to your customer.
    POST /items
  2. Fund the Item with your float user’s wallet (use the ID you got in Step 5 from the inbound payment section)
    PATCH /items/:id/make_payment
  3. (Main wallet account) Create a withdraw request on the wallet to pay to your user’s nominated bank account. (use the bank account ID from step 3)
    POST /wallet_accounts/:id/withdraw
    a. (Sub-wallet account) Create a withdraw request on the sub-wallet to pay to your user's nominated bank account. (use the bank account ID from step 3) POST /sub_wallets/:id/withdraw

Failed Direct Credit Payouts
In the case of a failure relating to direct credit payouts, the batch_transactions object will be transitioned to invalid_account_details. In this case you should verify the account details with the user and aim to create a new account with the correct details before reattempting the payment

Additional Details

For a list of payout error codes, please refer to: Common error messages
For a list of statuses, please refer to: Statuses
For FAQs on payouts, please refer to: