# Errors

All Platform API endpoints use a consistent error format. When a request fails, the response body contains an `error` object with a human-readable message and a machine-readable code.

## Error Format

```json
{
  "error": {
    "message": "Partner ACME has no outbound flow. Flows are built in the Tediware app, not over the API: open the partner, then click Build Outbound Flow on its Flows card.",
    "code": "configuration_error",
    "reason": "flow_inactive"
  }
}
```

- `message`: a description of what went wrong, intended for developers. May include specific identifiers to help with debugging.
- `code`: a short, stable string naming the class of failure. Branch on this.
- `reason`: a stable string naming the specific condition within that class, on the errors that have one. Branch on this when the class is not specific enough to act on. Omitted when there is nothing more to say than the code already says.

The split exists because the useful question is usually "what do I have to do about this", which the code answers, while "which check failed" is a separate question that would otherwise force the code list to grow a value per condition.

## Can I Just Retry?

The status tells you whether resending the identical request could ever succeed:

- **400**: no. The request is malformed. Fix the payload.
- **404**: no. An identifier does not resolve (`not_found`), or there is no endpoint at that path (`no_route`). Fix the identifier, create the thing it names, or fix the URL.
- **413**: no. The document is over the size limit. Send it as smaller interchanges, or ask us to raise the limit for your account.
- **422 with `code: configuration_error`**: yes, once someone finishes the setup in Tediware. The request was valid; configuration is incomplete. This is the only case where waiting and retrying is the right behavior.
- **422 with any other code**: no. The resend codes and the format codes describe a state that resending cannot change.
- **429**: yes, after the `Retry-After` interval.

Branch on `code`, not on the status alone: `422` covers both "finish the setup and this works" and "this can never work", and only the code separates them.

## HTTP Status Codes

```
| Status | Meaning                                                                 |
|--------|-------------------------------------------------------------------------|
| 200    | Success. The request was processed and the response contains the result. |
| 202    | Accepted. The work was started in the background and is not finished yet. |
| 400    | Bad request. A required parameter is missing or a value is invalid.      |
| 401    | Unauthorized. The API key is missing or invalid.                         |
| 403    | Forbidden. The key is valid but not permitted on this endpoint.          |
| 404    | Not found. The requested resource does not exist.                        |
| 413    | Content too large. The submitted document is over the size limit.        |
| 422    | Unprocessable. The request is valid but cannot be completed due to a configuration issue. |
| 429    | Too many requests. You have exceeded the rate limit; retry after the delay in the Retry-After header. |
```

## Error Codes

```
| Code                | Status | Description                                                          |
|---------------------|--------|----------------------------------------------------------------------|
| unauthorized        | 401    | The API key is missing or does not match any key in the system.       |
| missing_parameter   | 400    | A required field (such as contents, or the logs trace) was not provided. |
| invalid_parameter   | 400    | A supplied value could not be used: an unrecognized filter value, an unparseable timestamp or cursor, or contents that are not a string. |
| invalid_filename    | 400    | The filename contains disallowed characters or exceeds 128 characters. |
| invalid_envelope    | 400    | The envelope configuration is incomplete or contains invalid values.   |
| forbidden           | 403    | The key is valid but not allowed here: a sandbox key on an endpoint that requires a standard key, a read-only key on a request that would change something, or an account that has been disabled. |
| not_found           | 404    | An identifier does not resolve: a partner key, transaction set code, trace GUID, result, or artifact. |
| no_route            | 404    | There is no endpoint at that path. Check the base URL and the path, not the identifier. |
| content_too_large   | 413    | The submitted EDI is over the 1 MB processing limit.                  |
| invalid_edi         | 422    | The submitted contents are not a complete X12 interchange: no readable ISA header, or missing or implausible trailers. |
| configuration_error | 422    | Setup in Tediware is incomplete or ambiguous. The request was valid.   |
| rate_limited        | 429    | Too many requests in the rate-limit window. Retry after the Retry-After interval. |
```

`no_route` is worth handling separately from `not_found`. Every unrouted path under `/platform` answers in this error shape rather than with an HTML page, so a wrong base URL (a stray `/api` on the end, say) tells you the endpoint is missing instead of reporting that an existing record does not exist. Treat it as a configuration bug on your side, never as a missing record.

Resending an outbound transaction returns its own `422` codes (`not_outbound`, `document_not_generated`, `content_unavailable`, `delivery_node_missing`, `not_flow_delivered`, `delivery_target_missing`), each naming why that transaction cannot be resent. Redelivering an inbound transaction has its own set (`not_inbound`, `content_unavailable`, `delivery_node_missing`, `delivery_target_missing`, `delivery_not_reached`, `poll_delivery`). Both are described on the [EDI Transactions](/resources/api-docs/edi-transactions) page.

### Reason Codes

`configuration_error` and `not_found` carry a `reason` naming which check failed. These are the reasons the submission endpoints can return:

```
| Reason                    | Code                | What is missing                                          |
|---------------------------|---------------------|----------------------------------------------------------|
| partner                   | not_found           | No partner in your organization has this key.             |
| transaction_set           | not_found           | The partner has no transaction setting for this code and direction. |
| transaction_set_not_ready | configuration_error | The setting exists but has no implementation or mapping.  |
| flow_inactive             | configuration_error | The partner has no flow for this direction, or has one whose status is pending. The message says which. Flows are built and activated in the app; nothing on the API creates or activates one. |
| missing_endpoint_node     | configuration_error | The flow has no endpoint node to submit to.               |
| ambiguous_endpoint_node   | configuration_error | The flow has more than one matching node.                 |
| missing_external_envelope | configuration_error | The partner has no external envelope assigned.            |
| missing_internal_envelope | configuration_error | The partner has no internal envelope assigned.            |
```

## Rate Limits

Platform API requests are rate-limited per API key:

- **Read endpoints** (fetching results, artifacts, logs, transactions, and the feed): 240 requests per minute.
- **Write endpoints** (submitting EDI or JSON): 240 requests per minute.
- **Resending an outbound transaction, or redelivering an inbound one**: 10 requests per minute combined, on top of the broad write limit. It is lower because every resend puts another copy of a document on a partner's wire, and every redelivery reaches your own endpoint again.

When you exceed a limit, the API responds with `429` and a `Retry-After` header giving the number of seconds to wait:

```json
{
  "error": {
    "message": "Rate limit exceeded. Please try again later.",
    "code": "rate_limited"
  }
}
```

Wait for the `Retry-After` interval before retrying, and use exponential backoff if you keep hitting the limit.

If your integration needs higher limits, [contact us](https://tediware.com/contact) and we'll be happy to help.

## Handling Errors

A general approach to error handling:

- **401**: check that your API key is correct and has not been revoked
- **400**: review the request body for missing or malformed fields. The `message` will indicate which field is problematic.
- **403** means the key is valid but cannot be used here. Confirm you are using a standard key rather than a sandbox or read-only key; retrying or reissuing the key will not help.
- **404**: verify that the partner key or transaction set code is correct, or create the thing it names. Retrying the same request will not help.
- **422**: setup in Tediware is incomplete. The `reason` names which check failed; fix it in the app and the identical request then succeeds. Check the flow builder for the affected partner. On a resend or redelivery, the `code` names the specific reason the transaction cannot be resent or redelivered, and retrying will not change that one.
- **429**: you are exceeding the rate limit. Wait for the `Retry-After` interval and back off before retrying.
