Payments
Read the payment records associated with your orders.
A payment represents a single attempt to pay an order through a chosen payment method. Use these endpoints to look up a payment or list payments for your project.
All amounts are in minor currency units (e.g., cents for EUR) and returned as
integers (e.g., 2500 for €25.00).
Get a Payment​
GET /payment-executor/integration/v1/payments/{id}
Retrieves a single payment by its identifier. The payment must belong to your project.
Request​
curl https://api.paysera.com/payment-executor/integration/v1/payments/{id} \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Path Parameters​
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Payment identifier |
Response​
{
"id": "0c2d4e6a-1b3c-4d5e-9f8a-2b4c6d8e0f12",
"order_id": "a6f2b8e3-5e5f-47d9-b13f-87ed2db2938a",
"order_reference": "ORDER-12345",
"project_id": "your-project-id",
"amount": 2500,
"currency": "EUR",
"status": "settled",
"payment_method_id": "3f9a1c20-7b4d-4e8a-9c1f-5d6e7a8b9c0d",
"payment_method_key": "swedbank",
"payment_method_name": "Swedbank",
"payment_method_type": "banklink",
"purpose": "Order #12345",
"original_amount": null,
"original_currency": null,
"exchange_rate": null,
"external_payment_id": "EXT-998877",
"created_at": 1736433500,
"updated_at": 1736433570,
"distributions": [
{
"id": "b7c9d1e3-4f5a-4b6c-8d9e-0f1a2b3c4d5e",
"account_number": "LT121000011101001000",
"amount": 2000,
"currency": "EUR",
"status": "SETTLED",
"is_tip": false,
"error_code": null,
"created_at": 1736433510,
"settled_at": 1736433560,
"returned_payout": null,
"resend_of_distribution_id": null,
"resend_initiated_by": null,
"delivered_amount": null
}
]
}
Response Fields​
| Field | Type | Description |
|---|---|---|
id | UUID | Payment identifier |
order_id | UUID | Associated order identifier |
order_reference | string | Merchant reference of the order |
project_id | string | Project the payment belongs to |
amount | integer | Amount in minor units |
currency | string | ISO 4217 currency |
status | string | Payment status |
payment_method_id | string | Payment method identifier |
payment_method_key | string | Payment method key (e.g., swedbank) |
payment_method_name | string | Human-readable payment method name |
payment_method_type | string | Payment method type (e.g., banklink) |
purpose | string | Payment purpose — the merchant-supplied payment_details.purpose if the link carried one, otherwise the generated default narrative. See Payment purpose |
original_amount | integer | Original amount before conversion (nullable) |
original_currency | string | Original currency before conversion (nullable) |
exchange_rate | number | Applied exchange rate (nullable) |
external_payment_id | string | Identifier in the external provider system |
created_at | integer | Creation Unix timestamp (seconds) |
updated_at | integer | Last update Unix timestamp (seconds) |
distributions | array | Payout legs of a split payment. null for a non-split payment, and also while the distribution service is unreachable |
Distribution Fields​
Each entry of distributions describes one payout leg of a split payment.
| Field | Type | Description |
|---|---|---|
id | UUID | Payout leg identifier; the id every leg-level action takes |
account_number | string | Beneficiary account of this leg; null for internal clearing legs |
amount | integer | Leg amount in minor units |
currency | string | ISO 4217 currency |
status | string | Leg status: HELD, IN_PROGRESS, SETTLED, COMPLETED, FAILED. A leg that goes through reads SETTLED, then COMPLETED once no leg on the payment is still open |
is_tip | boolean | Whether this leg is a tip |
error_code | string | Why a FAILED leg failed: payout_returned, destination_not_allowed, payout_failed — see Failure Causes. Set on every FAILED leg, null on any other status (nullable) |
held_reason | string | Why a HELD leg is waiting and which action releases it: account_unusable, name_mismatch, on_hold — see Correct the Account of a Held Payout. Absent on any other status, and while a name check is still being resolved (nullable) |
created_at | integer | Creation Unix timestamp (seconds) |
settled_at | integer | Settlement Unix timestamp (seconds); null until the leg settles |
returned_payout | object | Set only on a leg the beneficiary bank sent back; drives the re-send action (nullable) |
resend_of_distribution_id | UUID | Set on a re-send leg: the returned leg it answers (nullable) |
resend_initiated_by | string | Set on a re-send leg: the channel that started it, MERCHANT or SUPPORT (nullable) |
delivered_amount | integer | What stayed with the recipient in minor units, set only when the bank returned part of this payout. The leg itself is failed, so this is money delivered under a failed leg (nullable) |
Failure Causes​
error_code | What happened | What you can do |
|---|---|---|
payout_returned | The recipient's bank sent the payout back | Re-send it |
destination_not_allowed | The recipient's IBAN is in a country Paysera does not send payouts to | Replace the recipient's account with one in an allowed country — later payouts to the same account fail the same way |
payout_failed | The payout failed for a reason you cannot act on through the API | Paysera support |
error_code is set on every FAILED leg, and on no other. Treat a value you do not recognise the
way you treat payout_failed: more values may be added.
The error_code of the distribution.failed webhook
carries the distribution service's own codes (SETTLEMENT_FAILED), not the values above.
List Payments​
GET /payment-executor/integration/v1/payments
Returns a cursor-paginated list of payments for your project. Filters can be combined freely.
Request​
curl "https://api.paysera.com/payment-executor/integration/v1/payments?order_id=ORDER_ID&size=20" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Filter Parameters​
| Parameter | Type | Description |
|---|---|---|
order_id | UUID | Filter by order |
status | string | Filter by one or more payment statuses (repeat the parameter: status=settled&status=failed) |
payment_method_id | string | Filter by payment method id (repeatable) |
payment_method_key | string | Filter by payment method key (repeatable) |
currency | string | Filter by ISO 4217 currency (repeatable) |
amount, amount_gte, amount_lte | integer | Exact / range filter on amount (minor units) |
created_at_gte, created_at_lte | integer | Created-at range (Unix seconds) |
q | string | Prefix search over order_reference — matches references that start with the value (max 255 chars) |
Repeatable filters use the plain repeated form (status=settled&status=failed).
The bracket form status[]=settled is not recognized — like any unknown
parameter it is silently ignored, so the list comes back unfiltered.
Pagination Parameters​
The order, payment link, payment and refund list endpoints share the same cursor-based
pagination. Cursors are opaque strings — read them from _metadata.cursors and send
them back unchanged; never build or parse them yourself.
| Parameter | Type | Default | Description |
|---|---|---|---|
size | integer | 10 | Items per page (1–1000). Out-of-range values return 400 invalid_properties. |
after | string | — | Cursor for the next page. Take it from _metadata.cursors.after. |
before | string | — | Cursor for the previous page. Take it from _metadata.cursors.before. Mutually exclusive with after. |
order_direction | string | desc | Sort direction: asc or desc. |
order_by | string | creation time | Field to sort by. Allowed values differ per endpoint — see the endpoint's parameter table. |
include_total_count | boolean | false | Set to true to populate _metadata.total. Off by default because counting is more expensive. |
Every list response has the same envelope:
{
"items": [],
"_metadata": {
"has_next": true,
"has_previous": false,
"cursors": {
"after": "MDE5ZmRiMmUtOWE5NC03ZDVmLThmY2ItNGRhMTQ1MzFkZGQ0",
"before": "MDE5ZmRjMjgtNWQ5YS03NjQ5LWExOWItZDhjNDAzODQ2NTRi"
},
"total": null
}
}
total is null unless you pass include_total_count=true.
To walk the whole collection, keep sending after until has_next is false:
# first page
curl "https://api.paysera.com/merchant-order/integration/v1/orders?size=50" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# next page — reuse _metadata.cursors.after from the previous response
curl "https://api.paysera.com/merchant-order/integration/v1/orders?size=50&after=MDE5ZmRiMmUtOWE5NC03ZDVmLThmY2ItNGRhMTQ1MzFkZGQ0" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
The API does not reject parameters it doesn't know. Sending limit or cursor (names
used by some other APIs) does not return an error — the values are silently dropped
and you keep receiving the first page with the default size of 10. If pagination seems
stuck, check the parameter names first.
order_by accepts created_at (default), amount, currency, status,
order_reference and payment_method_key.
Response​
{
"items": [
{
"id": "0c2d4e6a-1b3c-4d5e-9f8a-2b4c6d8e0f12",
"amount": 2500,
"currency": "EUR",
"status": "settled",
"payment_method_id": "019493f8-f999-7000-0000-0000000000d1",
"payment_method_key": "swedbank",
"payment_method_name": "Swedbank",
"order_id": "a6f2b8e3-5e5f-47d9-b13f-87ed2db2938a",
"order_reference": "ORDER-12345",
"project_id": "your-project-id",
"created_at": 1736433500,
"updated_at": 1736433560,
"external_payment_id": "019fdb2e-eec2-756c-91f4-8623333f7b24",
"purpose": "Payment for order #ORDER-12345 at My Shop",
"original_amount": null,
"original_currency": null,
"exchange_rate": null
}
],
"_metadata": {
"has_next": true,
"has_previous": false,
"total": null,
"cursors": {
"after": "MDE5ZmRiMmUtZWNlMi03ZmVjLWFkNzMtYTFmYTUwYzFlMWZi",
"before": "MDE5ZmRjMjgtYmYxYy03ZDYxLThlZGItOWExNzQ0MjkyYmI0"
}
}
}
original_amount, original_currency and exchange_rate are populated only when the
payment was converted from another currency.
Resolve a Held Payout Leg (Verification of Payee)​
Split-payment integrations paying external (non-Paysera) recipients
may receive a recipient.vop_checked webhook:
the recipient's name did not strongly match their IBAN's account holder, and the
payout leg is held until you act. There are two ways to release it — confirm the
pair as-is, or correct the recipient. Both take the distribution_id from the
webhook's data, and both are also available in the Paysera Checkout portal.
Confirm (Pay Anyway)​
POST /payment-executor/integration/v1/payments/{id}/distributions/confirm-vop-mismatch
Records your explicit authorization to pay out despite the name mismatch and
dispatches the held leg. {id} is the payment ID. Send the IBAN and name exactly
as received in the webhook — the confirmation is pinned to that reviewed pair:
curl -X POST https://api.paysera.com/payment-executor/integration/v1/payments/{id}/distributions/confirm-vop-mismatch \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"beneficiary_iban": "DE89370400440532013000",
"beneficiary_name": "Partner GmbH"
}'
| Parameter | Type | Required | Description |
|---|---|---|---|
distribution_id | UUID | Yes | The held payout leg, from the webhook's data |
beneficiary_iban | string (max 34) | Yes | The IBAN you reviewed, exactly as received in the webhook |
beneficiary_name | string (max 140) | Yes | The name you reviewed, exactly as received in the webhook |
// Response — 202 Accepted
{
"distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"debit_uuid": "6a1b3c4d-5e9f-4a2b-8c6d-8e0f120c2d4e",
"beneficiary_iban": "DE89370400440532013000",
"beneficiary_name": "Partner GmbH",
"outcome": "ACCEPTED"
}
The confirmation applies to that IBAN + name pair only — correcting the recipient afterwards discards it and triggers a fresh check.
Correct the Recipient​
POST /payment-executor/integration/v1/payments/{id}/distributions/correct-beneficiary
Rewrites the held leg's recipient IBAN and name — both are required, so resend
the unchanged value when correcting only one. The corrected pair is
re-verified with a fresh name check; if it still does not match strongly, a new
recipient.vop_checked webhook is sent. Use this when the webhook's
beneficiary_name is genuinely wrong rather than merely a weak match:
curl -X POST https://api.paysera.com/payment-executor/integration/v1/payments/{id}/distributions/correct-beneficiary \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"beneficiary_iban": "DE89370400440532013000",
"beneficiary_name": "Partner Handels GmbH"
}'
| Parameter | Type | Required | Description |
|---|---|---|---|
distribution_id | UUID | Yes | The held payout leg, from the webhook's data |
beneficiary_iban | string (max 34) | Yes | Corrected beneficiary IBAN |
beneficiary_name | string (max 140) | Yes | Corrected beneficiary name, as held by the receiving bank |
The response has the same shape as confirm, carrying the corrected pair.
Response and Errors​
Both endpoints return 202 Accepted — the leg is released or re-verified
asynchronously:
| Field | Type | Description |
|---|---|---|
distribution_id | UUID | The resolved payout leg |
debit_uuid | string | Stable identifier of the leg |
beneficiary_iban | string | Beneficiary IBAN the resolution applies to |
beneficiary_name | string | Beneficiary name the resolution applies to |
outcome | string | What was scheduled — see below |
outcome | Meaning |
|---|---|
ACCEPTED | The retry was signalled; the leg dispatches (or re-verifies) shortly |
ALREADY_RETRYING | A retry for this leg is already running — nothing further to do |
MISSING_RETRY_DATA | The leg cannot be retried automatically — contact Paysera support |
| Error Code | HTTP Status | Endpoint | Meaning |
|---|---|---|---|
invalid_properties | 400 | Both | Missing distribution_id, or blank/oversized IBAN (max 34 chars) or name (max 140 chars) — per-field details in error_properties |
forbidden | 403 | Both | The payment belongs to another project |
payment_not_found | 404 | Both | Payment not found |
vop_confirmation_superseded | 409 | Confirm | The recipient changed since the webhook you are answering — review the latest pair before confirming |
vop_mismatch_not_confirmable | 422 | Confirm | The leg is not awaiting a name-check confirmation (already released, corrected, or settled) |
vop_beneficiary_not_correctable | 422 | Correct | The leg is not correctable (not held on a name mismatch, or already at the bank) |
vop_beneficiary_iban_invalid | 422 | Correct | The corrected IBAN is not a valid IBAN — its structure or checksum does not hold |
vop_beneficiary_iban_not_eea | 422 | Correct | The corrected IBAN is valid but belongs to a country outside the European Economic Area |
distribution_retry_unavailable | 503 | Both | The release could not be scheduled — retry later |
Correct the Account of a Held Payout​
Split-payment integrations paying Paysera (EVP) recipients can have a leg stop in
HELD because its destination account cannot be paid — closed, blocked, or otherwise
unusable by the time the payment was distributed. The money never left: the leg keeps its
amount and is retried every four hours. Point it at another Paysera account and it is
dispatched immediately. The same correction is available in the Paysera Checkout portal.
Find the legs to correct​
No webhook announces this hold: the only webhook that reports a held leg,
recipient.vop_checked, fires when a name check stopped it and never for anything else.
The payment list carries no legs either, so read the payment itself and look at its
distributions:
curl https://api.paysera.com/payment-executor/integration/v1/payments/{id} \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
// Response — 200 OK (abridged)
{
"id": "0c2d4e6a-1b3c-4d5e-9f8a-2b4c6d8e0f12",
"status": "partially_distributed",
"distributions": [
{
"id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"account_number": "EVP0010001000101",
"amount": 2000,
"currency": "EUR",
"status": "HELD",
"is_tip": false,
"held_reason": "account_unusable"
}
]
}
A payment with a held leg is never settled: once any leg has settled it reads
partially_distributed, so a poller that filters on settled never sees a correctable
leg. distributions is null, not an empty array, in two cases: the order had no splits,
and the distribution service could not be reached while the payment itself still answered.
Expect null on card-only orders, and on a split order treat it as "unknown", not as
"nothing held" — otherwise every hold during an outage is skipped in silence.
A leg with status HELD, held_reason account_unusable and is_tip false is one
this endpoint answers. Take two values from it: id — the leg to correct — and
account_number, which you send back as expected_account_number. A tip leg carries the
same reason but is refused with tip_not_correctable: the recipient chose that account
themselves, and only Paysera support may move it.
held_reason | What stopped the leg | What releases it |
|---|---|---|
account_unusable | The recipient's Paysera account cannot be paid | Correct the account — unless is_tip is true, then Paysera support |
name_mismatch | The recipient's name did not match their IBAN | Confirm or correct the recipient |
on_hold | Held for a reason you cannot answer through the API | Paysera support |
held_reason is set on HELD legs only, and is withheld while a name check is still being
resolved — such a leg reports IN_PROGRESS and returns to HELD if the check does not
complete. Treat a value you do not recognise the way you treat on_hold: more values may
appear as more actions do.
Poll the payments you are still waiting on. Every retry re-reads the leg, so a leg parked on an account that was only blocked for a while clears on its own once the block is lifted, with no correction needed; one parked on a closed account retries into the same closed account every four hours and never clears by itself. A check every few hours catches the second kind without redirecting the first by reflex.
The held_reason of the recipient.vop_checked webhook
carries the distribution service's own names (EXTERNAL_VOP_MISMATCH), not the values above.
Correct the account​
POST /payment-executor/integration/v1/payments/{id}/distributions/correct-account
{id} is the payment ID. Send the IBAN of the Paysera account to pay instead, together
with the account the leg is paying now:
curl -X POST https://api.paysera.com/payment-executor/integration/v1/payments/{id}/distributions/correct-account \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"beneficiary_iban": "LT403500010018497892",
"expected_account_number": "EVP0010001000101"
}'
| Parameter | Type | Required | Description |
|---|---|---|---|
distribution_id | UUID | Yes | The held leg, from the payment's distributions |
beneficiary_iban | string (max 34) | Yes | IBAN of the Paysera account the leg is moved to |
expected_account_number | string (max 34) | Yes | The account the leg pays now — account_number exactly as the payment read returned it |
expected_account_number is a lock against a stale read. If the leg moved after you read
it — support in the admin panel, a colleague in the portal, or an earlier call of your own
— the correction is refused rather than applied on top.
// Response — 202 Accepted
{
"distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"account_number": "EVP0010001000102",
"original_account_number": "EVP0010001000101",
"corrected_at": 1736433600,
"outcome": "ACCEPTED",
"saved_recipient_updated": true,
"also_moved_distribution_ids": ["019efc90-1c44-7a02-8b71-2ce9f0a71b55"],
"owner_changed": null
}
| Field | Type | Description |
|---|---|---|
distribution_id | UUID | The corrected leg — the same leg, not a new one |
account_number | string | The Paysera account it pays now: the IBAN you sent, resolved to its account number |
original_account_number | string | The account the leg was created with, kept across repeated corrections (nullable) |
corrected_at | integer | When the correction was recorded, Unix timestamp (seconds) |
outcome | string | ACCEPTED — the corrected leg was signalled to deliver now. Every 202 from this endpoint carries ACCEPTED; the field is shared with the other leg actions, which can also answer ALREADY_RETRYING or MISSING_RETRY_DATA. Should you ever meet either here, the account is already rewritten — re-read the payment instead of repeating the call |
saved_recipient_updated | boolean | Whether your registered beneficiary now points at the corrected account, so later orders skip the account that failed. true — it followed. null — the leg pays no registered beneficiary: your own share, or a recipient declared on the order. false — the payout is on its way, but your registered beneficiary still holds the account that failed, because the registry refused the change or could not be reached: update it yourself via split beneficiaries, or the next order to that recipient is held again (nullable) |
also_moved_distribution_ids | array | The recipient's other legs of this project that were held on the same account and moved with this correction. Empty means nothing else moved — see below for why that is not the same as nothing else held |
owner_changed | boolean | false — both accounts' owners were known and match. null — the owners were not compared, which is the usual case: the account that parked the leg is often one Paysera can no longer name. Never true here — a correction into another user's account is refused with account_owner_changed when the check can tell. null is no guarantee: the destination account is yours to verify (nullable) |
What moves with the leg. The correction cascades to the same recipient's other legs of
your project that are held on the same account, but only when the corrected leg pays a
registered beneficiary — never for
your own share or a recipient declared on the order — and it stops at 50 legs per call
without saying so. A repeat of the same correction moves nothing. So an empty
also_moved_distribution_ids says nothing about what is still held: re-read the affected
payments to be sure.
A correction never produces a second payment: the leg is redirected, not re-sent. A repeat
of the same request is not acknowledged as a success, though — once the leg pays the new
account, expected_account_number no longer matches, and the repeat is refused with
409 held_account_correction_superseded. After a timeout, re-read the payment before
retrying: if the leg already pays the account you sent, the correction landed.
The leg stays HELD until a delivery attempt goes through; it then becomes SETTLED, and
COMPLETED once no leg on the payment is still open. A poller waiting for the literal
SETTLED can miss it.
| Error Code | HTTP Status | Meaning |
|---|---|---|
invalid_properties | 400 | Missing distribution_id, or a blank or oversized account (max 34 chars) — per-field details in error_properties |
forbidden | 403 | The payment belongs to another project |
payment_not_found | 404 | Payment not found |
held_account_correction_superseded | 409 | The leg no longer pays expected_account_number — re-read the payment and send the account it pays now. A repeat of your own request after a timeout lands here too |
held_account_unusable | 422 | The IBAN you sent does not resolve to a usable Paysera account |
held_account_not_correctable | 422 | The leg is not waiting on an unusable account — it settled or failed, it is held for another reason, it belongs to another payment, or it pays a bank outside Paysera |
tip_not_correctable | 422 | The leg is a tip: the recipient chose that account themselves, so only Paysera support may move it |
account_owner_changed | 422 | The account you sent belongs to another Paysera user, or its owner could not be confirmed — the one refusal here that a later retry may pass |
distribution_retry_unavailable | 503 | The correction could not be scheduled. Two cases hide behind it: the distribution service was unreachable and nothing was recorded, or it recorded the new account but could not signal delivery — that leg goes out on the next four-hour retry. Re-read the payment before retrying; if the leg already pays the corrected account, check your registered beneficiary too, because that update did not run |
Re-send a Returned Payout​
Split-payment integrations paying external (non-Paysera) recipients
may receive a recipient.returned webhook:
a payout that had already settled was sent back by the recipient's bank, days later. The
money is back on your settlement account and that leg is final — but the return can be
answered with a new payout leg carrying corrected recipient details.
POST /payment-executor/integration/v1/payments/{id}/distributions/resend-returned
{id} is the payment ID; distribution_id is the returned leg, from the webhook's
data. The corrected pair is optional — omit both to send the same details again (useful
when the bank returned for a reason that has since been resolved on the recipient's side):
curl -X POST https://api.paysera.com/payment-executor/integration/v1/payments/{id}/distributions/resend-returned \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"beneficiary_iban": "DE89370400440532013000",
"beneficiary_name": "Partner Handels GmbH"
}'
| Parameter | Type | Required | Description |
|---|---|---|---|
distribution_id | UUID | Yes | The returned payout leg, from the webhook's data |
beneficiary_iban | string (max 34) | No | Corrected beneficiary IBAN. Omit to keep the one that was returned |
beneficiary_name | string (max 140) | No | Corrected beneficiary name. Omit to keep the one that was returned |
// Response — 202 Accepted
{
"distribution_id": "019efc90-1c44-7a02-8b71-2ce9f0a71b55",
"returned_distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"return_id": 8412394,
"amount": 4000,
"currency": "EUR",
"beneficiary_iban": "DE89370400440532013000",
"beneficiary_name": "Partner Handels GmbH",
"outcome": "CREATED",
"saved_recipient_updated": true
}
outcome | Meaning |
|---|---|
CREATED | A new leg was created and is being verified before dispatch |
ALREADY_RESENT | This return was already answered — the response names the existing leg |
ALREADY_RETRYING | The leg exists and its dispatch was already running — nothing further to do |
Three things worth knowing before you build against this:
- At most one re-send per return. The re-send is keyed on the return itself — that is what
return_ididentifies, the bank's own reference for the reversal. It is unrelated to a refund id, which is a UUID and describes money going back to the payer. Retrying the call is safe: it answersALREADY_RESENTwith the leg that already exists rather than paying twice. Two cases answer differently and neither pays twice either: if that earlier re-send failed without reaching the bank, the call revives that same leg and answersCREATED; and if the earlier re-send was itself returned, the call is refused withpayout_not_resendable— answer the newer return. - The new leg carries the amount that came back, which under a partial return is less
than the original leg carried. Read
amountfrom the response rather than assuming. - Correcting the pair also updates your saved recipient — best-effort. When you send a
corrected IBAN or name, the registered beneficiary
stored under the returned account is rewritten too, so orders created afterwards are addressed
to the corrected account. Orders that already exist keep the account they were created with.
Read
saved_recipient_updatedin the response:truemeans it followed;nullmeans there was nothing to carry over, which includes anALREADY_RESENTorALREADY_RETRYINGanswer, where this call changed no recipient even if you sent a corrected pair; andfalsemeans the payout is on its way but your saved recipient still holds the account that bounced, because the registry refused the change or could not be reached — update it yourself via split beneficiaries, or the next order will be addressed to it again. The re-send itself is never refused over this: the money has already moved by then.
Not every return is yours to answer. Only the reason codes that a corrected name and IBAN
actually fix — AC01, AC04, AC06 and RR03 — are self-service; a return with any other
reason, with no reason code at all, or one that has already been re-sent is refused with
422 payout_not_resendable and is handled by Paysera customer support.
| Error Code | HTTP Status | Meaning |
|---|---|---|
invalid_properties | 400 | Missing distribution_id, or oversized IBAN (max 34 chars) or name (max 140 chars) — per-field details in error_properties |
forbidden | 403 | The payment belongs to another project |
payment_not_found | 404 | Payment not found |
payout_not_resendable | 422 | The leg does not belong to this payment, is not a returned payout, its reason code is a support case, or the returned amount could not be established |
distribution_retry_unavailable | 503 | The re-send could not be created — retry later |
Related Documentation​
- Payment Orders - Read and manage orders
- Refunds - Initiate and track refunds
- Activity - Event log for orders and payments