> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goshippo.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Shippo is a multi-carrier shipping API. For agent integrations that execute shipping operations (rates, labels, tracking, address validation, customs), connect the hosted Shippo MCP server at https://mcp.shippo.com (per-user OAuth; setup at /guides/mcp-server). To search and read this documentation from an agent, a docs search MCP is available at https://docs.goshippo.com/mcp. Shipping workflow knowledge (agent skills and a knowledge pack) is published at https://github.com/goshippo/ai. For REST integrations start at /guides/api-quickstart; test mode uses shippo_test_ API keys.

# Reporting API Errors and Troubleshooting

> How to read Reporting API errors and recover from them, from a rejected request to a run that fails partway through.

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](/guides/authentication).

## Error format

When Reporting API rejects a request, you get an [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) problem document with `Content-Type: application/problem+json`, except for authentication failures. Problem documents have four fields:

| Field    | Type    | Meaning                                                                                                                 |
| -------- | ------- | ----------------------------------------------------------------------------------------------------------------------- |
| `type`   | string  | An identifier URI ending in the stable error code. It is not a documentation link; match on the last path segment only. |
| `title`  | string  | Short human-readable summary.                                                                                           |
| `status` | integer | HTTP status code.                                                                                                       |
| `detail` | string  | Human-readable explanation.                                                                                             |

Match on the last segment of `type` (for example, `invalid-column`). Treat `title` and `detail` as display text. We may change their wording.

<Info>
  A missing or invalid token returns `401 application/json` with a `detail` field.
</Info>

## HTTP errors

| Scenario                                                                    | HTTP  | `type` segment          | Recovery                                                                                           |
| --------------------------------------------------------------------------- | ----- | ----------------------- | -------------------------------------------------------------------------------------------------- |
| Token missing, malformed, or not recognized                                 | `401` | —                       | Send `Authorization: ShippoToken <token>` on every call and handle the response by status.         |
| A column is not listed in the report type's `available_columns`             | `400` | `invalid-column`        | Use `available_columns` `name` values, or omit `columns` for the defaults.                         |
| Interval exceeds the 92-day cap, or a column is requested more than once    | `400` | `invalid-parameter`     | Shrink the interval or remove duplicate columns.                                                   |
| Unknown fields in body, or `interval.end` not greater than `interval.start` | `422` | `validation-error`      | Remove undocumented fields. Set `end > start`.                                                     |
| `report_type` key not in active catalog                                     | `404` | `report-type-not-found` | List `/v2/reporting/reports` for valid `{name}.v{version}` keys.                                   |
| Run id not found                                                            | `404` | `run-not-found`         | Use the id from a create or list response. An unknown run id returns `404`.                        |
| Interval outside the report type's data window                              | `404` | `data-unavailable`      | Check the report type's `data_availability` window, then request an interval that falls inside it. |
| Same `idempotency_key` reused with a different request body                 | `409` | `idempotency-conflict`  | Use a new key for a new request, or resend the exact original body to replay the first run.        |
| Service temporarily unavailable                                             | `503` | `service-unavailable`   | Retry with exponential backoff.                                                                    |

## 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.

<Warning>
  A run with `status: FAILED` is HTTP 200 with `status: FAILED`, not an HTTP error. Read `error.code` to handle it.
</Warning>

| `error.code`            | Meaning                   | Recovery                                                          |
| ----------------------- | ------------------------- | ----------------------------------------------------------------- |
| `invalid_run_request`   | Request was invalid.      | Fix the report type, interval, or columns, then create a new run. |
| `run_timeout`           | Exceeded processing time. | Narrow the interval or reduce columns, then create a new run.     |
| `service_unavailable`   | Temporarily unavailable.  | Wait, then create a new run.                                      |
| `run_processing_failed` | Data processing failed.   | Create a new run.                                                 |
| `run_failed`            | Run could not complete.   | Create a new run.                                                 |

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`:

```shell title="cURL" theme={null}
curl -X POST https://api.goshippo.com/v2/reporting/runs \
  -H "Authorization: ShippoToken <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "report_type": "invoice.v1",
    "interval": { "start": 1704067200, "end": 1706745600 },
    "columns": ["invoice_number", "invoice_total_amount"],
    "output_format": "csv_gzip"
  }'
```

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.

```shell title="cURL" theme={null}
curl -L -o report.csv.gz "https://<download-host>/<opaque-path>?<signature>"
```

The download request does not require a Shippo `Authorization` header.

## Next steps

* [Create and poll a report run](/reporting-api/create-and-poll) for the full walkthrough
* [Authentication](/guides/authentication) for API token setup
* API reference: [create a run](/api-reference/report-runs/create-report-run) · [get a run](/api-reference/report-runs/get-report-run)


## Related topics

- [Reporting API overview](/reporting-api/overview.md)
- [Reporting API Quickstart](/reporting-api/quickstart.md)
- [Date intervals](/reporting-api/intervals.md)
