Refund lifecycle
A merchant administrator can select Request refund on an eligible order in the Orders table, then use Manage refunds to track or cancel the request and issue a new customer link. A backend with refunds_write can use the API. The customer opens a one-time link and confirms the destination on the original asset and network. Operations then approves or rejects the request. Approval reserves the required funds; it does not send them.
- Request an order-value refund in USD cents, a surplus return, or a return of unapplied partial funds in token base units. Cancel an active partially paid checkout before requesting an unapplied-funds return.
- Share the confirmation link only with the customer. The link expires after seven days. Copying a new link invalidates the previous one; an expired request can be reissued if refundable capacity remains. Keep links out of logs and analytics.
- After customer confirmation, Operations checks the order and available funds. A transfer is executed externally, recorded, and independently checked against its treasury source, customer destination, original token, exact amount, and chain finality.
- Read the refund status until it reaches
completed,rejected,failedorcancelled. A transfer recorded asprocessingmay still be pending or ambiguous; do not treat it as completed. You can cancel before Operations approval; cancellation restores the available refund amount without changing the paid order status. Contact Operations to stop an approved refund.
The customer confirmation link is a bearer capability. The current order contract does not identify the payer, so the link alone cannot prove that the person opening it paid the order. The merchant must deliver it to the intended customer; Operations must review this evidence before approval.
The order’s frozen refund policy controls fee reversal. With non_refundable, the customer fee is retained and order-value refunds are limited to the original subtotal. With proportional, the customer and merchant fees reverse proportionally. A surplus return is separate from an order-value refund.
Checkout wallet
Workspace administrators can view the separate, read-only USD Checkout wallet. It shows pending, available and held balances in integer cents. order.paid means payment was verified; the merchant’s saved net amount first becomes pending and becomes available only after Vault verifies a sufficient treasury sweep for that order. An approved refund moves its merchant portion from available to held. A verified completed refund consumes the hold; a rejected or proven failed transfer releases it.
Surplus and unapplied partial funds stay in a separate system-held liability in token base units. They never increase the Checkout wallet. The existing ecommerce wallet is separate. Checkout wallet withdrawals are not part of this API.
Request a refund
/v2/orders/{orderId}/refundsRequest an order-value refund or a return of surplus or unapplied partial funds. The customer must confirm the return address before Operations can approve it.
Required scope: refunds_write
Authorization
API key · Send the complete secret in x-api-key.
Path parameters
orderIdstringrequired- The identifier returned by the creation request. Must belong to your merchant.
Header parameters
x-api-keystringrequired- Your server-side merchant secret key.
Request body
purposestringrequired- order_value, surplus, or unapplied_partial.
refundAmountMinorintegeroptional- Required for order_value only. Positive USD cents, capped by the order’s frozen fee policy and remaining refundable value.
amountBaseUnitsstringoptional- Required for surplus or unapplied_partial only. Positive integer amount in the original token’s base units.
Behavior
- Idempotency-Key is required. Retry with the same key and body to retrieve the same refund; a replay does not reveal the original confirmation link.
- A partially paid checkout must be cancelled before unapplied funds can be returned. Cancellation does not itself return funds.
- The confirmationUrl is a bearer link shown once. Give it only to the customer; renew it with the confirmation-link endpoint if needed. The link expires after seven days.
- A request does not execute a transfer or reserve wallet funds. Operations approval reserves funds after a verified treasury sweep.
Responses
201 Created · illustrative refund request. See errors and retry guidance.
// Node.js 22+. Run on your server, never in the browser.
const apiKey = process.env.TYGA_SECRET_KEY;
if (!apiKey) throw new Error('Set TYGA_SECRET_KEY');
const resourceId = process.env.ORDER_ID;
if (!resourceId) throw new Error('Set ORDER_ID');
const apiBase = process.env.TYGA_API_BASE;
if (!apiBase) throw new Error('Set TYGA_API_BASE from the API keys page');
const response = await fetch(`${apiBase.replace(/\/+$/, "")}/v2/orders/${encodeURIComponent(resourceId)}/refunds`, {
method: 'POST',
headers: {
'x-api-key': apiKey,
'Content-Type': 'application/json',
'Idempotency-Key': 'refund-1042-request-001',
},
body: JSON.stringify({
"purpose": "order_value",
"refundAmountMinor": 5000
}),
});
if (!response.ok) throw new Error(`Request failed: HTTP ${response.status}`);
const result = response.status === 204 ? null : await response.json();
// Use result in your server flow. Do not log checkout URLs or credentials.{
"refundId": "33333333-3333-4333-8333-333333333333",
"orderId": "11111111-1111-4111-8111-111111111111",
"purpose": "order_value",
"status": "awaiting_customer_confirmation",
"refundAmountMinor": 5000,
"amountBaseUnits": "50000000",
"confirmationUrl": "https://checkout.example/#refund=<one-time-token>&environment=<api-environment>"
}List order refunds
/v2/orders/{orderId}/refundsRead refund requests and the order’s committed refund totals.
Required scope: refunds_read
Authorization
API key · Send the complete secret in x-api-key.
Path parameters
orderIdstringrequired- The identifier returned by the creation request. Must belong to your merchant.
Header parameters
x-api-keystringrequired- Your server-side merchant secret key.
Behavior
- This is a separate refund summary. It does not change the order’s payment history or imply that a requested refund completed.
- Unknown or inaccessible orders return 404. Confirmation links are never returned by this read.
Responses
200 OK · illustrative refund summary. See errors and retry guidance.
// Node.js 22+. Run on your server, never in the browser.
const apiKey = process.env.TYGA_SECRET_KEY;
if (!apiKey) throw new Error('Set TYGA_SECRET_KEY');
const resourceId = process.env.ORDER_ID;
if (!resourceId) throw new Error('Set ORDER_ID');
const apiBase = process.env.TYGA_API_BASE;
if (!apiBase) throw new Error('Set TYGA_API_BASE from the API keys page');
const response = await fetch(`${apiBase.replace(/\/+$/, "")}/v2/orders/${encodeURIComponent(resourceId)}/refunds`, {
method: 'GET',
headers: {
'x-api-key': apiKey,
},
});
if (!response.ok) throw new Error(`Request failed: HTTP ${response.status}`);
const result = response.status === 204 ? null : await response.json();
// Use result in your server flow. Do not log checkout URLs or credentials.{
"orderId": "11111111-1111-4111-8111-111111111111",
"summary": {
"orderValueMinor": 5000,
"refundableOrderValueMinor": 10000,
"refundPolicy": "non_refundable",
"surplusBaseUnits": "0",
"partialBaseUnits": "0"
},
"items": [
{
"refundId": "33333333-3333-4333-8333-333333333333",
"orderId": "11111111-1111-4111-8111-111111111111",
"purpose": "order_value",
"status": "requested",
"refundAmountMinor": 5000,
"amountBaseUnits": "50000000"
}
]
}Retrieve a refund
/v2/refunds/{refundId}Read the current status of one refund belonging to your merchant account.
Required scope: refunds_read
Authorization
API key · Send the complete secret in x-api-key.
Path parameters
refundIdstringrequired- The identifier returned by the creation request. Must belong to your merchant.
Header parameters
x-api-keystringrequired- Your server-side merchant secret key.
Behavior
- A completed refund has independently verified on-chain finality. A processing refund has a recorded external transfer but is not yet final.
Responses
200 OK · illustrative refund. See errors and retry guidance.
// Node.js 22+. Run on your server, never in the browser.
const apiKey = process.env.TYGA_SECRET_KEY;
if (!apiKey) throw new Error('Set TYGA_SECRET_KEY');
const resourceId = process.env.REFUND_ID;
if (!resourceId) throw new Error('Set REFUND_ID');
const apiBase = process.env.TYGA_API_BASE;
if (!apiBase) throw new Error('Set TYGA_API_BASE from the API keys page');
const response = await fetch(`${apiBase.replace(/\/+$/, "")}/v2/refunds/${encodeURIComponent(resourceId)}`, {
method: 'GET',
headers: {
'x-api-key': apiKey,
},
});
if (!response.ok) throw new Error(`Request failed: HTTP ${response.status}`);
const result = response.status === 204 ? null : await response.json();
// Use result in your server flow. Do not log checkout URLs or credentials.{
"refundId": "33333333-3333-4333-8333-333333333333",
"orderId": "11111111-1111-4111-8111-111111111111",
"purpose": "order_value",
"status": "processing",
"refundAmountMinor": 5000,
"amountBaseUnits": "50000000",
"assetId": "USDC",
"networkId": "ethereum-sepolia"
}Renew a customer confirmation link
/v2/refunds/{refundId}/confirmation-linkInvalidate the old link and issue a new one while the refund still awaits customer confirmation.
Required scope: refunds_write
Authorization
API key · Send the complete secret in x-api-key.
Path parameters
refundIdstringrequired- The identifier returned by the creation request. Must belong to your merchant.
Header parameters
x-api-keystringrequired- Your server-side merchant secret key.
Behavior
- Send an empty JSON object. The replacement confirmationUrl is shown once and invalidates the previous link.
- An expired request can be reissued if its original order still has enough refundable capacity. A confirmed, cancelled, rejected or completed refund cannot receive a new link.
Responses
200 OK · illustrative renewed link. See errors and retry guidance.
// Node.js 22+. Run on your server, never in the browser.
const apiKey = process.env.TYGA_SECRET_KEY;
if (!apiKey) throw new Error('Set TYGA_SECRET_KEY');
const resourceId = process.env.REFUND_ID;
if (!resourceId) throw new Error('Set REFUND_ID');
const apiBase = process.env.TYGA_API_BASE;
if (!apiBase) throw new Error('Set TYGA_API_BASE from the API keys page');
const response = await fetch(`${apiBase.replace(/\/+$/, "")}/v2/refunds/${encodeURIComponent(resourceId)}/confirmation-link`, {
method: 'POST',
headers: {
'x-api-key': apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
if (!response.ok) throw new Error(`Request failed: HTTP ${response.status}`);
const result = response.status === 204 ? null : await response.json();
// Use result in your server flow. Do not log checkout URLs or credentials.{
"refundId": "33333333-3333-4333-8333-333333333333",
"status": "awaiting_customer_confirmation",
"confirmationUrl": "https://checkout.example/#refund=<new-one-time-token>&environment=<api-environment>"
}Cancel a refund request
/v2/refunds/{refundId}/cancelCancel a refund before Operations approves it. The order payment status is unchanged.
Required scope: refunds_write
Authorization
API key · Send the complete secret in x-api-key.
Path parameters
refundIdstringrequired- The identifier returned by the creation request. Must belong to your merchant.
Header parameters
x-api-keystringrequired- Your server-side merchant secret key.
Behavior
- Send an empty JSON object. Cancellation invalidates any customer link and releases an active refund commitment. An expired link can also be cancelled; its commitment was already released at expiry.
- An approved or processing refund cannot be cancelled by the merchant because a transfer may already have occurred. Contact Operations if the refund must be stopped; they can reject an approved refund after confirming no transfer was sent.
Responses
200 OK · illustrative cancelled refund. See errors and retry guidance.
// Node.js 22+. Run on your server, never in the browser.
const apiKey = process.env.TYGA_SECRET_KEY;
if (!apiKey) throw new Error('Set TYGA_SECRET_KEY');
const resourceId = process.env.REFUND_ID;
if (!resourceId) throw new Error('Set REFUND_ID');
const apiBase = process.env.TYGA_API_BASE;
if (!apiBase) throw new Error('Set TYGA_API_BASE from the API keys page');
const response = await fetch(`${apiBase.replace(/\/+$/, "")}/v2/refunds/${encodeURIComponent(resourceId)}/cancel`, {
method: 'POST',
headers: {
'x-api-key': apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
if (!response.ok) throw new Error(`Request failed: HTTP ${response.status}`);
const result = response.status === 204 ? null : await response.json();
// Use result in your server flow. Do not log checkout URLs or credentials.{
"refundId": "33333333-3333-4333-8333-333333333333",
"orderId": "11111111-1111-4111-8111-111111111111",
"purpose": "order_value",
"status": "cancelled",
"refundAmountMinor": 5000,
"amountBaseUnits": "50000000"
}