Payment Docs
Refunds

Refunds overview

Return the payment of a completed order to the payer

A refund returns the payment of a completed order to the payer. You create it with one request; the amount is taken from your merchant balance, and every status change after creation is reported with a signed webhook.

Before you start

Authorization

How to sign your requests with the API token.

Refunds are enabled per payment method. Ask your manager to enable them for the methods you use: for other orders the request is rejected with 403.

How it works

Create the refund

One POST /v1/refunds request with the id of a COMPLETED order. The debit amount is held on your balance right away; the response is the refund object. Store its id.

If the payment provider supports refunds via API, the refund is created at the provider and gets the PENDING status. Otherwise it gets the MANUAL status and is processed by the platform.

Track the result

We send a signed REFUND_UPDATE webhook on every status change after creation. Polling GET /v1/refunds/{refundId} is a fallback.

Get the order status

When the refund is COMPLETED, the held amount is debited and the order moves to REFUND: you also receive the order webhook (ORDER_UPDATE). If the refund ends as FAILED or CANCELED, the held amount returns to your balance.

Rules

  • Only an order with the COMPLETED status can be refunded.
  • Only full refunds are supported for now: the payer gets the whole order amount back.
  • An order can have one active (MANUAL, PENDING) or completed refund. After FAILED or CANCELED you can create a new one.
  • Your balance must cover the debit amount: otherwise the refund is not created (409).

How much is taken from your balance

The debit is taken from the balance the order was credited to (debitCurrency). Depending on the payment method settings, it is either the whole credited order amount or only what you received, that is, the amount minus the commission. The refund object shows both the amount (debitAmount) and the basis (debitBasis).

Create a refund

POST /v1/refunds

Request parameters

FieldTypeRequiredDescription
orderIdstring (UUID)✅ YesID of the order to refund. The order must belong to you and have the COMPLETED status
amountnumber❌ NoRefund amount in the order currency. Only full refunds are supported for now: when passed, it must equal the order amount
externalIdstring❌ NoRefund ID in your system, unique among your refunds (up to 255 characters)
reasonstring❌ NoRefund reason (up to 1000 characters)
callbackUrlstring❌ NoURL for webhook notifications about this refund. When omitted, the order callbackUrl and then the merchant settings are used

Request example

curl -X POST "https://api.platinout.com/v1/refunds" \
  -H "Content-Type: application/json" \
  -H "X-Api-Token: YOUR_API_TOKEN" \
  -d '{
    "orderId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5c",
    "externalId": "refund-123",
    "reason": "Customer request",
    "callbackUrl": "https://example.com/webhooks/refunds"
  }'

Response

A successful request returns the refund object with the initial status (MANUAL, PENDING or, if the provider rejected the refund at once, FAILED). The same object is returned by the status endpoints.

{
  "id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
  "orderId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5c",
  "orderExternalId": "order_1234",
  "terminalId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5d",
  "merchantId": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5e",
  "status": "PENDING",
  "amount": "1000.5",
  "currency": "RUB",
  "debitAmount": "1000.5",
  "debitCurrency": "RUB",
  "debitBasis": "ORDER_AMOUNT",
  "externalId": "refund-123",
  "reason": "Customer request",
  "callbackUrl": "https://example.com/webhooks/refunds",
  "statusMessage": null,
  "gatewayRefundId": null,
  "metadata": null,
  "createdAt": "2026-09-23T12:34:56.000Z",
  "updatedAt": "2026-09-23T12:34:56.000Z",
  "completedAt": null
}

Refund object fields

FieldTypeDescription
idstring (UUID)Refund ID. Use it to check the refund status
orderIdstring (UUID)ID of the refunded order
orderExternalIdstring / nullexternalId of the refunded order
terminalIdstring / nullID of the terminal the order was paid through
merchantIdstring (UUID)Your merchant ID
statusstringCurrent refund status, see Refund statuses
amountstringAmount returned to the payer, in the order currency
currencystringOrder currency
debitAmountstringAmount taken from your balance: held while the refund is MANUAL or PENDING, debited on COMPLETED, released on FAILED and CANCELED
debitCurrencystringCurrency of the balance the refund is taken from: the balance the order was credited to
debitBasisstringORDER_AMOUNT: the whole credited order amount is debited; MERCHANT_RECEIVED: only what you received (the amount minus the commission)
externalIdstring / nullRefund ID in your system
reasonstring / nullRefund reason
callbackUrlstring / nullURL for webhook notifications about this refund (null: the order URL or the merchant settings are used)
statusMessagestring / nullService field, always null in the merchant API
gatewayRefundIdstring / nullService field, always null in the merchant API
metadataobject / nullService field, always null in the merchant API
createdAtstringCreation time (ISO 8601)
updatedAtstringLast update time (ISO 8601)
completedAtstring / nullCompletion time (ISO 8601)

Errors

HTTP statusWhen
400Invalid parameters, or amount differs from the order amount
403Refunds are not enabled for the payment method of the order
404The order is not found or belongs to another merchant
409The order is not COMPLETED, already has an active or completed refund, the externalId is taken, or the balance does not cover the debit

Refund statuses

StatusFinalDescription
MANUALNoThe payment provider does not support refunds via API: the refund is processed by the platform manually
PENDINGNoThe refund is created at the payment provider and is being processed
COMPLETEDConditionallyThe refund is completed: the amount is debited from your balance and the order moves to REFUND
FAILEDConditionallyThe refund failed: the held amount is back on your balance
CANCELEDConditionallyThe refund is canceled: the held amount is back on your balance

Final statuses change only when the platform corrects a refund manually. Your balance always follows the status, and every change is reported with a REFUND_UPDATE webhook.

Check refund status

GET /v1/refunds/{refundId}
curl -X GET "https://api.platinout.com/v1/refunds/0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b" \
  -H "X-Api-Token: YOUR_API_TOKEN"

By your refund ID:

GET /v1/refunds/external/{externalId}

Use webhooks as the primary channel. Poll by ID only as a fallback, for example every 30 minutes for refunds that have not received a final status.

Recommendations

  • Pass externalId and store the refund id from the response: a repeated request with the same externalId is rejected with 409, which protects you from double refunds.
  • Keep enough funds on the balance: the debit amount is held at creation.
  • Handle the order webhook with the REFUND status: it arrives when the refund is completed.
  • Treat MANUAL as "in progress": such refunds are completed by the platform, the result comes in a webhook.

Next steps

On this page