When a comment (or DM) on an account matches your keywords, an automation replies publicly and sends the person one private message — optionally asking for their email address or phone number and saving what they send as a lead.
{
"id": "d2a6f8b1-5c94-4e07-9b3a-1e7c0f4d6a25",
"account_id": "a3d9f1c4-7b2e-4c60-9e85-1f4b6d2a0c73",
"network": "instagram",
"name": "Catalogue",
"trigger": "comment_keyword",
"keywords": ["catalogue", "price"],
"match_mode": "contains",
"post_external_ids": null,
"public_replies": ["Sent you a DM!"],
"dm_message": "Here is the catalogue.",
"link_url": "https://example.com/catalogue",
"link_label": "Open catalogue",
"collect": "email",
"collect_prompt": "Want the restock alert? Reply with your email.",
"use_ai": false,
"enabled": true,
"triggered_count": 42,
"dms_sent": 40,
"leads_captured": 17,
"created_at": "2026-09-30T11:00:00+00:00",
"updated_at": "2026-09-30T11:00:00+00:00"
}
account_id |
Required on create; cannot be changed afterwards. |
name |
Required on create. 1–80 characters. |
dm_message |
Required on create. 1–1000 characters. |
trigger |
comment_keyword (default) or dm_keyword. |
keywords |
Up to 20. None matches everything. |
match_mode |
contains (default) or exact. |
post_external_ids |
Up to 50 of the network's post ids; null means every post. |
public_replies |
Up to 10; one is picked each time. |
link_url, link_label |
An http(s) link sent with the DM, and its button text. |
collect |
none (default), email or phone. |
collect_prompt |
The question asked when collecting. |
use_ai |
Personalise the DM with AI. Uses one of the plan's monthly AI drafts, else today's AI credits (ai_credits_per_day in GET /usage); with neither left, a written reply is sent. |
enabled |
Defaults to true — an automation runs as soon as it is created. |
Each person gets one private reply per comment, only within the network's window (seven days for Instagram and Facebook), and a business never starts a conversation. These rules are the networks', they are enforced here, and no setting turns them off.
Operations
GET /v1/automations
List automations
Scope social:read.
Every automation, with its counters. Never paged (next_cursor is always null).
Answers
| Status | Body | |
|---|---|---|
| 200 | AutomationList |
The automations. |
| 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 comment-to-DM rules — answers 200
One response, never paged. Each rule's counters say how often it has fired.
curl https://api.boltclaw.ai/v1/automations \
-H "Authorization: Bearer bc_live_EXAMPLExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
const res = await fetch("https://api.boltclaw.ai/v1/automations", {
headers: {
Authorization: `Bearer ${process.env.BOLTCLAW_KEY}`,
},
});
// 200
const data = await res.json();
POST /v1/automations
Create an automation
Scope social:write. Accepts Idempotency-Key. Can make something publicly visible. Can record a platform charge.
A rule that answers a keyword in a comment or a message with a DM, and optionally a public
reply and a request for an email or phone number. What a network supports is checked
here (400). The plan sets how many automations an organisation may hold and how many
DMs a month they may send (402 PLAN_LIMIT). DMs on X are billed per call on top of the plan.
Request body (required)
| Field | Type | |
|---|---|---|
name |
string |
(required; min 1; max 80) |
dm_message |
string |
(required; min 1; max 1000) |
trigger |
string |
(one of comment_keyword, dm_keyword) |
keywords |
string[] |
(<= 20 items) |
match_mode |
string |
(one of contains, exact) |
post_external_ids |
array | null |
(<= 50 items) |
public_replies |
string[] |
(<= 10 items) |
link_url |
string | null |
(max 2048) |
link_label |
string | null |
(max 80) |
collect |
string |
(one of none, email, phone) |
collect_prompt |
string | null |
(max 1000) |
use_ai |
boolean |
|
enabled |
boolean |
|
account_id |
uuid |
(required) |
{
"name": "<name>",
"dm_message": "<dm_message>",
"account_id": "3f8c1e42-9b7a-4d61-a0f5-2c9e1b7d4a83"
}
Answers
| Status | Body | |
|---|---|---|
| 201 | Automation |
Created. |
| 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. |
| 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. |
PATCH /v1/automations/{id}
Update an automation
Scope social:write. Another workspace's answers 404. Can make something publicly visible. Can record a platform charge.
Changes the fields you send. The account cannot change; delete it and create another.
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 | |
|---|---|---|
name |
string |
(min 1; max 80) |
dm_message |
string |
(min 1; max 1000) |
trigger |
string |
(one of comment_keyword, dm_keyword) |
keywords |
string[] |
(<= 20 items) |
match_mode |
string |
(one of contains, exact) |
post_external_ids |
array | null |
(<= 50 items) |
public_replies |
string[] |
(<= 10 items) |
link_url |
string | null |
(max 2048) |
link_label |
string | null |
(max 80) |
collect |
string |
(one of none, email, phone) |
collect_prompt |
string | null |
(max 1000) |
use_ai |
boolean |
|
enabled |
boolean |
Answers
| Status | Body | |
|---|---|---|
| 200 | Automation |
The automation. |
| 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. |
| 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. |
DELETE /v1/automations/{id}
Delete an automation
Scope social:write. Another workspace's answers 404.
Deletes the automation. Leads it captured are kept.
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 | Deleted |
Deleted. |
| 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. |
| 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. |
Shapes
The objects these operations send and answer.
Automation
| Field | Type | |
|---|---|---|
id |
uuid |
(required) |
account_id |
uuid |
(required) |
network |
Network |
(required; one of instagram, facebook, threads, bluesky, x, tiktok, linkedin, youtube, pinterest) |
name |
string |
(required) |
trigger |
string |
(required; one of comment_keyword, dm_keyword) |
keywords |
string[] |
(required) |
match_mode |
string |
(required; one of contains, exact) |
post_external_ids |
array | null |
(required) |
public_replies |
string[] |
(required) |
dm_message |
string |
(required) |
link_url |
string | null |
(required) |
link_label |
string | null |
(required) |
collect |
string |
(required; one of none, email, phone) |
collect_prompt |
string | null |
(required) |
use_ai |
boolean |
(required) |
enabled |
boolean |
(required) |
triggered_count |
integer |
(required) |
dms_sent |
integer |
(required) |
leads_captured |
integer |
(required) |
created_at |
date-time |
(required) |
updated_at |
date-time |
(required) |
AutomationCreate
| Field | Type | |
|---|---|---|
name |
string |
(required; min 1; max 80) |
dm_message |
string |
(required; min 1; max 1000) |
trigger |
string |
(one of comment_keyword, dm_keyword) |
keywords |
string[] |
(<= 20 items) |
match_mode |
string |
(one of contains, exact) |
post_external_ids |
array | null |
(<= 50 items) |
public_replies |
string[] |
(<= 10 items) |
link_url |
string | null |
(max 2048) |
link_label |
string | null |
(max 80) |
collect |
string |
(one of none, email, phone) |
collect_prompt |
string | null |
(max 1000) |
use_ai |
boolean |
|
enabled |
boolean |
|
account_id |
uuid |
(required) |
AutomationList
| Field | Type | |
|---|---|---|
data |
Automation[] |
(required) |
next_cursor |
NullCursor |
Always null; this list is never paged. (required) |
AutomationUpdate
At least one field. The account cannot change.
| Field | Type | |
|---|---|---|
name |
string |
(min 1; max 80) |
dm_message |
string |
(min 1; max 1000) |
trigger |
string |
(one of comment_keyword, dm_keyword) |
keywords |
string[] |
(<= 20 items) |
match_mode |
string |
(one of contains, exact) |
post_external_ids |
array | null |
(<= 50 items) |
public_replies |
string[] |
(<= 10 items) |
link_url |
string | null |
(max 2048) |
link_label |
string | null |
(max 80) |
collect |
string |
(one of none, email, phone) |
collect_prompt |
string | null |
(max 1000) |
use_ai |
boolean |
|
enabled |
boolean |
Deleted
| Field | Type | |
|---|---|---|
deleted |
true |
(required) |
id |
uuid |
(required) |
Network
One of: instagram, facebook, threads, bluesky, x, tiktok, linkedin, youtube, pinterest.
NullCursor
Always null; this list is never paged.
any | null.