Card payout

Send money from the shop balance to a bank card.

Use the key of a payout shop; a payin shop key gets 403 method_not_enabled. Rules shared by every method — Authentication, Idempotency, Errors, Webhooks — apply here and are not repeated.

Flow

  1. Generate merchant_reference and save it with your payout record before the call.
  2. Create the payout: POST /v2/payouts/card. Save id from the response.
  3. Wait for the webhook payout.completed or payout.failed. Verify the signature and act on data.object — the full payout.
  4. Fallback: if no webhook has come for a minute, you can start polling the status with GET /v2/transactions/{transaction_id} until it is final. Polling instead of webhooks loads the API and gets 429 rate_limited (R20).

Endpoints

Step Endpoint Operation
Create POST /v2/payouts/card createCardPayout
Read one GET /v2/transactions/{transaction_id} — common for all transactions, see Read one transaction retrieveTransaction

Separate from a payout: the shop balance, GET /v2/balances (listBalances) — see Balance.

The create has its own section below: request, response, example, errors. Reading returns the Payout object.

POST /v2/payouts/card

Creates a payout and blocks its amount on the balance.

Request

Headers: Authorization: Bearer <payout shop key>, Content-Type: application/json.

Field Type Required Format and limits
merchant_reference string yes 1–100 characters. Unique per merchant across payins, refunds, payouts and pay sessions. The idempotency key of this call
amount object yes
amount.value string yes Major units, at most two decimals, at least 0.01: "300.00". A string, never a number
amount.currency string yes ISO 4217, the shop currency: EUR
card object yes
card.number string yes 12–19 digits, no spaces, passes the Luhn check
card.cardholder_name string yes Latin letters and spaces, as printed on the card
recipient object no The person who receives the money
recipient.phone string no +14155550123
recipient.first_name string no First and middle names. If first_name or last_name is omitted, both are taken from card.cardholder_name
recipient.last_name string no
recipient.email string no Email address
recipient.birth_date string no YYYY-MM-DD
recipient.nationality string no ISO 3166-1 alpha-2: US
recipient.address object no
recipient.address.country string no ISO 3166-1 alpha-2: US
recipient.address.state string for US, CA, AU recipients ISO 3166-2 subdivision code without the country prefix, 2–3 letters: CA
recipient.address.city string no Not digits only
recipient.address.postal_code string no
recipient.address.line1 string no Street and house number
recipient.address.line2 string no Apartment, suite, floor
sender object no The person on whose behalf you send the payout
sender.birth_date string no YYYY-MM-DD
sender.ip_address string no IPv4 or IPv6
purpose object no What the payout is for
purpose.details_url string no A page with the payment details on your side, http:// or https://, up to 255 characters
purpose.digital_asset_type string no The digital asset the payout is for: cbdc, stablecoin, native_token, nft, other — see Digital asset type
sub_merchant object no Your client the payout is made for, when you pay out on behalf of other businesses
sub_merchant.id string no Your id for the client, letters and digits, up to 15 characters. If omitted, your merchant account id is used
sub_merchant.country string no ISO 3166-1 alpha-2: PT. If omitted, your shop country is used
curl -X POST https://payments-api.mrcr.io/v2/payouts/card \
  -H "Authorization: Bearer $PAYOUT_SHOP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_reference": "payout-10001",
    "amount": { "value": "300.00", "currency": "EUR" },
    "card": { "number": "4111111111111111", "cardholder_name": "JOHN SMITH" },
    "recipient": {
      "first_name": "John", "last_name": "Smith", "phone": "+14155550123",
      "address": { "country": "US", "state": "CA", "city": "San Francisco", "postal_code": "94105", "line1": "1 Market Street" }
    }
  }'

Response 201 Created

The Payout object in data, in created. Any status is possible, including a final one when the call repeats an earlier one.

Header Meaning
Idempotent-Replayed: true The same merchant_reference and body were sent before. This is the existing payout in its current status; nothing was created again
{
  "data": {
    "id": "6b2f0e8a-3c7d-4e4b-9a9f-1d8c4e7b0ad6",
    "object": "payout",
    "merchant_reference": "payout-10001",
    "method_type": "payout_card",
    "status": "created",
    "status_details": null,
    "amount": { "value": "300.00", "currency": "EUR" },
    "card": { "brand": "visa", "last4": "1111" },
    "created_at": "2026-09-29T12:00:00Z",
    "updated_at": "2026-09-29T12:00:00Z"
  }
}

Errors

No payout is created on any of them.

HTTP code When Retry
400 validation_failed A field is missing or malformed — details[] names it. Also: recipient.address.state missing for US, CA, AU; the amount has more than two decimals or is not in the shop currency No. Fix the request
401 authentication_failed The key is missing, malformed, revoked, or sent to the wrong host No
403 method_not_enabled Card payouts are not enabled for the shop, or the key is of a payin shop No. Use the payout shop key, or ask Mercuryo to enable the method
409 merchant_reference_conflict The reference was used with a different body No. Resend the original body, or use a new reference for a new payout
429 rate_limited Too many requests Yes, after Retry-After seconds
500 internal_error Failure on our side Yes, the identical request
{
  "error": {
    "code": "validation_failed",
    "message": "amount.currency must be the shop currency",
    "details": [{ "field": "amount.currency", "message": "must be EUR" }]
  }
}

After a timeout or a dropped connection the payout may or may not exist. Resend the identical request (R4), never with a new reference.

The Payout object

Returned by the create and by GET /v2/transactions/{transaction_id}, and sent in webhooks as data.object. It has the common fields of every transaction, with these values and fields for a card payout:

Field Type Always set Meaning
id string yes UUID. Use it in GET /v2/transactions/{transaction_id}
object string yes payout
method_type string yes payout_card
status string yes created, processing, completed, failed — see Statuses
status_details.code string when failed See Decline reasons
rrn string no Retrieval reference number of the payout in the card network, when the provider returns it. Give it to the recipient if their bank cannot find the payment
card object no The card the payout went to
card.brand string no Card scheme: visa, mastercard
card.last4 string no Last four digits

Digital asset type

Card networks classify payouts made for digital assets. Send purpose.digital_asset_type when the payout is for one.

Value The payout is for
cbdc A central bank digital currency or a tokenized deposit
stablecoin A fiat-backed stablecoin
native_token A blockchain native token or coin
nft A non-fungible token
other Another or an unknown digital asset

If the value is omitted or the provider of the payout does not take it, the provider's default for the card brand is used, other if there is none.

Statuses

Status What it means The amount on your balance Webhook What you do Next
created Accepted and recorded. Processing has not started Blocked — Wait processing, failed
processing Checks run; the payout is at the provider or the recipient's bank Blocked — Wait. Do not create another payout for the same purpose completed, failed
completed Final. Credited to the recipient's card Written off payout.completed Mark it paid on your side —
failed Final. Not credited. status_details.code says why Returned to the balance payout.failed Act on the decline reason —
  • Only completed and failed are outcomes. A final status never changes.
  • Webhooks come for the final statuses: payout.completed and payout.failed. Wait for them; read the status only as the fallback (R20).
  • How long processing lasts depends on the recipient's bank. Do not resolve a payout by a timeout on your side: keep waiting for the webhook (R8).
  • A payout cannot be canceled once accepted.
  • An unknown status is not final — keep waiting (R13).

Decline reasons

status_details.code on a failed payout. Every decline is final: a new attempt is a new payout with a new merchant_reference. The blocked amount is already back on the balance.

Code When What you do
card_not_supported Card type, country or issuer is not accepted Ask the recipient for another card
invalid_recipient_details The recipient's bank rejected the card number or the recipient data Check the card number and recipient data with the recipient, then create a new payout
method_unavailable No route for this card right now Try again later with a new payout
insufficient_funds The shop balance did not cover the payout Top up the balance, then create a new payout
limit_exceeded A per-transaction, daily or monthly limit Wait for the limit to reset, or send a smaller amount
risk_rejected Rejected by antifraud Do not retry automatically. Contact support
provider_declined The provider declined Try again later. If declines repeat, ask for another card
internal_error Failure on our side; nothing was credited Try again with a new payout. Quote id to support if it repeats

An unknown code: handle it like provider_declined.

Webhooks

type When data.object
payout.completed The payout reaches completed The Payout object
payout.failed The payout reaches failed The Payout object, with status_details
{
  "id": "9e5c3b1d-6f0a-4b7e-8d2c-4a1f7b0e3d09",
  "type": "payout.failed",
  "shop_id": 1025,
  "created_at": "2026-09-29T12:00:09Z",
  "data": {
    "object": {
      "id": "6b2f0e8a-3c7d-4e4b-9a9f-1d8c4e7b0ad6",
      "object": "payout",
      "status": "failed",
      "status_details": { "code": "invalid_recipient_details" },
      "…": "…"
    }
  }
}

Headers, signature check and redelivery — Webhooks.

Balance

The shop balance payouts are paid from.

Moment What happens to the amount
POST /v2/payouts/card accepted Blocked: subtracted from available
The balance does not cover the amount The payout ends failed with insufficient_funds; nothing is blocked
The payout is completed Written off
The payout is failed Returned to available

GET /v2/balances

The balance payouts are paid from. Call it when you need the balance — for example, to show it or to plan payouts. A payout does not need it: the create checks the balance itself.

Request

Headers: Authorization: Bearer <payout shop key>.

curl https://payments-api.mrcr.io/v2/balances \
  -H "Authorization: Bearer $PAYOUT_SHOP_KEY"

Response 200 OK

Field Type Always set Meaning
data[] array yes One item: a shop has one currency
data[].currency string yes ISO 4217, the shop currency: EUR
data[].available object yes What you can pay out now. Amounts of payouts that are not final yet are already subtracted
data[].available.value string yes Major units, at most two decimals: "12500.00". A string, never a number
data[].available.currency string yes ISO 4217, the shop currency: EUR
{
  "data": [
    { "currency": "EUR", "available": { "value": "12500.00", "currency": "EUR" } }
  ]
}

Errors

401 authentication_failed, 429 rate_limited, 500 internal_error — see Errors.

In the sandbox

The cents of amount.value select the outcome.

Cents Example Outcome
any other 300.00 completed
.51 300.51 failed — provider_declined
.52 300.52 failed — risk_rejected
.53 300.53 failed — limit_exceeded
.54 300.54 failed — card_not_supported
.55 300.55 failed — method_unavailable
.56 300.56 failed — internal_error
.60 300.60 failed — invalid_recipient_details
.61 300.61 processing for 60 seconds, then completed

Sandbox keys and fixtures — Sandbox.

Next: Authentication