Skip to content

Refund a payment

POST/v1/refunds

Pays amount of the payment’s asset back from the master wallet to destination, as a payout. Requires the Idempotency-Key header; an API key needs refunds:write and payouts:write (neither is given to a key by default) and is restricted as for POST /payouts: only to the destinations on its payout allowlist, or, with an IP allowlist alone, to any valid address when called from those IP addresses. The refunds of a payment cannot exceed it. When a refund completes, its invoice becomes refunded, or keeps its status and gains the flag partially_refunded when only part went back. Test mode: the refund of a simulated payment is played, not paid: it is completed at once with simulated: true, emits refund.created and refund.completed, and creates no payout and no transaction.

Send your API key as a bearer token in the Authorization header. Secret API key: Authorization: Bearer ctp_live_sk_…. A publishable key (ctp_…_pk_…) is accepted by these operations only, which serve reference data: getRate, listAssets, listNetworks (each marked x-publishable-key: true). Every other operation refuses it with 403 publishable_key_not_allowed, whatever Origin the request carries: that header binds browsers, not other clients, and nothing on this API relies on it.

Name Type Description
Idempotency-Key required string Required: this operation moves money, and without the header it answers 400 idempotency_key_required. Use one key per payout or refund and send the same key again when you retry: the same key with the same request returns the payout or refund that exists and never pays twice; the same key with a different request is 422 idempotency_key_reused. For 24 hours the first response is replayed as it was (Idempotent-Replayed: true); while the first request is still running, 409 idempotency_request_in_progress with Retry-After. At most 255 characters. Pattern ^[!-~]+$.
Field Type Description
payment_id required string At most 100 characters.
amount required string Base units of the payment’s asset. A whole number written as a string of digits.
destination required string The payer’s address. At most 128 characters.
reason required string At most 200 characters.
Terminal window
curl -X POST "https://api.ctpeway.com/v1/refunds" \
-H "Authorization: Bearer $CTPEWAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: <a-unique-key>" \
-d '{
"payment_id": "<payment-id>",
"amount": "25000000",
"destination": "<payer-address>",
"reason": "Order canceled"
}'
Field Type Description
id string
status string One of pending, completed, failed.
payment_id string
invoice_id optional string
payout_id optional string
network string
asset string
asset_id string
contract optional string
decimals integer
amount string Base units of asset, the asset of the refunded payment. A whole number written as a string of digits.
destination string
reason string
tx_hash optional string
failure_code optional string
created_at string (date-time)
completed_at optional string (date-time)
failed_at optional string (date-time)
livemode boolean
simulated boolean true: the refund of a simulated payment (test mode). It was played, not paid: no payout, no transaction, and tx_hash is a sim_… placeholder.
failure_message optional string What failure_code means, in a sentence for a person. Absent for codes that need no explanation.
{
"id": "string",
"status": "pending",
"payment_id": "string",
"invoice_id": "string",
"payout_id": "string",
"network": "string",
"asset": "string",
"asset_id": "string",
"contract": "string",
"decimals": 0,
"amount": "1000000",
"destination": "string",
"reason": "string",
"tx_hash": "string",
"failure_code": "string",
"created_at": "2026-01-01T00:00:00.000Z",
"completed_at": "2026-01-01T00:00:00.000Z",
"failed_at": "2026-01-01T00:00:00.000Z",
"livemode": true,
"simulated": true,
"failure_message": "string"
}

The values are placeholders generated from the schema. They show the shape, not real data.

A failed request answers with the error format described under Errors.

Besides the codes any endpoint can return, this endpoint can answer with these code values: