Skip to main content
A request that is refused answers application/problem+json with a stable code, a title, a human detail, retryable and request_id (also the X-Request-Id header). x-should-retry mirrors retryable, and Retry-After gives the wait when there is one. A run that starts and then fails is not an HTTP error: the run object answers 200 with status: "failed" and an error with one of the run codes below.

Request errors

Run errors

Retry guidance

  1. Retry automatically only on network errors and timeouts (same Idempotency-Key) and on the codes below. retryable and x-should-retry on the answer always win over this table (for example RATE_LIMITED is not retryable once the day policy is exhausted). Every other code needs a change before the request is sent again.
  1. Backoff: capped exponential with full jitter, sleep = random(0, min(30 s, 1 s × 2^attempt)), at most 4 retries and about 2 minutes in total for a create.
  2. Retry-After is a floor: wait at least that long (add 0–20 % jitter). Magica never sends more than 60 s.
  3. Keys: send a UUIDv4 Idempotency-Key on every POST /v1/runs and store it with your task before sending. Reuse it for every retry of the same intent, including after a restart; a replay returns the original run with Idempotent-Replayed: true, never a second run or charge. Use a new key only to start a new run deliberately. A key is remembered for 3 days, then may start a new run.
  4. Unknown outcome of a create (timeout, connection reset): retry with the same key. Never switch to a new key until the first request is known not to have created a run.
  5. Failed runs: start at most one new run (new key), and only when error.retryable is true. PROVIDER_OUTCOME_UNKNOWN is not retryable: check your own results first, because the provider may have done the work.
  6. Polling: prefer webhooks. Otherwise use GET /v1/runs/{id}?wait_seconds=30, or poll by Retry-After on 202 and running responses; without it start at 1 s (search and scrape), 2 s (images), 5 s (video and audio), back off × 1.5 up to 30 s, and stop at a final status. Polling has its own rate-limit policy (poll), so it never consumes create quota.
  7. Webhooks: verify the signature over the raw body with your org’s signing secret (any Standard Webhooks library; during a secret rotation the header carries two signatures and either verifies); answer 2xx fast and do the work afterwards (a delivery that takes over 10 s is re-sent); ignore event types you do not handle (types are added over time); dedupe on webhook-id for at least 7 days (every re-send carries the same one); on truncated: true read the run with GET /v1/runs/{id} (the body left out output to stay under receiver limits); ignore a delivery whose run sequence is lower than one already applied; download outputs within 24 hours. A webhook is sent right after the run is charged, so it usually carries the final charge (sequence 4); when the provider’s bill comes later it carries cost.state: held (sequence 3) and the final charge is on GET /v1/runs/{id} (sequence 4). No separate billing event is sent.
  8. Runs cannot be canceled after submission. Poll the run or wait for its configured terminal webhook.