Payment initiation
Products
| Product | Path segment |
|---|---|
| SEPA credit transfer | sepa-credit-transfers |
| SEPA instant credit transfer | instant-sepa-credit-transfers |
EUR only. Bulk payments, periodic payments and signing baskets are outside the interface because they are outside the customer’s own portal — not because they are unbuilt.
Initiating
curl -X POST https://xs2a.paysaxas.com/v1/payments/sepa-credit-transfers \
--cert tpp-qwac.pem --key tpp-qwac.key \
-H "Content-Type: application/json" \
-H "X-Request-ID: 3c2f0e9a-2b62-4b0e-9b2d-2f0a8a2b1c33" \
-H "TPP-Redirect-URI: https://your-app.example/psd2/return" \
-H "PSU-IP-Address: 203.0.113.9" \
-d '{
"debtorAccount": {"iban": "FI2112345600000785"},
"instructedAmount": {"currency": "EUR", "amount": "125.50"},
"creditorAccount": {"iban": "FI4550009420888888"},
"creditorName": "Beta Oy",
"remittanceInformationUnstructured": "Invoice 2026-114"
}'
{
"transactionStatus": "RCVD",
"paymentId": "8f14...",
"_links": {
"scaRedirect": { "href": "https://platform.paysaxas.com/customer/xs2a/authorise/AUTH-...?state=..." },
"status": { "href": "/v1/payments/sepa-credit-transfers/8f14.../status" }
}
}
Then send the customer to scaRedirect. Nothing moves until they authorise. Store
the state on that link against the payment and check it when they come back —
see what state is and how long it lasts.
Field notes
creditorAccount.bicis optional. We resolve it where we can. Send it if you have it.creditorAccount.ibanmust be one we can route to. A malformed IBAN is aFORMAT_ERROR. Anything else we cannot route — including a PaySaxas IBAN with no live account behind it — is refused asPAYMENT_FAILEDlike every other initiation failure, so you cannot tell those cases apart, by design. The rule is the same one the account holder’s own banking screen applies; nothing is reachable through this interface that is not reachable there.remittanceInformationUnstructuredis optional on the wire; if you omit it we record a neutral description rather than rejecting the instruction.requestedExecutionDateis accepted but future-dating follows the customer portal’s behaviour.
Idempotency
X-Request-ID is the idempotency key. Replaying the same value returns the
first payment rather than initiating a second — safe to retry a timeout.
Use a fresh UUID for a genuinely new payment.
The full contract, so you can reason about your retry logic:
| Question | Answer |
|---|---|
| Same key, same body | Returns the first payment. Retry a timeout freely |
| Same key, different body | 409 REQUEST_ID_REUSED. We do not silently return the first payment — that would let a client bug believe a second, different instruction had been executed |
| How long is a key remembered? | 48 hours for body-conflict detection. The payment itself keeps its key permanently, so a much later replay still resolves to the original payment rather than creating a second one |
| Scope | Global. One key binds to exactly one payment across every product and every provider. A key used on sepa-credit-transfers cannot be reused on instant-sepa-credit-transfers — it resolves to the first payment. Generate a fresh UUID v4 per instruction and this never arises |
| Other endpoints | X-Request-ID is required everywhere and is echoed back on every response, but it only carries idempotency on writes. On GETs it is a correlation id — quote it to support and we can find your exact request in the traceability log |
The one case that matters: do not reuse a key across retries of a request you changed. If a retry carries a different amount, payee or debtor, it is a new instruction and needs a new key. Reusing the key is refused rather than guessed at.
Status values
| Code | Meaning |
|---|---|
RCVD |
Received, not yet authorised |
ACTC |
Accepted, technical validation passed |
PDNG |
Pending — we are waiting on something before the payment can move, usually the customer confirming the payee. Poll it. It resolves to ACSP or RJCT, or to CANC if the customer does not come back within 24 hours. You may still cancel it yourself |
ACSP |
Accepted, settlement in process |
ACSC |
Accepted, settlement completed |
RJCT |
Rejected |
CANC |
Cancelled |
ACSP covers more than one internal state. A payment under compliance
review reports ACSP, exactly as one moving normally through the rails does.
This is deliberate and not negotiable: a distinguishable status would disclose
that a specific payment attracted scrutiny. Do not build logic that infers
anything beyond “in progress” from ACSP.
Cancellation
DELETE /v1/payments/{product}/{paymentId}
Possible only while the payment could still be cancelled in the customer’s own
portal. Past that point the funds have moved and you get
CANCELLATION_INVALID (405).
No customer authorisation is required, and there is no cancellation
authorisation sub-resource. Berlin Group allows an ASPSP to demand a second
ceremony for cancellation; we do not. The DELETE takes effect immediately and
returns the resulting transactionStatus.
The reasoning, so you can rely on it rather than infer it: cancelling only ever stops money moving. SCA exists to protect the customer from a payment they did not intend, and a cancellation cannot create one. Demanding a fresh ceremony would mean a customer who wants to stop a payment must first be available to authenticate — worst exactly when it matters.
Two limits keep that safe:
- You can only cancel a payment you initiated. Another provider’s payment is not addressable by you at all.
- You can only cancel it before it becomes final; after that the answer is
CANCELLATION_INVALID, not a reversal.
If you receive 403, the cancellation was refused because your registration or
certificate role does not permit it — not because the payment was uncancellable.
Why a payment can be refused
Initiation failures return PAYMENT_FAILED with one sentence, always the
same. Insufficient funds, an unknown debtor account, a limit, a compliance
hold — all identical from outside.
This is not unhelpfulness. A refusal that varied by cause would let anyone with a certificate probe our customers’ balances, limits and standing. The customer can see the real reason in their own portal, where they are authenticated as themselves.
Malformed requests are different and do say what is wrong: FORMAT_ERROR for a
bad IBAN or amount, PRODUCT_INVALID for a non-EUR currency.