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
COMPLETEDstatus 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. AfterFAILEDorCANCELEDyou 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/refundsRequest parameters
| Field | Type | Required | Description |
|---|---|---|---|
orderId | string (UUID) | ✅ Yes | ID of the order to refund. The order must belong to you and have the COMPLETED status |
amount | number | ❌ No | Refund amount in the order currency. Only full refunds are supported for now: when passed, it must equal the order amount |
externalId | string | ❌ No | Refund ID in your system, unique among your refunds (up to 255 characters) |
reason | string | ❌ No | Refund reason (up to 1000 characters) |
callbackUrl | string | ❌ No | URL 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
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Refund ID. Use it to check the refund status |
orderId | string (UUID) | ID of the refunded order |
orderExternalId | string / null | externalId of the refunded order |
terminalId | string / null | ID of the terminal the order was paid through |
merchantId | string (UUID) | Your merchant ID |
status | string | Current refund status, see Refund statuses |
amount | string | Amount returned to the payer, in the order currency |
currency | string | Order currency |
debitAmount | string | Amount taken from your balance: held while the refund is MANUAL or PENDING, debited on COMPLETED, released on FAILED and CANCELED |
debitCurrency | string | Currency of the balance the refund is taken from: the balance the order was credited to |
debitBasis | string | ORDER_AMOUNT: the whole credited order amount is debited; MERCHANT_RECEIVED: only what you received (the amount minus the commission) |
externalId | string / null | Refund ID in your system |
reason | string / null | Refund reason |
callbackUrl | string / null | URL for webhook notifications about this refund (null: the order URL or the merchant settings are used) |
statusMessage | string / null | Service field, always null in the merchant API |
gatewayRefundId | string / null | Service field, always null in the merchant API |
metadata | object / null | Service field, always null in the merchant API |
createdAt | string | Creation time (ISO 8601) |
updatedAt | string | Last update time (ISO 8601) |
completedAt | string / null | Completion time (ISO 8601) |
Errors
| HTTP status | When |
|---|---|
400 | Invalid parameters, or amount differs from the order amount |
403 | Refunds are not enabled for the payment method of the order |
404 | The order is not found or belongs to another merchant |
409 | The 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
| Status | Final | Description |
|---|---|---|
MANUAL | No | The payment provider does not support refunds via API: the refund is processed by the platform manually |
PENDING | No | The refund is created at the payment provider and is being processed |
COMPLETED | Conditionally | The refund is completed: the amount is debited from your balance and the order moves to REFUND |
FAILED | Conditionally | The refund failed: the held amount is back on your balance |
CANCELED | Conditionally | The 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
externalIdand store the refundidfrom the response: a repeated request with the sameexternalIdis rejected with409, 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
REFUNDstatus: it arrives when the refund is completed. - Treat
MANUALas "in progress": such refunds are completed by the platform, the result comes in a webhook.