---
title: "Errors"
description: "The OnePageCRM API success and error envelopes, error fields, and the HTTP status codes with fixes."
canonical_url: https://developer.onepagecrm.com/api/errors/
source: Markdown mirror of https://developer.onepagecrm.com/api/errors/
---

Success and error responses use two different JSON envelopes.
Check the HTTP status code first, then branch on `error_name`.

## The envelopes

A successful response:

```json
{
  "status": 0,
  "message": "OK",
  "timestamp": 1765456800,
  "data": { "...": "..." }
}
```

An error response drops `timestamp` and `data` and carries error fields
instead:

```json
{
  "status": 400,
  "message": "Invalid request data",
  "error_name": "invalid_request_data",
  "error_message": "A validation error has occurred",
  "errors": { "...": "..." }
}
```

| Field | Type | Appears in | Description |
| --- | --- | --- | --- |
| `status` | integer | both | `0` on success. On errors, an application status code — it can differ from the HTTP status (see below) |
| `message` | string | both | Short human-readable summary |
| `timestamp` | integer | success only | Unix time in seconds |
| `data` | object | success only | The payload |
| `error_name` | string | errors only | Machine identifier, e.g. `invalid_auth_token`. Branch on this, not on `error_message` |
| `error_message` | string | errors only | Human explanation of what went wrong |
| `errors` | object | errors only | Field-level details on validation failures; `{}` otherwise |

Exact `error_message` text can change. Match on `error_name` in code;
log `error_message` for debugging.

One trap: the body's `status` does not always match the HTTP status.
Bad credentials, for example, return HTTP `401` with `"status": 400` in
the body. Trust the HTTP status code and `error_name`.

## HTTP status codes

| HTTP | Meaning | `error_name` examples | Fix |
| --- | --- | --- | --- |
| `400` | Invalid or incomplete request data. Also the default for unclassified errors — including duplicate records (`duplicated_entity`, `resource_already_exists`) | `invalid_request_data`, `duplicated_entity`, `resource_already_exists` | Inspect `errors` for the offending fields; check the request shape against the [API reference](/api/reference/) |
| `401` | Authentication missing or invalid | `invalid_auth_token` (API key), `invalid_access_token` (OAuth), `invalid_login`, `authorization_data_not_found` (no auth sent) | Re-check credentials; refresh an expired OAuth token. See [Authentication](/api/authentication/) |
| `402` | Payment required | `trial_expired`, `subscription_canceled` | The account's subscription has lapsed — the account owner needs to update billing |
| `403` | Forbidden, including OAuth scope errors | `no_permission_to_complete_action` | Check the user's permissions and your token's scope (below) |
| `404` | Resource not found | `resource_not_found` | Check the path and record ID against the [API reference](/api/reference/) |
| `405` | HTTP method not allowed | `method_not_allowed` | Use the verb the endpoint documents |
| `429` | Too many concurrent connections | — | Back off and retry. Note: the request-rate throttle returns `403` with a plain-text `Rate Limit Exceeded` body instead. See [Rate limits](/api/rate-limits/) |
| `500` | Internal server error | `internal_server_error` | Retry; if it persists, contact support |
| `503` | Maintenance or temporary unavailability | `service_unavailable` | Retry later. See the [maintenance note](/api/rate-limits/) |

## Worked examples

### 401 — bad credentials

```bash
curl -u "USER_ID:WRONG_KEY" \
  https://app.onepagecrm.com/api/v3/contacts.json
```

```json
{
  "status": 400,
  "message": "Invalid auth token",
  "error_name": "invalid_auth_token",
  "error_message": "Authorization token is invalid",
  "errors": {}
}
```

Note the mismatch: the HTTP status is `401`, but the body says
`"status": 400`. This is expected — branch on the HTTP status and
`error_name`, not the body's `status` field.

Re-copy your `user_id` and `api_key` from the
[API settings page](https://app.onepagecrm.com/app/api). OAuth requests
fail the same way but with `error_name: invalid_access_token` — that
usually means the access token expired. Refresh it and retry.

### 403 — insufficient OAuth scope

A `crm.readonly` token attempting a write:

```bash
curl -X POST \
  -H "Authorization: Bearer READONLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"last_name": "Lovelace"}' \
  https://app.onepagecrm.com/api/v3/contacts.json
```

```json
{
  "status": 403,
  "message": "You have no permission to complete this action",
  "error_name": "no_permission_to_complete_action",
  "error_message": "Insufficient OAuth scope. Required: crm",
  "errors": {}
}
```

Write endpoints require the `crm` scope. Request it during
authorization — see [Authentication](/api/authentication/) for the
scope rules, and note that some endpoints reject OAuth tokens entirely
with `This endpoint is not available for OAuth applications`.

### 400 — validation failure

Creating a contact with incomplete data:

```bash
curl -u "USER_ID:API_KEY" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{}' \
  https://app.onepagecrm.com/api/v3/contacts.json
```

```json
{
  "status": 400,
  "message": "Invalid request data",
  "error_name": "invalid_request_data",
  "error_message": "A validation error has occurred",
  "errors": {
    "last_name": "Required field last_name is missing"
  }
}
```

The `errors` object is keyed by field name; the exact message varies by
field. Fix each listed field and resend the request.

## Handling errors in code

- Branch on the HTTP status first, then on `error_name`.
- On `401`, refresh credentials or re-authenticate — don't retry blindly.
- On `429`, a plain-text `Rate Limit Exceeded` `403`, and `503`, retry with backoff — see [Rate limits](/api/rate-limits/).
- On `400`, fix the request — retrying the same payload returns the same error.
- Log `error_message` and `errors` — they tell you exactly what to fix.

## Common questions

**Why does the body say status 400 when the HTTP status is 401?**
The body's `status` is an application code and doesn't always match
the HTTP status — bad credentials return HTTP `401` with
`"status": 400` in the body. Trust the HTTP status code and
`error_name`.

**Which field should my code branch on?**
`error_name` — it's the stable machine identifier. The
`error_message` text can change, so log it for debugging, but never
match on it.

## What to read next

- **[Rate limits](/api/rate-limits/)** — what triggers a throttle and how to back off.
- **[Authentication](/api/authentication/)** — both auth methods and the OAuth scope rules.
- **[API reference](/api/reference/)** — per-endpoint parameters and response shapes.
