GoCardlessDeveloper Docs
Create a sandbox account

Custom Payment Pages#

View as Markdown

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.

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#

EventMeaningApplies to
billing_requests.fulfilledBilling Request completedAll flows
mandates.activeMandate is active and readyMandate-only, payment + mandate
payments.confirmedPay By Bank payment settledPayment-only, payment + mandate
payments.failedPayment failedPayment-only, payment + mandate
mandates.cancelledMandate cancelledMandate-only, payment + mandate

What's next?#