Keyword auto-reply
Every bot on the RCS Panel can carry a small rule table that reacts to inbound free-form messages. When a recipient replies to your bot with text (not a suggestion tap), the panel walks the rules in order, and the first match wins.
Common patterns:
STOP→ add the phone to your account-wide opt-out list.SALES→ reply with a canned message + forward the reply to[email protected].- Regex like
^i want (a )?refund→ fire a webhook so your CRM ticks a support ticket open. - One optional fallback rule per bot (empty keyword) catches everything nothing else matched.
Access
Bot scope from the caller's JWT bots claim applies: a rule under a
bot the member can't act on returns 403 bot_not_assigned.
Endpoints
GET /api/v1/rcs/auto-replies/{bot_id}
POST /api/v1/rcs/auto-replies/{bot_id}
PATCH /api/v1/rcs/auto-replies/{bot_id}/{rule_id}
DELETE /api/v1/rcs/auto-replies/{bot_id}/{rule_id}
POST /api/v1/rcs/auto-replies/{bot_id}/reorder
GET /api/v1/rcs/auto-replies/{bot_id}/fires?limit=100Rule schema
{
"keyword": "STOP",
"match_mode": "exact",
"order_index": 0,
"actions": ["reply", "add_to_opt_out"],
"reply_message": "You've been unsubscribed. Reply START to resubscribe.",
"forward_email": "",
"webhook_url": "",
"add_to_list_id": "",
"enabled": true
}match_mode
Actions
Every rule can declare one or more of these primitives, order inside the array is cosmetic; the engine always fires them in a fixed order (reply first, then opt-out mutation, then external side-effects):
Fallback rule
Set keyword to "" to build the fallback. Only one fallback per
bot is allowed, a second POST returns 409 fallback_exists. The
fallback fires only when nothing else in the table matched.
Ordering & reorder
Rules render in order_index ascending (ties broken by creation
time). To move a rule, send its full list of rule_ids in the desired
order:
POST /api/v1/rcs/auto-replies/{bot_id}/reorder
{ "rule_ids": ["<id_1>", "<id_2>", "<id_3>"] }Unknown IDs are silently skipped. The endpoint writes
order_index = index for every element in the array.
Fires log
Every match is recorded in the rule's fires log with the matched
keyword, inbound message id, reply message id (when the reply action
fired), and the ordered list of actions that actually ran. Read it via:
GET /api/v1/rcs/auto-replies/{bot_id}/fires?limit=100Sorted newest first. A match is recorded as the inbound message is handled, so it is usually visible within a fraction of a second.
Defaults on bot activation
When an admin activates a fresh bot, the router seeds two rules:
STOP → add_to_opt_out + reply "You've been unsubscribed. Reply START to resubscribe."
START → remove_from_opt_out + reply "You're back, welcome!"Idempotent per (user, bot), re-activating an existing bot doesn't
blow away edits the operator made.
Regex tips
- Anchor to the start with
^unless you wantcontainssemantics. - Group the parts you want to keep out of the match with non-capturing
groups:
(?:foo|bar). - Wrap variable text in
.*sparingly, a runaway.*can inflate the fire log on chatty inbounds. - Compile fails at write-time; the panel modal shows the compiler error inline so you don't waste a fire.
Webhook payload
{
"event": "auto_reply.fired",
"user_id": "9f…",
"bot_id": "3c…",
"rule_id": "de…",
"keyword": "SALES",
"match_mode": "starts_with",
"recipient_phone": "+919876543210",
"recipient_name": "Priya",
"inbound_text": "SALES please quote for 500 units",
"inbound_message_id": "msg_a1b2c3",
"fired_at": "2026-07-16T09:12:03Z"
}Webhook targets go through the standard SSRF guard: only public HTTPS hosts, no follow-redirects to internal IPs. Non-2xx responses are logged but the fire row still lands.