Skip to main content

Errors

Every error the Evinor API returns is an RFC 9457 "problem detail" document. Branch on the stable type URI or the HTTP status code — never on the human-readable title or detail, which may change.

Anatomy​

Error responses use the application/problem+json content type and this shape:

{
"type": "https://docs.evinor.ai/problems/validation-failed",
"title": "Validation failed",
"status": 400,
"retryable": false,
"detail": "The request payload failed validation.",
"request_id": "8f0b2c1e-4a7d-4b93-9c2e-1d5a6f0b3c8e",
"errors": [{ "field": "filter.min_reports", "message": "Expected integer" }]
}
MemberAlways presentMeaning
typeyesStable, versioned problem-type URI under https://docs.evinor.ai/problems/. Branch on this.
titleyesShort, constant, human-readable summary for the type. Not request-specific.
statusyesThe HTTP status code, repeated in the body.
retryableyesWhether retrying this request can succeed. See retryable.
detailusuallyHuman-readable explanation of this specific occurrence.
request_idyesCorrelation id for this request. Include it when contacting support.

Some problem types add extension members:

  • validation-failed includes an errors array of { field, message } objects, one per failed field. When a well-formed request is refused because of its content, it carries a stable code string instead. Today that is POST /v1/reports; see Reporting events for the codes.
  • not-entitled includes a code naming which entitlement check failed (FORBIDDEN, GRANT_FORBIDDEN, or GRANT_NOT_USABLE).
  • missing-scope includes a missing_scopes array naming the scope(s) the key lacks.
  • insufficient-credits includes as_of, the timestamp of the balance snapshot the request was refused against.
  • search-execution-expired includes execution_id and expired_at, naming the search execution the cursor pointed at.

retryable​

Every error body carries a boolean retryable — the server's verdict on whether retrying this request can ever succeed. It is a verdict, not a policy: it does not tell you how long to wait, whether to reuse your Idempotency-Key, or how many times to try. Two types carry qualifiers you must honor:

  • idempotency-in-flight is retryable only under the right key. On billed search (POST /v1/events/search), retry with a new Idempotency-Key — a billed execution claims its key non-reclaimably, so the same key cannot succeed for 24 hours. On POST /v1/reports, retry with the same key, so a retry can never file a second report.
  • upstream-error warrants at most one retry. On a billed call made without an Idempotency-Key, do not retry automatically: the facade cannot prove whether the original was charged, and a repeat may charge again.

If retryable is absent (a response from an older server replica during a deployment), use the Retryable column in the catalog below, not the status code. A status-based rule would call the 409 idempotency-in-flight permanent.

request_id​

Every response — success or error — carries an x-request-id header, and every error body repeats it as request_id. Log it. When you report a problem to Evinor support, quoting the request_id lets us find the exact request in our logs.

You may also send your own x-request-id on a request (up to 64 characters of A-Za-z0-9_-) and Evinor will echo it back, so you can correlate a call with your own tracing. Any value outside that charset/length is ignored and a fresh id is generated instead.

Problem-type catalog​

Each type URI resolves to a page on this site describing the problem and how to handle it:

StatusProblem typeRetryableWhen it happens
400validation-failednoThe request payload, query, or path failed validation.
400malformed-bodynoThe request body is not valid JSON.
400query-too-complexnoThe upstream service refused the operation as too complex.
401invalid-api-keynoThe API key is missing, malformed, unknown, or revoked.
402insufficient-creditsnoA billed endpoint was called with an exhausted balance.
403missing-scopenoThe key lacks a scope the endpoint requires.
403not-entitlednoThe key owner is not entitled to the operation.
403quota-exceedednoA plan quota for the resource is reached.
404not-foundnoThe referenced resource doesn't exist or isn't yours.
409idempotency-in-flightyes, with qualifiersAnother request with the same Idempotency-Key is running.
409report-already-submittednoA report with this submission id already exists.
409state-conflictnoThe request conflicts with the resource's current state.
410search-execution-expirednoA pagination cursor's search execution has expired.
413payload-too-largenoThe request body exceeds the 1 MB JSON body limit.
422idempotency-key-conflictnoAn Idempotency-Key was reused for a different request.
422entity-list-inaccessiblenoA referenced entity list doesn't exist or isn't usable.
429rate-limitedyesThe per-key rate limit was exceeded.
502upstream-erroryes, with qualifiersAn upstream dependency returned an unexpected error.
503write-unavailableyesA write could not be accepted right now; safe to retry.
500internal-erroryesAn unexpected server error. Not your fault; retry or report.

Handling errors​

Prefer the body's retryable member (and the qualifiers above) over these status-code rules of thumb:

  • 4xx generally means something about the request must change — fix the input, the key, or the scope before retrying.
  • 429 and 503 are safe to retry after a backoff; both include a Retry-After header telling you how long to wait.
  • 502 and 500 indicate a server-side issue; retry with backoff and, if it persists, contact support with the request_id.