Offline Mandates#
Collecting your customer's payment authorisation via an online form isn't always possible. GoCardless also supports paper and phone authorisation collection (subject to regional availability).
This feature only applies to merchants on our Custom and self-serve Pro packages with the Custom
Payment Pages feature enabled. Contact
support to get this set up.
This page details the compliance requirements for collecting authorisation this way. Please read it before proceeding.
Integration options#
There are two approaches you may take based on your current integration and future plans.
Billing Requests API: This is our newest API, which supports both bank debit and open banking use cases. Use it if you intend to build open banking functionality in future.
Core Endpoints: This API supports bank debit features. Use it if you are already integrated with it and don't intend to implement Pay By Bank.
Billing Requests API#
Billing Request Actions#
Whenever you create a Billing Request, it will have a list of actions that require completion before it's possible to fulfil the request.
The possible actions are:
choose_currency, have the payer choose from a list of supported currencies
collect_customer_details, collect customer details required for the schemes
collect_bank_account, create the bank account for the mandate/payment
confirm_payer_details, confirm customer and bank account details provided by the payer
Once all required actions have been completed, the Billing Request will become ready_to_fulfil, and an integrator can use the fulfil action to create all associated resources.
Which actions should I implement?#
When creating a Billing Request, the response will show you which actions you need to implement to complete it. We will also go through the actions you need to implement in the following steps.
Ordering and control flow#
The actions presented on a Billing Request are unordered, but each action may have dependencies, or have other actions depending on it.
These are specified in the fields completes_actions and requires_actions.
Taking collect_customer_details as an example:
{
"type" : "collect_customer_details" ,
"required" : true ,
"completes_actions" : [],
"requires_actions" : [ "choose_currency" ],
"status" : "pending"
}
Action status: is either pending or completed.
requires_actions: Actions can only be executed if the actions listed in requires_action have been completed — in this example, we can't complete collect_customer_details until we've chosen the currency.
completes_actions: Actions can complete other actions, which can help streamline checkout flows. In this example, collect_customer_details does not complete any other action as a side effect.
For offline mandates, we suggest building the flow that process the actions in the order of:
collect_customer_details, requires the choose_currency action to be completed but this will be completed automatically when creating the ACH mandate via the Billing Request create API
collect_bank_account, collect the customer's bank account details
confirm_payer_details, as a scheme compliance rule it is required to crosscheck the details entered and confirm them
Create an offline mandate via Billing Request API#
Create a Billing Request that specifies the type of mandate you wish to create via the mandate_request field. In this example, we are specifying the scheme as ACH which will default the currency to USD.
For ACH specific mandates, you can provide the authorisation_source field to create an offline mandate. The authorisation_source field specifies the way in which the payer gave authorisation to the merchant. This is sometimes referred to as SEC code . This field takes values of either telephone, paper or web.
Use the Create a Billing Request endpoint:
HTTP PHP Python Ruby Java JavaScript .NET Go Shell
POST https://api.gocardless.com/billing_requests HTTP / 1.1
{
"billing_requests" : {
"mandate_request" : {
"scheme" : "ach" ,
"authorisation_source" : "telephone"
}
}
} $client = new \GoCardlessPro\Client ( array (
'access_token' => 'your_access_token_here' ,
'environment' => \GoCardlessPro\Environment :: SANDBOX
));
$client -> billingRequests () -> create ([
"params" => [
"mandate_request" => [
"scheme" => "ach" ,
"authorisation_source" => "telephone"
]
]
]); import gocardless_pro
client = gocardless_pro.Client( access_token = "your_access_token_here" , environment = 'sandbox' )
client.billing_requests.create( params = {
"mandate_request" : {
"scheme" : "ach" ,
"authorisation_source" : "telephone"
}
}) @client = GoCardlessPro :: Client . new (
access_token: "your_access_token" ,
environment: :sandbox
)
@client. billing_requests . create (
params: {
mandate_request: {
scheme: "ach" ,
authorisation_source: "telephone"
}
}
) import static com.gocardless.GoCardlessClient.Environment.SANDBOX;
String accessToken = "your_access_token_here" ;
GoCardlessClient client = GoCardlessClient
. newBuilder (accessToken)
. withEnvironment (SANDBOX)
. build ();
BillingRequest billingRequest = client. billingRequests (). create ()
. withMandateRequestScheme ( "ach" )
. withMandateRequestAuthorisationSource ( "telephone" )
. execute (); const constants = require ( "gocardless-nodejs/constants" );
const gocardless = require ( "gocardless-nodejs" );
const client = gocardless ( "your_access_token_here" , constants.Environments.Sandbox);
const billingRequest = await client.billingRequests. create ({
mandate_request: {
scheme: "ach" ,
authorisation_source: "telephone" ,
},
}); String accessToken = "your_access_token" ;
GoCardlessClient gocardless = GoCardlessClient. Create (accessToken, Environment.SANDBOX);
var mandateRequest = new gocardless . BillingRequestCreateRequest . BillingRequestMandateRequest
{
Scheme = "ach" ,
AuthorisationSource = "telephone"
};
var resp = await gocardless.BillingRequests. CreateAsync (
new GoCardless . Services . BillingRequestCreateRequest ()
{
MandateRequest = mandateRequest,
}
);
GoCardless . Resources . BillingRequest billingRequest = resp.BillingRequest; package main
import (
gocardless " github.com/gocardless/gocardless-pro-go/v6 "
)
accessToken := "your_access_token_here"
opts := gocardless. WithEndpoint (gocardless.SandboxEndpoint)
config, err := gocardless. NewConfig (accessToken, opts)
if err != nil {
fmt. Printf ( "got err in initialising config: %s " , err. Error ())
return
}
client, err := gocardless. New (config)
if err != nil {
fmt. Println ( "error in initialisating client: %s " , err. Error ())
return
}
billingRequestCreateParams := gocardless . BillingRequestCreateParams {
MandateRequest: & gocardless . BillingRequestCreateParamsMandateRequest {
Scheme: "ach" ,
AuthorisationSource: "telephone" ,
},
}
billingRequest, err := client.BillingRequests. Create (context, billingRequestCreateParams) gc create billing_request \
-d 'mandate_request[scheme]=ach' \
-d 'mandate_request[authorisation_source]=telephone'
You'll receive a full Billing Request resource back.
It will look like this, some detail omitted:
{
"billing_requests" : {
"id" : "BRQ123" ,
"status" : "pending" ,
"payment_request" : null ,
"mandate_request" : {
"currency" : "USD" ,
"scheme" : "ach" ,
"authorisation_source" : "telephone"
},
"links" : {
"customer" : "CU00016WDAM7BS" ,
"customer_billing_detail" : "CBD000010PDF4WD" ,
"mandate_request" : "MRQ123" ,
"organisation" : "OR123"
},
"actions" : [
{
"type" : "collect_customer_details" ,
"status" : "pending"
},
{
"type" : "collect_bank_account" ,
"status" : "pending"
}
]
}
}
This completes the choose_currency action, and we can now continue with completing the rest of the Billing Request actions to set up this mandate.
Action: collect_customer_details#
Billing Requests aim to create billing resources against a customer, either a mandate (Direct Debit, PayTo or VRPs), an Pay By Bank, or both.
Payment schemes vary in what customer details you are required to collect. The collect_customer_details action is about collecting all the details required by either the mandate or the payment scheme, to ensure we meet regulatory needs.
As an example, we can created a Billing Request for ACH scheme:
POST /billing_requests
{
"billing_requests" : {
"mandate_request" : {
"scheme" : "ach" ,
"authorisation_source" : "telephone"
}
}
}
This returns a Billing Request that looks like this:
{
"billing_requests" : {
"id" : "BRQ123" ,
"status" : "pending" ,
"mandate_request" : {
"currency" : "USD" ,
"scheme" : "ach" ,
"authorisation_source" : "telephone"
},
"links" : {
"customer" : "CU00016WDAM7BS" ,
"customer_billing_detail" : "CBD000010PDF4WD" ,
"mandate_request" : "MRQ123" ,
"organisation" : "OR123"
},
"actions" : [
{
"type" : "collect_customer_details" ,
"required" : true ,
"completes_actions" : [],
"requires_actions" : [
"choose_currency"
],
"status" : "pending" ,
"collect_customer_details" : {
"incomplete_fields" : {
"customer" : [
"email" ,
"given_name" ,
"family_name"
],
"customer_billing_detail" : [
"address_line1" ,
"city" ,
"postal_code" ,
"country_code" ,
"region" ,
"ip_address"
]
}
}
},
... ,
],
"resources" : {
"customer" : {
"id" : "CU00016WDAM7BS" ,
"created_at" : "2021-04-08T14:06:30.977Z" ,
"email" : null ,
"given_name" : null ,
"family_name" : null ,
"company_name" : null ,
"language" : "en" ,
"phone_number" : null ,
"metadata" : {}
},
"customer_billing_detail" : {
"id" : "CBD000010PDF4WD" ,
"created_at" : "2021-04-08T14:06:30.997Z" ,
"address_line1" : null ,
"address_line2" : null ,
"address_line3" : null ,
"city" : null ,
"region" : null ,
"postal_code" : null ,
"country_code" : null ,
"swedish_identity_number" : null ,
"danish_identity_number" : null
}
}
}
}
Note that:
There is a collect_customer_details action, which is pending
A new customer has been created for us, as the Billing Request wasn't attached to an existing customer
The customer and customer_billing_detail resources are presented back to us, and they have no filled fields
The collect_customer_details action is designed to help us collect the required information from our customers.
Taking a closer look at the action:
{
"type" : "collect_customer_details" ,
"required" : true ,
"completes_actions" : [],
"requires_actions" : [ "choose_currency" ],
"status" : "pending" ,
"collect_customer_details" : {
"incomplete_fields" : {
"customer" : [ "email" , "given_name" , "family_name" ],
"customer_billing_detail" : [
"address_line1" ,
"city" ,
"postal_code" ,
"country_code" ,
"region" ,
"ip_address"
]
}
}
}
The collect_customer_details.incomplete_fields object tells us what fields we need to collect, for both resources. Which fields are required changes depending on the schemes of the mandate (or payment).
We can complete this action by POST'ing to the Collect customer details for the billing request endpoint.
Note that:
For online mandates with "authorisation_source": "web", you will need to provide payer's ip_address as a result of their completion of a mandate setup flow in their browser.
For paper and telephone mandates, you will still need to provide an ip_address in the customer_billing_detail, however, this can just be a dummy ip_address (e.g. 192.0.0.0).
HTTP PHP Python Ruby Java JavaScript .NET Go
POST /billing_requests/BRQ123/actions/collect_customer_details HTTP / 1.1
{
"data" : {
"customer" : {
"email" : "user@example.com" ,
"given_name" : "Frank" ,
"family_name" : "Osborne"
},
"customer_billing_detail" : {
"address_line1" : "27 Acer Road" ,
"city" : "Los Angeles" ,
"postal_code" : "90213" ,
"country_code" : "US" ,
"region" : "CA" ,
"ip_address" : "192.0.1.2"
}
}
} $client = new \GoCardlessPro\Client ( array (
'access_token' => 'your_access_token_here' ,
'environment' => \GoCardlessPro\Environment :: SANDBOX
));
$client -> billingRequests () -> collectCustomerDetails ( "BR123" , [
"params" => [
"customer" => [
"email" => "user@example.com" ,
"given_name" => "Frank" ,
"family_name" => "Osborne"
],
"customer_billing_detail" => [
"address_line1" => "1 Somewhere Lane" ,
"city" => "Los Angeles" ,
"postal_code" => "90213" ,
"country_code" => "US" ,
"region" => "CA"
"ip_address" => "192.0.2.1"
]
]
]); import gocardless_pro
client = gocardless_pro.Client( access_token = "your_access_token_here" , environment = 'sandbox' )
client.billing_requests.collect_customer_details( "BR123" , params = {
customer: {
email: "user@example.com"
given_name: "Frank" ,
family_name: "Osborne" ,
},
customer_billing_detail: {
address_line1: "27 Acer Road" ,
city: "Los Angeles" ,
postal_code: "90213" ,
country_code: "US" ,
region: "CA" ,
ip_address: "192.0.2.1"
}
}) @client = GoCardlessPro :: Client . new (
access_token: "your_access_token" ,
environment: :sandbox
)
@client. billing_requests . collect_customer_details ( "BR123" , {
params: {
customer: {
email: "user@example.com"
given_name: "Frank" ,
family_name: "Osborne" ,
},
customer_billing_detail: {
address_line1: "27 Acer Road" ,
city: "Los Angeles" ,
postal_code: "90213" ,
country_code: "US" ,
region: "CA" ,
ip_address: "192.0.2.1"
}
}
}) import static com.gocardless.GoCardlessClient.Environment.SANDBOX;
String accessToken = "your_access_token_here" ;
GoCardlessClient client = GoCardlessClient
. newBuilder (accessToken)
. withEnvironment (SANDBOX)
. build ();
client. billingRequests (). collectCustomerDetails ( "BR123" )
. withCustomerEmail ( "user@example.com" )
. withCustomerGivenName ( "Frank" )
. withCustomerFamilyName ( "Osborne" )
. withCustomerBillingDetailAddressLine1 ( "27 Acer Road" )
. withCustomerBillingDetailCity ( "Los Angeles" )
. withCustomerBillingDetailPostalCode ( "90213" )
. withCustomerBillingDetailCountryCode ( "US" )
. withCustomerBillingDetailRegion ( "CA" )
. withCustomerBillingDetailIpAddress ( "192.0.2.1" )
. execute (); const constants = require ( 'gocardless-nodejs/constants' );
const gocardless = require ( 'gocardless-nodejs' );
const client = gocardless ( 'your_access_token_here' , constants.Environments.Sandbox);
const resp = await client.billingRequests. collectCustomerDetails ( "BR123" , {
customer: {
email: "user@example.com"
given_name: "Frank" ,
family_name: "Osborne" ,
},
customer_billing_detail: {
address_line1: "27 Acer Road" ,
city: "Los Angeles" ,
postal_code: "90213" ,
country_code: "US" ,
region: "CA" ,
ip_address: "192.0.1.2"
}
}); String accessToken = "your_access_token" ;
GoCardlessClient gocardless = GoCardlessClient. Create (accessToken, Environment.SANDBOX);
var customer = new GoCardless . Services . BillingRequestCollectCustomerDetailsRequest . BillingRequestCustomer
{
Emil = "user@example.com"
GivenName = "Frank" ,
FamilyName = "Osborne" ,
};
var customerBillingDetail = new new GoCardless . Services . BillingRequestCollectCustomerDetailsRequest . BillingRequestCustomerBillingDetail
{
AddressLine1 = "27 Acer Road" ,
City = "Los Angeles" ,
PostalCode = "90213" ,
CountryCode = "US" ,
Region = "CA" ,
IpAddress = "192.0.1.2"
};
var resp = await gocardless.BillingRequests. CollectCustomerDetails ( "BR123" ,
new BillingRequestCollectCustomerDetailsRequest
{
Customer = customer,
CustomerBillingDetail = customerBillingDetail,
}); package main
import (
gocardless " github.com/gocardless/gocardless-pro-go/v6 "
)
accessToken := "your_access_token_here"
config, err := gocardless. NewConfig (accessToken, gocardless. WithEndpoint (gocardless.SandboxEndpoint))
if err != nil {
fmt. Printf ( "got err in initialising config: %s " , err. Error ())
return
}
client, err := gocardless. New (config)
if err != nil {
fmt. Println ( "error in initialisating client: %s " , err. Error ())
return
}
billingRequestCollectCustomerDetailsParams := gocardless . BillingRequestCollectCustomerDetailsParams {
Customer: & gocardless . BillingRequestCollectCustomerDetailsParamsCustomer {
GivenName: "Frank" ,
FamilyName: "Osborne" ,
Email: "user@example.com" ,
},
CustomerBillingDetail: & gocardless . BillingRequestCollectCustomerDetailsParamsCustomerBillingDetail {
AddressLine1: "27 Acer Road" ,
City: "Los Angeles" ,
PostalCode: "90213" ,
CountryCode: "US" ,
Region: "CA" ,
IpAddress: "192.0.1.2"
},
}
billingRequest, err := client.BillingRequests. CollectCustomerDetails (context, "BR123" , billingRequestCollectCustomerDetailsParams)
As with all action endpoints, the response is the Billing Request. What we get back is:
{
"billing_requests" : {
"id" : "BRQ123" ,
"status" : "pending" ,
"mandate_request" : {
"scheme" : "ach" ,
"currency" : "USD" ,
"authorisation_source" : "telephone"
},
"links" : {
"customer" : "CU00016WDAM7BS" ,
"customer_billing_detail" : "CBD000010PDF4WD" ,
"organisation" : "OR123" ,
"mandate_request" : "MRQ123"
},
"actions" : [
{
"type" : "collect_customer_details" ,
"required" : true ,
"completes_actions" : [],
"requires_actions" : [
"choose_currency"
],
"status" : "completed" ,
"collect_customer_details" : {
"incomplete_fields" : {
"customer" : [],
"customer_billing_detail" : []
}
}
},
... ,
],
"resources" : {
"customer" : {
"id" : "CU00016WDAM7BS" ,
"created_at" : "2021-04-08T14:06:30.977Z" ,
"email" : "user@example.com" ,
"given_name" : "Frank" ,
"family_name" : "Osborne" ,
"company_name" : null ,
"language" : "en" ,
"phone_number" : null ,
"metadata" : {}
},
"customer_billing_detail" : {
"id" : "CBD000010PDF4WD" ,
"created_at" : "2021-04-08T14:06:30.997Z" ,
"address_line1" : "27 Acer Road" ,
"address_line2" : null ,
"address_line3" : null ,
"city" : "Los Angeles" ,
"region" : null ,
"postal_code" : "90213" ,
"country_code" : "US" ,
"swedish_identity_number" : null ,
"danish_identity_number" : null
}
}
}
}
Note that:
The collect_customer_details action is now completed, meaning we can move on to other actions
As a result of us collecting the details, our customer and customer_billing_detail has been populated with the details we collected
Most integrators will collect these details via web forms, filled by their payers. Integrators are expected to build forms that can collect all possible customer and customer_billing_detail fields (see the Collect customer details schema) but only display the inputs required, as per incomplete_fields.
Action: collect_bank_account#
Depending on the scheme, we might need to collect bank account details before fulfilling the Billing Request. An example is a Direct Debit mandate, where we need to capture the payer's bank in order to create Direct Debit payments against them.
As an example, we created a Billing Request for ACH scheme:
POST /billing_requests
{
"billing_requests" : {
"mandate_request" : {
"scheme" : "ach" ,
"authorisation_source" : "telephone"
}
}
}
This returns a Billing Request that looks like this:
{
"billing_requests" : {
"id" : "BRQ123" ,
"status" : "pending" ,
"mandate_request" : {
"currency" : "USD" ,
"scheme" : "ach" ,
"authorisation_source" : "telephone"
},
"links" : {
"customer" : "CU00016WDAM7BS" ,
"customer_billing_detail" : "CBD000010PDF4WD" ,
"mandate_request" : "MRQ123" ,
"organisation" : "OR123"
},
"actions" : [
{
"type" : "collect_bank_account" ,
"required" : true ,
"completes_actions" : [
"choose_currency"
],
"available_country_codes" : [
"US"
],
"requires_actions" : [],
"status" : "pending"
},
... ,
],
}
}
Note that:
There is a collect_bank_account action that is pending
We have no links.customer_bank_account, confirming no bank account is attached
We can complete this action by POST'ing to the Collect bank account for the billing request endpoint:
HTTP PHP Python Ruby Java JavaScript .NET Go
POST /billing_requests/BRQ123/actions/collect_bank_account HTTP / 1.1
{
"data" : {
"account_number" : "2715500356" ,
"bank_code" : "026073150" ,
"account_holder_name" : "Frank Osborne" ,
"country_code" : "US" ,
"account_type" : "checking"
}
} $client = new \GoCardlessPro\Client ( array (
'access_token' => 'your_access_token_here' ,
'environment' => \GoCardlessPro\Environment :: SANDBOX
));
$client -> billingRequests () -> collectBankAccount ( "BRQ000010NMDMH2" , [
"params" => [
"account_number" => "2715500356" ,
"bank_code" => "026073150" ,
"account_holder_name" => "Frank Osborne" ,
"country_code" => "US" ,
"account_type" : "checking"
]
]); import gocardless_pro
client = gocardless_pro.Client( access_token = "your_access_token_here" , environment = 'sandbox' )
client.billing_requests.collect_bank_account( "BRQ000010NMDMH2" , params = {
account_number: "2715500356" ,
bank_code: "026073150" ,
account_holder_name: "Frank Osborne" ,
country_code: "US" ,
"account_type" : "checking" ,
}) @client = GoCardlessPro :: Client . new (
access_token: "your_access_token" ,
environment: :sandbox
)
@client. billing_requests . collect_bank_account ( "BRQ000010NMDMH2" , {
params: {
account_number: "2715500356" ,
bank_code: "026073150" ,
account_holder_name: "Frank Osborne" ,
country_code: "US" ,
account_type: "checking" ,
}
}) import static com.gocardless.GoCardlessClient.Environment.SANDBOX;
String accessToken = "your_access_token_here" ;
GoCardlessClient client = GoCardlessClient
. newBuilder (accessToken)
. withEnvironment (SANDBOX)
. build ();
client. billingRequests (). collectBankAccount ( "BRQ123" )
. withAccountNumber ( "2715500356" )
. withBankCode ( "026073150" )
. withAccountHolderName ( "Frank Osborne" )
. withCountryCode ( "US" )
. withAccountType ( "checking" )
. execute (); const constants = require ( "gocardless-nodejs/constants" );
const gocardless = require ( "gocardless-nodejs" );
const client = gocardless ( "your_access_token_here" , constants.Environments.Sandbox);
const resp = await client.billingRequests. collectBankAccount ( "BRQ123" , {
account_number: "2715500356" ,
bank_code: "026073150" ,
account_holder_name: "Frank Osborne" ,
country_code: "US" ,
account_type: "checking" ,
}); String accessToken = "your_access_token" ;
GoCardlessClient gocardless = GoCardlessClient. Create (accessToken, Environment.SANDBOX);
var resp = await gocardless.BillingRequests. CollectBankAccountAsync ( "BRQ123" ,
new BillingRequestCollectBankAccountRequest
{
AccountNumber = "2715500356" ,
BankCode = "026073150" ,
AccountHolderName = "Frank Osborne" ,
CountryCode = "US" ,
AccountType = "checking" ,
}); package main
import (
gocardless " github.com/gocardless/gocardless-pro-go/v6 "
)
accessToken := "your_access_token_here"
config, err := gocardless. NewConfig (accessToken, gocardless. WithEndpoint (gocardless.SandboxEndpoint))
if err != nil {
fmt. Printf ( "got err in initialising config: %s " , err. Error ())
return
}
client, err := gocardless. New (config)
if err != nil {
fmt. Println ( "error in initialisating client: %s " , err. Error ())
return
}
billingRequestCollectBankAccountParams := gocardless . BillingRequestCollectBankAccountParams {
BankCode: "026073150" ,
CountryCode: "US" ,
AccountNumber: "2715500356" ,
AccountHolderName: "Frank Osborne" ,
AccountType: "checking" ,
}
billingRequest, err := client.BillingRequests. CollectBankAccount (context, "BRQ123" , billingRequestCollectBankAccountParams)
As with all action endpoints, the response is the Billing Request. What we get back is:
{
"billing_requests" : {
"id" : "BRQ123" ,
"status" : "pending" ,
"mandate_request" : {
"currency" : "USD" ,
"scheme" : "ach" ,
"authorisation_source" : "telephone"
},
"links" : {
"customer" : "CU00016WDAM7BS" ,
"customer_billing_detail" : "CBD000010P52VRF" ,
"customer_bank_account" : "BA123" ,
"organisation" : "OR123"
},
"actions" : [
{
"type" : "collect_bank_account" ,
"required" : true ,
"completes_actions" : [
"choose_currency"
],
"requires_actions" : [],
"status" : "completed"
},
... ,
],
"resources" : {
"customer_bank_account" : {
"id" : "BA123" ,
"created_at" : "2021-04-08T15:30:36.019Z" ,
"account_number_ending" : "56" ,
"account_holder_name" : "FRANK OSBORNE" ,
"account_type" : "checking" ,
"bank_name" : "Community Federal Savings Bank" ,
"currency" : "USD" ,
"country_code" : "US" ,
"metadata" : {},
"enabled" : true ,
"links" : {
"customer" : "CU00016WDAM7BS"
}
}
}
}
}
Note that:
The collect_bank_account action is now completed, meaning we can move on to other actions
As a result of us collecting the bank account, we have created a customer_bank_account resource and you can see the links.customer_bank_account ID has been set
Action: confirm_payer_details#
As part of scheme compliance, we need to ensure the payer was presented with a confirmation screen before fulfilling the Billing Request. All mandate requests will require this action.
As an example, we created a Billing Request for ACH scheme:
POST /billing_requests
{
"billing_requests" : {
"mandate_request" : {
"scheme" : "ach" ,
"authorisation_source" : "telephone"
}
}
}
This returns a Billing Request that looks like this:
{
"billing_requests" : {
"id" : "BRQ123" ,
"status" : "pending" ,
"mandate_request" : {
"currency" : "USD" ,
"scheme" : "ach" ,
"authorisation_source" : "telephone"
},
"links" : {
"customer" : "CU00016WDAM7BS" ,
"customer_billing_detail" : "CBD000010PDF4WD" ,
"mandate_request" : "MRQ123" ,
"organisation" : "OR123"
},
"actions" : [
{
"type" : "confirm_payer_details" ,
"required" : true ,
"completes_actions" : [],
"requires_actions" : [
"collect_customer_details" ,
"collect_bank_account"
],
"status" : "pending"
}
... ,
],
}
}
Note that:
There is a confirm_payer_details action that is pending
The action is required — all mandate requests will require this
The action requires collect_customer_details and collect_bank_account, as we can only show the confirmation page once these actions are completed.
We can complete this action by POST'ing to the confirm payer details endpoint:
HTTP PHP Python Ruby Java JavaScript .NET Go
POST https://api.gocardless.com/billing_requests/BRQ123/actions/confirm_payer_details HTTP / 1.1 $client = new \GoCardlessPro\Client ( array (
'access_token' => 'your_access_token_here' ,
'environment' => \GoCardlessPro\Environment :: SANDBOX
));
$client -> billingRequests () -> confirmPayerDetails ( "BR123" ); import gocardless_pro
client = gocardless_pro.Client( access_token = "your_access_token_here" , environment = 'sandbox' )
client.billing_requests.confirm_payer_details( "BR123" ) @client = GoCardlessPro :: Client . new (
access_token: "your_access_token" ,
environment: :sandbox
)
@client. billing_requests . confirm_payer_details ( "BR123" ) import static com.gocardless.GoCardlessClient.Environment.SANDBOX;
String accessToken = "your_access_token_here" ;
GoCardlessClient client = GoCardlessClient
. newBuilder (accessToken)
. withEnvironment (SANDBOX)
. build ();
client. billingRequests (). confirmPayerDetails ( "BR123" ). execute (); const constants = require ( "gocardless-nodejs/constants" );
const gocardless = require ( "gocardless-nodejs" );
const client = gocardless ( "your_access_token_here" , constants.Environments.Sandbox);
const resp = await client.billingRequests. confirmPayerDetails ( "BR123" ); String accessToken = "your_access_token" ;
GoCardlessClient gocardless = GoCardlessClient. Create (accessToken, Environment.SANDBOX);
var resp = await gocardless.BillingRequests. ConfirmPayerDetailsAsync ( "BR123" ); package main
import (
gocardless " github.com/gocardless/gocardless-pro-go/v6 "
)
accessToken := "your_access_token_here"
config, err := gocardless. NewConfig (accessToken, gocardless. WithEndpoint (gocardless.SandboxEndpoint))
if err != nil {
fmt. Printf ( "got err in initialising config: %s " , err. Error ())
return
}
client, err := gocardless. New (config)
if err != nil {
fmt. Println ( "error in initialisating client: %s " , err. Error ())
return
}
billingRequestFulfilParams := gocardless . BillingRequestFulfilParams {}
billingRequest, err := client.BillingRequests. Fulfil (context, "BR123" , billingRequestFulfilParams)
As with all action endpoints, the response is the Billing Request. What we get back is:
{
"billing_requests" : {
"id" : "BRQ123" ,
"status" : "ready_to_fulfil" ,
"mandate_request" : {
"currency" : "USD" ,
"scheme" : "ach" ,
"authorisation_source" : "telephone"
},
"links" : {
"customer" : "CU00016WDAM7BS" ,
"customer_billing_detail" : "CBD000010P52VRF" ,
"customer_bank_account" : "BA0000QDWEEAFB" ,
"mandate_request" : "MRQ123" ,
"organisation" : "OR123"
},
"actions" : [
{
"type" : "confirm_payer_details" ,
"required" : true ,
"completes_actions" : [],
"requires_actions" : [
"collect_customer_details" ,
"collect_bank_account"
],
"status" : "completed"
},
... ,
],
"resources" : {
...,
}
}
}
}
Note that:
The confirm_payer_details action is now completed, and the billing request is in ready_to_fulfil state.
We can now move on to fulfilling this Billing Request.
Action: fulfil#
Once the billing request is in ready_to_fulfil state we can complete setting up this mandate by POST'ing to the billing request fulfil endpoint.
HTTP PHP Python Ruby Java JavaScript .NET Go
POST https://api.gocardless.com/billing_requests/BRQ123/actions/fulfil HTTP / 1.1 $client = new \GoCardlessPro\Client ( array (
'access_token' => 'your_access_token_here' ,
'environment' => \GoCardlessPro\Environment :: SANDBOX
));
$client -> billingRequests () -> fulfil ( "BR123" ); import gocardless_pro
client = gocardless_pro.Client( access_token = "your_access_token_here" , environment = 'sandbox' )
client.billing_requests.fulfil( "BR123" ) @client = GoCardlessPro :: Client . new (
access_token: "your_access_token" ,
environment: :sandbox
)
@client. billing_requests . fulfil ( "BR123" ) import static com.gocardless.GoCardlessClient.Environment.SANDBOX;
String accessToken = "your_access_token_here" ;
GoCardlessClient client = GoCardlessClient
. newBuilder (accessToken)
. withEnvironment (SANDBOX)
. build ();
client. billingRequests (). fulfil ( "BR123" ). execute (); const constants = require ( "gocardless-nodejs/constants" );
const gocardless = require ( "gocardless-nodejs" );
const client = gocardless ( "your_access_token_here" , constants.Environments.Sandbox);
const resp = await client.billingRequests. fulfil ( "BR123" ); String accessToken = "your_access_token" ;
GoCardlessClient gocardless = GoCardlessClient. Create (accessToken, Environment.SANDBOX);
var resp = await gocardless.BillingRequests. FulfilAsync ( "BR123" ); package main
import (
gocardless " github.com/gocardless/gocardless-pro-go/v6 "
)
accessToken := "your_access_token_here"
config, err := gocardless. NewConfig (accessToken, gocardless. WithEndpoint (gocardless.SandboxEndpoint))
if err != nil {
fmt. Printf ( "got err in initialising config: %s " , err. Error ())
return
}
client, err := gocardless. New (config)
if err != nil {
fmt. Println ( "error in initialisating client: %s " , err. Error ())
return
}
billingRequestFulfilParams := gocardless . BillingRequestFulfilParams {}
billingRequest, err := client.BillingRequests. Fulfil (context, "BR123" , billingRequestFulfilParams)
As with all action endpoints, the response is the Billing Request. What we get back is:
{
"billing_requests" : {
"id" : "BRQ123" ,
"status" : "fulfilled" ,
"mandate_request" : {
"currency" : "USD" ,
"scheme" : "ach" ,
"authorisation_source" : "teleohone"
},
"links" : {
"customer" : "CU00016WDAM7BS" ,
"customer_billing_detail" : "CBD000010P52VRF" ,
"customer_bank_account" : "BA123" ,
"organisation" : "OR123" ,
"mandate_request" : "MRQ123"
},
"actions" : [],
"resources" : {
...,
}
}
}
}
Done!#
The mandate has now been created and is ready to create payments against. The Billing Request is fulfilled, and cannot be modified.
Core Endpoints#
Create a customer#
Before you create a mandate using the core endpoints, you will need to create a customer and create a bank account for that customer. We will start by creating a customer.
Use the Create a customer endpoint.
HTTP PHP Python Ruby Java JavaScript .NET Go
POST https://api.gocardless.com/customers HTTP / 1.1
{
"customers" : {
"email" : "user@example.com" ,
"given_name" : "Frank" ,
"family_name" : "Osborne" ,
"address_line1" : "27 Acer Road" ,
"address_line2" : "Apt 2" ,
"city" : "Los Angeles" ,
"postal_code" : "90213" ,
"country_code" : "US" ,
"region" : "CA"
}
} $client = new \GoCardlessPro\Client ( array (
'access_token' => 'your_access_token_here' ,
'environment' => \GoCardlessPro\Environment :: SANDBOX
));
$client -> customers () -> create ([
"params" => [ "email" => "user@example.com" ,
"given_name" => "Frank" ,
"family_name" => "Osborne" ,
"address_line1" => "27 Acer Road" ,
"address_link2" => "Apt 2" ,
"city" => "Los Angeles" ,
"postal_code" => "90213" ,
"country_code" => "US" ,
"region" => "CA" ]
]); import gocardless_pro
client = gocardless_pro.Client( access_token = "your_access_token_here" , environment = 'sandbox' )
client.customers.create( params = {
"email" : "user@example.com" ,
"given_name" : "Frank" ,
"family_name" : "Osborne" ,
"address_line1" : "27 Acer Road" ,
"address_line2" : "Apt 2" ,
"city" : "Los Angeles" ,
"postal_code" : "90213" ,
"country_code" : "US" ,
"region" : "CA"
}) @client = GoCardlessPro :: Client . new (
access_token: "your_access_token" ,
environment: :sandbox
)
@client. customers . create (
params: {
email: "user@example.com" ,
given_name: "Frank" ,
family_name: "Osborne" ,
address_line1: "27 Acer Road" ,
address_line2: "Apt 2" ,
city: "Los Angeles" ,
postal_code: "90213" ,
country_code: "US" ,
region: "CA"
}
) import static com.gocardless.GoCardlessClient.Environment.SANDBOX;
String accessToken = "your_access_token_here" ;
GoCardlessClient client = GoCardlessClient
. newBuilder (accessToken)
. withEnvironment (SANDBOX)
. build ();
Customer customer = client. customers (). create ()
. withEmail ( "user@example.com" )
. withGivenName ( "Frank" )
. withFamilyName ( "Osborne" )
. withAddressLine1 ( "27 Acer Road" )
. withAddressLine2 ( "Apt 2" )
. withCity ( "Los Angeles" )
. withPostalCode ( "90213" )
. withCountryCode ( "US" )
. withRegion ( "CA" )
. execute (); const constants = require ( "gocardless-nodejs/constants" );
const gocardless = require ( "gocardless-nodejs" );
const client = gocardless ( "your_access_token_here" , constants.Environments.Sandbox);
const customer = await client.customers. create ({
email: "user@example.com" ,
given_name: "Frank" ,
family_name: "Osborne" ,
address_line1: "27 Acer Road" ,
address_line2: "Apt 2" ,
city: "Los Angeles" ,
postal_code: "90213" ,
country_code: "US" ,
region: "CA" ,
}); String accessToken = "your_access_token" ;
GoCardlessClient gocardless = GoCardlessClient. Create (accessToken, Environment.SANDBOX);
var customerRequest = new GoCardless . Services . CustomerCreateRequest ()
{
Email = "user@example.com" ,
GivenName = "Frank" ,
FamilyName = "Osborne" ,
AddressLine1 = "27 Acer Road" ,
AddressLine2 = "Apt 2" ,
City = "Los Angeles" ,
PostalCode = " 90213" ,
CountryCode = "US" ,
Region = "CA" ,
};
var customerResponse = await gocardless.Customers. CreateAsync (customerRequest);
GoCardless . Resources . Customer customer = customerResponse.Customer; package main
import (
gocardless " github.com/gocardless/gocardless-pro-go/v6 "
)
accessToken := "your_access_token_here"
config, err := gocardless. NewConfig (accessToken, gocardless. WithEndpoint (gocardless.SandboxEndpoint))
if err != nil {
fmt. Printf ( "got err in initialising config: %s " , err. Error ())
return
}
client, err := gocardless. New (config)
if err != nil {
fmt. Println ( "error in initialisating client: %s " , err. Error ())
return
}
customerCreateParams := gocardless . CustomerCreateParams {
AddressLine1: "27 Acer Road" ,
AddressLine2: "Apt 2" ,
City: "Los Angeles" ,
PostalCode: "90213" ,
CountryCode: "US" ,
Region: "CA" ,
Email: "user@example.com" ,
GivenName: "Frank" ,
FamilyName: "Osborne" ,
}
customer, err := client.Customers. Create (context, customerCreateParams)
This returns a customer that will look something like this:
{
"customers" : {
"id" : "CU0000QGB3Q04D" ,
"created_at" : "2022-12-14T10:44:07.558Z" ,
"email" : "user@example.com" ,
"given_name" : "Frank" ,
"family_name" : "Osborne" ,
"company_name" : null ,
"address_line1" : "27 Acer Road" ,
"address_line2" : "Apt 2" ,
"address_line3" : null ,
"city" : "Los Angeles" ,
"region" : "CA" ,
"postal_code" : "90213" ,
"country_code" : "US" ,
"language" : "en" ,
"swedish_identity_number" : null ,
"danish_identity_number" : null ,
"phone_number" : null ,
"metadata" : {}
}
}
We will be using the id of the customer in the next step to create the customer bank account.
Create a customer bank account#
Create a new customer bank account, linking the customer id from the previous step.
Use the Create a customer bank account endpoint.
HTTP PHP Python Ruby Java JavaScript .NET Go
POST https://api.gocardless.com/customer_bank_accounts HTTP / 1.1
{
"customer_bank_accounts" : {
"account_number" : "2715500356" ,
"branch_code" : "026073150" ,
"account_holder_name" : "Frank Osborne" ,
"currency" : "USD" ,
"country_code" : "US" ,
"account_type" : "checking" ,
"links" : {
"customer" : "CU123"
}
}
} $client = new \GoCardlessPro\Client ( array (
'access_token' => 'your_access_token_here' ,
'environment' => \GoCardlessPro\Environment :: SANDBOX
));
$client -> customerBankAccounts () -> create ([
"params" => [ "account_number" => "2715500356" ,
"branch_code" => "026073150" ,
"account_holder_name" => "Frank Osborne" ,
"currency" => "USD" ,
"country_code" => "US" ,
"account_type" => "checking" ,
"links" => [ "customer" => "CU123" ]]
]); import gocardless_pro
client = gocardless_pro.Client( access_token = "your_access_token_here" , environment = 'sandbox' )
client.customer_bank_accounts.create( params = {
"account_number" : "2715500356" ,
"branch_code" : "026073150" ,
"account_holder_name" : "Frank Osborne" ,
"country_code" : "US" ,
"currency" : "USD" ,
"account_type" : "checking" ,
"links" : {
"customer" : "CU123"
}
}) @client = GoCardlessPro :: Client . new (
access_token: "your_access_token" ,
environment: :sandbox
)
@client. customer_bank_accounts . create (
params: {
account_holder_name: "Frank Osborne" ,
account_number: "2715500356" ,
branch_code: "026073150" ,
country_code: "US" ,
currency: "USD" ,
account_type: "checking" ,
links: {
customer: "CU123"
}
}
) import static com.gocardless.GoCardlessClient.Environment.SANDBOX;
String accessToken = "your_access_token_here" ;
GoCardlessClient client = GoCardlessClient
. newBuilder (accessToken)
. withEnvironment (SANDBOX)
. build ();
CustomerBankAccount customerBankAccount = client. customerBankAccounts (). create ()
. withAccountNumber ( "2715500356" )
. withBranchCode ( "026073150" )
. withAccountHolderName ( "Frank Osborne" )
. withCurrency ( "USD" )
. withCountryCode ( "US" )
. withAccountType ( "checking" )
. withLinksCustomer ( "CU123" )
. execute (); const constants = require ( "gocardless-nodejs/constants" );
const gocardless = require ( "gocardless-nodejs" );
const client = gocardless ( "your_access_token_here" , constants.Environments.Sandbox);
const customerBankAccount = await client.customerBankAccounts. create ({
account_number: "2715500356" ,
branch_code: "026073150" ,
account_holder_name: "Frank Osborne" ,
country_code: "US" ,
currency: "USD" ,
account_type: "checking" ,
links: {
customer: "CU123" ,
},
}); String accessToken = "your_access_token" ;
GoCardlessClient gocardless = GoCardlessClient. Create (accessToken, Environment.SANDBOX);
var customerBankAccountRequest = new GoCardless . Services . CustomerBankAccountCreateRequest ()
{
AccountHolderName = "Frank Osborne" ,
AccountNumber = "2715500356" ,
BranchCode = "026073150" ,
CountryCode = "US" ,
Currency = "USD" ,
AccountType = "checking"
Links = new GoCardless . Services . CustomerBankAccountCreateRequest . CustomerBankAccountLinks ()
{
Customer = "CU0123"
}
};
var customerBankAccountResponse = await gocardless.CustomerBankAccounts. CreateAsync (customerBankAccountRequest);
GoCardless . Resources . CustomerBankAccount customerBankAccount = customerBankAccountResponse.CustomerBankAccount; package main
import (
gocardless " github.com/gocardless/gocardless-pro-go/v6 "
)
accessToken := "your_access_token_here"
config, err := gocardless. NewConfig (accessToken, gocardless. WithEndpoint (gocardless.SandboxEndpoint))
if err != nil {
fmt. Printf ( "got err in initialising config: %s " , err. Error ())
return
}
client, err := gocardless. New (config)
if err != nil {
fmt. Println ( "error in initialisating client: %s " , err. Error ())
return
}
customerBankAccountCreateParams := gocardless . CustomerBankAccountCreateParams {
AccountNumber: "2715500356" ,
BranchCode: "026073150" ,
AccountHolderName: "Frank Osborne" ,
CountryCode: "US" ,
Currency: "USD" ,
AccountType: "checking" ,
Links: gocardless . CustomerBankAccountCreateParamsLinks {
Customer: "CU123" ,
},
}
customerBankAccount, err := client.CustomerBankAccounts. Create (context, customerBankAccountCreateParams)
This will return a customer bank account that will look like this:
{
"customer_bank_accounts" : {
"id" : "BA123" ,
"created_at" : "2014-05-08T17:01:06.000Z" ,
"account_number_ending" : "56" ,
"account_holder_name" : "FRANK OSBORNE" ,
"account_type" : "checking" ,
"bank_name" : "Community Federal Savings Bank" ,
"currency" : "USD" ,
"country_code" : "US" ,
"metadata" : {},
"enabled" : true ,
"links" : {
"customer" : "CU123"
}
}
}
We will be using the id of the customer_bank_account in the next step to create a mandate.
Create an offline mandate via Mandate API#
Create a Mandate request linking the customer_bank_account in the request.
For ACH specific mandates, you can provide the authorisation_source field to create an offline mandate. The authorisation_source field specifies the way in which the payer gave authorisation to the merchant. This is sometimes referred to as SEC code . This field takes values of either telephone, paper or web.
Use the Create a mandate endpoint:
HTTP PHP Python Ruby Java JavaScript .NET Go
POST https://api.gocardless.com/mandates HTTP / 1.1
{
"mandates" : {
"scheme" : "ach" ,
"links" : {
"customer_bank_account" : "BA123" ,
"creditor" : "CR123"
},
"authorisation_source" : "telephone"
}
} $client = new \GoCardlessPro\Client ( array (
'access_token' => 'your_access_token_here' ,
'environment' => \GoCardlessPro\Environment :: SANDBOX
));
$client -> mandates () -> create ([
"params" => [ "scheme" => "ach" ,
"authorisation_source" => "telephone" ,
"links" => [ "customer_bank_account" => "BA123" , "creditor" => "CR123" ]]
]); import gocardless_pro
client = gocardless_pro.Client( access_token = "your_access_token_here" , environment = 'sandbox' )
client.mandates.create( params = {
"scheme" : "ach" ,
"links" : {
"customer_bank_account" : "BA123" ,
"creditor" : "CR123"
},
"authorisation_source" : "telephone"
}) @client = GoCardlessPro :: Client . new (
access_token: "your_access_token" ,
environment: :sandbox
)
@client. mandates . create (
params: {
scheme: "ach" ,
links: {
customer_bank_account: "BA123" ,
creditor: "CR123"
},
authorisation_source: "telephone"
}
) import static com.gocardless.GoCardlessClient.Environment.SANDBOX;
String accessToken = "your_access_token_here" ;
GoCardlessClient client = GoCardlessClient
. newBuilder (accessToken)
. withEnvironment (SANDBOX)
. build ();
Mandate mandate = client. mandates (). create ()
. withScheme ( "ach" )
. withLinksCustomerBankAccount ( "BA123" )
. withLinksCreditor ( "CR123" )
. withAuthorisationSource ( "telephone" )
. execute (); const constants = require ( "gocardless-nodejs/constants" );
const gocardless = require ( "gocardless-nodejs" );
const client = gocardless ( "your_access_token_here" , constants.Environments.Sandbox);
const mandate = await client.mandates. create ({
scheme: "ach" ,
links: {
customer_bank_account: "BA123" ,
creditor: "CR123" ,
},
authorisation_source: "telephone" ,
}); String accessToken = "your_access_token" ;
GoCardlessClient gocardless = GoCardlessClient. Create (accessToken, Environment.SANDBOX);
var mandateRequest = new GoCardless . Services . MandateCreateRequest ()
{
Links = new GoCardless . Services . MandateCreateRequest . MandateLinks ()
{
CustomerBankAccount = "BA0123" ,
Creditor = "CR0123"
},
Scheme = "ach" ,
AuthorisationSource = "telephone"
};
var mandateResponse = await gocardless.Mandates. CreateAsync (mandateRequest);
GoCardless . Resources . Mandate mandate = mandateResponse.Mandate; package main
import (
gocardless " github.com/gocardless/gocardless-pro-go/v6 "
)
accessToken := "your_access_token_here"
config, err := gocardless. NewConfig (accessToken, gocardless. WithEndpoint (gocardless.SandboxEndpoint))
if err != nil {
fmt. Printf ( "got err in initialising config: %s " , err. Error ())
return
}
client, err := gocardless. New (config)
if err != nil {
fmt. Println ( "error in initialisating client: %s " , err. Error ())
return
}
mandateCreateParams := gocardless . MandateCreateParams {
Scheme: "ach" ,
AuthorisationSource: "telephone" ,
Links: gocardless . MandateCreateParamsLinks {
CustomerBankAccount: "BA123" ,
Creditor: "CR123" ,
}
}
mandate, err := clinet. Mandates (). Create (context, mandateCreateParams)
You'll receive a full Mandate resource back.
It will look like this, some detail omitted:
{
"mandates" : {
"id" : "MD123" ,
"created_at" : "2014-05-08T17:01:06.000Z" ,
"reference" : "REF-123" ,
"status" : "pending_submission" ,
"scheme" : "ach" ,
"next_possible_charge_date" : "2014-11-10" ,
"metadata" : {
"contract" : "ABCD1234"
},
"links" : {
"customer_bank_account" : "BA123" ,
"creditor" : "CR123" ,
"customer" : "CU123"
},
"authorisation_source" : "telephone"
}
}
You can now take payments from this mandate using the Create a payment API.
What's next?#