Direct debit
You pull money from the customer's bank account under a standing authorization — a mandate. Ideal for subscriptions and invoicing: cheap, bank-grade, and reusable for years. The trade-offs are speed (days, not seconds) and returnability, and this page covers both.
The direct-debit methods
| Scheme | payment_method | Region | Mandate instrument |
|---|---|---|---|
| SEPA Direct Debit | sepa_debit | EU | SEPA mandate (IBAN + acceptance) |
| ACH | ach | US | ACH authorization (routing + account no.) |
| Bacs Direct Debit | bacs_debit | UK | Direct Debit Instruction |
| BECS Direct Debit | becs_debit | Australia / NZ | Direct Debit Request |
| ACSS Debit | acss_debit | Canada | Pre-authorized debit agreement |
Five schemes, one model: collect a mandate once, then debit it on your schedule. Each scheme prescribes its own mandate wording and rules — hosted checkout records the correct text and acceptance evidence per scheme, so you never draft mandate language yourself.
The mandate lifecycle
Collection. Hosted checkout collects the account details and mandate acceptance, and the result is a reusable pm_… instrument attached to the customer — with the account tail and the mandate reference on it (sepa_debit: { iban_last4, mandate_reference }). The save-and-charge pattern is in the payment methods overview.
Active. The mandate stays valid until the customer or their bank revokes it — or, under SEPA rules, until it lapses after 36 months without a debit. Charging a dead mandate fails cleanly; see errors.
The mandate reference travels. The scheme requires the reference (and your name) to appear on the customer's bank statement — that's what mandate_reference is for. Surface it in your receipts too; unrecognized statement lines are the top cause of avoidable returns.
Charging a mandate
Pass the method and the saved instrument. No return_url: there is no customer interaction to return from —
curl https://api.finscale.dev/v1/payments \
-H "Authorization: Bearer sk_test_51FinscaleDemo…" \
-H "Idempotency-Key: idem_ord_9f21_0716" \
-H "Content-Type: application/json" \
-d '{
"amount": 4900,
"currency": "EUR",
"payment_method": "sepa_debit",
"payment_method_id": "pm_6Wq2rT8nB4Yx",
"customer": { "id": "cus_4T8nQb2Wp1Zr" },
"reference": "ord_9f21_0716"
}'
{
"id": "pay_8Q2mX4nT1cVb",
"object": "payment",
"amount": 4900,
"currency": "EUR",
"status": "processing",
"payment_method": "sepa_debit",
"payment_method_id": "pm_6Wq2rT8nB4Yx",
"payment_method_details": {
"sepa_debit": {
"iban_last4": "3401",
"mandate_reference": "FSCL-MNDT-8Q2MX4NT"
}
},
"customer": "cus_4T8nQb2Wp1Zr",
"reference": "ord_9f21_0716",
"provider": "prov_eu_acq_01",
"next_action": null,
"created_at": "2026-07-16T09:24:31Z",
"livemode": false
}
The payment goes straight to processing — next_action is null and stays that way. First-time flows (no saved instrument yet) start from hosted checkout, which collects the mandate and can take the first debit in the same session.
The debit timeline
Direct debit is the slowest family in the catalog, by design of the underlying schemes:
processinglasts 2–5 business days while the debit clears — the exact spread depends on the scheme and submission cut-off times.payment.succeededfires when the scheme confirms the debit. Gate fulfillment of high-value goods on this event, not on payment creation.- Success is still not final — see the return window below.
Returns & disputes
A direct debit can bounce at two points, and the API distinguishes them:
- Early return, during
processing— the bank rejects the pull: no funds, closed account, revoked mandate. The payment moves tofailedwith a normalizedfailure_codesuch asinsufficient_fundsoraccount_closed, andpayment.failedfires. No dispute object is involved. - Late return, after
succeeded— the customer exercises their scheme rights and the settled debit is pulled back. This surfaces as a Dispute (dp_…) viadispute.created:reason: "fraudulent"for an unauthorized-debit claim,reason: "general"otherwise. The dispute flow — evidence, deadlines, outcomes — applies unchanged; your strongest evidence is the mandate acceptance record, which Finscale stores from checkout.
Settlement timing
Funds confirm at succeeded and join your unified payout on the standard T+2 schedule. Late returns net against a later payout as negative settlement lines — the settlement report ties each one back to the original payment id, and reconciliation shows the id chain.
Error scenarios
| Scenario | What the API does |
|---|---|
| Debit bounces in clearing | payment.failed with failure_code: "insufficient_funds", "account_closed", … Retrying is allowed by the schemes within limits — create a new payment; don't loop retries on insufficient_funds faster than your dunning policy. |
| Mandate revoked or lapsed | The charge fails without touching the bank; payment.failed carries the failure reason, and the pm_… instrument stops being chargeable. Re-collect a mandate through checkout. |
payment_method_id missing or not attached to the customer | 400 invalid_request_error at create — a debit needs a mandate-backed instrument. |
| Settled debit returned by the customer | Dispute object via dispute.created — see Returns & disputes. |
| Currency doesn't match the scheme | 400 invalid_request_error with currency_not_supported — sepa_debit is EUR-only, ach USD-only, and so on. |
Related
- Payment methods overview — the save-and-charge pattern and full catalog
- Disputes — how late returns are represented and defended
- Settlement & payouts — negative lines and netting
- Risk engine — gating first debits from new customers
- Method catalog on finscale.dev — regions and currencies