# Errors

# Errors

Every Hostex OpenAPI v3 response uses the same envelope:

```json
{
  "request_id": "RT2026052115252141408b3",
  "error_code": 0,
  "error_msg": "",
  "data": {}
}
```

* **The HTTP status is `200` even for application-level errors.** Always
  branch on `error_code`, not on the HTTP status.
* `error_code: 0` means success. Any non-zero value is a failure.
* `error_msg` is a short human-readable English string. Do **not** parse it
  programmatically — match on `error_code` instead.
* `request_id` should be quoted in any support ticket about a specific
  failing call. Every server-side log line is keyed by it.

***

## Error code reference

The codes mirror standard HTTP status semantics so you can map them
straight into your own client.

| `error_code`          | Class                  | When                                                                                                                                                                    | What to do                                                                                                     |
| --------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `0`                   | success                | The call succeeded.                                                                                                                                                     | —                                                                                                              |
| `400`                 | bad request            | Input is malformed — missing required body, wrong JSON, parameter conflict (e.g. `entire` scope + non-empty `property_ids`).                                            | Fix the request; do not retry.                                                                                 |
| `401`                 | auth                   | Token is missing, invalid, deleted, or has the wrong scope. Also returned by OAuth endpoints on bad `client_id` / `client_secret` / `code_verifier`.                    | Re-issue / re-authorize. See [Authentication](https://apidoc.hostex.io/reference/authentication).              |
| `403`                 | forbidden              | You are authenticated, but the resource is not yours — e.g. accessing a conversation thread belonging to another operator.                                              | Stop attempting; the user does not own this resource.                                                          |
| `404`                 | not found              | The resource id does not exist (or the path is misspelled).                                                                                                             | Verify the id; do not retry.                                                                                   |
| `409`                 | conflict               | The action would create a duplicate (e.g. creating a tag whose name already exists).                                                                                    | Reconcile state and retry without the duplicate.                                                               |
| `422`                 | validation             | Body parsed but failed validation rules (e.g. `note` too long, `start_date` after `end_date`, `id_required` value not in enum). `error_msg` includes the failing field. | Fix the request; do not retry.                                                                                 |
| `420`                 | account / subscription | Underlying Hostex account problem: subscription expired, Basic edition trying to use Pro-only feature, account suspended.                                               | **Do not retry.** Surface the message to the host — only they can fix it in the portal.                        |
| `429`                 | rate-limit             | Per-token, per-endpoint, or per-thread throttle hit. May also come from OAuth brute-force guard.                                                                        | Back off according to `Retry-After` header. See [Rate Limits](https://apidoc.hostex.io/reference/rate-limits). |
| `500`                 | server                 | Hostex-side bug, partial outage, or upstream channel timeout.                                                                                                           | Retry with exponential backoff; if it persists, file a support ticket with `request_id`.                       |
| `501`                 | not implemented        | Endpoint exists but the feature is disabled for your account (e.g. dynamic OAuth client registration when no public client is configured).                              | Surface to the user; do not retry.                                                                             |
| `502` / `503` / `504` | upstream               | Hostex is reachable but a downstream channel (Airbnb, Booking.com, etc.) failed.                                                                                        | Retry. If repeated, file a ticket.                                                                             |

***

## Common error scenarios — recipes

### "Invalid access token" on the very first call

* Header typo: `Hostex-Access-Token` (note the dash), not
  `Hostex_Access_Token` or `Hostex-Access_Token`.
* Whitespace: trim the token before sending.
* Token revoked or rotated in the Host Portal.

### "Not authorized for this action"

Your access token is **read-only**. Token scope is fixed at creation
time — create a new writable token in the OpenAPI Settings page and
revoke the old one.

### `429` on `POST /conversations/{id}` even though you are well under 1200/min

You hit the per-thread message throttle (Layer 3 in
[Rate Limits](https://apidoc.hostex.io/reference/rate-limits)), not the per-token quota. Spread sends
across threads, or drop the cadence on automated replies in the same
thread.

### `409` when creating a reservation tag or property tag

A tag with the same name already exists (or was soft-deleted and will
be transparently restored — try a `GET /reservation_tags?keyword=…`
first). Names are unique within the operator's account.

### `420` on every call

The host's subscription expired or downgraded to Basic. The token is
fine; the host must renew / upgrade in the Host Portal. There is
nothing your integration can do server-side.

### `404` on `DELETE /reservation_tags/{id}`

The tag is a system **default** tag (`is_default: true`), which cannot
be deleted via the API. Only custom tags belonging to the operator can
be removed. Filter on `is_default` in the `GET /reservation_tags`
response before offering "delete" in your UI.

### `429` from OAuth `/oauth/authorizations`

Brute-force guard. After 10 failed `client_secret` / `code_verifier`
attempts in 10 minutes for a single `client_id`, exchange calls are
rejected for the rest of the window. Make sure your refresh-token
flow has a deduplication mutex (one in-flight refresh per token).

***

## When to file a support ticket

Include all of the following so the on-call team can reproduce the
issue in under a minute:

1. The `request_id` from the failing response body.
2. Approximate timestamp (UTC) of the failure.
3. The HTTP method and path called (no need to redact the path — only
   the body / headers contain secrets).
4. Whether you authenticated with an access token or OAuth bearer
   token (do **not** include the token itself).
5. Expected behaviour vs observed behaviour.