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

# Error Codes

> All error codes the Return Helper API can emit, with their meaning, the wire HTTP status and body shape, and how to handle each.

Every API response uses the standard envelope (`correlationId` + `meta` + payload — see [Error Handling](/introduction#error-handling)). When something goes wrong, `meta.errorCode` is the machine-readable error key. This page lists every value the User and Public APIs can emit.

## How to read this page

The **Wire HTTP** column is the HTTP status the response actually arrives with.
The **`meta.status`** column is the application-level status inside the body.
The **`meta.errorCode`** column is the value to branch on in your client code.
The **`meta.error`** column describes the shape of the per-failure detail.

Most error paths in this API return HTTP `200` with the logical status carried in `meta.status` (the soft-error convention). A small number of cases return real HTTP `400`/`401`/`403`/`409` because they arrive before the API's own response envelope can be applied or because they represent genuine HTTP semantics.

## Validation failures

Almost every write endpoint goes through FluentValidation. Anything caught there — missing required field, value out of range, invalid country code, batch over the limit, business-rule violations expressed as `Custom(...)` checks — collapses to one error code:

| Wire HTTP | `meta.status` | `meta.errorCode`    | `meta.error`                                                                                                                                                                                                                                           |
| --------- | ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `200`     | `400`         | `VALIDATION_FAILED` | Object keyed by request property name → comma-joined list of error messages. For batch endpoints (e.g. `/api/Recall/createRecallByReturnInventoryId`), entries inside `jobEntryList` are surfaced individually so you can spot which list item failed. |

Sample body:

```json theme={null}
{
  "correlationId": "0HNLB2U6T1QG1:00000001",
  "meta": {
    "status": 400,
    "data": {},
    "errorCode": "VALIDATION_FAILED",
    "error": {
      "returnInventoryId": "'return Inventory Id' must not be empty."
    }
  }
}
```

Inspect `meta.error` — the keys tell you which fields are wrong.

## Business-rule and resource errors

These are thrown from business logic when a request is structurally valid but conflicts with the current state of the data, your account, or supporting infrastructure. Each carries a distinct `errorCode` so you can branch on it.

| `meta.errorCode`                         | Wire HTTP | `meta.status` | When it occurs                                                                                                                                                                                                                                                                                                                                                                      |
| ---------------------------------------- | --------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TRACKING_ALREADY_EXIST`                 | `200`     | `400`         | A `trackingNumber` you submitted is already used by another active record. Most often hit on [Create FBA shipment](/api-reference/fbashipment/create-fba-shipment) when the same Amazon tracking number is submitted twice; can also fire on return shipment / resend creates that supply tracking values directly. `meta.error` carries the conflicting record's identifying data. |
| `ACCOUNT_IS_BLOCKED`                     | `200`     | `403`         | Your API account has been blocked (e.g. for unpaid balance, terms violation). All write endpoints fail until the block is cleared by Return Helper support. `meta.error.message` contains the human-readable reason. Contact `support@returnhelper.com`.                                                                                                                            |
| `S3_OBJECT_NOT_FOUND_OR_NOT_PUBLIC_READ` | `200`     | `404`         | An endpoint that needs to fetch a file from S3 (typically VAS file attachments or label artifacts) couldn't read the object. Check that the file you uploaded earlier still exists and is reachable.                                                                                                                                                                                |
| `UPLOAD_FILE_TO_S3_FAIL`                 | `200`     | `500`         | An endpoint that needs to upload a file to S3 failed. Transient infrastructure issue — retry with the same idempotency key.                                                                                                                                                                                                                                                         |
| `REQUEST_CUSTOM_DOMAIN_FAIL`             | `200`     | `500`         | A label / branding endpoint that calls out to the custom-domain service failed. Transient — retry.                                                                                                                                                                                                                                                                                  |

## Auth and access-control failures

These are not soft errors. They arrive with real HTTP status codes because they're produced before (or outside of) the normal request pipeline.

| Wire HTTP | `meta.status` | `meta.errorCode` | When it occurs                                                                                                                                                                               |
| --------- | ------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`     | `401`         | `null`           | `x-rr-apikey` or `x-rr-apitoken` is missing, malformed, or doesn't match an active account. `meta.error.message` is `"Missing request header"` or similar.                                   |
| `403`     | `403`         | `null`           | A `RrForbiddenException` was thrown — your account is authenticated but not authorised for the action (e.g. trying to read another tenant's record). `meta.error.message` is human-readable. |

## Conflict and not-found

Less common, but reachable:

| Wire HTTP | `meta.status` | `meta.errorCode` | When it occurs                                                                                                                                                                                                                                                                                             |
| --------- | ------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`     | `404`         | `null`           | A `RrInvalidException` or `RrNotfoundException` — the resource you referenced (by ID) doesn't exist or doesn't belong to your account. `meta.error.message` is human-readable.                                                                                                                             |
| `409`     | `409`         | `null`           | A `DuplicateTransactionException` — you submitted a state-changing request that the idempotency layer recognised as a duplicate of one already in flight or completed. `meta.error` includes `transactionType`, `headId`, `uniqueChargeKey`, `message`. Treat as a no-op — the original request succeeded. |

## Idempotency key failures

These apply to write endpoints when you send the `x-returnhelper-idempotency-key` header. They arrive with real HTTP status codes.

| Wire HTTP | `meta.status` | `meta.errorCode`                  | When it occurs                                                                                                                     |
| --------- | ------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `400`     | `400`         | `IDEMPOTENCY_KEY_INVALID`         | The key is not 1–128 characters made up of letters, digits, `_`, `.`, `:` or `-`.                                                  |
| `409`     | `409`         | `IDEMPOTENCY_REQUEST_IN_PROGRESS` | A request with the same key is still running. Retry once it completes.                                                             |
| `409`     | `409`         | `IDEMPOTENCY_KEY_ALREADY_USED`    | A request with the same key already completed inside the validation window. Treat as a no-op — the original request was processed. |

A key is scoped to one account plus one HTTP method and route, so the same key value can be reused on a different endpoint without colliding.

## Recommended client-side handling

In pseudocode:

```
response = call(endpoint, body)

if response.status == 401:
  reauth or alert ops
elif response.status == 403:
  surface meta.error.message — account or permission issue
elif response.status == 409:
  treat as success (idempotency replay) — do NOT retry
elif response.status == 200:
  meta = response.body.meta
  if meta.errorCode == 'VALIDATION_FAILED':
    surface meta.error map to the user — field-level messages
  elif meta.errorCode == 'TRACKING_ALREADY_EXIST':
    handle duplicate — likely already created on a previous attempt
  elif meta.errorCode == 'ACCOUNT_IS_BLOCKED':
    halt all calls, alert ops, do not retry
  elif meta.errorCode in ('UPLOAD_FILE_TO_S3_FAIL', 'REQUEST_CUSTOM_DOMAIN_FAIL'):
    retry with the same idempotency key (transient infra)
  elif meta.errorCode == 'S3_OBJECT_NOT_FOUND_OR_NOT_PUBLIC_READ':
    re-upload the source file, then retry
  elif meta.status == 200 and meta.errorCode is null:
    success — read the payload at the top level
  elif meta.status == 404 and meta.errorCode is null:
    resource not found — the ID is wrong or out of scope
```

`correlationId` is present on every response. **Always log it** — it's how Return Helper support traces a specific request through internal systems.

## Where each error code is defined

The error codes documented here come from `RrErrorCode` constants in the `ReturnRequestApiModel.RrException` namespace. If you encounter an `errorCode` value not listed on this page, treat it as an undocumented internal error and report it via support with the `correlationId`.
