>_ WDK / REFERENCE
ERROR
CODES.
Stable, machine-readable error codes for every WDK + USDT0 failure mode. Each code tells you exactly what happened and what to do about it.
Error Payload Format
All error responses use this JSON structure. The code field is machine-readable and stable across API versions. The message is human-readable and may change.
HTTP/1.1 410 Gone
Content-Type: application/json
{
"error": {
"code": "P402_QUOTE_EXPIRED",
"message": "The quote q_123 expired at 2026-04-16T12:01:00.000Z.",
"details": {
"quoteId": "q_123",
"expiredAt": "2026-04-16T12:01:00.000Z"
}
}
}HTTP Status Mapping
| HTTP Status | Meaning | Retry? |
|---|---|---|
| 400 Bad Request | Malformed payload. Your code is wrong. | No — fix the request first. |
| 401 Unauthorized | Signature or authentication failure. | No — re-sign with correct parameters. |
| 402 Payment Required | Insufficient balance. | No — top up the wallet first. |
| 409 Conflict | Replay detected — nonce already used. | No — generate a new nonce. |
| 410 Gone | Quote expired. | Yes — re-quote and resubmit. |
| 422 Unprocessable | Policy denied the route. | Yes — with a different route. |
| 503 Service Unavailable | No route available or receipt delayed. | Yes — with backoff. |
| 504 Gateway Timeout | Settlement not finalized in SLA. | Poll receipt endpoint; do not retry settle. |
Full Error Reference
P402_QUOTE_EXPIREDHTTP 410The quote TTL (60 seconds) elapsed before the settlement was submitted.
Re-request the quote (/api/v1/liquidity/quote) and prompt the user to sign within the TTL. Do not retry the settle call with the expired quoteId.
P402_ROUTE_UNAVAILABLEHTTP 503No viable route exists for the requested asset and constraints at this time.
Relax constraints (increase maxFeeBps or maxLatencyMs), or change the sourceAssets order. Retry with exponential backoff. If persistent, fall back to USDC.
P402_POLICY_BLOCKED_ROUTEHTTP 422The policy engine denied the selected route (e.g. route is on a blocked jurisdiction).
Select a different route from the quote options that is compliant with your account policy. Do not retry the same routeId.
P402_SIGNATURE_REJECTEDHTTP 401The EIP-712 signature failed verification. Common causes: wrong chainId, wrong contract address, wrong nonce, or malformed signature bytes.
Rebuild the authorization object from scratch. Verify chainId matches the token's deployment chain. Re-sign with the WDK adapter and resubmit.
P402_AUTH_INVALIDHTTP 400The authorization payload is structurally malformed (missing fields, wrong types, invalid address format).
Check all required fields: from, to, value, validAfter, validBefore, nonce. Ensure nonce is a 0x-prefixed 32-byte hex string.
P402_INSUFFICIENT_BALANCEHTTP 402The wallet does not hold enough of the source asset to cover amount + fees.
Check the wallet balance before submitting. Offer a fallback route with a cheaper asset (e.g. try USDC if USDT0 balance is insufficient).
P402_SETTLEMENT_TIMEOUTHTTP 504The on-chain transaction was submitted but not finalized within the SLA window (30 seconds on Base).
Poll GET /api/v1/receipts/{receipt_id} with exponential backoff. The transaction may still finalize. If receipt shows settled: true, the payment succeeded.
P402_RECEIPT_UNAVAILABLEHTTP 503Receipt generation is delayed — the transaction settled on-chain but the receipt record is not yet written.
Retry GET /api/v1/receipts/{receipt_id} with 2-second intervals for up to 60 seconds. Keep the txHash visible in your UI so users can verify on a block explorer.
P402_REPLAY_DETECTEDHTTP 409The nonce in the authorization has already been used in a prior settlement for this wallet.
Generate a new nonce (32 random bytes) and re-sign. Never reuse nonces. Use the receipt scheme instead if you need idempotent retries.
Recommended Retry Strategy
const RETRYABLE_CODES = new Set([
'P402_QUOTE_EXPIRED',
'P402_ROUTE_UNAVAILABLE',
'P402_POLICY_BLOCKED_ROUTE',
'P402_SETTLEMENT_TIMEOUT',
'P402_RECEIPT_UNAVAILABLE',
]);
async function settleWithRetry(payload: SettlePayload, maxAttempts = 3) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const response = await fetch('/api/v1/router/settle', {
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (response.ok) return response.json();
const error = await response.json();
const code = error.error?.code;
if (!RETRYABLE_CODES.has(code) || attempt === maxAttempts) {
throw new Error(`Settlement failed: ${code}`);
}
// For expired quotes: re-quote before retrying
if (code === 'P402_QUOTE_EXPIRED') {
const newQuote = await requestQuote(payload);
payload = { ...payload, quoteId: newQuote.quoteId, routeId: newQuote.routes[0].routeId };
}
// Exponential backoff: 1s, 2s, 4s
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt - 1)));
}
}