Skip to main content
Errors show up in two places: a request can be rejected with an HTTP error, or a queued run can fail before it finishes. Authentication works the same as the rest of the Shippo API. See the Authentication guide.

Error format

When Reporting API rejects a request, you get an RFC 7807 problem document with Content-Type: application/problem+json, except for authentication failures. Problem documents have four fields: Match on the last segment of type (for example, invalid-column). Treat title and detail as display text. We may change their wording.
A missing or invalid token returns 401 application/json with a detail field.

HTTP errors

Idempotency

idempotency_key is optional. It’s a string you supply, 1 to 128 characters.
  • No key, or a new unused key: you get 201 Created and a new run with status: QUEUED.
  • The same key with an identical request body: you get 200 OK and the run that key already created, a safe replay.
  • The same key with a different request body: you get 409 with the idempotency-conflict type.
Without a key, every create makes a new run. There is no keyless deduplication. Send a key when you want a retry to land on the same run.

Run failures

A run moves from QUEUED to PROCESSING, then to SUCCEEDED or FAILED. When a run fails in the background, you get HTTP 200 with status: FAILED and an error object, not an HTTP error code. A timeout is one kind of FAILED, not a separate state.
A run with status: FAILED is HTTP 200 with status: FAILED, not an HTTP error. Read error.code to handle it.
To retry any failed run, create a new run. Don’t re-GET the same id.

Example flow

1. Create a run POST /v2/reporting/runs returns 201 Created with status: QUEUED:
cURL
To make a retry safe, add an idempotency_key. Resending the same key with the same body returns 200 with the existing run; reusing it with a different body returns 409. 2. Poll Poll GET /v2/reporting/runs/{run_id} until status is terminal (SUCCEEDED or FAILED). If processing time is exceeded, the run has status: FAILED and error.code: run_timeout. 3. Download A SUCCEEDED run includes result.download. The signed URL expires at the result.download.expires timestamp (a UTC epoch second). If download.status is unavailable or the URL has expired, re-GET the run for a fresh one.
cURL
The download request does not require a Shippo Authorization header.

Next steps

Last modified on September 16, 2026