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

# Errors

> Every error code the API returns, what it means, and how to fix it.

When a request doesn't go through, you get an HTTP status and a body with one shape:

```json theme={null}
{
  "error": {
    "type": "invalid_request",
    "code": "button_label_too_long",
    "message": "The button label is 26 characters. Instagram shows up to 20, so it would be cut off.",
    "param": "button_label",
    "doc_url": "https://docs.indm.social/errors#button_label_too_long"
  }
}
```

| Field | What it's for |
| - | - |
| `type` | The family. Each `type` has one HTTP status, listed below. |
| `code` | The specific reason. **Branch on this.** Codes are stable and never renamed. |
| `message` | A sentence for a person, saying what happened and what to do. Fine to log or show; it may be reworded, so don't parse it. |
| `param` | The request field the error is about. Left out when no single field is. |
| `doc_url` | A link to the code's entry on this page. |

Every response, error or not, carries an `X-Request-Id` header. Include it when you write to [support@indm.social](mailto:support@indm.social) and we can find the request at once.

> [!TIP]
>
> New codes can appear within `v1` as the API grows. If you meet a `code` you don't recognise, fall back to its `type`: that tells you the status and the right response.

## Types at a glance

| `type` | Status | Meaning |
| - | - | - |
| `invalid_request` | `400` | The request can't be processed as sent. Fix it before retrying. (`405` for [`method_not_allowed`](#method_not_allowed).) |
| `authentication_error` | `401` | The API key is missing, unknown, expired or revoked. |
| `permission_error` | `403` | The key's scope doesn't cover this route. |
| `plan_limit` | `403` | The workspace's plan doesn't include what this write needs. |
| `not_found` | `404` | Nothing with that id exists in this key's workspace. |
| `conflict` | `409` | The request clashes with the current state. |
| `rate_limited` | `429` | Too many requests from this key. Wait, then retry. |
| `api_error` | `500` | Something failed on our side. (`503` when inDM briefly can't reach its database.) |

## `invalid_request` (400)

The request can't be processed as sent. Retrying it unchanged gets the same answer.

> [!NOTE]
>
> [Validate an automation](https://docs.indm.social/api-reference/automations/validate-automation.md) answers `200` and lists what it finds under `problems`, using these same codes. A few codes only ever appear there, because they describe copy that would work badly rather than a request inDM can't save: [`keyword_has_space`](#keyword_has_space), [`keyword_too_long`](#keyword_too_long) and [`dm_promises_missing_link`](#dm_promises_missing_link).

### `invalid_json`

The body isn't valid JSON, or a field has the wrong type, such as `keywords` sent as a string instead of an array. Check the body against the endpoint's reference page.

### `method_not_allowed`

The path exists, but not for this HTTP method, such as `DELETE /automations/{automation_id}`. This one answers `405` rather than `400`. Each endpoint's reference page shows its method.

### `media_id_required`

[Create an automation](https://docs.indm.social/api-reference/automations/create-automation.md) needs a `media_id`, the post or reel to attach it to. Get one from [List media](https://docs.indm.social/api-reference/media/list-media.md).

### `automation_id_required`

The request didn't name an automation. Put its `id` in the path; [List automations](https://docs.indm.social/api-reference/automations/list-automations.md) has the ids.

### `keywords_required`

An automation needs at least one keyword for fans to comment. On an update, `keywords: []` is refused for the same reason.

### `too_many_keywords`

One automation carries up to 20 keywords. Split them across automations on different posts, or keep the ones fans actually type.

### `keyword_has_space`

From [Validate an automation](https://docs.indm.social/api-reference/automations/validate-automation.md). The keyword has a space in it, and a keyword is matched as one word. Pick a single word, such as `GUIDE` or `LINK`.

### `keyword_too_long`

From [Validate an automation](https://docs.indm.social/api-reference/automations/validate-automation.md). The keyword is over 30 characters. Fans have to type it exactly, so short ones get used.

### `dm_message_required`

An automation needs a `dm_message`: it's the message the fan receives. On an update, `""` is refused for the same reason.

### `dm_message_too_long`

`dm_message` is up to 1,000 characters. Instagram truncates long messages, so shorter lands better anyway.

### `dm_promises_missing_link`

From [Validate an automation](https://docs.indm.social/api-reference/automations/validate-automation.md). The DM talks about a link, but no `link_url` was given, so the fan would be promised something that never arrives. Add the link, or reword the DM.

### `button_label_too_long`

`button_label` is up to 20 characters. Instagram cuts off anything longer on the button.

### `public_reply_too_long`

`public_reply` is up to 2,200 characters, Instagram's length for a comment.

### `link_url_invalid`

A link has to start with `https://` or `http://` and name a host. `param` points at the field, such as `link_url` or `links[1].url`.

### `link_title_too_long`

`link_title`, and `title` on each item in `links`, is up to 20 characters.

### `too_many_links`

One message carries at most three links. That ceiling is Instagram's, on every plan, so no plan lifts it. Keep the three that matter most.

### `gate_requires_link`

A follow gate or email gate holds the link back until the fan follows or shares an email. With no link, there's nothing to hold back. Add `link_url` or `links`, or leave the gates off.

### `gate_label_too_long`

`follow_gate_visit_label` and `follow_gate_following_label` are up to 20 characters each.

### `gate_copy_without_gate`

You sent gate wording, such as `follow_gate_message`, but its gate is off, so nobody would ever read it. Turn the gate on, or leave the wording out.

### `opener_message_too_long`

`opener_message` is up to 1,000 characters.

### `opener_plain_text_without_opener`

`opener_plain_text` sets the shape of the opening message, so it needs an `opener_message` to apply to.

### `followup_message_too_long`

`followup_message` is up to 1,000 characters.

### `followup_delay_out_of_range`

`followup_delay_minutes` runs from 30 to 1,200 (20 hours). The ceiling is Instagram's 24-hour messaging window minus headroom; the floor keeps a follow-up from landing on top of the first message.

### `followup_delay_without_message`

There's a `followup_delay_minutes` but no `followup_message`. Send the message with it, or leave the delay out.

### `followup_unreachable`

This follow-up could never send. Instagram opens the 24-hour window only when the fan messages the account, and nothing in this automation asks them to: the single private reply a comment earns is the whole conversation. Add an `opener_message`, or turn on `follow_gate` or `email_gate`, and the follow-up becomes possible. [Instagram's rules](https://docs.indm.social/instagram-rules.md#a-comment-earns-one-private-reply) explains the window.

### `status_invalid`

`status` isn't a value this endpoint takes. [Set automation status](https://docs.indm.social/api-reference/automations/set-automation-status.md) takes `live` or `paused`; the `status` filter on [List automations](https://docs.indm.social/api-reference/automations/list-automations.md) takes `draft`, `live` or `paused`.

### `limit_invalid`

`limit` runs from 1 to 100.

### `cursor_invalid`

The `cursor` isn't one this list issued. Pass `next_cursor` back exactly as you received it, to the same endpoint with the same filters, or start again without a cursor. See [Pagination](https://docs.indm.social/pagination.md).

### `days_invalid`

`days` on [Get automation stats](https://docs.indm.social/api-reference/automations/get-automation-stats.md) runs from 1 to 90.

## `authentication_error` (401)

The request didn't carry a key inDM can accept. [Authentication](https://docs.indm.social/authentication.md) covers keys end to end.

### `api_key_missing`

No `Authorization` header, or one that isn't `Bearer`. Send `Authorization: Bearer indm_...`.

### `api_key_invalid`

The key isn't one inDM issued. Most often it's a copy that lost characters on the way, or a stray space or quote. Copy it again, or create a new key in **Settings → API keys**.

### `api_key_expired`

The key passed its expiry date. Create a new one in **Settings → API keys**, and see [rotation](https://docs.indm.social/authentication.md#expiry-and-rotation) to switch without downtime.

### `api_key_revoked`

Someone revoked this key, or the member who created it left the workspace. Create a new one.

## `permission_error` (403)

### `insufficient_scope`

This route needs a `full` key and the request used a `read` key. Use a key with full access for writes. See [scopes](https://docs.indm.social/authentication.md#scopes).

## `plan_limit` (403)

The workspace's plan doesn't include something this write needs. The API works on every plan; what a workspace can build through it is exactly what it can build in the app. `message` names what the write needs. The plans are described at [indm.social/pricing](https://indm.social/pricing).

### `automation_limit_reached`

The workspace holds as many automations as its plan includes. Every automation counts, whatever its status, so pausing one doesn't make room.

### `link_limit_reached`

The workspace's plan includes fewer links per automation than this request sets. Links written into the message text count too when they go somewhere different from the attached links. Naming the attached link in a sentence is fine.

### `followup_requires_paid_plan`

Follow-up messages are part of Growth. Everything else in the request can be saved: send it again without `followup_message` and `followup_delay_minutes`.

### `plan_limit_reached`

The plan refused this write for a reason the codes above don't cover. `message` names what it needs.

## `not_found` (404)

Nothing with that id exists in this key's workspace. An id from another workspace reads the same way: a key sees only its own.

### `account_not_found`

No connected account with that id. [List accounts](https://docs.indm.social/api-reference/accounts/list-accounts.md) has the ids.

### `media_not_found`

No synced post or reel with that id. [List media](https://docs.indm.social/api-reference/media/list-media.md) has the ids. A post published moments ago may not be synced yet; see [Refresh content](https://docs.indm.social/api-reference/accounts/refresh-account.md).

### `automation_not_found`

No automation with that id. [List automations](https://docs.indm.social/api-reference/automations/list-automations.md) has the ids.

### `refresh_job_not_found`

No refresh with that `job_id` on that account. Use the `job_id` and `account_id` that [Refresh content](https://docs.indm.social/api-reference/accounts/refresh-account.md) returned together.

### `route_not_found`

There's no endpoint at that path for that method. Check the path against the [API reference](https://docs.indm.social/api-reference/introduction.md), including the `/v1` prefix.

## `conflict` (409)

The request clashes with how things stand right now. Change the state, then retry.

### `live_automation_on_media`

Another automation is already live on this post. A post runs one live automation at a time, so a fan who comments once gets one DM. Pause the other automation first, or choose a different post. [Create an automation](https://docs.indm.social/api-reference/automations/create-automation.md) returns this too, so you never build a draft that can't go live.

### `account_needs_reconnect`

Instagram has paused this account's connection, so nothing syncs or sends until it's reconnected in the inDM app. [Get account health](https://docs.indm.social/api-reference/accounts/get-account-health.md) says why and how it recovers.

## `rate_limited` (429)

### `rate_limited`

This key made more requests than its limit allows. Wait the number of seconds in the `Retry-After` header, then try again. See [Rate limits](https://docs.indm.social/rate-limits.md).

## `api_error` (500)

### `internal_error`

Something failed on our side, and we log every one. It comes with `500`, or `503` when inDM briefly can't reach its database. Reads are safe to retry with backoff. For a write, read the current state before retrying, so a write that did land isn't made twice. If it keeps happening, write to [support@indm.social](mailto:support@indm.social) with the `X-Request-Id`.


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