Instagram is a Meta channel, so it follows WhatsApp’s pattern: one Meta app per Herald deployment, a process-level app secret and verify token, and one channel account per Instagram professional account with its own access token. It differs from WhatsApp in three ways:
- No templates. There is no way to write outside Meta’s 24-hour window, so a cold contact is impossible on Instagram, not just harder.
- Messenger-shaped payloads (
entry[].messaging[]), not WhatsApp’s. - Inbound media is an expiring CDN URL, not a media id. Herald downloads it only when a forwarding binding needs it.
Before you start
Section titled “Before you start”Three things outside Herald must be true before real traffic flows. None of them is Herald configuration:
- An Instagram professional account linked to a Facebook page.
- The
instagram_manage_messagespermission approved in Meta’s App Review, which can take weeks. - The Meta app subscribed to the Instagram webhook, pointing at Herald.
Until App Review clears, the emulator is the way to test the channel end to end.
-
Configure the Meta app. Add the Instagram product, note the App secret and invent a verify token.
-
Start Herald with the channel on.
Terminal window HERALD_INSTAGRAM_APP_SECRET="<app secret>" \HERALD_INSTAGRAM_VERIFY_TOKEN="<your invented token>" \./bin/herald # plus the required settings from the Quick StartSet both or neither; one without the other stops the boot.
-
Subscribe the webhook in Meta’s dashboard with the callback URL
https://<your-herald-host>/webhooks/instagramand the same verify token. -
Register the professional account as a channel account:
Terminal window curl -X POST localhost:8081/admin/accounts \-H "X-Admin-Token: $HERALD_ADMIN_TOKEN" -H "Content-Type: application/json" \-d '{"tenant_id":"tenant-1","channel":"instagram","external_id":"<Instagram account id>","credentials":{"access_token":"<access token>"}}' -
Bind it to an entry agent, the same way as on WhatsApp but with
"channel":"instagram".
What arrives
Section titled “What arrives”| Event | Reaches the agent as |
|---|---|
| A text message | The text |
| Image, audio, video or file | A bracketed description, unless the message also has text |
| A story mention or a shared post | Treated as an image |
| Echoes of the account’s own messages, reads, deliveries | Ignored |
sender is the Instagram-scoped id of the person, and it is also the recipient for any
reply. Instagram does not report a name or a phone, so contact_name and sender_phone are
empty, and reply_to is always null.
Sending
Section titled “Sending”Text and one attachment per message (image, audio, video or file, by public URL) go
to POST /<account id>/messages on the Graph API with the account’s access token as a
Bearer token. Templates fail permanently.
The emulator
Section titled “The emulator”HERALD_INSTAGRAM_EMULATED_ACCOUNT takes one Instagram account id, or a comma-separated
list, whose sends are recorded instead of reaching Meta. The rules are the same as
WhatsApp’s emulator, including the per-account
emulated flag and the environment gate. Pass "canal":"instagram" to the emulator routes:
curl -X POST localhost:8081/admin/emulator/inbound \ -H "X-Admin-Token: $HERALD_ADMIN_TOKEN" -H "Content-Type: application/json" \ -d '{"canal":"instagram","cuenta":"<emulated account id>","desde":"<igsid>","texto":"hola"}'
curl "localhost:8081/admin/emulator/outbox?canal=instagram&cuenta=<emulated account id>" \ -H "X-Admin-Token: $HERALD_ADMIN_TOKEN"Configuration
Section titled “Configuration”| Flag | Environment | Default |
|---|---|---|
--instagram-app-secret | HERALD_INSTAGRAM_APP_SECRET | empty |
--instagram-verify-token | HERALD_INSTAGRAM_VERIFY_TOKEN | empty |
--instagram-graph-base-url | HERALD_INSTAGRAM_GRAPH_BASE_URL | https://graph.facebook.com/v21.0 |
--instagram-emulated-account | HERALD_INSTAGRAM_EMULATED_ACCOUNT | empty (emulator off) |