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
}
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) |
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 |
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