Cloak API
Errors
Flat objects with a stable error code. Branch on the code, never on the message. Messages are written for humans and will change.
Error shape
{
"success": false,
"error": "AMOUNT_BELOW_MIN",
"message": "Minimum for this asset is 2 USDC.",
"min": "2000000",
"max": "200000000000"
}Codes
| Code | HTTP | What to do |
|---|---|---|
| AMOUNT_BELOW_MIN | 400 | Carries both min and max, so you can render a real bound. |
| AMOUNT_ABOVE_MAX | 400 | Carries both min and max. |
| QUOTE_FAILED | 400 / 503 | Unsupported pair, or pricing temporarily unavailable. |
| ASSET_NOT_SUPPORTED | 400 | Unknown asset, not enabled for your account, or a pair that cannot be swapped. |
| INVALID_ADDRESS | 400 | Carries field, so you can mark the right input. |
| PARTNER_FEE_TOO_HIGH | 400 | Carries maxBps. Clamp and retry. |
| FEE_RECIPIENT_NOT_REGISTERED | 400 | Address is valid but is not registered to your account. Add it from the portal's Fees page or choose an eligible wallet. |
| FEE_RECIPIENT_COOLING_OFF | 400 | Registered too recently. Resolves on its own; wait rather than reconfigure. |
| QUOTE_EXPIRED | 409 | Request a new quote. |
| QUOTE_MISMATCH | 409 | The echoed quote failed authentication. Send it back unmodified. |
| ORDER_IN_PROGRESS | 409 | An identical order is being created. Retry shortly. |
| IDEMPOTENCY_CONFLICT | 409 | Key reused with different terms. Use a fresh key. |
| ORDER_NOT_REFRESHABLE | 409 | Order is past PENDING. Poll status instead. |
| ORDER_NOT_FOUND | 404 | Unknown, or belongs to another account. |
| UNAUTHORIZED | 401 | Missing, unknown, revoked, or disabled key, deliberately indistinguishable. |
| LIMIT_EXCEEDED | 429 | Carries retryAfter in seconds. |
| SERVICE_UNAVAILABLE | 503 | Carries retryAfter. Back off; do not hammer. |
Limit errors carry both bounds
AMOUNT_BELOW_MIN and AMOUNT_ABOVE_MAX both return min and max, so a single failed request always tells you the full usable range. You never need a second call to discover the other end.
Examples in
Handling codes
Handling codes
# Branch on .error, never on .message.
RES=$(curl -sS -X POST "https://api.veilo.network/cloak/v1/quote" -H "Authorization: Bearer $VEILO_KEY" \
-H "Content-Type: application/json" -d "$BODY")
case "$(echo "$RES" | jq -r '.error // "OK"')" in
OK) echo "$RES" | jq -r '.quote.amountOut' ;;
AMOUNT_BELOW_MIN) echo "minimum is $(echo "$RES" | jq -r .min)" ;;
PARTNER_FEE_TOO_HIGH) echo "cap is $(echo "$RES" | jq -r .maxBps) bps" ;;
LIMIT_EXCEEDED) sleep "$(echo "$RES" | jq -r .retryAfter)" ;;
*) echo "$RES" | jq -r '.error + ": " + .message' ;;
esacWhich are worth retrying
| Class | Retry? | Codes |
|---|---|---|
| Transient | Yes, after retryAfter | LIMIT_EXCEEDED, SERVICE_UNAVAILABLE, ORDER_IN_PROGRESS |
| Stale state | Re-quote, then retry | QUOTE_EXPIRED |
| Your bug | No. Fix the call | IDEMPOTENCY_CONFLICT, QUOTE_MISMATCH, INVALID_ADDRESS |
| Configuration | No. Update the portal or contact support | FEE_RECIPIENT_NOT_REGISTERED, UNAUTHORIZED, ASSET_NOT_SUPPORTED |
| Waiting | Yes, but slowly | FEE_RECIPIENT_COOLING_OFF |
Retrying a non-transient error simply reproduces it. For LIMIT_EXCEEDED in particular, retrying sooner than retryAfter extends the window. See Limits.
