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

# Errors

> Every error has one shape, and a stable code that says what went wrong: decide by it.

A call that fails answers an HTTP status of 400 or more and a body of type `application/problem+json`, the standard
shape of [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457):

```json theme={null}
{
  "type": "urn:rfc:problem:core.forbidden",
  "status": 403,
  "title": "Forbidden",
  "code": "core.forbidden",
  "params": { "permission": "core.exports.run" },
  "requestId": "5d0c2b7e-8f3a-4c61-9e2d-1b7a4f6c3e90"
}
```

| Field | What it holds |
| - | - |
| `code` | What went wrong, as `<module>.<error>`. It never changes for the same problem: **decide by it.** |
| `params` | The values that explain it, such as the permission the key lacks. Each code has its own. |
| `title` | A fixed English summary of the code, the same on every answer, for you to read while you build. Never show it to your users, and never decide by it. |
| `status` | The HTTP status, the same as the answer's. Several codes share a status. |
| `type` | `urn:rfc:problem:` and the code. |
| `requestId` | This call's id. Quote it when you report a problem to us. |
| `errors` | Only with `422 core.validation_failed`: each field the request got wrong (below). |

## Decide by the code

Each operation's page in the [API reference](https://developers.readyforcommerce.com/api-reference/introduction.md) lists the codes it may answer, such as
[Start an export](https://developers.readyforcommerce.com/api-reference/core-start-export.md)'s. Read `code`, and `params` when the code has them:

```bash curl theme={null}
curl --silent "https://app.readyforcommerce.com/api/v1/orgs/$RFC_ORG_ID/core/org" \
  --header "Authorization: Bearer $RFC_API_KEY" | jq '{code, params, requestId}'
```

```python Python theme={null}
import os

import requests

response = requests.get(
    f"https://app.readyforcommerce.com/api/v1/orgs/{os.environ['RFC_ORG_ID']}/core/org",
    headers={"Authorization": f"Bearer {os.environ['RFC_API_KEY']}"},
)
if not response.ok:
    problem = response.json()
    if problem["code"] == "core.forbidden":
        print("The key lacks", problem["params"]["permission"])
    else:
        print(problem["code"], problem["requestId"])
```

```php PHP theme={null}
<?php
$curl = curl_init('https://app.readyforcommerce.com/api/v1/orgs/' . getenv('RFC_ORG_ID') . '/core/org');
curl_setopt_array($curl, [
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('RFC_API_KEY')],
    CURLOPT_RETURNTRANSFER => true,
]);
$body = json_decode(curl_exec($curl), true);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    if ($body['code'] === 'core.forbidden') {
        echo 'The key lacks ' . $body['params']['permission'];
    } else {
        echo $body['code'] . ' ' . $body['requestId'];
    }
}
```

```javascript JavaScript theme={null}
const response = await fetch(
  `https://app.readyforcommerce.com/api/v1/orgs/${process.env.RFC_ORG_ID}/core/org`,
  { headers: { Authorization: `Bearer ${process.env.RFC_API_KEY}` } },
);
if (!response.ok) {
  const problem = await response.json();
  if (problem.code === 'core.forbidden') console.log('The key lacks', problem.params.permission);
  else console.log(problem.code, problem.requestId);
}
```

## A request with wrong fields

`422 core.validation_failed` lists in `errors` each part of the request it refused:

```json theme={null}
{
  "type": "urn:rfc:problem:core.validation_failed",
  "status": 422,
  "title": "Validation failed",
  "code": "core.validation_failed",
  "requestId": "a3e1f9c2-4b7d-4e08-8a6f-2c5d9b1e7f34",
  "errors": [
    {
      "location": "body",
      "pointer": "#/name",
      "code": "invalid_type",
      "params": { "expected": "string" }
    }
  ]
}
```

* `location` is the part of the request: `body`, `querystring`, `params` (the path) or `headers`.
* `pointer` is a [JSON Pointer](https://www.rfc-editor.org/rfc/rfc6901) to the field, `#` for the whole part.
* `code` says what is wrong with it, and `params` its limits, such as `minimum` or `maximum`. The value you sent never
  comes back.

## The common codes

These codes come from what every call goes through, or from a kind of call many operations share; each operation's
page lists the ones it may answer:

| Status | Code | What happened | What to do |
| - | - | - | - |
| `400` | `core.bad_request` | The request could not be read: a body that is not JSON, or a wrong `Idempotency-Key`. | Fix the request. |
| `401` | `core.invalid_credential` | The key is unknown, revoked, expired or another organization's. | Send a live key of this organization ([Authentication](https://developers.readyforcommerce.com/authentication.md)). |
| `403` | `core.forbidden` | The key lacks the permission in `params.permission`. | Use a key that holds it: an existing key's permissions never grow. |
| `403` | `core.module_not_enabled` | Your organization has not activated the module the operation belongs to; store connections need `catalog` or `pricing`. | Someone who may activate modules activates it in the app. |
| `403` | `core.module_read_only` | The module in `params.module` is read-only: your organization went over its free allowance with no card. Reads, exports and deletions still pass. | Someone who manages billing adds a card in the app, or deleting records brings usage back within the allowance. |
| `403` | `core.human_only` | Only a person does this, in the app; `params.reason` says which group of actions. | Do it in the app. |
| `404` | `core.not_found`, or the module's own, such as `core.job_not_found` | The path does not exist, or your organization holds no such record. | Check the path and the id. |
| `409` | `core.org_deletion_scheduled` | The organization's deletion is scheduled, so every call is refused. | Stop calling. The owner can restore the organization in the app until it is deleted. |
| `409` | `core.version_conflict` | A change sent with an `expectedVersion` the record has left behind: someone saved it after you read it, and nothing of your change was written. | Read the record again, apply your change to what it holds now, and send it with its new `version`. |
| `422` | `core.validation_failed` | A field is wrong; `errors` says which. | Fix the fields as the operation's page describes them. |
| `429` | `core.rate_limited` | Too many calls ([Rate limits](https://developers.readyforcommerce.com/rate-limits.md)). | Wait the seconds `Retry-After` says, then call again. |
| `429` | `core.job_limit_reached` | Too many unfinished jobs ([Rate limits](https://developers.readyforcommerce.com/rate-limits.md#unfinished-jobs)). | Wait the seconds `Retry-After` says, or until a job ends. |
| `500` | `core.internal` | Something failed on our side. | Retry later; if it persists, tell us the `requestId`. |
| `503` | `core.unavailable`, `core.surface_disabled` | The API is briefly unavailable, or paused for everyone. | Wait the seconds `Retry-After` says, then call again. |

> [!TIP]
>
> A `4xx` other than `429` answers the same until you change the request or the key: do not retry it
> unchanged. A `429` or a `503` says how long to wait in `Retry-After`: wait that long, never in a
> tight loop. To retry a write without doing it twice, send an
> [`Idempotency-Key`](https://developers.readyforcommerce.com/rate-limits#retry-a-write-safely).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.