> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs-parschat.parstechai.com/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.

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

`user_id` is the customer as we give them to you: `sender.id` on a `message` event, or `client.id`
on a conversation.

```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` |