Custom Payment Pages#
Build your own payment flow from scratch using the GoCardless API directly. You have complete control over the user experience, design, and logic.
This is the highest-effort integration. Consider starting with Hosted Pages to validate your integration — the underlying API is identical.
Supports: Direct Debit, Pay By Bank, VRP.
Best for#
- White-label or embedded product requirements
- Complex, multi-step user journeys
- Deep integration with your application
- Full control over UX and brand experience
How it works#
- Build your own UI for collecting customer and bank account details
- Use the Billing Requests API to create and progress through the payment flow
- Call API actions to collect customer details, collect bank account information, and confirm
- Handle all states, errors, and edge cases in your application
What you need to handle:
- Input validation and error messaging
- Scheme-specific bank account fields per country
- Compliance requirements (e.g., Direct Debit Guarantee display)
- Loading states and timeouts
- Mobile responsiveness
Step-by-step guide#
Create a billing request#
The Billing Request defines what you're collecting and applies to all flows. The payload differs by payment type:
curl -X POST https://api.gocardless.com/billing_requests \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"billing_requests": {
"mandate_request": {
"scheme": "bacs"
}
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->create([
'params' => [
'mandate_request' => [
'scheme' => 'bacs',
],
],
]);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.create(
params={
'mandate_request': {
'scheme': 'bacs',
}
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.create(
params: {
mandate_request: {
scheme: 'bacs'
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCreateResponse response = client.billingRequests()
.create()
.withMandateRequestScheme("bacs")
.execute();const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");
const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);
const response = await client.billingRequests.create({
mandate_request: {
scheme: "bacs",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CreateAsync(
new BillingRequestCreateRequest
{
MandateRequest = new BillingRequestCreateRequest.BillingRequestMandateRequest
{
Scheme = "bacs"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestCreateParams{
MandateRequest: &gocardless.BillingRequestCreateParamsMandateRequest{
Scheme: "bacs",
},
}
result, err := client.BillingRequests.Create(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"billing_requests": {
"id": "BRQ0000301",
"status": "pending",
"mandate_request": {
"scheme": "bacs",
"currency": "GBP"
},
"actions": [
{
"type": "collect_customer_details",
"required": true,
"completed": false
},
{
"type": "collect_bank_account",
"required": true,
"completed": false
},
{
"type": "confirm_payer_details",
"required": true,
"completed": false
}
]
}
}curl -X POST https://api.gocardless.com/billing_requests \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"billing_requests": {
"payment_request": {
"description": "Order #9012",
"amount": 12000, "currency": "GBP", "scheme": "faster_payments"
}
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->create([
'params' => [
'payment_request' => [
'description' => 'Order #9012',
'amount' => 12000,
'currency' => 'GBP',
'scheme' => 'faster_payments',
],
],
]);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.create(
params={
'payment_request': {
'description': 'Order #9012',
'amount': 12000,
'currency': 'GBP',
'scheme': 'faster_payments',
}
}
)
require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.create(
params: {
payment_request: {
description: 'Order #9012',
amount: 12000,
currency: 'GBP',
scheme: 'faster_payments'
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCreateResponse response = client.billingRequests()
.create()
.withPaymentRequestDescription("Order #9012")
.withPaymentRequestAmount(12000)
.withPaymentRequestCurrency("GBP")
.withPaymentRequestScheme("faster_payments")
.execute();const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");
const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);
const response = await client.billingRequests.create({
payment_request: {
description: "Order #9012",
amount: 12000,
currency: "GBP",
scheme: "faster_payments",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CreateAsync(
new BillingRequestCreateRequest
{
PaymentRequest = new BillingRequestCreateRequest.BillingRequestPaymentRequest
{
Description = "Order #9012",
Amount = 12000,
Currency = "GBP",
Scheme = "faster_payments"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestCreateParams{
PaymentRequest: &gocardless.BillingRequestCreateParamsPaymentRequest{
Description: "Order #9012",
Amount: 12000,
Currency: "GBP",
Scheme: "faster_payments",
},
}
result, err := client.BillingRequests.Create(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"billing_requests": {
"id": "BRQ0000302",
"status": "pending",
"payment_request": {
"amount": 12000,
"currency": "GBP",
"scheme": "faster_payments"
},
"actions": [
{
"type": "collect_customer_details",
"required": true,
"completed": false
},
{
"type": "select_institution",
"required": true,
"completed": false
},
{
"type": "bank_authorisation",
"required": true,
"completed": false
}
]
}
}curl -X POST https://api.gocardless.com/billing_requests \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"billing_requests": {
"payment_request": {
"description": "First payment",
"amount": 5000, "currency": "GBP", "scheme": "faster_payments"
},
"mandate_request": { "scheme": "bacs" }
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->create([
'params' => [
'payment_request' => [
'description' => 'First payment',
'amount' => 5000,
'currency' => 'GBP',
'scheme' => 'faster_payments',
],
'mandate_request' => [
'scheme' => 'bacs',
],
],
]);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.create(
params={
'payment_request': {
'description': 'First payment',
'amount': 5000,
'currency': 'GBP',
'scheme': 'faster_payments',
},
'mandate_request': {
'scheme': 'bacs',
},
}
) require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.create(
params: {
payment_request: {
description: 'First payment',
amount: 5000,
currency: 'GBP',
scheme: 'faster_payments'
},
mandate_request: {
scheme: 'bacs'
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCreateResponse response = client.billingRequests()
.create()
.withPaymentRequestDescription("First payment")
.withPaymentRequestAmount(5000)
.withPaymentRequestCurrency("GBP")
.withPaymentRequestScheme("faster_payments")
.withMandateRequestScheme("bacs")
.execute();const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");
const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);
const response = await client.billingRequests.create({
payment_request: {
description: "First payment",
amount: 5000,
currency: "GBP",
scheme: "faster_payments",
},
mandate_request: {
scheme: "bacs",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CreateAsync(
new BillingRequestCreateRequest
{
PaymentRequest = new BillingRequestCreateRequest.BillingRequestPaymentRequest
{
Description = "First payment",
Amount = 5000,
Currency = "GBP",
Scheme = "faster_payments"
},
MandateRequest = new BillingRequestCreateRequest.BillingRequestMandateRequest
{
Scheme = "bacs"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestCreateParams{
PaymentRequest: &gocardless.BillingRequestCreateParamsPaymentRequest{
Description: "First payment",
Amount: 5000,
Currency: "GBP",
Scheme: "faster_payments",
},
MandateRequest: &gocardless.BillingRequestCreateParamsMandateRequest{
Scheme: "bacs",
},
}
result, err := client.BillingRequests.Create(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"billing_requests": {
"id": "BRQ0000303",
"status": "pending",
"payment_request": {
"amount": 5000,
"currency": "GBP",
"scheme": "faster_payments"
},
"mandate_request": {
"scheme": "bacs",
"currency": "GBP"
},
"actions": [
{
"type": "collect_customer_details",
"required": true,
"completed": false
},
{
"type": "collect_bank_account",
"required": true,
"completed": false
},
{
"type": "confirm_payer_details",
"required": true,
"completed": false
},
{
"type": "select_institution",
"required": true,
"completed": false
},
{
"type": "bank_authorisation",
"required": true,
"completed": false
}
]
}
}curl -X POST https://api.gocardless.com/billing_requests \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"billing_requests": {
"fallback_enabled": true,
"purpose_code": "loan",
"payment_context_code": "billing_goods_and_services_in_arrears",
"payment_purpose_code": "bank_loan_delayed_draw_funding",
"mandate_request": {
"scheme": "faster_payments",
"constraints": {
"start_date": "2024-11-01",
"max_amount_per_payment": 50000,
"periodic_limits": [{
"period": "month",
"max_total_amount": 100000,
"alignment": "creation_date"
}]
}
}
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->create([
'params' => [
'fallback_enabled' => true,
'purpose_code' => 'loan',
'payment_context_code' => 'billing_goods_and_services_in_arrears',
'payment_purpose_code' => 'bank_loan_delayed_draw_funding',
'mandate_request' => [
'scheme' => 'faster_payments',
'constraints' => [
'start_date' => '2024-11-01',
'max_amount_per_payment' => 50000,
'periodic_limits' => [
[
'period' => 'month',
'max_total_amount' => 100000,
'alignment' => 'creation_date',
],
],
],
],
],
]);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.create(
params={
'fallback_enabled': True,
'purpose_code': 'loan',
'payment_context_code': 'billing_goods_and_services_in_arrears',
'payment_purpose_code': 'bank_loan_delayed_draw_funding',
'mandate_request': {
'scheme': 'faster_payments',
'constraints': {
'start_date': '2024-11-01',
'max_amount_per_payment': 50000,
'periodic_limits': [
{
'period': 'month',
'max_total_amount': 100000,
'alignment': 'creation_date',
},
],
},
},
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.create(
params: {
fallback_enabled: true,
purpose_code: 'loan',
payment_context_code: 'billing_goods_and_services_in_arrears',
payment_purpose_code: 'bank_loan_delayed_draw_funding',
mandate_request: {
scheme: 'faster_payments',
constraints: {
start_date: '2024-11-01',
max_amount_per_payment: 50000,
periodic_limits: [
{
period: 'month',
max_total_amount: 100000,
alignment: 'creation_date'
}
]
}
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCreateResponse response = client.billingRequests()
.create()
.withFallbackEnabled(true)
.withPurposeCode("loan")
.withPaymentContextCode("billing_goods_and_services_in_arrears")
.withPaymentPurposeCode("bank_loan_delayed_draw_funding")
.withMandateRequestScheme("faster_payments")
.withMandateRequestConstraintsStartDate("2024-11-01")
.withMandateRequestConstraintsMaxAmountPerPayment(50000)
.withMandateRequestConstraintsPeriodicLimitsBuilder()
.withPeriod("month")
.withMaxTotalAmount(100000)
.withAlignment("creation_date")
.done()
.execute();const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");
const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);
const response = await client.billingRequests.create({
fallback_enabled: true,
purpose_code: "loan",
payment_context_code: "billing_goods_and_services_in_arrears",
payment_purpose_code: "bank_loan_delayed_draw_funding",
mandate_request: {
scheme: "faster_payments",
constraints: {
start_date: "2024-11-01",
max_amount_per_payment: 50000,
periodic_limits: [
{
period: "month",
max_total_amount: 100000,
alignment: "creation_date",
},
],
},
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CreateAsync(
new BillingRequestCreateRequest
{
FallbackEnabled = true,
PurposeCode = "loan",
PaymentContextCode = "billing_goods_and_services_in_arrears",
PaymentPurposeCode = "bank_loan_delayed_draw_funding",
MandateRequest = new BillingRequestCreateRequest.BillingRequestMandateRequest
{
Scheme = "faster_payments",
Constraints = new BillingRequestCreateRequest.BillingRequestMandateRequestConstraints
{
StartDate = "2024-11-01",
MaxAmountPerPayment = 50000,
PeriodicLimits = new List<BillingRequestCreateRequest.BillingRequestMandateRequestConstraintsPeriodicLimit>
{
new BillingRequestCreateRequest.BillingRequestMandateRequestConstraintsPeriodicLimit
{
Period = "month",
MaxTotalAmount = 100000,
Alignment = "creation_date"
}
}
}
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
fallbackEnabled := true
params := gocardless.BillingRequestCreateParams{
FallbackEnabled: &fallbackEnabled,
PurposeCode: "loan",
PaymentContextCode: "billing_goods_and_services_in_arrears",
PaymentPurposeCode: "bank_loan_delayed_draw_funding",
MandateRequest: &gocardless.BillingRequestCreateParamsMandateRequest{
Scheme: "faster_payments",
Constraints: &gocardless.BillingRequestCreateParamsMandateRequestConstraints{
StartDate: "2024-11-01",
MaxAmountPerPayment: 50000,
PeriodicLimits: []gocardless.BillingRequestCreateParamsMandateRequestConstraintsPeriodicLimit{
{
Period: "month",
MaxTotalAmount: 100000,
Alignment: "creation_date",
},
},
},
},
}
result, err := client.BillingRequests.Create(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"billing_requests": {
"id": "BRQ0000301",
"created_at": "2024-11-01T00:00:00.000Z",
"status": "pending",
"mandate_request": {
"scheme": "faster_payments",
"currency": "GBP",
"verify": "recommended",
"consent_type": "recurring",
"description": null,
"sweeping": false,
"payer_requested_dual_signature": false,
"metadata": {},
"links": {},
"constraints": {
"start_date": "2024-11-01",
"end_date": null,
"max_amount_per_payment": 50000,
"periodic_limits": [
{
"period": "month",
"max_total_amount": 100000,
"alignment": "creation_date",
"max_amount_per_period": null,
"max_payments": null
}
]
}
},
"payment_request": null,
"subscription_request": null,
"instalment_schedule_request": null,
"metadata": {},
"fallback_enabled": true,
"fallback_occurred": false,
"purpose_code": "loan",
"payment_purpose_code": "bank_loan_delayed_draw_funding",
"sign_flow_url": null,
"auto_fulfil": false,
"actions": [
{
"type": "collect_customer_details",
"required": true,
"completes_actions": [],
"requires_actions": [],
"status": "pending",
"collect_customer_details": {
"default_country_code": "GB",
"incomplete_fields": {
"customer": ["given_name", "family_name", "email"],
"customer_billing_detail": ["address_line1", "city", "postal_code", "country_code"]
}
}
},
{
"type": "select_institution",
"required": true,
"completes_actions": [],
"requires_actions": [],
"status": "pending",
"institution_guess_status": "pending"
},
{
"type": "collect_bank_account",
"required": true,
"completes_actions": [],
"requires_actions": [],
"status": "pending",
"available_country_codes": ["GB"]
},
{
"type": "bank_authorisation",
"required": true,
"completes_actions": [],
"requires_actions": ["select_institution", "collect_customer_details"],
"status": "pending",
"bank_authorisation": {
"authorisation_type": "mandate"
}
},
{
"type": "confirm_payer_details",
"required": true,
"completes_actions": [],
"requires_actions": ["collect_customer_details", "collect_bank_account"],
"status": "pending"
}
],
"links": {
"mandate_request": "MRQ0000301"
}
}
}Collect Customer Details#
Applies to all flows
curl -X POST https://api.gocardless.com/billing_requests/BRQ0000301/actions/collect_customer_details \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"customer": {
"email": "frank.osborne@example.com",
"given_name": "Frank", "family_name": "Osborne"
},
"customer_billing_detail": {
"address_line1": "27 Acer Road", "city": "London",
"postal_code": "E8 3GX", "country_code": "GB"
}
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->collectCustomerDetails(
'BRQ0000301',
[
'data' => [
'customer' => [
'email' => 'frank.osborne@example.com',
'given_name' => 'Frank',
'family_name' => 'Osborne',
],
'customer_billing_detail' => [
'address_line1' => '27 Acer Road',
'city' => 'London',
'postal_code' => 'E8 3GX',
'country_code' => 'GB',
],
],
]
);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.collect_customer_details(
'BRQ0000301',
params={
'data': {
'customer': {
'email': 'frank.osborne@example.com',
'given_name': 'Frank',
'family_name': 'Osborne',
},
'customer_billing_detail': {
'address_line1': '27 Acer Road',
'city': 'London',
'postal_code': 'E8 3GX',
'country_code': 'GB',
},
}
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.collect_customer_details(
'BRQ0000301',
data: {
customer: {
email: 'frank.osborne@example.com',
given_name: 'Frank',
family_name: 'Osborne'
},
customer_billing_detail: {
address_line1: '27 Acer Road',
city: 'London',
postal_code: 'E8 3GX',
country_code: 'GB'
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCollectCustomerDetailsResponse response = client.billingRequests()
.collectCustomerDetails("BRQ0000301")
.withCustomerEmail("frank.osborne@example.com")
.withCustomerGivenName("Frank")
.withCustomerFamilyName("Osborne")
.withCustomerBillingDetailAddressLine1("27 Acer Road")
.withCustomerBillingDetailCity("London")
.withCustomerBillingDetailPostalCode("E8 3GX")
.withCustomerBillingDetailCountryCode("GB")
.execute();
const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");
const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);
const response = await client.billingRequests.collectCustomerDetails("BRQ0000301", {
data: {
customer: {
email: "frank.osborne@example.com",
given_name: "Frank",
family_name: "Osborne",
},
customer_billing_detail: {
address_line1: "27 Acer Road",
city: "London",
postal_code: "E8 3GX",
country_code: "GB",
},
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CollectCustomerDetailsAsync(
"BRQ0000301",
new BillingRequestCollectCustomerDetailsRequest
{
Data = new BillingRequestCollectCustomerDetailsRequest.CollectCustomerDetailsData
{
Customer = new BillingRequestCollectCustomerDetailsRequest.CustomerData
{
Email = "frank.osborne@example.com",
GivenName = "Frank",
FamilyName = "Osborne"
},
CustomerBillingDetail = new BillingRequestCollectCustomerDetailsRequest.CustomerBillingDetailData
{
AddressLine1 = "27 Acer Road",
City = "London",
PostalCode = "E8 3GX",
CountryCode = "GB"
}
}
}
);package main
import (
"context"
"fmt"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestCollectCustomerDetailsParams{
Data: gocardless.BillingRequestCollectCustomerDetailsParamsData{
Customer: gocardless.BillingRequestCollectCustomerDetailsParamsCustomer{
Email: "frank.osborne@example.com",
GivenName: "Frank",
FamilyName: "Osborne",
},
CustomerBillingDetail: gocardless.BillingRequestCollectCustomerDetailsParamsCustomerBillingDetail{
AddressLine1: "27 Acer Road",
City: "London",
PostalCode: "E8 3GX",
CountryCode: "GB",
},
},
}
result, err := client.BillingRequests.CollectCustomerDetails(context.TODO(), "BRQ0000301", params)
if err != nil {
panic(err)
}
fmt.Println(result)Response: Billing Request with collect_customer_details marked completed.
Collect a bank account#
Applies to all flows.
Required fields vary by country and scheme. Example for UK (Bacs / Faster Payments):
curl -X POST https://api.gocardless.com/billing_requests/BRQ0000301/actions/collect_bank_account \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"country_code": "GB",
"account_holder_name": "Frank Osborne",
"account_number": "55779911",
"branch_code": "200000"
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->collectBankAccount(
'BRQ0000301',
[
'data' => [
'country_code' => 'GB',
'account_holder_name' => 'Frank Osborne',
'account_number' => '55779911',
'branch_code' => '200000',
],
]
);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.collect_bank_account(
'BRQ0000301',
params={
'data': {
'country_code': 'GB',
'account_holder_name': 'Frank Osborne',
'account_number': '55779911',
'branch_code': '200000',
}
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.collect_bank_account(
'BRQ0000301',
data: {
country_code: 'GB',
account_holder_name: 'Frank Osborne',
account_number: '55779911',
branch_code: '200000'
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCollectBankAccountResponse response = client.billingRequests()
.collectBankAccount("BRQ0000301")
.withCountryCode("GB")
.withAccountHolderName("Frank Osborne")
.withAccountNumber("55779911")
.withBranchCode("200000")
.execute();const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");
const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);
const response = await client.billingRequests.collectBankAccount("BRQ0000301", {
data: {
country_code: "GB",
account_holder_name: "Frank Osborne",
account_number: "55779911",
branch_code: "200000",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CollectBankAccountAsync(
"BRQ0000301",
new BillingRequestCollectBankAccountRequest
{
Data = new BillingRequestCollectBankAccountRequest.CollectBankAccountData
{
CountryCode = "GB",
AccountHolderName = "Frank Osborne",
AccountNumber = "55779911",
BranchCode = "200000"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestCollectBankAccountParams{
Data: gocardless.BillingRequestCollectBankAccountParamsData{
CountryCode: "GB",
AccountHolderName: "Frank Osborne",
AccountNumber: "55779911",
BranchCode: "200000",
},
}
result, err := client.BillingRequests.CollectBankAccount(context.TODO(), "BRQ0000301", params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response: Billing Request with collect_bank_account marked completed.
Confirm payer details#
Applies to: all flows including mandates (Mandate-only flow and Payment + Mandate flow
The customer confirms their consent to set up the mandate. No request body required.
curl -X POST https://api.gocardless.com/billing_requests/BRQ0000301/actions/confirm_payer_details \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->confirmPayerDetails('BRQ0000301');import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.confirm_payer_details('BRQ0000301')require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.confirm_payer_details('BRQ0000301')import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestConfirmPayerDetailsResponse response = client.billingRequests()
.confirmPayerDetails("BRQ0000301")
.execute();const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");
const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);
const response = await client.billingRequests.confirmPayerDetails("BRQ0000301");using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.ConfirmPayerDetailsAsync("BRQ0000301");package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
result, err := client.BillingRequests.ConfirmPayerDetails(context.TODO(), "BRQ0000301", gocardless.BillingRequestConfirmPayerDetailsParams{})
if err != nil {
panic(err)
}
fmt.Println(result)
}Response: Billing Request with collect_bank_account marked completed.
{
"billing_requests": {
"id": "BRQ0000301",
"status": "ready_to_fulfil",
"actions": [
{
"type": "collect_customer_details",
"required": true,
"completed": true
},
{ "type": "collect_bank_account", "required": true, "completed": true },
{ "type": "confirm_payer_details", "required": true, "completed": true }
]
}
}Select Institution#
Applies to: all flows including payments (Payment-only flow and Payment + Mandate flow
Identify the customer's bank for the open banking authorisation. Use the list institutions endpoint to present a bank picker to the customer.
curl -X POST https://api.gocardless.com/billing_requests/BRQ0000302/actions/select_institution \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"institution": "MONZO_MONZGB2L",
"country_code": "GB"
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->selectInstitution(
'BRQ0000302',
[
'data' => [
'institution' => 'MONZO_MONZGB2L',
'country_code' => 'GB',
],
]
);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.select_institution(
'BRQ0000302',
params={
'data': {
'institution': 'MONZO_MONZGB2L',
'country_code': 'GB',
}
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.select_institution(
'BRQ0000302',
data: {
institution: 'MONZO_MONZGB2L',
country_code: 'GB'
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestSelectInstitutionResponse response = client.billingRequests()
.selectInstitution("BRQ0000302")
.withInstitution("MONZO_MONZGB2L")
.withCountryCode("GB")
.execute();const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");
const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);
const response = await client.billingRequests.selectInstitution("BRQ0000302", {
data: {
institution: "MONZO_MONZGB2L",
country_code: "GB",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.SelectInstitutionAsync(
"BRQ0000302",
new BillingRequestSelectInstitutionRequest
{
Data = new BillingRequestSelectInstitutionRequest.SelectInstitutionData
{
Institution = "MONZO_MONZGB2L",
CountryCode = "GB"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestSelectInstitutionParams{
Data: gocardless.BillingRequestSelectInstitutionParamsData{
Institution: "MONZO_MONZGB2L",
CountryCode: "GB",
},
}
result, err := client.BillingRequests.SelectInstitution(context.TODO(), "BRQ0000302", params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response: Billing Request with select_institution marked completed.
Create a bank authorisation#
Applies to: all flows including payments (Payment-only flow and Payment + Mandate flow.
curl -X POST https://api.gocardless.com/bank_authorisations \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"bank_authorisations": {
"redirect_uri": "https://yoursite.com/payment-complete",
"links": { "billing_request": "BRQ0000302" }
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->bankAuthorisations()->create([
'params' => [
'redirect_uri' => 'https://yoursite.com/payment-complete',
'links' => [
'billing_request' => 'BRQ0000302',
],
],
]);
import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.bank_authorisations.create(
params={
'redirect_uri': 'https://yoursite.com/payment-complete',
'links': {
'billing_request': 'BRQ0000302',
},
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.bank_authorisations.create(
params: {
redirect_uri: 'https://yoursite.com/payment-complete',
links: {
billing_request: 'BRQ0000302'
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BankAuthorisationCreateResponse response = client.bankAuthorisations()
.create()
.withRedirectUri("https://yoursite.com/payment-complete")
.withLinksBuilder()
.withBillingRequest("BRQ0000302")
.done()
.execute();const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");
const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);
const response = await client.bankAuthorisations.create({
redirect_uri: "https://yoursite.com/payment-complete",
links: {
billing_request: "BRQ0000302",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BankAuthorisations.CreateAsync(
new BankAuthorisationCreateRequest
{
RedirectUri = "https://yoursite.com/payment-complete",
Links = new BankAuthorisationCreateRequest.BankAuthorisationLinks
{
BillingRequest = "BRQ0000302"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BankAuthorisationCreateParams{
RedirectUri: "https://yoursite.com/payment-complete",
Links: gocardless.BankAuthorisationCreateParamsLinks{
BillingRequest: "BRQ0000302",
},
}
result, err := client.BankAuthorisations.Create(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"bank_authorisations": {
"id": "BAU0000101",
"url": "https://pay.gocardless.com/obauth/BAU0000101",
"redirect_uri": "https://yoursite.com/payment-complete",
"expires_at": "2024-06-01T18:15:00.000Z",
"links": { "billing_request": "BRQ0000302" }
}
}Redirect customer to their bank#
Applies to: all flows including payments (Payment-only flow and Payment + Mandate flow
Redirect your customer to the url from the bank authorisation response. The customer approves the payment in their banking app, then is returned to your redirect_uri.
Don't use the redirect to confirm the outcome. Always use webhooks.
Fulfill the billing request#
Applies to: Mandate-only flow
Once the Billing Request status is ready_to_fulfil, call the fulfil action to activate the mandate.
curl -X POST https://api.gocardless.com/billing_requests/BRQ0000301/actions/fulfil \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->fulfil('BRQ0000301');import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.fulfil('BRQ0000301')require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.fulfil('BRQ0000301')import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestFulfilResponse response = client.billingRequests()
.fulfil("BRQ0000301")
.execute();const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");
const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);
const response = await client.billingRequests.fulfil("BRQ0000301");using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.FulfilAsync("BRQ0000301");package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
result, err := client.BillingRequests.Fulfil(context.TODO(), "BRQ0000301", gocardless.BillingRequestFulfilParams{})
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"billing_requests": {
"id": "BRQ0000301",
"status": "fulfilled",
"links": { "mandate_request_mandate": "MD0000301" }
}
}Handle Webhooks#
| Event | Meaning | Applies to |
|---|---|---|
| billing_requests.fulfilled | Billing Request completed | All flows |
| mandates.active | Mandate is active and ready | Mandate-only, payment + mandate |
| payments.confirmed | Pay By Bank payment settled | Payment-only, payment + mandate |
| payments.failed | Payment failed | Payment-only, payment + mandate |
| mandates.cancelled | Mandate cancelled | Mandate-only, payment + mandate |
What's next?#
Guide to creating mandates and handling all mandate actions.
Understand the events fired during a billing request lifecycle.
Pre-populate customer data to reduce steps in your custom flow.
Start with hosted pages to validate your integration — same underlying API.