Errors
One error envelope for every endpoint, with a stable code to branch on.
Every endpoint shares one error shape. The HTTP status says what class of problem it is; data.code is the stable value to branch on, and data.explanation is something you can show.
{
"code": "BAD_REQUEST",
"status": 400,
"message": "…",
"data": {
"code": "invalid_request",
"param": "amount.value",
"requestId": "…",
"explanation": { "key": "…", "vars": {}, "message": "…" }
}
}Quote requestId when you contact support.
Codes
data.code | HTTP | What happened | What to do |
|---|---|---|---|
invalid_request | 400 | The request didn't match the contract; param names the field. | Fix the request. |
unauthorized | 401 | No key, or the key is wrong, revoked or expired. | Check Authorization. |
forbidden | 403 | The key can't do this, for example a missing scope. | Use a key with the right scope. |
not_found | 404 | No such object in this environment. | Check the id and that you're using the right environment's key. |
idempotency_conflict | 409 | The idempotency key was used for a different request. | Make the key more specific. |
quote_expired | 409 | The quote passed its expiresAt, or was already used. | Quote again and ask the user again. |
quote_changed | 409 | Something the digest covers no longer holds. | Quote again and ask the user again. |
route_infeasible | 422 | No route can serve the request. | Show the explanation; suggest another account. |
connection_not_ready | 422 | The provider connection isn't ready, for example missing credentials. | Fix the connection in the dashboard. |
provider_unavailable | 503 | The provider is down or unreachable. | Retry later with the same idempotency key. |
provider_rejected | 502 | The provider refused the request. | Show the explanation. |
rate_limited | 429 | Too many requests. | Back off and retry. |
In the SDK
The SDK throws an error carrying the same status and data:
try {
await unirail.paymentIntents.create({ quote: quote.id, digest: quote.digest, returnUri }, { context: { idempotencyKey } });
} catch (error) {
const code = (error as { data?: { code?: string } }).data?.code;
if (code === "quote_expired" || code === "quote_changed") {
return requote();
}
throw error;
}