# Comments to DM

A keyword rule that DMs everyone who comments it on Instagram or Facebook, and posts a public reply on TikTok, Threads, LinkedIn and YouTube.

import { Tab, Tabs } from 'fumadocs-ui/components/tabs';

A comment automation watches your posts for a keyword. When someone comments it, Zernio sends them a DM, replies publicly under their comment, or both. One API call sets it up.

## What you can build

- **Lead magnets.** "Comment GUIDE and I'll DM you the PDF", with `POST /v1/comment-automations`.
- **Price and link requests.** Answer every "PRICE" comment with a DM that carries a link button.
- **Public auto-replies.** Thank or answer commenters under their comment on TikTok, Threads, LinkedIn and YouTube.
- **Story replies and mentions.** DM people who reply to your Instagram story with a keyword, or mention you in theirs.
- **A doorway into a chat flow.** Add a button that continues the conversation in a [workflow](/products/automations/workflows).

## Platforms

<ProductPlatforms product="comments-to-dm" />

| Platform | What the automation sends | How fast |
|---|---|---|
| <PlatformName slug="instagram" page="inbox" /> | A DM with text, buttons, quick replies or a product card, plus an optional public reply. Follower rules and story triggers | Seconds |
| <PlatformName slug="facebook" page="inbox" /> | A DM with text, buttons, quick replies or a product card, plus an optional public reply | Seconds |
| <PlatformName slug="tiktok" page="inbox" /> | A public reply only. Business app connections | Seconds |
| <PlatformName slug="threads" page="inbox" /> | A public reply only | Seconds |
| <PlatformName slug="linkedin" page="inbox" /> | A public reply only | Up to 10 minutes on posts from the last 24 hours, longer on older ones |
| <PlatformName slug="youtube" page="inbox" /> | A public reply only | Up to 10 minutes on posts from the last 24 hours, longer on older ones |

TikTok, Threads, LinkedIn and YouTube have no private reply to a comment, so `commentReply` is required there and the DM fields are refused. X accounts are refused too.

## How it works

An automation belongs to one `accountId`. With `platformPostId` it watches one post. Without it, it watches every post on the account.

Each matching comment is checked against `keywords` with `matchMode`: `contains` (the default) matches inside words, `word` matches whole words, `exact` matches the whole comment.

On Instagram and Facebook the DM goes out as Meta's private reply, which opens a conversation in the [inbox](/products/inbox). The public reply waits for that DM by default (`publicReplyPolicy: after_dm`).

`trigger` decides what the automation answers. The default is `comment`; the others are Instagram only.

| `trigger` | Fires when |
|---|---|
| `comment` | Someone comments a keyword on a post or reel |
| `story_reply` | Someone replies to your story with a keyword |
| `story_mention` | Someone mentions your account in their story. Leave `keywords` empty |
| `live_comment` | Someone comments a keyword during your live broadcast |

## Your first request

You need an API key, a profile id and the `accountId` of a connected Instagram or Facebook account. This DMs a price list to everyone who comments "price" on one post.

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data } = await zernio.commentautomations.createCommentAutomation({
  body: {
    profileId: '66a1f0c2a4b9d3e8f1a2b3c4',
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    name: 'Price list',
    platformPostId: '17912345678901234',
    keywords: ['price'],
    matchMode: 'word',
    dmMessage: 'Hey {{first_name}}! Here are our prices.',
    buttons: [{ type: 'url', title: 'See prices', url: 'https://example.com/prices' }],
    commentReply: 'Sent you a DM, {{first_name}}!',
  },
});

console.log(data.automation.id);
```
</Tab>
<Tab value="Python">
```python
from zernio import Zernio

client = Zernio()

created = client.comment_automations.create_comment_automation(
    profile_id="66a1f0c2a4b9d3e8f1a2b3c4",
    account_id="66b2e19d8c3f5a7e9d0b1c2d",
    name="Price list",
    platform_post_id="17912345678901234",
    keywords=["price"],
    match_mode="word",
    dm_message="Hey {{first_name}}! Here are our prices.",
    buttons=[{"type": "url", "title": "See prices", "url": "https://example.com/prices"}],
    comment_reply="Sent you a DM, {{first_name}}!",
)

print(created["automation"]["id"])
```
</Tab>
<Tab value="curl">
```bash
curl -X POST "https://zernio.com/api/v1/comment-automations" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "66a1f0c2a4b9d3e8f1a2b3c4",
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "name": "Price list",
    "platformPostId": "17912345678901234",
    "keywords": ["price"],
    "matchMode": "word",
    "dmMessage": "Hey {{first_name}}! Here are our prices.",
    "buttons": [{ "type": "url", "title": "See prices", "url": "https://example.com/prices" }],
    "commentReply": "Sent you a DM, {{first_name}}!"
  }'
```
</Tab>
</Tabs>

Response (`200`):

```json
{
  "success": true,
  "automation": {
    "id": "66e7c3d4e5f6a7b8c9d0e1f2",
    "name": "Price list",
    "platform": "instagram",
    "trigger": "comment",
    "platformPostId": "17912345678901234",
    "keywords": ["price"],
    "matchMode": "word",
    "dmMessage": "Hey {{first_name}}! Here are our prices.",
    "buttons": [{ "type": "url", "title": "See prices", "url": "https://example.com/prices" }],
    "commentReply": "Sent you a DM, {{first_name}}!",
    "isActive": true,
    "createdAt": "2026-11-10T09:00:00Z"
  }
}
```

The automation is active at once. On Instagram, `platformPostId` is the media id from `GET /v1/external-posts`, not the number in an instagram.com link.

## Examples

### Reply publicly on TikTok

On the reply-only platforms, send `commentReply` and no DM fields. Omit `platformPostId` to watch every video on the account.

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
await zernio.commentautomations.createCommentAutomation({
  body: {
    profileId: '66a1f0c2a4b9d3e8f1a2b3c4',
    accountId: '66b2e19d8c3f5a7e9d0b1c2e',
    name: 'Where to buy',
    keywords: ['where', 'buy', 'link'],
    matchMode: 'word',
    commentReply: 'Hi {{first_name}}, it is on our site: example.com/shop',
  },
});
```
</Tab>
<Tab value="Python">
```python
client.comment_automations.create_comment_automation(
    profile_id="66a1f0c2a4b9d3e8f1a2b3c4",
    account_id="66b2e19d8c3f5a7e9d0b1c2e",
    name="Where to buy",
    keywords=["where", "buy", "link"],
    match_mode="word",
    comment_reply="Hi {{first_name}}, it is on our site: example.com/shop",
)
```
</Tab>
<Tab value="curl">
```bash
curl -X POST "https://zernio.com/api/v1/comment-automations" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "66a1f0c2a4b9d3e8f1a2b3c4",
    "accountId": "66b2e19d8c3f5a7e9d0b1c2e",
    "name": "Where to buy",
    "keywords": ["where", "buy", "link"],
    "matchMode": "word",
    "commentReply": "Hi {{first_name}}, it is on our site: example.com/shop"
  }'
```
</Tab>
</Tabs>

With the default `repeatPolicy` of `once`, each person gets one reply from this automation.

### Continue into a workflow

A postback button whose `payload` is `zernio:workflow:<workflowId>` starts that [workflow](/products/automations/workflows) when the commenter taps it. The workflow must be active, on the same account and profile.

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
await zernio.commentautomations.createCommentAutomation({
  body: {
    profileId: '66a1f0c2a4b9d3e8f1a2b3c4',
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    name: 'Book a demo',
    keywords: ['demo'],
    dmMessage: 'Want to book a demo, {{first_name}}?',
    buttons: [{ type: 'postback', title: 'Yes, book it', payload: 'zernio:workflow:66d4a1b2c3e4f5a6b7c8d9e1' }],
  },
});
```
</Tab>
<Tab value="Python">
```python
client.comment_automations.create_comment_automation(
    profile_id="66a1f0c2a4b9d3e8f1a2b3c4",
    account_id="66b2e19d8c3f5a7e9d0b1c2d",
    name="Book a demo",
    keywords=["demo"],
    dm_message="Want to book a demo, {{first_name}}?",
    buttons=[{"type": "postback", "title": "Yes, book it", "payload": "zernio:workflow:66d4a1b2c3e4f5a6b7c8d9e1"}],
)
```
</Tab>
<Tab value="curl">
```bash
curl -X POST "https://zernio.com/api/v1/comment-automations" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "66a1f0c2a4b9d3e8f1a2b3c4",
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "name": "Book a demo",
    "keywords": ["demo"],
    "dmMessage": "Want to book a demo, {{first_name}}?",
    "buttons": [{ "type": "postback", "title": "Yes, book it", "payload": "zernio:workflow:66d4a1b2c3e4f5a6b7c8d9e1" }]
  }'
```
</Tab>
</Tabs>

The DM alone does not start the workflow: the commenter must tap. Buttons, unlike quick replies, also show in Instagram's message requests folder.

### See who got the DM

[List automation logs](/comment-automations/list-comment-automation-logs) returns each comment that triggered the automation, with its `status` (`sent`, `failed`, `skipped`, `gated` or `pending`) and link clicks.

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
const { data: failed } = await zernio.commentautomations.listCommentAutomationLogs({
  path: { automationId: '66e7c3d4e5f6a7b8c9d0e1f2' },
  query: { status: 'failed' },
});

for (const log of failed.logs) console.log(log.commenterUsername, log.error);
```
</Tab>
<Tab value="Python">
```python
failed = client.comment_automations.list_comment_automation_logs(
    automation_id="66e7c3d4e5f6a7b8c9d0e1f2",
    status="failed",
)

for log in failed["logs"]:
    print(log["commenterUsername"], log["error"])
```
</Tab>
<Tab value="curl">
```bash
curl "https://zernio.com/api/v1/comment-automations/66e7c3d4e5f6a7b8c9d0e1f2/logs?status=failed" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"
```
</Tab>
</Tabs>

Comments that matched no keyword leave no log. The response's `misses` counts them, with a few samples, so you can tell when a keyword catches nothing.

## How it behaves

- **One answer per comment.** Instagram and Facebook comments arrive by webhook, and a 10-minute sweep catches any the webhook missed. Each comment is answered at most once.
- **Per-post wins.** A post can have one active per-post automation. Account-wide automations stack, and a per-post one takes priority on its post.
- **Automations beat workflows.** When an automation's keywords match a comment, a `comment` workflow on the same comment does not start.
- **Personal fields.** `{{first_name}}`, `{{name}}` and `{{username}}` fill in from the commenter, in the DM, the reply and button titles.
- **More options.** `audience` (Instagram followers only or not), `repeatPolicy`, `actions` (like or hide the comment) and `alsoMatchInDms` are on [Create comment automation](/comment-automations/create-comment-automation).
- **Billing.** Each DM sent is one [automation run](/pricing/automations) and one [outbound message](/pricing/inbox).

## If it fails

| Error | Cause | Fix |
|---|---|---|
| `400` `platform_not_supported` | The account is on X or another unsupported platform | Use one of the platforms above |
| `400` `invalid_field_value` | A DM field sent for TikTok, Threads, LinkedIn or YouTube | Send only `commentReply` there |
| `400` `missing_required_field` | No `dmMessage` on Instagram or Facebook, or no `commentReply` elsewhere | Add the field named in `param` |
| `409` | An active automation already covers this post | Update or pause the existing one |
| Nothing fires on Instagram | `platformPostId` is the number from an instagram.com link | Use the media id from `GET /v1/external-posts` |

## Related

- [Create comment automation](/comment-automations/create-comment-automation)
- [List comment automations](/comment-automations/list-comment-automations)
- [Update comment automation](/comment-automations/update-comment-automation)
- [ManyChat-style automations](/guides/manychat-patterns#comment-to-dm-with-buttons)
- [Comments API](/products/comments)
- [Workflows](/products/automations/workflows)