Setting Up the Instagram Channel
The Instagram integration uses the Instagram Messaging API (Meta) via OAuth. The process connects an Instagram Business account to your Application. You can authorize the connection either through Instagram directly or through a linked Facebook account.
Prerequisites
- An Instagram Business or Creator account (personal accounts are not supported)
- A Facebook Page linked to the Instagram account
- Administrator access to the Facebook Page (required for the Facebook flow)
- Hub Chatt2.me account with administrator permission on the Application
Connection process
1. Start authorization
In the Hub Chatt2.me dashboard:
- Go to Channels → Activate/Configure → Instagram
- Choose your preferred authorization method:
- Connect with Instagram — authorizes directly via Instagram OAuth
- Connect with Facebook — authorizes via the Facebook account linked to the Instagram Page
2a. Instagram OAuth flow
If you chose Connect with Instagram:
- Log in with your Instagram Business or Creator account
- Authorize the requested permissions (access to message inbox)
- The channel is created automatically
2b. Facebook OAuth flow
If you chose Connect with Facebook:
- Log in with the account that manages the Facebook Page linked to Instagram
- Authorize the requested permissions (access to Instagram message inbox)
- Select the corresponding Facebook Page
- Select the Instagram account linked to that Page
3. Channel ready
After authorization, the channel is created and the UUID becomes available in the channel details.
Comment automations
Comment automations watch direct, top-level text comments and send one literal
Instagram Direct reply. A rule can apply globally or to one media.id, and it
can also post an optional public confirmation beneath the source comment.
Use the API-key application channel with these operations:
GETandPOST /v1/communication/channel/instagram/{applicationChannelId}/comment-automationsGET,PATCH, andDELETE /v1/communication/channel/instagram/{applicationChannelId}/comment-automations/{automationId}GET /v1/communication/channel/instagram/{applicationChannelId}/comment-automations/mediaGET /v1/communication/channel/instagram/{applicationChannelId}/comment-automations/media/{mediaId}
The API key must belong to the application that owns the exact channel. You
may create multiple global rules and multiple rules for the same post. For a
post rule, select recent media or provide its exact media.id; the Hub checks
ownership with Meta before saving.
Triggers accept at most 100 characters. Exact mode respects case and accents;
flexible mode ignores them. Both match a whole word or contiguous phrase
anywhere, so pão matches quero um pão but not pãozinho. Public replies
accept 500 characters and private replies accept 1,000 UTF-8 bytes. If several
rules match, a post-specific rule wins, then the longest trigger, then the
oldest rule.
Subscribe the channel webhook to comment_automation to receive the automated
outbound after Meta accepts and the Hub persists it. The event includes a real
sessionId and message.is_automated: true; it is not duplicated as a
messages event. The automated outbound is not charged. The first inbound
starts the normal expiration window and is not charged; normal charging begins
with the first later non-automated outbound.
Only Feed posts and Reels are supported by this flow. Replies to comments, the connected account's own comments, Stories, Live, and ads are ignored. Comment private replies are a separate provider operation, not permission for ordinary send-message calls. Their eligibility (including time since the comment and any prior private reply) is evaluated by Meta. Provider policies and inbox placement can change; this guide does not guarantee delivery or a seven-day ordinary messaging window.
Create a global rule
curl -X POST 'https://app.chatt2.me/v1/communication/channel/instagram/YOUR_APPLICATION_CHANNEL_ID/comment-automations' \
-H 'x-api-key: YOUR_APPLICATION_API_KEY' \
-H 'content-type: application/json' \
--data '{"scope":"global","trigger":"catalog","matchMode":"flexible","privateReply":"Here is our catalog: https://example.com/catalog","publicReply":"Please check your inbox.","active":true}'
Keep the returned rule id as automationId. A global rule omits mediaId. Reply strings are
literal text; this endpoint does not interpolate template variables.
Discover media and create a post rule
curl 'https://app.chatt2.me/v1/communication/channel/instagram/YOUR_APPLICATION_CHANNEL_ID/comment-automations/media' \
-H 'x-api-key: YOUR_APPLICATION_API_KEY'
Use a returned data[].id; for another media page, pass the returned after cursor as ?after=....
When after is absent there is no next page. If you already know a media ID, fetch
GET /v1/communication/channel/instagram/{applicationChannelId}/comment-automations/media/{mediaId}
to confirm ownership/availability. An owned eligible post can then have its own rule:
curl -X POST 'https://app.chatt2.me/v1/communication/channel/instagram/YOUR_APPLICATION_CHANNEL_ID/comment-automations' \
-H 'x-api-key: YOUR_APPLICATION_API_KEY' \
-H 'content-type: application/json' \
--data '{"scope":"media","mediaId":"YOUR_OWNED_MEDIA_ID","trigger":"price","matchMode":"exact","privateReply":"See the price at https://example.com/product","active":true}'
List, read, replace, and delete
List rules with GET /v1/communication/channel/instagram/{applicationChannelId}/comment-automations?page=1&pageSize=10.
page starts at 1; pageSize defaults to 10 and accepts 1–100. Optional search, scope
(global/media), and active (true/false) filter the results. Read the stored configuration
with GET /v1/communication/channel/instagram/{applicationChannelId}/comment-automations/{automationId}.
PATCH replaces the whole rule, rather than merging individual fields. Send scope, trigger,
matchMode, privateReply, and active every time; send mediaId for media scope and publicReply
when you want to preserve it. Omitting publicReply clears it. Switching to global scope clears
the media association. To deactivate the media rule while preserving its other values:
curl -X PATCH 'https://app.chatt2.me/v1/communication/channel/instagram/YOUR_APPLICATION_CHANNEL_ID/comment-automations/YOUR_AUTOMATION_ID' \
-H 'x-api-key: YOUR_APPLICATION_API_KEY' \
-H 'content-type: application/json' \
--data '{"scope":"media","mediaId":"YOUR_OWNED_MEDIA_ID","trigger":"price","matchMode":"exact","privateReply":"See the price at https://example.com/product","active":false}'
This example intentionally has no public reply. To remove the rule, use
DELETE /v1/communication/channel/instagram/{applicationChannelId}/comment-automations/{automationId};
a successful deletion returns HTTP 200 with {"success": true}. See the automation API reference.
Errors and delivery recovery
All seven management operations, including list/get/delete, require a usable Instagram connection
and can return HTTP 400 INSTAGRAM_RECONNECT_REQUIRED. Reconnect in Hub with comment permissions,
then retry the management request. Missing or out-of-application channels return HTTP 404
APPLICATION_CHANNEL_NOT_FOUND; get/update/delete of a missing rule return HTTP 404
COMMENT_AUTOMATION_NOT_FOUND. Invalid bodies return HTTP 400; media ownership/eligibility or
provider access can also fail, so confirm the selected media instead of repeatedly creating rules.
The optional public reply is attempted only after the private reply succeeds. Its failure does
not undo the accepted private message. The Hub deduplicates by channel and comment ID before
submission: a repeated provider event does not retry a failed private/public attempt, and editing
the rule does not replay that comment. Resolve the connection/content problem for future comments;
do not expect webhook replay to resubmit a private reply. The comment_automation message event
confirms an accepted, persisted private reply, not success of the later public confirmation.
Identifying the recipient (to)
The to field on Instagram uses the IGSID — the unique ID of the user in Instagram Messaging. You obtain this ID from the webhook payload when the user sends you a message first.
Inbound participant fragment:
{
"sender": {
"id": "847495124317323"
}
}
Ordinary send-message calls cannot initiate conversations with users who have never sent you a message. Meta's private reply to an eligible comment is the limited exception used by comment automations; follow-up messaging requires the recipient to answer.
Required permissions
| Meta Permission | Purpose |
|---|---|
instagram_business_basic / instagram_basic | Access basic account information for Instagram Login / Facebook Login |
instagram_business_manage_messages / instagram_manage_messages | Send and receive messages |
instagram_business_manage_comments / instagram_manage_comments | Read comments and send public/private comment replies |
pages_read_engagement | Read Instagram media through the linked Page flow |
pages_manage_metadata | Manage Page metadata |
pages_messaging | Access message inbox via API |
Accounts connected before comment automations must be reconnected to grant the comment permission. Live accounts outside Meta application roles may also require Advanced Access, App Review, and Business Verification.
Data deletion (LGPD / GDPR)
Meta requires that apps integrating Instagram implement a data deletion endpoint. When a user requests data deletion from Meta, the Hub receives a notification and processes the deletion automatically.
Limitations
- Ordinary messages cannot be sent to users who have never started a conversation; eligible comment automation private replies are the limited exception
- Ordinary sends use the ordinary provider messaging path and require an eligible existing conversation; do not assume seven days of general messaging permission
- The Hub uses a 24-hour technical session for Instagram. Session existence/expiration and provider send eligibility are separate; Meta can reject a request even when the Hub has a session
- The private reply triggered by a comment is separate from ordinary follow-up messaging; the recipient must answer before treating it as an ordinary conversation
Media formats and limits
| Type | Formats currently accepted by the Hub | Outbound limit | Inbound mirror limit |
|---|---|---|---|
| Image | JPEG, PNG, GIF, WebP | 8 MB | Provider maximum not published; Hub safety ceiling 2 GiB |
| Audio | AAC, M4A/MP4 and WAV directly; OGG, Opus, MP3/MPEG and WebM input are converted to M4A | 25 MB | Provider maximum not published; Hub safety ceiling 2 GiB |
| Video | MP4; MOV/QuickTime, AVI, MKV and WebM input is converted to H.264/AAC MP4 | 25 MB | Provider maximum not published; Hub safety ceiling 2 GiB |
| Sticker | Instagram platform identifier such as like_heart; HTTP URLs are rejected | Platform value | Platform value |
| Document | Not supported | — | Attachments received from users are preserved when Meta supplies them |
The Hub applies only the audio and video conversions listed above. The 25 MB audio/video ceiling is the Hub's
conservative attachment contract because Meta's current public documentation does not provide a
stable exhaustive MIME-and-size matrix for every Instagram Messaging attachment.
Send an ordinary visual through imageUrl; an HTTP(S) value in stickerUrl returns
CHANNEL_MEDIA_NOT_SUPPORTED.
The inbound 2 GiB value is an operational download protection, not a Meta limit.
Instagram does not support content.text captions with imageUrl, videoUrl, or audioUrl;
companion text is discarded. Outbound documentUrl is not supported at all. Send the media and
its description in separate requests. See the caption support matrix.
Location/contact inputs become text fallbacks; see Content Types.
Identifying the sender (from)
In POST /v1/communication/instagram/message, set from to the application channel UUID or the Instagram username for that channel (with or without a leading @). The UUID always works as a fallback. See Sending messages overview.
Next steps
- Supported content types on Instagram
- Interactive messages — buttons and CTA URL on Instagram