SylphxModels

Errors and retries

Every failure returns one JSON envelope with a stable code, a human summary, and a clear decision: retry, fix the request, or change the key. Retries of POST calls are deduplicated with an Idempotency-Key, so a network retry never creates a second response.

One envelope for every errorLink to this section

The official error object stays first: type, code, message, and the offending param. Commercial typing rides beside it, so a client can decide what to do without parsing prose.

401 · invalid_api_key
{
  "code": "invalid_api_key",
  "error": {
    "code": "invalid_api_key",
    "message": "Invalid API key.",
    "param": null,
    "type": "invalid_api_key"
  },
  "next_action": null,
  "retry_after_seconds": null,
  "retryable": false,
  "summary": "Invalid API key."
}
404 · retired route
{
  "code": "chat_completions_retired",
  "error": {
    "code": "chat_completions_retired",
    "message": "POST /v1/chat/completions is retired. Use POST /v1/responses.",
    "param": null,
    "type": "chat_completions_retired"
  },
  "summary": "POST /v1/chat/completions is retired. Use POST /v1/responses.",
  "retryable": false
}
  • error.code is the stable identifier. Branch on this, never on the message text.
  • error.message is for humans, and error.param names the field to fix when the failure is about your request.
  • summary is a short sentence safe to log or surface; retryable says whether an identical retry can succeed after the advertised delay.
  • next_action states what to change; retry_after_seconds is the delay to honor when retrying. Rate-limit denials also send the Retry-After header.
  • Responses carry an x-request-id header. Quote it in support requests — it identifies the exact call.

Status codesLink to this section

Status classes are stable. The code inside the envelope tells you what actually happened.

StatusMeaning
400The request is invalid, violates the public protocol, or exceeds the model’s context window.
401The bearer key is missing, malformed, or revoked.
404The endpoint does not exist, the model is not in the catalog, or the stored object is not visible to your key.
409An identical idempotent request is still in progress, or its outcome is unresolved.
413The request body exceeds the bounded input limit.
422The request cannot be honored under public rules with the meaning you asked for, or an Idempotency-Key was reused with a different body.
429Rate, concurrency, or spend limits refused the call, or the serving provider throttled after failover.
501A documented facade or hosted tool type is not implemented yet — an honest not-ready, not a silent downgrade.
503Required capacity, catalog, credentials, or the model route is temporarily unavailable.

Codes you will meetLink to this section

Each code names the next action. This is the set a first integration can hit; the contract carries the full wire.

codeStatusWhat to do
invalid_api_key401Check the key: it must start with sk-sx-, be unrevoked, and be sent as Authorization: Bearer ….
invalid_request400Fix the field named in error.param. The message states what the protocol refused.
model_not_found404Copy an id from the catalog; an id we do not sell is never substituted silently.
context_length_exceeded400Shorten the input, or compact the conversation and continue from the compacted window.
invalid_idempotency_key400 / 413Send a non-empty key of at most 256 bytes; a UUID is the intended shape.
idempotency_in_progress409Wait, then resend the identical body with the same key. The in-flight attempt is still running.
idempotency_key_reuse422Use a new Idempotency-Key for a new request body. A key identifies one request.
idempotency_outcome_unknown409The original execution is unresolved and will not be repeated. Keep the reference and reconcile with GET or an idempotency replay; do not resend with a new key.
rate_limit_exceeded429Reduce rate or in-flight requests, then retry after retry_after_seconds / Retry-After.
model_unavailable503Retry later or choose another catalog model. This is a route or capacity failure, never a request-shape problem.
file_not_found400Re-upload the file and use the new file id; ids expire with the seven-day retention window.
response_not_cancellable400Responses are not background jobs. Retrieve the response instead of cancelling it.
chat_completions_retired404Move the call to POST /responses — the message names the replacement path.
shared_provision_retired404Machine keys are minted per tenant with POST /v1/admin/tenants/{org}/keys — see platform keys.

Retries are safe by keyLink to this section

POST /responses is the call to protect, and one header does it. A UUID Idempotency-Key binds one key to one exact request body.

POST /responses with a durable key
curl https://api.models.sylphx.ai/v1/responses \
  -H "Authorization: Bearer $SYLPHX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8f8d57c8-8868-4d90-90cb-55e892cb80a5" \
  -d '{
    "model": "openai/gpt-5.5",
    "input": "Summarise this incident report in three bullets."
  }'

What happens on each retry

  • Identical retry after completion200 with the original response and the header idempotency-replayed: true. No new generation, no second charge.
  • Same key, different body — typed 422 idempotency_key_reuse. The key is spent; use a fresh one.
  • Same key while the first attempt runs 409 idempotency_in_progress. Wait for the first call, then resend the same bytes with the same key.
  • Replay window — a completed response can be replayed for seven days from completion. After that the key may identify a new request.
  • Unresolved outcomes — if an execution outcome is unknown, the API reports it (idempotency_outcome_unknown, or response_commit_outcome_unknown with the original response reference) instead of pretending the call failed and running it again.
CautionOnly retry when it is safe
Retry an identical body with the same key when retryable is true, or on a network failure before you read a response. Do not reuse a key for a changed body — and do not build retry loops that ignore retry_after_seconds.

Rate limitsLink to this section

Limits are per key, enforced before a request reaches a model. Keys minted in the console or by a platform default to the pro tier, which is not subject to the free-tier envelope.

  • Free-tier envelope — one envelope across every model on that key: 300 burst requests per minute, 180 sustained requests per minute, and 32 requests in flight.
  • Headers on the response x-ratelimit-limit, x-ratelimit-remaining, and x-ratelimit-reset (Unix seconds when the window resets).
  • On a denial429 rate_limit_exceeded, a Retry-After header, and retry_after_seconds / next_action in the envelope. Concurrency denials tell you to reduce in-flight requests; rate denials tell you to slow the request rate.
  • Provider throttling — if the serving provider throttles after failover, you receive the same typed 429 with the provider’s retry hint, not a misleading model error.

A retry helperLink to this section

The shape every production client needs: a stable key per logical request, retry only what the envelope marks retryable, and honor the advertised delay.

TypeScript · retry with a stable key
import { randomUUID } from "node:crypto";

async function createResponse(body: unknown, key = randomUUID()) {
  const attempt = () =>
    fetch(`https://api.models.sylphx.ai/v1/responses`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.SYLPHX_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": key,
      },
      body: JSON.stringify(body),
    });

  for (let i = 0; i < 5; i += 1) {
    const response = await attempt(); // a network failure retries the same key
    if (response.ok) return response.json();

    const payload = await response.json();
    if (payload.retryable !== true) {
      throw new Error(`${payload.code}: ${payload.summary}`);
    }

    const wait = payload.retry_after_seconds ?? 2 ** i;
    await new Promise((resolve) => setTimeout(resolve, wait * 1000));
  }
  throw new Error("retries exhausted");
}

If you need helpLink to this section

Support needs four facts to reproduce a call. All four are in the response you already have.

  • The x-request-id header (and the UTC time of the call).
  • The error.code and summary values.
  • The model id you sent and whether the call streamed.
  • The body you sent, with the key redacted.

Write to [email protected] or continue with the quickstart and the API contract.