Error

Status Codes & Error Handling

This section describes HTTP status codes, a standard error schema, and endpoint-specific conditions for the Conversion API.

Error Object (standard)

{
  "error": {
    "code": "QUOTE_EXPIRED",
    "message": "The quote has expired. Please request a new quote.",
    "details": {
      "quote_id": "1f581bfe-25de-42aa-9429-6802412850b3",
      "valid_until": "2025-08-21T07:56:57.981Z",
      "reason": "TTL_EXPIRED"
    },
    "request_id": "req_01hx9p0v2d0c6n3",
    "timestamp": "2025-08-21T07:57:00.001Z"
  }
}

Common HTTP Statuses

HTTPMeaningWhen it happensClient action
200OKSuccessful GET (e.g., Retrieve Hedge)Proceed
201CreatedSuccessful POST (e.g., Create Hedge)Store identifiers
202AcceptedRequest accepted for processing (async hedging)Poll status
400Bad RequestMissing/invalid fields, bad enum, malformed amountFix payload and retry
401UnauthorizedMissing/invalid token, signatureRefresh/attach auth
403ForbiddenPortfolio not permitted, scope missingAdjust scopes/permissions
404Not FoundPortfolio/quote/hedge not foundVerify IDs
409ConflictQuote expired, already hedged, duplicate actionRe-quote or use idempotency
422Unprocessable EntityAmount out of bounds, unsupported pair/networkCorrect business inputs
429Too Many RequestsRate limit exceededBackoff and retry
500Internal Server ErrorUnexpected server faultRetry with backoff
503Service UnavailableDependency/venue down, maintenanceRetry later with backoff

Error Codes (enumeration)

error.codeTypical HTTPDescription
VALIDATION_ERROR400/422Generic validation failure (schema/enum/format)
MISSING_FIELD400Required field absent (e.g., from_asset.asset)
INVALID_AMOUNT422Amount is non-numeric, negative, or exceeds limits
UNSUPPORTED_ASSET422Asset not supported by portfolio or venue
UNSUPPORTED_NETWORK422Network not supported for given asset
PORTFOLIO_NOT_FOUND404Portfolio ID invalid or inaccessible
QUOTE_NOT_FOUND404quote_id not found
QUOTE_EXPIRED409valid_until has passed
QUOTE_ALREADY_HEDGED409Hedge already created for this quote_id
HEDGE_NOT_FOUND404Hedge not found for given identifiers
INSUFFICIENT_LIQUIDITY422Unable to price/execute requested size
RISK_LIMIT_EXCEEDED403/422Portfolio or product risk limit hit
AUTHENTICATION_FAILED401Invalid credentials/signature
AUTHORIZATION_FAILED403Token lacks required scopes/role
RATE_LIMITED429Too many requests
VENUE_UNAVAILABLE503External market/custody dependency down
INTERNAL_ERROR500Unexpected server error

Create Quote — Status & Errors

Endpoint: POST /v1/portfolios/{portfolio_id}/conversions/quote

HTTPExample error.codeWhen
200Quote priced successfully
400MISSING_FIELD, VALIDATION_ERRORfrom_asset/to_asset shape invalid; both amounts supplied or none supplied
401AUTHENTICATION_FAILEDToken missing/invalid
403AUTHORIZATION_FAILED, RISK_LIMIT_EXCEEDEDScope missing; product/risk guardrail
404PORTFOLIO_NOT_FOUNDPortfolio invalid/inaccessible
422UNSUPPORTED_ASSET, UNSUPPORTED_NETWORK, INVALID_AMOUNT, INSUFFICIENT_LIQUIDITYBusiness constraints
429RATE_LIMITEDRate cap hit
500/503INTERNAL_ERROR, VENUE_UNAVAILABLETemporary fault

Sample 422 (unsupported pair)

{
  "error": {
    "code": "UNSUPPORTED_ASSET",
    "message": "Asset pair USD→XYZ is not supported for this portfolio.",
    "details": { "from_asset": "USD", "to_asset": "XYZ" },
    "request_id": "req_...",
    "timestamp": "2025-08-21T07:56:58Z"
  }
}

Create Hedge — Status & Errors

Endpoint: POST /v1/portfolios/{portfolio_id}/conversions/hedge

HTTPExample error.codeWhen
201Hedge created (synchronous)
202Hedge accepted (asynchronous execution)
400VALIDATION_ERRORPayload not valid JSON or schema
401AUTHENTICATION_FAILEDToken invalid/missing
403AUTHORIZATION_FAILED, RISK_LIMIT_EXCEEDEDScope/limit issues
404QUOTE_NOT_FOUNDquote_id unknown
409QUOTE_EXPIRED, QUOTE_ALREADY_HEDGEDTTL passed or duplicate hedge
422UNSUPPORTED_ASSET, UNSUPPORTED_NETWORK, INSUFFICIENT_LIQUIDITYExecution constraints
429RATE_LIMITEDRate cap hit
500/503INTERNAL_ERROR, VENUE_UNAVAILABLETemporary fault

Sample 409 (expired)

{
  "error": {
    "code": "QUOTE_EXPIRED",
    "message": "The quote has expired. Please request a new quote.",
    "details": { "quote_id": "1f581bfe-25de-42aa-9429-6802412850b3", "valid_until": "2025-08-21T07:56:57.981Z" },
    "request_id": "req_...",
    "timestamp": "2025-08-21T08:00:02Z"
  }
}

Retrieve Hedge — Status & Errors

Endpoint: GET /v1/portfolios/{portfolio_id}/conversions/hedge

HTTPExample error.codeWhen
200Hedge details returned
400VALIDATION_ERRORBad query param (e.g., malformed conversion_id)
401AUTHENTICATION_FAILEDToken invalid/missing
403AUTHORIZATION_FAILEDNo access to portfolio/hedge
404HEDGE_NOT_FOUNDNo hedge matches the provided identifiers
429RATE_LIMITEDRate cap hit
500/503INTERNAL_ERROR, VENUE_UNAVAILABLETemporary fault

Sample 404

{
  "error": {
    "code": "HEDGE_NOT_FOUND",
    "message": "No hedge found for the specified conversion_id.",
    "details": { "conversion_id": "conv-uuid" },
    "request_id": "req_...",
    "timestamp": "2025-08-21T08:06:01Z"
  }
}

Idempotency, Retries & Rate Limits

TopicGuidance
IdempotencyFor POSTs (/conversions/hedge), send Idempotency-Key (v4 UUID). Reusing the same key within 24h yields the original result or a 409 if the parameters differ.
Retries (client)Retry GET on 500/503/429 with exponential backoff (e.g., 200ms → 400ms → 800ms, jitter). For POST hedge, rely on idempotency rather than blind retries.
Retry-AfterOn 429/503, honor Retry-After (seconds).
Time boundsQuotes are valid until valid_until (ISO-8601). Re-price after expiry.
Request IDsLog request_id from error responses for support correlation.

Security & Permissions (quick reference)

RequirementNotes
AuthBearer token with portfolio-scoped permissions.
Scopesconversions:quote:create, conversions:hedge:create, conversions:hedge:read (example naming).
Clock skewKeep client clock in sync (NTP). Quote TTL is time-sensitive.