Recover an expired payment to the customer
POST/payments/{id}/recoverReturn an expired direct payment to the customer's original deposit source. EVM-only, requires a signed request, and needs a single recorded source address.
Headers
| Header | Description | Required |
|---|---|---|
| Authorization | Bearer token with your secret API key | yes |
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | yes |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| deadline | integer | yes |
Example Request
Request
const response = await fetch('https://checkout-api.exodus-int.com/payments/<id>/recover', {
method: 'POST',
headers: {
Authorization: 'Bearer sk_live_xxxxxxxxxxxxxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"deadline": 0
}),
});Responses
| Status | Description |
|---|---|
| 200 | The recovered payment. |
| 400 | Validation error |
| 401 | Missing or invalid authentication |
| 404 | Resource not found |
Response body
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | yes | |
| object | enum: payment | yes | |
| status | enum: client_deposit_pending | client_deposit_detected | client_deposit_confirmed | client_deposit_expired | escrow_confirmed | settlement_pending | settled | failed | refunded | cancelled | yes | |
| payment_method | enum: direct | two_step | yes | |
| detected_token | string | null | yes | |
| detected_token_address | string | null | yes | |
| detected_chain | string | null | yes | |
| token_amount | string | null | yes | |
| token_amount_decimal | string | null | yes | |
| deposit_address | string | null | yes | |
| payer_addresses | string[] | yes | |
| on_chain_id | string | null | yes | |
| created_at | string · date-time | yes | |
| flagged | boolean | yes |
Example Response
{
"id": "tz4a98xxat96iws9zmbrgj3a",
"object": "payment",
"status": "settled",
"payment_method": "two_step",
"detected_token": "USDC",
"detected_token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"detected_chain": "eip155:1",
"token_amount": "50000000",
"token_amount_decimal": "50",
"deposit_address": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
"payer_addresses": [
"0x742d35Cc6634C0532925a3b844Bc9e7595f8fE21"
],
"on_chain_id": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
"created_at": "2024-01-15T12:30:00Z",
"flagged": false
}Error Response
Error (e.g. 400)
{
"error": {
"type": "validation_error",
"message": "amount must be a positive number",
"param": "amount"
}
}How Recovery Works
The customer’s address is read from the payment’s recorded payer_addresses — you never supply a destination. Based on the payment status, the server delegates internally:
escrow_confirmed— recovered as a refund to the source address.settled— recovered as a rescue of the detected token to the source address.
The response mirrors the underlying operation: a recovered escrow_confirmed payment returns the refund shape; a recovered settled payment returns the rescue shape.
Sign with signRefund() when the payment is escrow_confirmed, or signRescue() when it is
settled, binding the destination to payer_addresses[0]. Only the deadline is sent in the
body — the server derives the destination, token, and amount from the payment. Like every merchant
signature, it is EIP-712 typed data. See Signed Requests.
Recovery Errors
All return 400 with error.type: "invalid_request" unless noted:
| Message | Cause |
|---|---|
| ”Recovery is only supported on EVM chains” | The payment settled on a non-EVM chain. |
| ”Payment has nothing to recover” | No recoverable funds at the current status. |
| ”Recovery requires a single recorded source address” | Multiple payer_addresses recorded — the destination would be ambiguous. |
| ”Payment cannot be recovered in status: …” | Status is neither escrow_confirmed nor settled. |
| ”Token … is not configured on …” | The detected token is not configured for the chain. |
Recovery runs the same compliance screen as the underlying refund or rescue before moving funds.
If the screen blocks the destination or the screening provider is temporarily unavailable, the API
returns 422 with error.type: "cannot_process".
