Auto-reply to inbound messages with rules
Automation rules let a session reply on its own. Each rule watches the session's inbound messages and, when one matches, sends a fixed text back through the normal send path — no webhook listener or background worker required on your side. Rules live on the session and are evaluated in order, so you can layer a specific reply on top of a broad one.
This guide shows you how to create, list, update, and delete rules, how to write a match condition, and how the built-in guards keep replies from looping.
- A session in the
readystate — see Connect a Session. - An API key with the operator role or higher, sent as
X-API-Key— see Authentication. - A working understanding of sending messages, since a rule's reply uses the same send path.
The examples assume these shell variables:
export BASE="http://localhost:2785/api"
export API_KEY="YOUR_API_KEY"
export SESSION="8f3c2b1a-9d4e-4c7a-8b2f-1e6d5a4c3b2a"
In production, BASE is your own domain over TLS; the /api prefix is unchanged.
How a rule fires
Three guards run before any rule is evaluated: the message must not be fromMe (so the session's own sends never trigger a self-reply), it must be fresh, and the chat it came from must be outside its cooldown window. Only then are the rules walked in order, and the first match sends the reply and starts that chat's cooldown.
Create a rule
POST /api/sessions/{sessionId}/automation-rules takes a name, the replyText to send back, an optional conditions object, and a per-chat cooldownSeconds.
curl -X POST "$BASE/sessions/$SESSION/automation-rules" \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Greet new enquiries",
"replyText": "Thanks for reaching out — we reply within the hour.",
"conditions": {
"conditions": [
{ "field": "isGroup", "operator": "is", "value": false },
{ "field": "body", "operator": "contains", "value": "price" }
]
},
"cooldownSeconds": 300
}'
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Display name for the rule, max 100 characters. |
replyText | string | yes | Text sent back into the chat when the rule matches, max 4096 characters. |
conditions | object | no | Match conditions in the webhook filter format. Omit or leave empty to match every inbound message. |
cooldownSeconds | number | no | Per-chat quiet period after a reply, in seconds. Default 60, range 0–86400. 0 disables the cooldown for this rule. |
enabled | boolean | no | Whether the rule is active. Default true. |
The response (201 Created) echoes the saved rule:
{
"id": "b7c1d2e3-4f5a-6789-0abc-def012345678",
"sessionId": "8f3c2b1a-9d4e-4c7a-8b2f-1e6d5a4c3b2a",
"name": "Greet new enquiries",
"enabled": true,
"conditions": {
"conditions": [
{ "field": "isGroup", "operator": "is", "value": false },
{ "field": "body", "operator": "contains", "value": "price" }
]
},
"replyText": "Thanks for reaching out — we reply within the hour.",
"cooldownSeconds": 300,
"createdAt": "2026-08-05T09:14:00.000Z",
"updatedAt": "2026-08-05T09:14:00.000Z"
}
Save the id — you need it to fetch, update, or delete the rule.
A session can hold at most AUTOMATION_MAX_PER_SESSION rules (default 32). A create that lands at or over the cap is rejected with 400; rules that already exceed the cap are grandfathered — they keep working and are not deleted. Set the variable to 0 for no cap.
Write a match condition
Conditions reuse the webhook filter format, scoped to the message family. A condition is { field, operator, value, caseSensitive? }, and every condition in a rule must match (AND). Omit conditions or send it empty to match every inbound message — useful as a catch-all that still respects the guards and cooldown.
| Field | Type | Operators | value |
|---|---|---|---|
sender | contact id | is, isNot | array of ids or bare phone numbers |
recipient | contact id | is, isNot | array of ids or bare phone numbers |
mentions | contact id list | is, isNot | array of ids — matches if any mentioned id is in the list |
type | message type | is, isNot | array of text, image, video, audio, voice, document, sticker, location, contact, revoked, unknown |
body | text | contains, equals | a string; set "caseSensitive": true to match case |
isGroup | boolean | is | true or false |
fromMe | boolean | is | true or false |
hasMedia | boolean | is | true or false |
Id matching is dialect-aware: a bare phone number, a @c.us JID, and the underlying @lid for the same contact all match each other, so "628123456789" and "628123456789@c.us" are equivalent. A condition is validated on save — an unknown field, an unsupported operator, a wrong value type, or an oversized body string returns 400 with a field-level message.
List, update, and delete
All management routes are scoped to a session, so a wrong-session ruleId resolves to 404.
| Action | Method + path |
|---|---|
| List the session's rules | GET /api/sessions/{sessionId}/automation-rules |
| Get one | GET /api/sessions/{sessionId}/automation-rules/{ruleId} |
| Update | PUT /api/sessions/{sessionId}/automation-rules/{ruleId} |
| Delete | DELETE /api/sessions/{sessionId}/automation-rules/{ruleId} |
List rules
curl "$BASE/sessions/$SESSION/automation-rules" \
-H "X-API-Key: $API_KEY"
Rules come back in evaluation order — oldest first, with the rule id as the same-second tiebreak — so the order you read here is the order the session walks them:
[
{
"id": "b7c1d2e3-4f5a-6789-0abc-def012345678",
"sessionId": "8f3c2b1a-9d4e-4c7a-8b2f-1e6d5a4c3b2a",
"name": "Greet new enquiries",
"enabled": true,
"conditions": null,
"replyText": "Thanks for reaching out — we reply within the hour.",
"cooldownSeconds": 60,
"createdAt": "2026-08-05T09:14:00.000Z",
"updatedAt": "2026-08-05T09:14:00.000Z"
}
]
Update a rule
PUT accepts the same fields as create. Every field is optional — send only what changes. Disabling a rule stops it firing immediately, without deleting it:
curl -X PUT "$BASE/sessions/$SESSION/automation-rules/b7c1d2e3-4f5a-6789-0abc-def012345678" \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "enabled": false }'
The response (200 OK) is the updated rule in the same shape as create.
Delete a rule
curl -X DELETE "$BASE/sessions/$SESSION/automation-rules/b7c1d2e3-4f5a-6789-0abc-def012345678" \
-H "X-API-Key: $API_KEY" -i
A successful delete returns 204 No Content with an empty body.
How the guards bound reply loops
A rule that replies to anything would otherwise answer every message, including the ones it sent and the ones the contact sent seconds ago. Three guards keep that from happening:
fromMe. A rule never fires on a message the session itself sent, so a rule's own reply cannot trigger itself.- Freshness. A rule only fires on a recent message, so replaying history or a delayed webhook does not produce a stale reply.
- Per-chat cooldown. After a rule replies in a chat, that chat is silent to it for
cooldownSeconds(default60). This is the guard against two auto-repliers answering each other forever — disable it knowingly by settingcooldownSeconds: 0.
The reply itself goes through the normal send path, so it is subject to the same rate limiting, send pacing, and audit logging as any other send.
Common errors
Errors use the standard envelope { statusCode, message, error }. On a 400 validation failure, message is an array of field-level strings.
| Status | Cause | Fix |
|---|---|---|
400 Bad Request | A name or replyText over its cap, a malformed conditions object, the session is at its rule cap (AUTOMATION_MAX_PER_SESSION, default 32), or the session has no live engine. | Check the message array for the offending field; delete a rule before adding another at the cap. |
401 Unauthorized | Missing, invalid, or out-of-scope X-API-Key. | Send a valid key scoped to this session. See Authentication. |
403 Forbidden | The key is valid and in scope but its role is below operator. | Use an operator-or-higher key. |
404 Not Found | The session or rule id does not exist, or the rule belongs to a different session. | Confirm both sessionId and the rule id. |
Next steps
- Webhooks — the filter format rules reuse, and how to watch the replies a rule sends.
- Safe sending — the send path a rule's reply passes through, including pacing.
- API reference — the full automation-rule endpoint schemas.
Automation rules are REST-only in this release — there is no MCP tool for them yet.