Comments, mentions and DMs on your connected accounts, as they arrive.
{
"id": "4c7e1a90-3b26-4f5d-8e03-a9d1c6b27f54",
"account_id": "a3d9f1c4-7b2e-4c60-9e85-1f4b6d2a0c73",
"network": "instagram",
"kind": "comment",
"direction": "inbound",
"thread_key": "17912345678901234",
"post_external_id": "17998765432109876",
"author_handle": "roadrunner",
"author_name": "R. Runner",
"body": "Does this come in blue?",
"permalink": "https://www.example.com/p/…",
"status": "open",
"read_at": null,
"replied_at": null,
"occurred_at": "2026-10-01T09:14:22+00:00",
"automation_id": null
}
kind is comment, mention or dm. direction is outbound for your own
replies, which appear in the thread. status is open, done or hidden.
automation_id is set when an automation answered it.
Inbox items are kept for 180 days.
Operations
GET /v1/inbox
List the inbox
Scope social:read.
Comments, mentions and messages across connected accounts, newest first. Open items by default.
Parameters
| Name | In | Type | |
|---|---|---|---|
status |
query | string |
(one of open, done, hidden, all) |
kind |
query | InboxKind |
(one of comment, dm, mention) |
account_id |
query | uuid |
|
limit |
query | integer |
Page size, 1 to 200. Above 200 is clamped; anything else is the default. (default 50; >= 1; <= 200) |
cursor |
query | string |
The next_cursor from the previous page. Opaque; an invalid one is 400. (max 512) |
Answers
| Status | Body | |
|---|---|---|
| 200 | InboxList |
A page of items. |
| 400 | ErrorEnvelope |
VALIDATION_ERROR: a body that is not JSON, a field that breaks its rule (named in fields), a bad query value or cursor, or a malformed Idempotency-Key. |
| 401 | ErrorEnvelope |
UNAUTHORIZED: no key, or one that is malformed, unknown or revoked. It never says which. |
| 403 | ErrorEnvelope |
INSUFFICIENT_SCOPE: the key lacks this operation's scope. Or ORGANISATION_SUSPENDED: the key's organisation is suspended. Never about another workspace's object, which is 404. |
| 429 | ErrorEnvelope |
RATE_LIMITED: too many requests. Wait for Retry-After seconds. |
| 500 | ErrorEnvelope |
INTERNAL_ERROR: ours. Safe to retry; send the same Idempotency-Key on a POST. |
The social inbox — answers 200
Comments, mentions and DMs from every connected account in one list. The person who wrote is their public handle, never the network's internal id for them.
curl "https://api.boltclaw.ai/v1/inbox?limit=50" \
-H "Authorization: Bearer bc_live_EXAMPLExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
const res = await fetch("https://api.boltclaw.ai/v1/inbox?limit=50", {
headers: {
Authorization: `Bearer ${process.env.BOLTCLAW_KEY}`,
},
});
// 200
const data = await res.json();
GET /v1/inbox/{id}
Read an inbox thread
Scope social:read. Another workspace's answers 404.
The item and the rest of its thread, oldest first.
Parameters
| Name | In | Type | |
|---|---|---|---|
id |
path | uuid |
An id this API gave you. Anything that is not one of yours is 404. (required) |
Answers
| Status | Body | |
|---|---|---|
| 200 | InboxThread |
The thread. |
| 401 | ErrorEnvelope |
UNAUTHORIZED: no key, or one that is malformed, unknown or revoked. It never says which. |
| 403 | ErrorEnvelope |
INSUFFICIENT_SCOPE: the key lacks this operation's scope. Or ORGANISATION_SUSPENDED: the key's organisation is suspended. Never about another workspace's object, which is 404. |
| 404 | ErrorEnvelope |
NOT_FOUND: no such object, or it belongs to another workspace. The two are deliberately the same answer and never 403. If the id is right, check you are using the right key. |
| 429 | ErrorEnvelope |
RATE_LIMITED: too many requests. Wait for Retry-After seconds. |
| 500 | ErrorEnvelope |
INTERNAL_ERROR: ours. Safe to retry; send the same Idempotency-Key on a POST. |
PATCH /v1/inbox/{id}
Set an item's status
Scope social:write. Another workspace's answers 404.
Open or done, in your inbox only. Hiding is POST /inbox/{id}/hide.
Parameters
| Name | In | Type | |
|---|---|---|---|
id |
path | uuid |
An id this API gave you. Anything that is not one of yours is 404. (required) |
Request body (required)
| Field | Type | |
|---|---|---|
status |
string |
(required; one of open, done) |
{
"status": "open"
}
Answers
| Status | Body | |
|---|---|---|
| 200 | InboxItem |
The item. |
| 400 | ErrorEnvelope |
VALIDATION_ERROR: a body that is not JSON, a field that breaks its rule (named in fields), a bad query value or cursor, or a malformed Idempotency-Key. |
| 401 | ErrorEnvelope |
UNAUTHORIZED: no key, or one that is malformed, unknown or revoked. It never says which. |
| 403 | ErrorEnvelope |
INSUFFICIENT_SCOPE: the key lacks this operation's scope. Or ORGANISATION_SUSPENDED: the key's organisation is suspended. Never about another workspace's object, which is 404. |
| 404 | ErrorEnvelope |
NOT_FOUND: no such object, or it belongs to another workspace. The two are deliberately the same answer and never 403. If the id is right, check you are using the right key. |
| 413 | ErrorEnvelope |
PAYLOAD_TOO_LARGE: the body is over 64 KB. |
| 429 | ErrorEnvelope |
RATE_LIMITED: too many requests. Wait for Retry-After seconds. |
| 500 | ErrorEnvelope |
INTERNAL_ERROR: ours. Safe to retry; send the same Idempotency-Key on a POST. |
POST /v1/inbox/{id}/reply
Reply to an item
Scope social:write. Accepts Idempotency-Key. Another workspace's answers 404. Can make something publicly visible. Can record a platform charge.
Replies on the network as the connected account: in public to a comment, privately to a
message. The network's own refusal is UPSTREAM_ERROR (422, 502 or 503) with a sentence
that says what happened. Replying on X is billed per call on top of the plan.
Parameters
| Name | In | Type | |
|---|---|---|---|
id |
path | uuid |
An id this API gave you. Anything that is not one of yours is 404. (required) |
Request body (required)
| Field | Type | |
|---|---|---|
text |
string |
Trimmed; not blank. (required; min 1; max 5000) |
{
"text": "<text>"
}
Answers
| Status | Body | |
|---|---|---|
| 201 | InboxItem |
The reply, as an outbound item. |
| 400 | ErrorEnvelope |
VALIDATION_ERROR: a body that is not JSON, a field that breaks its rule (named in fields), a bad query value or cursor, or a malformed Idempotency-Key. |
| 401 | ErrorEnvelope |
UNAUTHORIZED: no key, or one that is malformed, unknown or revoked. It never says which. |
| 402 | ErrorEnvelope |
PLAN_LIMIT: the organisation's plan does not allow more of this. The message names the allowance. |
| 403 | ErrorEnvelope |
INSUFFICIENT_SCOPE: the key lacks this operation's scope. Or ORGANISATION_SUSPENDED: the key's organisation is suspended. Never about another workspace's object, which is 404. |
| 404 | ErrorEnvelope |
NOT_FOUND: no such object, or it belongs to another workspace. The two are deliberately the same answer and never 403. If the id is right, check you are using the right key. |
| 409 | ErrorEnvelope |
CONFLICT: the request clashes with the object's current state (the message says how). IDEMPOTENCY_KEY_CONFLICT / IDEMPOTENCY_KEY_IN_FLIGHT: the Idempotency-Key was used with another request, or its first request is still running. |
| 413 | ErrorEnvelope |
PAYLOAD_TOO_LARGE: the body is over 64 KB. |
| 422 | ErrorEnvelope |
VALIDATION_ERROR: the request is well formed but cannot be done as asked, such as a network that does not support it. Or UPSTREAM_ERROR: the network refused it. |
| 428 | ErrorEnvelope |
APPROVAL_REQUIRED on the first try, APPROVAL_PENDING on a retry before a person has decided. NOTHING HAS HAPPENED either way. The body carries approval with a url: show it to the person, and once they approve, send the identical request again with the approval id in BoltClaw-Approval. Only an AI assistant's request is ever answered this way. |
| 429 | ErrorEnvelope |
RATE_LIMITED: too many requests. Wait for Retry-After seconds. |
| 500 | ErrorEnvelope |
INTERNAL_ERROR: ours. Safe to retry; send the same Idempotency-Key on a POST. |
| 502 | ErrorEnvelope |
UPSTREAM_ERROR: the social network failed the call. The message says what happened. |
| 503 | ErrorEnvelope |
UPSTREAM_ERROR: the social network is temporarily unavailable; retrying later may work. |
POST /v1/inbox/{id}/hide
Hide or unhide a comment
Scope social:write. Accepts Idempotency-Key. Another workspace's answers 404.
Hides the comment on the network (an empty body means hide), or { "hidden": false }
unhides it. A network that cannot hide comments is 422.
Parameters
| Name | In | Type | |
|---|---|---|---|
id |
path | uuid |
An id this API gave you. Anything that is not one of yours is 404. (required) |
Request body (optional)
| Field | Type | |
|---|---|---|
hidden |
boolean |
(default true) |
Answers
| Status | Body | |
|---|---|---|
| 200 | InboxItem |
The item. |
| 400 | ErrorEnvelope |
VALIDATION_ERROR: a body that is not JSON, a field that breaks its rule (named in fields), a bad query value or cursor, or a malformed Idempotency-Key. |
| 401 | ErrorEnvelope |
UNAUTHORIZED: no key, or one that is malformed, unknown or revoked. It never says which. |
| 403 | ErrorEnvelope |
INSUFFICIENT_SCOPE: the key lacks this operation's scope. Or ORGANISATION_SUSPENDED: the key's organisation is suspended. Never about another workspace's object, which is 404. |
| 404 | ErrorEnvelope |
NOT_FOUND: no such object, or it belongs to another workspace. The two are deliberately the same answer and never 403. If the id is right, check you are using the right key. |
| 409 | ErrorEnvelope |
CONFLICT: the request clashes with the object's current state (the message says how). IDEMPOTENCY_KEY_CONFLICT / IDEMPOTENCY_KEY_IN_FLIGHT: the Idempotency-Key was used with another request, or its first request is still running. |
| 413 | ErrorEnvelope |
PAYLOAD_TOO_LARGE: the body is over 64 KB. |
| 422 | ErrorEnvelope |
VALIDATION_ERROR: the request is well formed but cannot be done as asked, such as a network that does not support it. Or UPSTREAM_ERROR: the network refused it. |
| 428 | ErrorEnvelope |
APPROVAL_REQUIRED on the first try, APPROVAL_PENDING on a retry before a person has decided. NOTHING HAS HAPPENED either way. The body carries approval with a url: show it to the person, and once they approve, send the identical request again with the approval id in BoltClaw-Approval. Only an AI assistant's request is ever answered this way. |
| 429 | ErrorEnvelope |
RATE_LIMITED: too many requests. Wait for Retry-After seconds. |
| 500 | ErrorEnvelope |
INTERNAL_ERROR: ours. Safe to retry; send the same Idempotency-Key on a POST. |
| 502 | ErrorEnvelope |
UPSTREAM_ERROR: the social network failed the call. The message says what happened. |
| 503 | ErrorEnvelope |
UPSTREAM_ERROR: the social network is temporarily unavailable; retrying later may work. |
Shapes
The objects these operations send and answer.
InboxHide
| Field | Type | |
|---|---|---|
hidden |
boolean |
(default true) |
InboxItem
| Field | Type | |
|---|---|---|
id |
uuid |
(required) |
account_id |
uuid |
(required) |
network |
Network |
(required; one of instagram, facebook, threads, bluesky, x, tiktok, linkedin, youtube, pinterest) |
kind |
InboxKind |
(required; one of comment, dm, mention) |
direction |
string |
(required; one of inbound, outbound) |
thread_key |
string |
(required) |
post_external_id |
string | null |
(required) |
author_handle |
string | null |
(required) |
author_name |
string | null |
(required) |
body |
string |
(required) |
permalink |
string | null |
(required) |
status |
string |
(required; one of open, done, hidden) |
read_at |
date-time | null |
(required) |
replied_at |
date-time | null |
(required) |
occurred_at |
date-time |
(required) |
automation_id |
uuid | null |
(required) |
InboxKind
One of: comment, dm, mention.
InboxList
| Field | Type | |
|---|---|---|
data |
InboxItem[] |
(required) |
next_cursor |
NextCursor |
Pass back as cursor for the next page; null on the last page. (required) |
InboxReply
| Field | Type | |
|---|---|---|
text |
string |
Trimmed; not blank. (required; min 1; max 5000) |
InboxStatusUpdate
| Field | Type | |
|---|---|---|
status |
string |
(required; one of open, done) |
InboxThread
| Field | Type | |
|---|---|---|
item |
InboxItem |
(required) |
thread |
InboxItem[] |
(required) |
Network
One of: instagram, facebook, threads, bluesky, x, tiktok, linkedin, youtube, pinterest.
NextCursor
Pass back as cursor for the next page; null on the last page.
string | null.