Direct Debit

Quickstart

From zero to a Direct Debit collection on the playpen sandbox.

This page walks through one complete collection as a sequence of API calls, in the order you would make them: create a customer, put them on a schedule, set up the Direct Debit, take a payment and check its outcome.

Get playpen access

You need a playpen API key. Contact our sales team to get one.

The examples below run against the playpen base URL, https://playpen.accesspaysuite.com/api/v3 — see Environments for that and the production one. Replace the LUISCT client code in each path with your own (also called the client prefix), and the customer, contract and payment GUIDs with the ones your own calls return.

Every request carries your key in an apiKey header, not an Authorization header and not a query parameter:

apiKey: xAQdXTPtZPG3QqsUJw73wx1o
Accept: application/json
Content-Type: application/json

The API accepts input as a JSON body or as x-www-form-urlencoded — either query string or form body — and the Accept header selects JSON or XML for the response. See Authentication and API Technical Details.

How the pieces fit together

Direct Debit has four core objects, and they nest in this order:

Customer  ──has many──▶  Contract  ──has many──▶  Payment
                            │
                            └── follows a ──▶ Schedule
ObjectWhat it is
CustomerThe person or organisation you collect from — their name, address and bank details.
ScheduleA collection pattern, such as monthly or annually. Schedules are configured on your account during onboarding; you choose one rather than create it.
ContractA single Direct Debit mandate for a customer, following one schedule. Creating it registers the mandate with the customer's bank.
PaymentOne collection of money against a contract.
Money cannot move immediately. The scheme requires a lead time, counted in working days, between setting up a mandate and collecting against it — by default 10 working days before a first collection and 5 before an ad-hoc one, though your SUN may allow less. Working Days has worked examples, including bank holidays.

Step 1 — Find the schedules on your account

Because you do not create schedules, the first job is to see which ones your client already has. The response lists your services and the schedules underneath each of them.

Quickstart — list your schedules
EndpointDefinition
GET /client/LUISCT/schedules
cURL
curl -L -g -X GET 'https://playpen.accesspaysuite.com/api/v3/client/LUISCT/schedules' \
  -H 'apiKey: {apiKey}'
Response
HTTP/1.1 200 OK

{
  "Services": [
    {
      "RefPrefix": "MT",
      "RefProtocol": "Auto-number",
      "Schedules": [
        {
          "AtTheEnd": "Switch to further notice",
          "DayOfWeek": "Free",
          "DaysOfMonth": "Free",
          "Description": "Ad-hoc > Payments lodged individually\n",
          "Every": 1,
          "Frequency": "Monthly",
          "IsExpiryDateReached": false,
          "IsNotScheduled": true,
          "IsSuspended": false,
          "MonthOfYear": "Free",
          "Name": "adhoc_monthly_free",
          "RegistrationCharge": 0,
          "ScheduleId": "7663e3a0-514c-4ec7-808e-05d27a85fa98",
          "StartType": "As soon as possible",
          "TerminationType": "Until further notice"
        }
      ],
      "Title": "Membership"
    }
  ]
}
Notes:
  • Request: no body and no query parameters — only your client code in the path and the apiKey header.
  • Response: a Services array, each service carrying its reference numbering (RefPrefix, RefProtocol) and a Schedules array. Each schedule gives its ScheduleId and Name, its Frequency and Every (so Annually with 1 is once a year), the DaysOfMonth and DayOfWeek it may collect on — Free meaning the customer chooses — and the StartType, TerminationType and AtTheEnd defaults the contract in step 3 has to agree with.
  • Note a schedule's ScheduleId or Name — you pass one of them in step 3 to tell the contract which collection pattern to follow. Skip any schedule with IsSuspended or IsExpiryDateReached set to true.
  • If no schedules come back, your playpen account is not fully set up yet: raise a support case before continuing. See Obtaining Available Schedules for what each field means.

Step 2 — Create a customer

Now create the person you will collect from. customerRef is your own reference and must be unique within your client; accountNumber and bankSortCode are the customer's bank details, digits only with leading zeros intact.

Quickstart — create a customer
EndpointDefinition
POST /client/LUISCT/customer
Request body
{
  "CustomerRef": "CUST-0001",
  "Title": "Mrs",
  "FirstName": "Jane",
  "Surname": "Smith",
  "Email": "[email protected]",
  "Line1": "1 Tebbit Mews",
  "Line2": "Winchcombe Street",
  "PostCode": "GL52 2NF",
  "AccountHolderName": "Mrs Jane Smith",
  "AccountNumber": "12345678",
  "BankSortCode": "123456"
}
cURL
curl -L -g -X POST 'https://playpen.accesspaysuite.com/api/v3/client/LUISCT/customer' \
  -H 'Content-Type: application/json' \
  -H 'apiKey: {apiKey}' \
  -d '{"CustomerRef":"CUST-0001","Title":"Mrs","FirstName":"Jane","Surname":"Smith","Email":"[email protected]","Line1":"1 Tebbit Mews","Line2":"Winchcombe Street","PostCode":"GL52 2NF","AccountHolderName":"Mrs Jane Smith","AccountNumber":"12345678","BankSortCode":"123456"}'
Response
HTTP/1.1 200 OK

{
  "CustomerRef": "CUST-0001",
  "Id": "12f5734a-bfc3-45f2-9edd-44d4a05cf751"
}
Notes:
  • Request: CustomerRef, Surname, AccountHolderName (≤ 18 alphanumeric characters), AccountNumber (8 digits), BankSortCode (6 digits), Title, PostCode and Line1 are mandatory. Email is mandatory unless that has been disabled for your client — the CustomerEmailMandatory flag in Client Info tells you which. Strip hyphens, spaces and punctuation from the bank details and keep leading zeros.
  • Response: the customer's Id (a GUID) and the CustomerRef you sent back. Save the Id — step 3 needs it, and so does any later update to the customer.
  • 400 Bad Request: the Message field carries the reason, with an ErrorCode alongside it. Reusing a CustomerRef is the common one: it reports an existing customer with the same client and reference.
  • The same call also takes these fields as query parameters; see Customer Manipulation for every field and the query-parameter form.

Step 3 — Create the contract

This registers the Direct Debit mandate with the customer's bank. It hangs off the customer Id from step 2 and points at a schedule from step 1 — identified by either scheduleName or scheduleId.

Quickstart — create a contract
EndpointDefinition
POST /client/LUISCT/customer/12f5734a-bfc3-45f2-9edd-44d4a05cf751/contract
Request body
{
  "ScheduleName": "adhoc_monthly_free",
  "Start": "2026-09-18T00:00:00.000",
  "IsGiftAid": false,
  "TerminationType": "Until further notice",
  "AtTheEnd": "Switch to further notice"
}
cURL
curl -L -g -X POST 'https://playpen.accesspaysuite.com/api/v3/client/LUISCT/customer/12f5734a-bfc3-45f2-9edd-44d4a05cf751/contract' \
  -H 'Content-Type: application/json' \
  -H 'apiKey: {apiKey}' \
  -d '{"ScheduleName":"adhoc_monthly_free","Start":"2026-09-18T00:00:00.000","IsGiftAid":false,"TerminationType":"Until further notice","AtTheEnd":"Switch to further notice"}'
Response
HTTP/1.1 200 OK

{
  "DirectDebitRef": "LUISCT-MT000341",
  "Id": "e39940cc-9917-4b9e-8da4-5da4866862f1",
  "Message": null
}
Notes:
  • Request: the schedule (ScheduleName or ScheduleId), Start, TerminationType, AtTheEnd and IsGiftAid are all an ad-hoc contract needs. Start is YYYY-MM-DDT00:00:00.000, at least 10 working days ahead, on a permitted date and no more than 364 days out — omit it and DDCMS calculates the earliest legal date for you.
  • A regular contract adds the collection pattern: amount, every, and paymentDayInMonth / paymentMonthInYear / paymentDayInWeek for the schedule's frequency, plus initialAmount, extraInitialAmounts or finalAmount where the first or last collection differs.
  • Response: Id is the contract GUID, which step 5 needs to take a payment. DirectDebitRef is the reference quoted to the customer's bank, which some banks show on the customer's statement — store both. Message is null on success.
  • 400 Bad Request: Message names what did not validate — an unrecognised schedule name, a start date that is not permitted, or a terminationType and atTheEnd pair the schedule does not allow. An ad-hoc contract must use Until further notice with Switch to further notice.
  • Contract Querying and Creation covers the ad-hoc and regular variants in full, and the contract statuses that follow.
DDCMS has now queued an instruction to the customer's bank. The mandate is not collectable straight away, so start must be a permitted date far enough ahead — step 4 asks the API what that is rather than making you work it out.

Step 4 — Check the earliest date you can collect

Rather than reproduce the working-day arithmetic, which has to skip weekends and UK bank holidays, ask the API. It returns the earliest first collection date and the earliest subsequent collection date currently allowed by your SUN.

Quickstart — earliest collection dates
EndpointDefinition
GET /client/LUISCT/paymentdate
cURL
curl -L -g -X GET 'https://playpen.accesspaysuite.com/api/v3/client/LUISCT/paymentdate' \
  -H 'apiKey: {apiKey}'
Response
HTTP/1.1 200 OK

{
  "NextFirstPaymentDate": "2026-09-18T00:00:00+01:00",
  "NextSubsequentPaymentDate": "2026-09-11T00:00:00+01:00"
}
Notes:
  • Request: no body and no query parameters — the answer depends only on your client's SUN and today's date.
  • Response: NextFirstPaymentDate, the earliest contract start or first collection date currently allowed, and NextSubsequentPaymentDate, the earliest ad-hoc collection date on a contract that is already collecting. Both come back as dates with an offset, such as 2022-08-22T00:00:00+01:00.
  • Use NextFirstPaymentDate or later for Start in step 3, and NextSubsequentPaymentDate or later for Date in step 5. An earlier date is rejected. See Payment Dates if you would like these delays reduced on your account.

Step 5 — Take a payment

Now collect against the contract Id from step 3. Amount and Date are required, and the date must be on or after the earliest date from step 4.

Quickstart — take a payment
EndpointDefinition
POST /client/LUISCT/contract/e39940cc-9917-4b9e-8da4-5da4866862f1/payment
Request body
{
  "Amount": 25.00,
  "Date": "2026-09-18T00:00:00.000",
  "Comment": "First collection",
  "IsCredit": false
}
cURL
curl -L -g -X POST 'https://playpen.accesspaysuite.com/api/v3/client/LUISCT/contract/e39940cc-9917-4b9e-8da4-5da4866862f1/payment' \
  -H 'Content-Type: application/json' \
  -H 'apiKey: {apiKey}' \
  -d '{"Amount":25.00,"Date":"2026-09-18T00:00:00.000","Comment":"First collection","IsCredit":false}'
Response
HTTP/1.1 200 OK

{
  "Amount": 25.00,
  "Contract": "e39940cc-9917-4b9e-8da4-5da4866862f1",
  "DueDate": "2026-09-18T00:00:00.000Z",
  "Id": "1b2ac277-5f1f-424a-b55f-323d5bcef8f6",
  "Message": null
}
Notes:
  • Request: Amount to two decimal places and Date as YYYY-MM-DDT00:00:00.000, at least 5 working days ahead, on a permitted date and not before the contract's start date. Comment is free text of up to 255 characters and comes back in step 6. Pass IsCredit as true only if you are paying money out to the customer by prior arrangement.
  • Response: the payment's Id (a GUID), the Contract it was applied to, the DueDate it will collect on and the Amount. Save the Id: it identifies the payment when you amend, delete or query it, and it appears in any callback about the payment. Error and Message are normally absent, and carry any system message when they are not.
  • Errors: 400 for an amount or date the API will not take, 403 if the contract is protected, and 404 if the contract GUID does not exist — each with the reason in Message.
  • This call has no idempotency key, so if a request times out, query the contract's payments as in step 6 before you retry — otherwise you may lodge the payment twice.

Step 6 — Check what happened

You can list the payments held against a contract at any time. They come back most recent first; rows sets how many you get, up to 100.

Quickstart — check the outcome
EndpointDefinition
GET /client/LUISCT/contract/e39940cc-9917-4b9e-8da4-5da4866862f1/payment
cURL
curl -L -g -X GET 'https://playpen.accesspaysuite.com/api/v3/client/LUISCT/contract/e39940cc-9917-4b9e-8da4-5da4866862f1/payment?rows=10' \
  -H 'apiKey: {apiKey}'
Response
HTTP/1.1 200 OK

{
  "Payments": [
    {
      "Amount": 25.00,
      "Comment": "First collection",
      "Date": "2026-09-18T00:00:00.000Z",
      "Id": "1b2ac277-5f1f-424a-b55f-323d5bcef8f6",
      "IsAdhoc": true,
      "IsCredit": false,
      "ReasonCode": 0,
      "Status": "Pending",
      "Type": "BACS"
    }
  ]
}
Notes:
  • Request: no body. rows is the only query parameter, between 1 and 100, and payments come back ordered by date descending — so the most recent is first.
  • Response: a Payments array. Each entry gives its Id, Amount, Date (the due date), Status, the Comment you sent, IsAdhoc and IsCredit flags, and Type — BACS for a scheme collection, Manual for one added through the UI.
  • Status is one of Pending, Paid, Represented, Unpaid, Withdrawn or Indemnity Claimed. On anything returned unpaid, ReasonCode holds the BACS code from 0 to 8 — 0 refer to payer, 1 instruction cancelled, 6 no instruction, and so on — with ReasonMessage spelling it out; the message may also be appended to the comment.
  • A payment stays Pending until it is submitted to the bank, 3 to 4 working days before the collection date, at which point it becomes Paid — BACS works by exception, so the real outcome is only known 1 to 2 working days after the due date. Wait 2 to 3 working days after the due date before you record an outcome in your own system. Adding/Querying Payments lists every status and reason code in full.

A payment moves through roughly this lifecycle:

Pending ──▶ Paid
   │
   └──(returned unpaid by the bank)──▶ Represented / Unpaid

While a payment is still Pending you can amend or delete it; once it has been submitted to the bank it can no longer be changed.

Next steps

  • The Direct Debit Process — how instructions and collections flow through BACS.
  • Payment Manipulation — query, amend or delete a payment that has not been submitted.
  • Patches — skip, freeze or change the amount of a contract's collections between two dates.
  • Return Endpoints — register a callback so DDCMS tells you when payments and mandates change state, instead of polling step 6.
  • Frequency Switching — move a contract onto a different collection pattern.
  • Errors — the error envelope and what the status codes mean.