> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs-parschat.parstechai.com/documentation/guides/instagram-automation/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-parschat.parstechai.com/_mcp/server. # Instagram automation > Keyword triggers, public comment replies, private follow-ups, follow gating, and targeting one Instagram post. > **Warning** > > **Pre-release `v1`, published 2026-09-14.** This page commits to the shape of the API, not to a > date. We will build exactly what is documented here. The shape can still change until > **2026-10-14**; after that, changes follow [Versioning and stability](/documentation/reference/versioning-stability). > > The banner comes off one page at a time as each route goes live. While it is here, build against > the contract and assume the route is not callable yet. Automation rules are **Instagram only**. They let your customers automate replies without raising a support ticket with you, or with us. A rule belongs to one Instagram channel and fires on a **direct message** or a **comment** on a post or reel. ## A DM rule **`cURL`** ```bash cURL curl -X POST https://api-chat.parstechai.com/v1/robots/rbt_8fK2mQ/channels/chn_3pQ7xL/rules \ -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "name": "Price enquiry to catalogue", "trigger": { "on": "dm", "keywords": ["price", "how much", "cost"], "match": "contains" }, "actions": [ { "type": "text", "body": "Hello! Here is our catalogue." }, { "type": "link", "url": "https://shop.example/catalogue", "label": "Catalogue" } ], "is_active": true }' ``` **`Python`** ```python Python rule = requests.post( f"{BASE}/robots/rbt_8fK2mQ/channels/chn_3pQ7xL/rules", headers={"Authorization": f"Bearer {API_KEY}"}, json={ "name": "Price enquiry to catalogue", "trigger": {"on": "dm", "keywords": ["price", "how much", "cost"], "match": "contains"}, "actions": [ {"type": "text", "body": "Hello! Here is our catalogue."}, {"type": "link", "url": "https://shop.example/catalogue", "label": "Catalogue"}, ], "is_active": True, }, timeout=10, ).json() ``` **`Response`** ```json Response { "id": "rul_7nQ2xK", "name": "Price enquiry to catalogue", "trigger": { "on": "dm", "keywords": ["price", "how much", "cost"], "match": "contains" }, "actions": [ { "type": "text", "body": "Hello! Here is our catalogue." }, { "type": "link", "url": "https://shop.example/catalogue", "label": "Catalogue" } ], "is_active": true, "created_at": "2026-09-20T11:12:00Z" } ``` Keywords match in any language the channel supports. Write them the way your customer's audience actually types. | Field | Values | | ---------------- | -------------------------------- | | `trigger.on` | `dm` or `comment` | | `trigger.match` | `contains` or `equal` | | `actions[].type` | `text`, `image`, `link`, `delay` | > **Info** > > The `actions` vocabulary is closed: `text`, `image`, `link`, `delay`. An unknown `type` returns > `422`. An open-ended array would turn every future action type into a silent breaking change for > anyone who guessed. ## A comment rule does three things ```json { "name": "Comment on launch post, public reply and DM", "trigger": { "on": "comment", "keywords": ["available", "in stock"], "match": "contains", "media_id": "17895695668004550" }, "reply_in_comment": { "body": "Yes, it is in stock. Details sent by DM." }, "then_send_dm": [ { "type": "text", "body": "Hello! This product is available." }, { "type": "link", "url": "https://shop.example/p/1042", "label": "Buy" } ], "is_active": true } ``` | | | | ------------------ | -------------------------------------------------------------------------------- | | `media_id` | Scopes the rule to one post or reel. Omit it and the rule applies to every post. | | `reply_in_comment` | Posts a public reply under the comment | | `then_send_dm` | Follows up privately, which is what turns a public comment into a private sale | Your customer picks the post themselves, exactly as our own users do, so you will want to show them a post list to choose from. That is what [the media endpoint](/api-reference/instagram-media/list-media) is for. ## Follow gating A rule can withhold its answer until the commenter follows the page. It is a core Instagram growth mechanic. ```json { "name": "Price enquiry, follow first", "trigger": { "on": "comment", "keywords": ["price", "1"], "match": "contains" }, "reply_in_comment": { "body": "Your answer has been sent by DM." }, "then_send_dm": [ { "type": "text", "body": "Hello, message us for more help." } ], "require_follow": { "enabled": true, "prompt": "Follow the page first, then tap the button below.", "button_text": "I followed", "retry_prompt": "We checked, and the follow is not confirmed yet. Follow the page, then tap again." }, "is_active": true } ``` When `require_follow.enabled` is true and the sender does not follow the page, the rule holds its payload. It sends `prompt` with a button instead, then delivers the real response once the follow is verified. `retry_prompt` covers someone tapping the button before actually following. The button is a postback, a tap inside Instagram rather than a link out. You do not implement the verification round-trip. The rule engine does. ### A held response expires after 24 hours After that, the person has to trigger the rule again. ### Checking follow status ```bash curl "https://api-chat.parstechai.com/v1/robots/rbt_8fK2mQ/channels/chn_3pQ7xL/follow-status?user_id=iguser_88213" \ -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxx" ``` **`Response`** ```json Response { "user_id": "iguser_88213", "follows_business": true, "checked_at": "2026-09-20T11:30:00Z" } ``` > **Warning** > > **`follows_business` is three-valued: `true`, `false`, or `null`.** `null` means Meta did not > confirm the relationship. It does not mean "does not follow". > > **`null` is not `false`.** We fail open: on `null`, the answer is sent. A withheld answer to a > genuine follower reads to the end user as the automation being broken, which is worse than > answering someone who has not followed. Treat `null` the same way. ## Managing rules | Method | Path | | -------- | -------------------------------------------------------- | | `GET` | `/v1/robots/{robot_id}/channels/{channel_id}/rules` | | `GET` | `/v1/robots/{robot_id}/channels/{channel_id}/rules/{id}` | | `PATCH` | `/v1/robots/{robot_id}/channels/{channel_id}/rules/{id}` | | `DELETE` | `/v1/robots/{robot_id}/channels/{channel_id}/rules/{id}` | List is paginated and filterable by `is_active` and `on`. To pause a rule without losing it, patch `is_active` to `false`. Full parameters and a live playground: [Automation reference](/api-reference/instagram-automation/create-rule). ## Errors | Status | Meaning | | ------ | ---------------------------------------------------------------------------------------------------------------- | | `403` | Plan does not include automation, or the role is missing | | `404` | No such rule, or not yours | | `422` | A comment rule with neither `reply_in_comment` nor `then_send_dm`, an unknown action `type`, or empty `keywords` | > Keyword triggers, public comment replies, private follow-ups, follow gating, and targeting one Instagram post.