NAV Navbar
  • Handling Direct Debit Instructions as a Paying Bank
  • Handling Direct Debit Instructions as a Paying Bank

    This tutorial explains how to use the Form3 API to receive, return and cancel Bacs Direct Debit Instructions (DDI) as a paying bank. We start with the basic rules for DDIs in Bacs. Then follows a step by step guide for how to handle DDIs in the Form3 API including code examples.

    For more detailed information about Bacs Direct Debit, please take a look at the Bacs scheme documentation available for members at https://www.bacs.co.uk.

    Terminology

    The terms Direct Debit Instruction and mandate are sometimes used interchangeably, which can cause confusion. To avoid this problem, this tutorial adheres to the following terminology: we will use Direct Debit Instruction or DDI to refer to the Bacs concept of an instruction that gives an originator the permission to collect a Direct Debit. We'll use Mandate to refer to the Mandate resource in the Form3 API, which for Bacs represents a DDI at a specific point in time.

    Direct Debit Instructions in Bacs

    Direct Debit Instructions document an authorisation agreement between a debtor and a beneficiary to collect Direct Debits in Bacs.

    Before a Direct Debit collection can happen, the debtor and the beneficiary of the Direct Debit agree on the terms of the collection (such as amount and frequency). A Direct Debit Instruction is set up as a record of this agreement and stored by both the beneficiary's and the debtor's bank.

    While Form3 keeps records of all received Direct Debit Instructions on its system, it is not suitable to use as a comprehensive Direct Debit Instruction management tool since instructions can also reach you in other ways (such as paper).

    Like any other Bacs transaction, Direct Debit Instructions adhere to the Bacs 3-day cycle. The cycle starts when the beneficiary submits the Direct Debit Instruction. The debtor bank will receive the inbound instruction on day 2 and the instruction will become effective on day 3. In case a Direct Debit Instruction is rejected by the debtor bank, the rejection needs to be submitted to the scheme on day 3.

    Bacs Cycle

    AUDDIS

    The AUtomated Direct Debit Instruction Service (AUDDIS) is used to notify the paying bank of the setup of a new Direct Direct Instruction or the cancellation of an existing DDI by the originator. When receiving a Direct Debit Instruction, the paying bank has the ability to reject the instruction if it is invalid.

    In the Form3 API, DDIs are represented by the Mandate resource and the Mandate Admission resource for inbound instructions.

    An inbound Direct Debit Instruction is automatically rejected by Form3 if no account with the provided account number and sort code has been registered with Form3's Account Management Service. See here to learn more about how to register an account with Form3.

    You can manually reject a Direct Debit Instruction in the Form3 API by creating a Mandate Return resource and a Mandate Return Submission resource to send the rejection.

    ADDACS

    To cancel or amend a Direct Debit Instruction, the paying bank can use the Automated Direct Debit Amendment and Cancellation Service (ADDACS). A reason code in the ADDACS message informs the receiver of the type of change.

    In the Form3 API, the PATCH method of the Mandate resource is used to change or cancel a Direct Debit Instruction, while the Mandate Submission resource is used to send out these changes. The submission_reason attribute of the Mandate Submission resource contains the reason code.

    Direct Debit Instructions in the Form3 API

    Mandate resource

    The current state of a Bacs Direct Debit Instruction is represented by the Mandate resource. Throughout the life of a Direct Debit Instruction, you can receive several different Mandate resources that relate to the same instruction.

    The resource has a similar structure as a Direct Debit collection request or payment. The amount attribute is commonly set to 0 for instructions with varying amounts.

    The reference attribute contains the Bacs core reference to match an instruction to a Direct Debit collection request. Bacs requires the core reference to be between 6 and 18 characters long and only allows upper case alpha and numeric characters, as well as /, &, ., - and (space). It cannot contain DDIC as the first 4 characters. The core reference needs to be quoted in each Direct Debit collection request. Note that in a Direct Debit collection request, the reference field can contain additional characters to distinguish between different collection requests against the same instruction.

    The key Mandate resource attributes are:

    See the API documentation for a full list of attributes and detailed descriptions.

    Mandate Admission resource

    When receiving a Direct Debit Instruction, a Mandate Admission is created to signal its arrival. The admission resource contains information about the status of the instruction, when it was created (field admission_datetime) and other transmission-relation information.

    Mandate Submission resource

    Use a Mandate Submission resource to send out a Direct Debit Instruction. The submission_reason attribute is used to set the ADDACS reason code.

    Step by step guide

    In this section, we'll walk you through each step of receiving, returning and canceling a Direct Debit Instruction using the Form3 API. In the guide, you will:

    The code example is written in Python using the requests package to access the Form3 API. Each code snippet is a standalone Python program, but make sure to paste the required data for each program at the top. The snippets are tested with Python 3.5, but most likely will also work with other Python versions.

    Prerequisites

    Before getting started, make sure you have the following things ready to follow along:

    Subscribe to Direct Debit Admission Events

    You can use Form3's Notification API to be notified when inbound transactions or other messages arrive. Create a subscription for an event type and record type to be notified through a webhook or SQS queue when this event occurs for a type of resource.

    When an inbound Direct Debit Instruction arrives, Form3 creates a Mandate resource and a Mandate Admission resource. In order to be notified when this happens, subscribe to the creation of Mandate Admission resources using the following parameters:

    See this tutorial to learn how to create a subscription and receive notifications.

    Receiving a Direct Debit Instruction

    Trigger an inbound Direct Debit Instruction

    An inbound Direct Debit Instruction is represented in the API by a Mandate resource and a Mandate Admission resource. Form3's transaction simulator can be used to trigger an inbound instruction by sending a payment with the amount 750.00 in the sandbox environment.

    To send a trigger payment, first create a Payment resource. Note the following key attributes:

    Take a look at this tutorial and example to learn how to create a Payment resource.

    Finally, to send the trigger payment, create a Payment Submission resource. See here to learn how.

    Receive the Mandate

    Upon successful delivery of the trigger payment, the transaction simulator will generate a Mandate resource to indicate an inbound Direct Debit Instruction. Since you subscribed to the creation of Mandate Admissions above, check your webhook or SQS queue for a notification that an admission has been created. Note that it may take a few minutes to receive the notification.

    The notification contains a data section with the Mandate Admission resource embedded. There, you can also find the Mandate resource embedded in the relationships.mandate section of the Mandate Admission.

    The notification will look like this:

    Mandate Admission notification

    {
      "id": "ccd78cae-98fa-4210-9f51-539399706796",
      "organisation_id": "2a1b948f-d818-4d41-a742-899262200779",
      "event_type": "created",
      "record_type": "mandate_admissions",
      "data": {
        "data": {
          "type": "mandate_admissions",
          "version": 0,
          "id": "76cf6e0d-bdb5-4e06-8771-c7ef0f3f01d4",
          "organisation_id": "2a1b948f-d818-4d41-a742-899262200779",
          "attributes": {
            "admission_datetime": "2019-02-19T07:54:13.074Z",
            "status": "confirmed",
            "status_reason": "accepted"
          },
          "relationships": {
            "mandate": {
              "data": [
                {
                  "type": "mandates",
                  "version": 0,
                  "id": "16cbb701-5d51-49c8-b814-242396fdea7f",
                  "organisation_id": "2a1b948f-d818-4d41-a742-899262200779",
                  "attributes": {
                    "amount": "0.00",
                    "beneficiary_party": {
                      "account_name": "MR SENDING TEST",
                      "account_number": "87654321",
                      "account_number_code": "BBAN",
                      "account_with": {
                        "bank_id": "333333",
                        "bank_id_code": "GBDSC"
                      }
                    },
                    "clearing_id": "112233",
                    "currency": "GBP",
                    "debtor_party": {
                      "account_name": "MRS RECEIVING TEST",
                      "account_number": "12345678",
                      "account_number_code": "BBAN",
                      "account_with": {
                        "bank_id": "123456",
                        "bank_id_code": "GBDSC"
                      }
                    },
                    "numeric_reference": "0001",
                    "payment_scheme": "BACS",
                    "processing_date": "2019-02-20",
                    "reference": "D/1234567890123456",
                    "scheme_payment_type": "DirectDebitInstructionNew",
                    "scheme_processing_date": "2019-02-19",
                    "unique_scheme_id": "12345678"
                  }
                }
              ]
            }
          }
        }
      }
    }
    


    In the Mandate resource above, amount is set to zero, allowing an arbitrary collection amount. The frequency attribute is not present, indicating the instruction is either for a one-time payment or the collection frequency is irregular.

    scheme_payment_type is set to DirectDebitInstructionNew, meaning that this is a new instruction. Finally, the reference attribute contains the value D/1234567890123456. Any Direct Debit collection request related to this instruction needs to contain this value (plus possible additional trailing characters) in its reference attribute.

    Rejecting/Returning a Direct Debit Instruction

    To reject a received Direct Debit Instruction, create a Mandate Return resource and send it by creating a Mandate Return Submission resource.

    A common reason for rejecting a Direct Debit Instruction is because the bank details of the debtor have changed. For this situation, the Mandate Return resource provides an optional debtor_party.new_bank_details section where the new account_number, bank_id and bank_id_code of the debtor can be added.

    The reason for a rejection needs to be set in the return_code attribute. See here for a list of possible return codes.

    Create the Mandate Return resource using this request: POST v1/transaction/mandates/{mandate_id}/returns. Note that the request references the ID of the Mandate resource of the original instruction you are rejecting.

    Below is an example of a Mandate Return resource. The return_code is set to 3, indicating that the debtor account has been transferred to a different bank. The new bank details are provided in the debtor_party section.

    Mandate Return resource

    {
      "data": {
        "type": "mandate_returns",
        "version": 0,
        "id": "4af2786f-d75b-4101-b71e-8de1f3589e06",
        "organisation_id": "2a1b948f-d818-4d41-a742-899262200779",
        "attributes": {
          "debtor_party": {
            "new_bank_details": {
              "account_number": "12345678",
              "account_with": {
                "bank_id": "654321",
                "bank_id_code": "GBDSC"
              }
            }  
          },
          "return_code": "3"
        },
        "relationships": {
          "mandate": {
            "data": [
              {
                "id": "16cbb701-5d51-49c8-b814-242396fdea7f",
                "organisation_id": "2a1b948f-d818-4d41-a742-899262200779",
                "type": "mandates",
                "version": 0,
                "attributes": {
                  "amount": "0.00",
                  "beneficiary_party": {
                    "account_name": "MR SENDING TEST",
                    "account_number": "87654321",
                    "account_number_code": "BBAN",
                    "account_with": {
                      "bank_id": "333333",
                      "bank_id_code": "GBDSC"
                    },
                    "address": null
                  },
                  "clearing_id": "112233",
                  "currency": "GBP",
                  "debtor_party": {
                    "account_name": "MRS RECEIVING TEST",
                    "account_number": "12345678",
                    "account_number_code": "BBAN",
                    "account_with": {
                      "bank_id": "123456",
                      "bank_id_code": "GBDSC"
                    },
                    "address": null
                  },
                  "numeric_reference": "0001",
                  "payment_scheme": "BACS",
                  "processing_date": "2019-02-20",
                  "reference": "REF",
                  "scheme_payment_type": "DirectDebitInstructionNew",
                  "scheme_processing_date": "2019-02-19",
                  "unique_scheme_id": "12345678"
                }
              }
            ]
          },
          "mandate_return_submission": {
            "data": []
          }
        }
      },
      "links": {
        "self": "/v1/transaction/mandates/16cbb701-5d51-49c8-b814-242396fdea7f/returns/4af2786f-d75b-4101-b71e-8de1f3589e06"
      }
    }
    


    Use the following request to create a Mandate Return Submission resource: POST v1/transaction/mandates/{mandate_id}/returns/{mandate_return_id}/submissions. It requires the ID of the Mandate resource of the Direct Debit Instruction you are returning as well as the ID of the Mandate Return resource you created above. No other parameters are required.

    Below is an example of a submitted Mandate Return Submission. It references the Mandate Return resource and the Mandate resource in its relationships section.

    Mandate Return Submission resource

    {
      "data": {
        "id": "270554af-3ad6-4a91-b4f6-954bae0d160e",
        "organisation_id": "2a1b948f-d818-4d41-a742-899262200779",
        "type": "mandate_return_submissions",
        "version": 0,
        "attributes": {
          "status": "accepted",
          "submission_datetime": "2019-02-19T10:53:33.916Z",
          "transaction_start_datetime": "2019-02-19T10:53:33.916Z"
        },
        "relationships": {
          "mandate": {
            "data": [
              {
                "attributes": {
                  "amount": "0.00",
                  "beneficiary_party": {
                    "account_name": "MR SENDING TEST",
                    "account_number": "87654321",
                    "account_number_code": "BBAN",
                    "account_with": {
                      "bank_id": "333333",
                      "bank_id_code": "GBDSC"
                      },
                      "address": null
                    },
                    "clearing_id": "112233",
                    "currency": "GBP",
                    "debtor_party": {
                      "account_name": "MRS RECEIVING TEST",
                      "account_number": "12345678",
                      "account_number_code": "BBAN",
                      "account_with": {
                        "bank_id": "123456",
                        "bank_id_code": "GBDSC"
                      },
                      "address": null
                    },
                    "numeric_reference": "0001",
                    "payment_scheme": "BACS",
                    "processing_date": "2019-02-20",
                    "reference": "REF",
                    "scheme_payment_type": "DirectDebitInstructionNew",
                    "scheme_processing_date": "2019-02-19",
                    "unique_scheme_id": "12345678"
                  },
                  "id": "16cbb701-5d51-49c8-b814-242396fdea7f",
                  "organisation_id": "2a1b948f-d818-4d41-a742-899262200779",
                  "type": "mandates",
                  "version": 0
              }
            ]
          },
          "mandate_return": {
            "data": [
              {
                "id": "4af2786f-d75b-4101-b71e-8de1f3589e06",
                "organisation_id": "2a1b948f-d818-4d41-a742-899262200779",
                "type": "mandate_returns",
                "version": 0,
                "attributes": {
                  "debtor_party": {
                    "new_bank_details": {
                      "account_number": "12345678",
                      "account_with": {
                        "bank_id": "654321",
                        "bank_id_code": "GBDSC"
                      }
                    }  
                  },
                  "return_code": "3"
                }
              }
            ]
          }
        }
      },
      "links": {
        "self": "/v1/transaction/mandates/16cbb701-5d51-49c8-b814-242396fdea7f/returns/4af2786f-d75b-4101-b71e-8de1f3589e06/submissions/270554af-3ad6-4a91-b4f6-954bae0d160e"
      }
    }
    


    This Python example creates a return for an existing inbound Direct Debit Instruction:

    import math, requests, time, uuid
    
    ### Replace these variables with your own data! ###
    client_id = 'YOUR CLIENT ID HERE'
    client_secret = 'YOUR CLIENT SECRET HERE'
    organisation_id = 'YOUR ORGANISATION ID HERE'
    mandate_id = 'ID OF THE ORIGINAL MANDATE HERE'
    
    base_url = 'https://api.staging-form3.tech/v1'
    
    # Generate IDs
    return_id = uuid.uuid4()
    print("Mandate Return ID: %s" % return_id)
    
    submission_id = uuid.uuid4()
    print("Mandate Submission ID: %s" % submission_id)
    
    # Obtain authentication token
    print("Obtaining bearer token...")
    auth_payload = "grant_type=client_credentials"
    auth_url = '%s/oauth2/token' % base_url
    auth_headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    auth_request = requests.auth.HTTPBasicAuth(client_id, client_secret)
    
    auth = requests.request('post', auth_url, data=auth_payload, auth=auth_request, headers=auth_headers)
    auth_token = auth.json().get('access_token')
    
    # Create the mandate return resource
    print("Creating Mandate Return resource...")
    return_url = "%s/transaction/mandates/%s/returns" % (base_url, mandate_id) 
    return_payload = """
    {
        "data": {
            "id": "%s",
            "organisation_id": "%s",
            "type": "mandate_returns",
            "attributes": {
              "return_code": "1"
            }
        }
    }
    """ % (return_id, organisation_id)
    
    return_headers = {
        'authorization': "bearer " + auth_token,
        'accept': "application/json",
        'content-type': "application/json",
        'cache-control': "no-cache"
        }
    
    mandate_return = requests.request("POST", return_url, data=return_payload, headers=return_headers)
    print(mandate_return.text)
    
    # Create the submission resource
    print("Creating Mandate Return Submission resource...")
    submission_url = "%s/transaction/mandates/%s/returns/%s/submissions" % (base_url, mandate_id, return_id)
    submission_payload = """
    {
      "data": {
        "id": "%s",
        "type": "mandate_return_submissions",
        "organisation_id": "%s"
      }
    }
    """ % (submission_id, organisation_id)
    
    submission_headers = {
        'authorization': "bearer %s" % auth_token,
        'accept': "application/json",
        'content-type': "application/json",
        'cache-control': "no-cache",
        }
    
    submission = requests.request("POST", submission_url, data=submission_payload, headers=submission_headers)
    print(submission.text)
    

    Canceling a Direct Debit Instruction

    To cancel a Direct Debit Instruction, use the PATCH method of the Mandate resource that refers to the instruction: PATCH /v1/transaction/mandates/{mandate_id}.

    The PATCH request does not need to contain attributes since no fields are changed, it is merely used to indicate that a change is being made to the instruction.

    Then, create a new Mandate Submission resource to submit the cancellation: POST v1/transaction/mandates/{mandate_id}/submissions

    When creating a Mandate Submission resource, choose the submission_reason attribute to be one of the codes indicating a cancelled instruction, either 0, 1 or 2.

    Below is the body of the Mandate PATCH request to cancel a Direct Debit Instruction.

    Mandate resource PATCH request

    { 
      "data": { 
        "type": "mandates",
        "organisation_id": "2a1b948f-d818-4d41-a742-899262200779", 
        "version":0,
        "attributes": {}
      } 
    }
    


    The following Python example cancels an existing Mandate using PATCH and creates a new Mandate Submission resource.

    import math, requests, time, uuid
    
    ### Replace these variables with your own data! ###
    client_id = 'YOUR CLIENT ID HERE'
    client_secret = 'YOUR CLIENT SECRET HERE'
    organisation_id = 'YOUR ORGANISATION ID HERE'
    mandate_id = 'ID OF THE ORIGINAL MANDATE HERE'
    
    base_url = 'https://api.staging-form3.tech/v1'
    
    # Generate IDs
    submission_id = uuid.uuid4()
    print("Mandate Submission ID: %s" % submission_id)
    
    # Obtain authentication token
    print("Obtaining bearer token...")
    auth_payload = "grant_type=client_credentials"
    auth_url = '%s/oauth2/token' % base_url
    auth_headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    auth_request = requests.auth.HTTPBasicAuth(client_id, client_secret)
    
    auth = requests.request('post', auth_url, data=auth_payload, auth=auth_request, headers=auth_headers)
    auth_token = auth.json().get('access_token')
    
    # Amend the mandate resource
    print("Patching Mandate resource...")
    mandate_url = "%s/transaction/mandates/%s" % (base_url, mandate_id) 
    patch_payload = """
    {
      "data": {
        "id": "%s",
        "organisation_id": "%s",
        "attributes": {}
      }
    }  
    """ % (mandate_id, organisation_id)
    
    patch_headers = {
        'authorization': "bearer " + auth_token,
        'accept': "application/json",
        'content-type': "application/json",
        'cache-control': "no-cache"
        }
    
    patch_mandate = requests.request("PATCH", mandate_url, data=patch_payload, headers=patch_headers)
    print(patch_mandate.text)
    
    # Create the submission resource
    print("Creating Mandate Submission resource...")
    submission_url = "%s/transaction/mandates/%s/submissions" % (base_url, mandate_id)
    submission_payload = """
    {
      "data": {
        "id": "%s",
        "type": "mandate_submissions",
        "organisation_id": "%s",
        "attributes": {
          "submission_reason": "0"
        }
      }
    }
    """ % (submission_id, organisation_id)
    
    submission_headers = {
        'authorization': "bearer %s" % auth_token,
        'accept': "application/json",
        'content-type': "application/json",
        'cache-control': "no-cache",
        }
    
    submission = requests.request("POST", submission_url, data=submission_payload, headers=submission_headers)
    print(submission.text)
    

    Receiving a Direct Debit Instruction Cancellation

    When the originator cancels a Direct Debit Instruction, you receive a Mandate resource and a Mandate Admission resource similar to receiving a new Direct Debit Instruction. The reference attribute of the Mandate resource can be used to identify the Direct Debit Instruction that has been cancelled.

    Further Reading

    This tutorial explained how to handle Bacs Direct Debit Instruction in the Form3 API. For more information on using Bacs with Form3, see our other tutorials: