Transfers (Pay-Outs)
The Transfer API (Pay-Out API) is simple to integrate, as it involves no interaction with end customers.Making a transfer
HUB2 API reference - Create a transfer Performing a transfer only requires one call to the dedicated endpoint. Sample request:201 Created and returns the transfer, including its ID: the unique identifier HUB2 creates for each transfer request.
When contacting HUB2 support about a transfer, provide the transfer ID, not your own reference.
Restrictions
For mobile money transfers to Togo (destination.country = TG), the reference field must not contain special characters. A reference with special characters is rejected with a 400 Bad Request error (code forbidden_special_chars).
Allowed characters are letters, digits, hyphens, underscores, spaces and the pipe character. As a regular expression character class: [A-Za-z0-9\-_\s|].
Instant payment destination
To pay a beneficiary identified by an alias on the BCEAO instant payment system, setdestination.type to instant_payment and send the beneficiary’s alias in destination.payeeAlias, with the destination.country. No provider and no msisdn are sent, and the currency must be XOF.
Retrying a payout safely
This section describes the behavior that applies when duplicate protection is enabled on your account. Duplicate protection is not enabled by default: HUB2 enables it for each merchant and each environment (sandbox or live). Contact HUB2 Support to enable it. Until it is enabled, HUB2 does not act on references, and sending a payout twice creates two payouts. HUB2 still records the references you send, so they already count when protection is activated: if you have reused references, switch to unique ones before asking for activation, otherwise a reused reference returns the earlier payout as a replay (
200 OK, or 409 Conflict if the details differ) and creates nothing.POST /transfersPOST /delegated/transfersPOST /delegated/transfers/irtPOST /irt/transferPOST /irt/transfer/verified-recipient
Same reference
Areference identifies one payout for your account and your environment, whichever of the endpoints above created it. References are compared exactly: they are case-sensitive and spaces count, so REF and REF are two different references.
Two requests carry the same payout details when they have the same destination type, destination country, beneficiary (phone number or bank account number), provider (or bank code), amount and currency. The description, the recipient name and the other fields are not compared.
On a replay, the body is the original payout as it is now, with its current status. The response differs from a creation only by the
200 status and the X-Idempotent-Replay header, which is sent on a replay only.
duplicate_reference error looks like this:
Same details under another reference
A second check protects you from paying the same beneficiary twice under two different references. A payout is refused when a payout with the same details was created for your account and environment under another reference less than 15 minutes ago, on any of the endpoints above. The details compared are the same as above: destination type, country, beneficiary, provider (or bank code), amount and currency. A payout that failed or was canceled is ignored. This check is enabled separately from the first one, on top of it. Ask HUB2 Support to enable both. The refused payout is not created, and its reference stays free. The API responds with409 Conflict and code possible_duplicate. This error body carries two more fields than the usual error format:
message above is abridged.
Confirming a possible duplicate
Check the earlier payout first. If it is a mistake, do nothing. If the second payout is intended, resend it to confirm it: same endpoint, samereference, same payout details, with the rejection_id of the refusal sent in a rejectionId field of the body, before expires_at.
- A
rejectionIdconfirms one payout, once. If you resend the confirmed payout again, you get the original payout back as a replay (200 OKwith theX-Idempotent-Replay: trueheader). - If another payout with the same details was created since the refusal, the confirmation is refused again with
possible_duplicateand a newrejection_id. - A
rejectionIdthat cannot confirm the payout is rejected with422 Unprocessable Entity, codeinvalid_duplicate_confirmation. The same response covers every case: an unknown or expired identifier, an identifier already used, or a payout, reference or endpoint different from the refused one. Nothing is created. Resend the payout withoutrejectionId: it is created if it is no longer a possible duplicate, or refused with a newrejection_id. - A
rejectionIdmust bendr_followed by 21 characters (letters, digits,_or-). Any other value is rejected with a400 Bad Requesterror. - Apart from its format,
rejectionIdis ignored unless both checks are enabled on your account.
Retrieving transfer details
HUB2 API reference - Get transfer details To retrieve a transfer, call the dedicated endpoint with the transfer ID obtained when the transfer was created (see previous step). This endpoint returns the full details of a transfer. It is not meant for polling status changes. To follow the status of a transfer, poll the status endpoint (GET /transfers/{id}/status) instead.
This endpoint, like every HUB2 API endpoint, is rate limited: when it is called too often, the HUB2 API responds with HTTP 429 Too Many Requests. The limits are:
GET /transfers/{id}: 1 request per 5 seconds per transferGET /transfers/{id}/status: 5 requests per 5 seconds per transferGET /transfers(list): 1 request per 30 seconds
Documentation about rate limits can be found here.
Documentation about webhooks can be found here.
Retrieve a transfer status
For performance reasons, the previous endpoint is not meant for retrieving a transfer status. A dedicated endpoint serves that purpose. HUB2 API reference - Retrieve a transfer status This endpoint is also rate limited, but its limit is less restrictive than that of the previous endpoint. Sample request:List transfers
HUB2 API reference - List transfers To fetch a list of transfers, use the dedicated endpoint. Several query parameters (see below) filter the results. Sample request:from: start of the date range, here2023-01-01T00:00:00.000.to: end of the date range, here2023-01-01T12:00:00.000.pageandperPage: navigation through the result pages.perPagedefaults to10and cannot exceed100.
By default, when no filter is set, this endpoint returns the 10 most recent transfers, ordered by creation date, descending.
isDelegated parameter is set, from is required and the date range must be shorter than one year, otherwise the API returns a 400 error.
Pagination
This endpoint is paginated. TheContent-Range response header gives the range of the returned results, for example 0-99/999999. The value after the slash is not an exact count of the matching transfers.
To retrieve every transfer matching the filter, request the next page until a page returns fewer results than perPage.
Set date filters in the initial request so the number of results stays the same while you paginate (unless to is in the future).