Make a payout to an external account
Learn about open-loop payouts and how to make one.
In an open-loop payout, a payment is made from a merchant account to an external account. There's no need for the external account to have previously made a payment via TrueLayer.
As the payment is made to an external account, it's important to collect the correct bank details for the payment. These are usually either the user's SCAN or IBAN, for UK or EU payouts respectively.
Open-loop payouts are a useful solution if you already have infrastructure for accepting payments, and want to integrate instant payouts to your users across the UK and Europe.
Open-loop payout overview
- Authentication: Properly configure authentication before your request. This means you need a suitable
access_token
and headers for your request. - Request configuration: Provide information including the value of the payout, the bank details of the beneficiary of the payout and metadata.
- Monitoring: Set up webhooks for your payout so that you can track its progress and confirm its success.
1. Authenticate an open-loop payout
Before you can make an open-loop payout, you must secure your requests. Authentication has two parts:
- Generating an access token
- Signing your requests and including an idempotency key.
1.1 Generate an access_token
access_token
You must include an access_token
with the payments
scope in order to make an open-loop payout. This should be included as a Bearer header along with your payout request.
1.2 Include headers for signing and idempotency
All requests to the Payments API v3, including payouts, should include a valid Tl-Signature
header for security purposes. Learn how to sign your requests.
To help with payment signatures, try our signing libraries.
It's also important that your requests include a valid Idempotency-Key
header. This should be a unique key, and enables you to run your request multiple times without duplicating it.
2. Configure an open-loop payout
After setting up your access_token
and headers, you're ready to configure and create your open-loop payout request.
To create a payout, send a POST request to the /payouts
endpoint.
2.1 Request parameters
The table below contains an overview of all the parameters you need to specify to initiate an open-loop payout.
Parameter | Description |
---|---|
merchant_account_id | The unique id of the merchant account to make the open-loop payout from. |
amount_in_minor | The value of the payout in a minor denomination. A minor denomination is the smallest unit of the specified currency, so 1 is equivalent to a penny or a cent. |
currency | The currency of the payment, provided as a 3-letter ISO 4217 currency code. For example, GBP for pounds, or EUR for euros.Note that you cannot make a payment from an account with one currency to an account with a different currency, for example from a GBP to EUR account. Ensure you provide the correct merchant_account_id . |
beneficiary.type | This must be set to external_account for an open-loop payout. |
beneficiary.reference | A reference for the payout. The beneficiary sees this reference alongside this payout on their statement or in their banking app. |
beneficiary.account_holder_name | The name of the person you're paying. You have to include this in your request. |
beneficiary.date_of_birth | The date of birth of the person you're paying, in the format YYYY-MM-DD. |
beneficiary.address | The address of the person you're paying. This object contains the following parameters: - address_line1 - address_line2 - city - state - zip - country_code |
beneficiary.account_identifier for SCAN payout | If you're making a SCAN payout, specify sort_code_account_number in the account_identifier.type parameter.You then need to provide the person you're paying's sort code and account number in the account_identifier.account_number and account_identifier.sort_code parameters respectively. |
beneficiary.account_identifier for IBAN payout | If you're making an IBAN payout, specify iban in the account_identifier.type parameter. |
submerchants | This parameter is only relevant if you are a partner of TrueLayer operating as a collecting PSP. As a partner, you are considered a merchant, and any businesses you process transactions for are 'submerchants'. If this applies to you, you must provide the details of the submerchant you're processing a transaction for in this parameter, along with the user information for the payout. Find more information about the required information in the submerchants.ultimate_counterparty parameter in the API reference for the /v3/payouts endpoint. |
metadata | This parameter is optional. You can include up to 10 key-value pairs in this object. This is typically used to attach metadata that helps with reconciliation to your payout. |
2.2 Merchant account id
id
Include the id
of the merchant account you want to pay out from in your request. You include it in the merchant_account_id
parameter. This ensures that you make the payout from the correct account.
The merchant account that you use must be in the same currency as the payout and beneficiary account. (For example, you cannot make a EUR payment from a GBP merchant account or to a GBP beneficiary account.)
You can get your merchant_account_id
through two methods:
- Use the
/v3/merchant-accounts
endpoint to return a list of your merchant accounts. - Open the Merchant Account page in Console, select the correct currency, then open the Details tab.
This is an example of the merchant_account_id
parameter filled out correctly:
{
"merchant_account_id": "2a485b0a-a29c-4aa2-bcef-b34d0f6f8d51",
//...
}
2.3 Payout amount and currency
When you make an open-loop payout, you must specify the value and currency of the payout.
To specify the value of your payout, provide a numeric value for the amount_in_minor
parameter. This should be the value of your payout in a minor denomination. This means that a value of 1500 corresponds to 15 GBP or 15 EUR.
To specify the currency of your payout, you need to provide a three-letter ISO 4217 currency code for the currency
parameter. For example, GBP for pounds sterling and EUR for euros.
This an example of the amount_in_minor
and currency
parameters filled out correctly:
{
//...
"amount_in_minor": 1500,
"currency": "GBP",
//...
}
2.4 Payout scheme selection
You can select a payment scheme for your payout to be made through by using the scheme_selection
object. Unlike creating a payment, the scheme_selection
object for payouts is not contained within a provider selection object.
There are three different values that you can set for scheme selection:
instant_preferred
instant_preferred
Preferably selects a payment scheme that supports instant payments for the currency and geography that you're making the payment in. However, the API will fall back to a non-instant scheme if instant payments are unavailable. The payout_executed
webhook will specify the actual scheme used. This is optimal when you don't mind if a payout settles slowly, and can help you avoid fees and payment failures.
instant_only
instant_only
Automatically selects a payment scheme that supports instant payments for the currency and geography that you're making the payment in. This type is optimal when you need payments to settle quickly.
preselected
preselected
Selects a payment scheme compatible with the currency of the payment and geographic region that you are paying in. This is useful if you want full control over the scheme that a payout goes through.
When you use a preselected scheme, you need to specify a scheme_id
. Ensure that your scheme ID is compatible with the currency that you are using. Possible scheme IDs are:
faster_payments_service
sepa_credit_transfer_instant
sepa_credit_transfer
See this code block for examples of each scheme selection type.
{
//[...]
"scheme_selection": {
"type" : "instant_preferred"
}
}
{
//[...]
"scheme_selection": {
"type" : "instant_only"
}
}
{
//[...]
"scheme_selection": {
"type" : "preselected",
"scheme_id": "polish_domestic_express"
}
}
We recommend using instant_preferred
or instant_only
for the best user experience.
<€100,000 payouts
Payouts that are €100,000 or over automatically move through the SEPA Credit scheme.
The payout appears as normal in the payments view on Console, with SEPA Credit displayed as the scheme.
Do not use the instant_only
scheme selection object for these payments, as they will immediately fail.
2.5 Beneficiary details
Use the beneficiary object in your payout creation request to specify who will receive your payout. For an open-loop payout, it contains four mandatory parameters and two optional parameters, explained below.
2.5.1 type
parameter
type
parameterIn an open-loop payout, you must use a value of external_account
for the type
parameter.
You can use a value of payment_source
or business_account
for a closed-loop payout or payout to your business account respectively.
2.5.2 account_holder_name
parameter
account_holder_name
parameterIn this parameter, specify the name of the person you're paying.
2.5.3 account_identifier
parameter
account_identifier
parameterThis is an object that contains several parameters. The first is account_identifier.type
, in which you use a value of sort_code_account_number
or iban
to specify a payment to SCAN or IBAN bank details respectively. The rest of the object varies based on your selection for type
:
- If you use
sort_code_account_number
, you need to provide this information in theaccount_identifier.sort_code
andaccount_identifier.account_number
parameters. - If you use
iban
, you need to provider this information in theaccount_identifier.iban
parameter.
2.5.4 reference
parameter
reference
parameterInclude a string for the reference parameter in your payout. The beneficiary sees this reference alongside this payout on their statement or in their banking app.
A common approach to take is to programmatically generate a reference that starts with a string that identifies your business. Then attach a number to it, which you and your user can use to reconcile the payout if needed.
We recommend that you keep your reference under 18 characters, and include no special characters other than -
or .
.
2.5.5 Optional user information parameters
The following two parameters are considered optional by the API, but are required if a client's transactions are subject to sanction screening. We recommend you always include the user's address and date of birth if possible to reduce the chance of RFIs.
The first optional parameter is beneficiary.address
. This is an object that contains the following six fields to describe the address, which you provide strings for:
address_line1
: Description of the street address and house number, between 1 and 50 characters.address_line2
: Optional in an address, this is for extra details like building name or apartment number, between 1 and 50 characters.city
: Name of the city or locality, between 1 and 50 characters.state
: Name of the county, province or state, between 1 and 50 characters.zip
: ZIP or postal code, between 1 and 20 characters.country_code
: The two-letter country code, in the ISO 3166-1 alpha-2 format.
The second optional parameter is beneficiary.date_of_birth
. For this parameter, provide a date in the format of YYYY-MM-DD.
User details for businesses
If you are making a payment to a business using the
external_account
method for thebeneficiary
, provide the business name, business founding date and business address.
Beneficiary object example
This an example of the beneficiary
object filled out correctly, including the six parameters described above:
{
//...
"beneficiary": {
"type": "external_account",
"account_holder_name": "JOHN SANDBRIDGE",
"account_identifier": {
"type": "sort_code_account_number",
"sort_code": "040668",
"account_number": "00000871"
},
"reference": "TestCo-627364",
"address": {
"address_line1": "40 Finsbury Square",
"state": "London",
"city": "London",
"country_code": "GB",
"zip": "EC2A 1AE"
},
"date_of_birth": "1990-01-31"
}
}
{
//...
"beneficiary": {
"type": "external_account",
"account_holder_name": "JOHN SANDBRIDGE",
"account_identifier": {
"type": "iban",
"iban": "GB75CLRB04066800000871"
},
"reference": "TestCo-627364",
"address": {
"address_line1": "40 Finsbury Square",
"state": "London",
"city": "London",
"country_code": "GB",
"zip": "EC2A 1AE"
},
"date_of_birth": "1990-01-31"
}
}
2.6 Optional metadata
You don't need to include any values for the metadata
object. However, if your business needs to include extra metadata in your payout requests (for internal processes for example), you can choose to include up to 10 key value pairs.
To include metadata, provide key value pairs in the object in the format below:
{
//...
"metadata": {
"Metadata_key_1": "Metadata_value_1",
"Metadata_key_2": "Metadata_value_2",
"Metadata_key_3": "Metadata_value_3"
}
}
Example open-loop payout requests
Specify the mandatory parameters above in your request to initiate an open-loop payout.
The code block below contains two examples of sandbox open-loop payouts: one for a SCAN payout, and one for a IBAN payout.
Note that the IBAN payout uses a UK IBAN. In sandbox, you must use a UK IBAN to make payouts.
POST /v3/payouts HTTP/1.1
Content-Type: application/json
Idempotency-Key: {RANDOM_UUID}
Tl-Signature:{SIGNATURE}
Authorization: Bearer {ACCESS_TOKEN}
Host: api.truelayer-sandbox.com
{
"merchant_account_id": "2a485b0a-a29c-4aa2-bcef-b34d0f6f8d51",
"amount_in_minor": 1,
"currency": "GBP",
"beneficiary": {
"type": "external_account",
"account_holder_name": "JOHN SANDBRIDGE",
"account_identifier": {
"type": "sort_code_account_number",
"sort_code": "040668",
"account_number": "00000871"
},
"reference": "TestCo-627364",
"address": {
"address_line1": "40 Finsbury Square",
"state": "London",
"city": "London",
"country_code": "GB",
"zip": "EC2A 1AE"
},
"date_of_birth": "1990-01-31"
}
}
POST /v3/payouts HTTP/1.1
Content-Type: application/json
Idempotency-Key: {RANDOM_UUID}
Tl-Signature:{SIGNATURE}
Authorization: Bearer {ACCESS_TOKEN}
Host: api.truelayer-sandbox.com
{
"merchant_account_id": "2a485b0a-a29c-4aa2-bcef-b34d0f6f8d51",
"amount_in_minor": 1,
"currency": "GBP",
"beneficiary": {
"type": "external_account",
"account_holder_name": "JOHN SANDBRIDGE",
"account_identifier": {
"type": "iban",
"iban": "GB75CLRB04066800000871"
},
"reference": "TestCo-627364",
"address": {
"address_line1": "40 Finsbury Square",
"state": "London",
"city": "London",
"country_code": "GB",
"zip": "EC2A 1AE"
},
"date_of_birth": "1990-01-31"
}
}
3. Monitor an open-loop payout
When you make a successful open-loop payout, you receive a response that contains the payout id
only:
{
"id": "a7e4e74f-d7da-43e2-af7f-f953724a461c"
}
You also receive a webhook when you make the payout, which contains extra information. (See below for more.)
You can use the id
and webhook you receive to confirm whether your payout was successful, and take action if needed.
Payout webhooks
We recommend that you use webhooks to monitor all of the requests you make with the Payments API v3. You receive webhooks for all events in your integration.
Webhooks for your integration are sent to the Webhook URI you have set on the Payments > Settings page in Console. You should ensure that you have set up request signing for your webhook notifications to ensure that they are reliable and not from external sources.
The two webhooks that you receive for a open-loop payout are payout_executed
or payout_failed
. You should receive these very soon after your payout request.
This is an example of a payout_executed
webhook for an open-loop payout:
{
"type": "payout_executed",
"event_id": "66eb019a-f103-4c0f-ae2e-c4a9f3bcb82d",
"event_version": 1,
"payout_id": "796cab79-cd1a-4e01-8241-93ac28bf4260",
"executed_at": "2024-01-17T12:09:54.117Z",
"beneficiary": {
"type": "external_account"
},
"scheme_id": "internal_transfer"
}
{
"type": "payout_failed",
"event_id": "24089aed-4fd4-9e13-f8b2-f458f30c836c",
"event_version": 1,
"payout_id": "0a495e9f-2f41-4669-ba33-85407c0b26cb",
"failed_at": "2024-01-12T14:56:05.117850644Z",
"failure_reason": "insufficient_funds",
"beneficiary": {
"type": "external_account"
},
}
GET payouts endpoint
You can use the payout id
you received when you made the payout to get information about it.
To do so, make a POST request to the /v3/payouts/{id}
endpoint, including the payout id
as a path parameter.
You receive a response in this format:
{
"id": "0cd1b0f7-71bc-4d24-b209-95259dadcc20",
"merchant_account_id": "AB8FA060-3F1B-4AE8-9692-4AA3131020D0",
"amount_in_minor": 0,
"currency": "GBP",
"beneficiary": {
"type": "external_account",
"reference": "string",
"account_holder_name": "string",
"account_identifiers": [
{
"type": "sort_code_account_number",
"sort_code": 560029,
"account_number": 26207729
},
]
},
"metadata": {
"prop1": "value1",
"prop2": "value2"
},
"scheme_id": "faster_payments_service",
"status": "pending",
"created_at": "string"
}
{
"id": "0cd1b0f7-71bc-4d24-b209-95259dadcc20",
"merchant_account_id": "AB8FA060-3F1B-4AE8-9692-4AA3131020D0",
"amount_in_minor": 0,
"currency": "GBP",
"beneficiary": {
"type": "external_account",
"reference": "string",
"account_holder_name": "string",
"account_identifiers": [
{
"type": "iban",
"iban": "GB32CLRB04066800012315"
}
]
},
"metadata": {
"prop1": "value1",
"prop2": "value2"
},
"scheme_id": "faster_payments_service",
"status": "pending",
"created_at": "string"
}
Use the status
object to check the progression of your payout through its lifecycle.
Payout lifecycle
After you create a payout, it moves through a series of different states depending on its outcome. See the full list of payout statuses for more information.
This is the journey a payout usually follows:
- When a payout is first made, it has a status of
pending
before it's sent to the payment scheme for authorisation. - Once the payout has been authorised and is with the payment scheme for execution, it has a status of
authorized
.
It usually takes just a few seconds for a successful payout to transition through thepending
andauthorized
statuses. - Depending on whether the payment scheme executes the payout, it transitions to one of two statuses:
payout_executed
if the payout was successful.payout_failed
if the payout could not be executed.
If a payout fails, the webhook contains a failure_reason
that you can use to identify why it failed.
A return occurs when a payout or refund is executed, but rejected by a banking provider. The money moves back to your merchant account.
The reasons for a return are decided by banks. For example, if the account you're attempting to pay has been closed by the bank, or is inaccessible as it's under investigation.
This occurs in less than 0.01% of payouts, but you must make sure that your system can handle a payout transitioning from executed
to failed
.
Low balance notifications
You can contact TrueLayer to enable an optional balance_notification
webhook. This can help ensure that you always have sufficient funds in order to make payouts to external accounts when needed.
Learn more about low balance notifications and how to enable them.
Updated about 1 month ago