Direct Debit

Direct Debit is a payment method where an authorised creditor collects funds directly from a payer's account, based on a standing authorisation called a mandate. On the IF Platform your client is the payer — a creditor initiates the collection through the underlying service provider (ClearBank for Bacs, Banking Circle for Bacs and SEPA), and the IF Platform represents it as an Outgoing Transfer that decreases the balance of the related account.

Direct Debit is built on two entities:

1. Direct Debit Mandate : the standing authorisation that allows a creditor to collect payments from a payer account. A mandate must exist and be active before collections can be honoured.

2. Direct Debit Payment : an individual collection initiated by the creditor against the mandate. Direct Debit Payments are not created through the API — they are driven by the service provider and surfaced to you through webhooks. Each Direct Debit Payment spawns an Outgoing Transfer that carries out the accounting.

Relation with an Outgoing Transfer

When a Direct Debit Payment is received, the IF Platform automatically creates an Outgoing Transfer on the payer account and links the two over the outgoingTransferId field on the Direct Debit Payment. The Outgoing Transfer follows the standard outgoing transfer lifecycle (please refer to the Outgoing Transfers page). If a payment that charged the payer account is later returned, the Incoming Transfer that credits the funds back is linked over the incomingTransferId field once the returned funds are booked. This happens after the direct-debit-payment-returned webhook, so retrieve the payment to read it. The field stays empty when the payer account was never charged.

Supported Schemes

Direct Debit is supported for the following schemes. The scheme field reflects the scheme of both the mandate and its payments:

  • bacs (GBP) — ClearBank, Banking Circle
  • sepa (EUR) — Banking Circle

Workflows

endpointworkflow.code
POST /direct-debit-mandatesclient.direct, organization.direct
PATCH /direct-debit-mandates/{id}client.direct, organization.direct
PATCH /direct-debit-payments/{id}client.direct, direct

client.direct creates a mandate owned by a client (the clientId is required); organization.direct creates one owned by the organization.


Direct Debit Mandate

Mandate Lifecycle

The status field of a mandate reflects its lifecycle:

  • pending : the mandate has been created and is awaiting activation by the service provider.
  • active : the mandate is activated; collections can be honoured.
  • cancelled : the mandate has been cancelled and no further collections will be honoured.
  • rejected : the mandate has been rejected, by you or by the service provider, and no further collections will be honoured.

A collection arriving for a mandate that is not active is returned automatically with the reason mandate-not-active.

Create a Direct Debit Mandate

POST /direct-debit-mandates

Depending on the underlying service provider, a mandate is either created on the provider side or registered directly on the IF Platform. The flowType is payment for payer-side mandates. The scheme must be one of the supported schemes. A successful request returns 201 Created.

{
  "workflow": {
    "code": "client.direct"
  },
  "data": {
    "directDebitMandate": {
      "clientId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "flowType": "payment",
      "creditorIdentifier": "DE98ZZZ09999999999",
      "creditorAccount": {
        "accountHolderName": "NETFLIX INTERNATIONAL B.V.",
        "country": "NL",
        "currency": "EUR",
        "iban": "NL91ABNA0417164300",
        "routingCodes": {}
      },
      "reference": "MNDT-2026-000123"
    }
  },
  "connect": {},
  "metadata": {}
}
{
  "workflow": {
    "code": "client.direct"
  },
  "data": {
    "directDebitMandate": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "flowType": "payment",
      "status": "pending",
      "creditorIdentifier": "DE98ZZZ09999999999",
      "creditorAccount": {
        "accountHolderName": "NETFLIX INTERNATIONAL B.V.",
        "alias": null,
        "currency": "EUR",
        "routingCodes": {},
        "accountNumber": null,
        "iban": "NL91ABNA0417164300",
        "bankName": null,
        "country": "NL"
      },
      "reference": "MNDT-2026-000123"
    }
  },
  "connect": {},
  "metadata": {}
}

Update a Direct Debit Mandate

PATCH /direct-debit-mandates/{id}

An active mandate can be cancelled or rejected; status is required and must be cancelled or rejected. Setting it to cancelled stops further collections from being honoured and requires a cancellationReason; rejected requires a rejectionReason. A pending mandate is activated by the service provider and cannot be updated, and a cancelled or rejected mandate is final. Only status, cancellationReason and rejectionReason can be updated. A successful request returns 202 Accepted with the updated mandate.

cancellationReason is one of user-requested, account-closed, organization-action. rejectionReason is one of user-requested, account-closed, other.

{
  "workflow": {
    "code": "client.direct"
  },
  "data": {
    "directDebitMandate": {
      "status": "cancelled",
      "cancellationReason": "user-requested"
    }
  },
  "connect": {},
  "metadata": {}
}

Get a Direct Debit Mandate

GET /direct-debit-mandates/{id}

The connect.connection object shows the mandate's state at the service provider.

{
  "workflow": {},
  "data": {
    "directDebitMandate": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "flowType": "payment",
      "status": "active",
      "creditorIdentifier": "DE98ZZZ09999999999",
      "creditorAccount": {
        "accountHolderName": "NETFLIX INTERNATIONAL B.V.",
        "alias": null,
        "currency": "EUR",
        "routingCodes": {},
        "accountNumber": null,
        "iban": "NL91ABNA0417164300",
        "bankName": null,
        "country": "NL"
      },
      "reference": "MNDT-2026-000123"
    }
  },
  "connect": {
    "connection": {
      "id": "6654321",
      "serviceProvider": "banking-circle",
      "status": "active",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}

List Direct Debit Mandates

GET /direct-debit-mandates

Supported query parameters:

parameterdescription
metadata.page.number0-indexed, default=0
metadata.page.sizedefault=20, max=100
data.directDebitMandate.statusone or more of pending, active, cancelled, rejected
data.directDebitMandate.clientIdUUID of client
data.directDebitMandate.accountIdUUID of account
data.directDebitMandate.ownerowner of mandate. one of client, organization

Mandate Webhooks

Direct Debit webhooks have "direct-debits" in the module field. The following mandate types are sent:

typesent whenstatus
direct-debit-mandate-createda mandate is created. A mandate created through the API is pending; a mandate set up at the service provider (for example a Bacs instruction received by ClearBank) is created already activepending or active
direct-debit-mandate-activatedthe service provider activates a pending mandateactive
direct-debit-mandate-cancelledthe mandate is cancelled through the API or by the service providercancelled
direct-debit-mandate-rejectedthe mandate is rejected through the API or by the service providerrejected

Changes of the mandate's connection at the service provider are sent as connection-created and connection-updated, with the same payload and the connection in connect.connection. The connect object is empty until the mandate has a connection.

Fields without a value are not included in the payload.

{
  "webhook": {
    "module": "direct-debits",
    "type": "direct-debit-mandate-created"
  },
  "data": {
    "directDebitMandate": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "flowType": "payment",
      "status": "pending",
      "creditorIdentifier": "DE98ZZZ09999999999",
      "creditorAccount": {
        "accountHolderName": "NETFLIX INTERNATIONAL B.V.",
        "alias": null,
        "currency": "EUR",
        "routingCodes": {},
        "accountNumber": null,
        "iban": "NL91ABNA0417164300",
        "bankName": null,
        "country": "NL"
      },
      "reference": "MNDT-2026-000123"
    }
  },
  "connect": {},
  "metadata": {}
}
{
  "webhook": {
    "module": "direct-debits",
    "type": "direct-debit-mandate-activated"
  },
  "data": {
    "directDebitMandate": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "flowType": "payment",
      "status": "active",
      "creditorIdentifier": "DE98ZZZ09999999999",
      "creditorAccount": {
        "accountHolderName": "NETFLIX INTERNATIONAL B.V.",
        "alias": null,
        "currency": "EUR",
        "routingCodes": {},
        "accountNumber": null,
        "iban": "NL91ABNA0417164300",
        "bankName": null,
        "country": "NL"
      },
      "reference": "MNDT-2026-000123"
    }
  },
  "connect": {
    "connection": {
      "id": "6654321",
      "serviceProvider": "banking-circle",
      "status": "active",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}
{
  "webhook": {
    "module": "direct-debits",
    "type": "direct-debit-mandate-cancelled"
  },
  "data": {
    "directDebitMandate": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "flowType": "payment",
      "status": "cancelled",
      "creditorIdentifier": "DE98ZZZ09999999999",
      "creditorAccount": {
        "accountHolderName": "NETFLIX INTERNATIONAL B.V.",
        "alias": null,
        "currency": "EUR",
        "routingCodes": {},
        "accountNumber": null,
        "iban": "NL91ABNA0417164300",
        "bankName": null,
        "country": "NL"
      },
      "reference": "MNDT-2026-000123",
      "cancellationReason": "user-requested"
    }
  },
  "connect": {
    "connection": {
      "id": "6654321",
      "serviceProvider": "banking-circle",
      "status": "cancelled",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}
{
  "webhook": {
    "module": "direct-debits",
    "type": "direct-debit-mandate-rejected"
  },
  "data": {
    "directDebitMandate": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "flowType": "payment",
      "status": "rejected",
      "creditorIdentifier": "DE98ZZZ09999999999",
      "creditorAccount": {
        "accountHolderName": "NETFLIX INTERNATIONAL B.V.",
        "alias": null,
        "currency": "EUR",
        "routingCodes": {},
        "accountNumber": null,
        "iban": "NL91ABNA0417164300",
        "bankName": null,
        "country": "NL"
      },
      "reference": "MNDT-2026-000123",
      "rejectionReason": "account-closed"
    }
  },
  "connect": {
    "connection": {
      "id": "6654321",
      "serviceProvider": "banking-circle",
      "status": "rejected",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}
{
  "webhook": {
    "module": "direct-debits",
    "type": "connection-updated"
  },
  "data": {
    "directDebitMandate": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "flowType": "payment",
      "status": "active",
      "creditorIdentifier": "DE98ZZZ09999999999",
      "creditorAccount": {
        "accountHolderName": "NETFLIX INTERNATIONAL B.V.",
        "alias": null,
        "currency": "EUR",
        "routingCodes": {},
        "accountNumber": null,
        "iban": "NL91ABNA0417164300",
        "bankName": null,
        "country": "NL"
      },
      "reference": "MNDT-2026-000123"
    }
  },
  "connect": {
    "connection": {
      "id": "6654321",
      "serviceProvider": "banking-circle",
      "status": "active",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}

Direct Debit Payment

Payment Lifecycle

Direct Debit Payments are driven by the service provider and follow the Bacs/SEPA collection cycle. The status field reflects the collection itself:

  • pending : the collection has been announced (advance notice) and is awaiting the settlement date.
  • completed : the collection settled and the payer account was charged; the linked Outgoing Transfer is completed.
  • failed : the payer account was not charged — the collection could not be settled (e.g. insufficient funds) or was returned before the account was charged; the linked Outgoing Transfer is failed.

A return or refund does not change the status on its own; it is tracked on returnContext.

Return Context

The returnContext object describes whether the payment can be returned and what happened to a return:

fielddescription
enabledtrue when the service provider accepts a return at this stage
cutOffDateTimelatest date-time (UTC) a return can be requested, according to the scheme rules
reasonthe return reason, whether requested by you or raised by the service provider
statusprocessing (return in flight), completed (funds returned) or failed (the return did not go through and can be retried)

reason is one of account-closed, advance-notice-disputed, amount-not-matched, amount-not-yet-due, insufficient-balance, mandate-not-active, no-account, no-mandate, presentation-overdue, service-user-not-match.

Initiate a Return / Refund

PATCH /direct-debit-payments/{id}

A Direct Debit Payment can be returned or refunded by setting returnContext.status to processing together with a returnContext.reason. Only returnContext can be updated. A successful request returns 202 Accepted with the payment; the IF Platform forwards the return to the service provider and returnContext.status moves to completed or failed once it settles. When the funds are credited back, an Incoming Transfer brings them back to the account.

A return is accepted only when:

  • returnContext.enabled is true,
  • returnContext.cutOffDateTime has not passed,
  • return is not processing or completed (a failed return can be retried),
  • the payment is pending or completed, or failed with a failed return.
{
  "workflow": {
    "code": "client.direct"
  },
  "data": {
    "directDebitPayment": {
      "returnContext": {
        "status": "processing",
        "reason": "advance-notice-disputed"
      }
    }
  },
  "connect": {},
  "metadata": {}
}

Get a Direct Debit Payment

GET /direct-debit-payments/{id}

The connect.connection object shows the payment's state at the service provider.

{
  "workflow": {},
  "data": {
    "directDebitPayment": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "mandateId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "status": "completed",
      "amount": 10.55,
      "currency": "EUR",
      "transferDate": "2026-07-22",
      "returnContext": {
        "enabled": true,
        "cutOffDateTime": "2026-09-16T12:00:00",
        "reason": null,
        "status": null
      },
      "outgoingTransferId": "00000000-0000-0000-0000-000000000000"
    }
  },
  "connect": {
    "connection": {
      "id": "130341d8-0000-0000-0000-000000000000",
      "serviceProvider": "banking-circle",
      "status": "completed",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}

List Direct Debit Payments

GET /direct-debit-payments

Supported query parameters:

parameterdescription
metadata.page.number0-indexed, default=0
metadata.page.sizedefault=20, max=100
data.directDebitPayment.statusesone or more of pending, completed, failed
data.directDebitPayment.clientIdUUID of client
data.directDebitPayment.accountIdUUID of account
data.directDebitPayment.mandateIdUUID of mandate
data.directDebitPayment.ownerowner of payment. one of client, organization

Payment Webhooks

Direct Debit webhooks have "direct-debits" in the module field. The following payment types are sent:

typesent whenstatus
direct-debit-payment-createdthe service provider announces a collectionpending
direct-debit-payment-completedthe collection settles and the payer account is chargedcompleted
direct-debit-payment-failedthe collection fails, or it is returned for insufficient-balance before the payer account is chargedfailed
direct-debit-payment-returneda return completes; returnContext.status is completed and returnContext.reason holds the reasoncompleted if the payer account was charged, otherwise failed

returnContext.enabled and returnContext.cutOffDateTime depend on the service provider. Banking Circle allows a return from the moment the collection is announced. ClearBank allows it only after settlement, so its direct-debit-payment-created webhook carries enabled: false and no cutOffDateTime.

When a return credits the funds back, incomingTransferId is filled after the direct-debit-payment-returned webhook, once the Incoming Transfer is booked; retrieve the payment to read it.

Changes of the payment's connection at the service provider are sent as connection-created and connection-updated, with the same payload and the connection in connect.connection.


{
  "webhook": {
    "module": "direct-debits",
    "type": "direct-debit-payment-created"
  },
  "data": {
    "directDebitPayment": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "mandateId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "status": "pending",
      "amount": 10.55,
      "currency": "EUR",
      "transferDate": "2026-07-22",
      "returnContext": {
        "enabled": true,
        "cutOffDateTime": "2026-09-16T12:00:00",
        "reason": null,
        "status": null
      },
      "outgoingTransferId": "00000000-0000-0000-0000-000000000000"
    }
  },
  "connect": {
    "connection": {
      "id": "130341d8-0000-0000-0000-000000000000",
      "serviceProvider": "banking-circle",
      "status": "pending",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}
{
  "webhook": {
    "module": "direct-debits",
    "type": "direct-debit-payment-completed"
  },
  "data": {
    "directDebitPayment": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "mandateId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "status": "completed",
      "amount": 10.55,
      "currency": "EUR",
      "transferDate": "2026-07-22",
      "returnContext": {
        "enabled": true,
        "cutOffDateTime": "2026-09-16T12:00:00",
        "reason": null,
        "status": null
      },
      "outgoingTransferId": "00000000-0000-0000-0000-000000000000"
    }
  },
  "connect": {
    "connection": {
      "id": "130341d8-0000-0000-0000-000000000000",
      "serviceProvider": "banking-circle",
      "status": "completed",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}
{
  "webhook": {
    "module": "direct-debits",
    "type": "direct-debit-payment-failed"
  },
  "data": {
    "directDebitPayment": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "mandateId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "status": "failed",
      "amount": 10.55,
      "currency": "EUR",
      "transferDate": "2026-07-22",
      "returnContext": {
        "enabled": true,
        "cutOffDateTime": "2026-09-16T12:00:00",
        "reason": "insufficient-balance",
        "status": "completed"
      },
      "outgoingTransferId": "00000000-0000-0000-0000-000000000000"
    }
  },
  "connect": {
    "connection": {
      "id": "130341d8-0000-0000-0000-000000000000",
      "serviceProvider": "banking-circle",
      "status": "returned",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}
{
  "webhook": {
    "module": "direct-debits",
    "type": "direct-debit-payment-returned"
  },
  "data": {
    "directDebitPayment": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "mandateId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "status": "completed",
      "amount": 10.55,
      "currency": "EUR",
      "transferDate": "2026-07-22",
      "returnContext": {
        "enabled": true,
        "cutOffDateTime": "2026-09-16T12:00:00",
        "reason": "advance-notice-disputed",
        "status": "completed"
      },
      "outgoingTransferId": "00000000-0000-0000-0000-000000000000"
    }
  },
  "connect": {
    "connection": {
      "id": "130341d8-0000-0000-0000-000000000000",
      "serviceProvider": "banking-circle",
      "status": "returned",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}
{
  "webhook": {
    "module": "direct-debits",
    "type": "connection-updated"
  },
  "data": {
    "directDebitPayment": {
      "id": "00000000-0000-0000-0000-000000000000",
      "owner": "client",
      "clientId": "00000000-0000-0000-0000-000000000000",
      "mandateId": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "scheme": "sepa",
      "status": "completed",
      "amount": 10.55,
      "currency": "EUR",
      "transferDate": "2026-07-22",
      "returnContext": {
        "enabled": true,
        "cutOffDateTime": "2026-09-16T12:00:00",
        "reason": null,
        "status": null
      },
      "outgoingTransferId": "00000000-0000-0000-0000-000000000000"
    }
  },
  "connect": {
    "connection": {
      "id": "130341d8-0000-0000-0000-000000000000",
      "serviceProvider": "banking-circle",
      "status": "completed",
      "state": "completed-up-to-date",
      "method": "built-in"
    }
  },
  "metadata": {}
}