Inbox
The Instagram side of a channel, handled by hand: its publications and the comments under them (read, reply, hide, delete), and the direct messages (the conversations, and sending one). What answers on its own (a template posed on a publication) is on Automations. These endpoints do not exist in the original project, and they are Instagram-only.
Endpoints
Section titled “Endpoints”GET /inbox/media/{integrationId} recent postsGET /inbox/stories/{integrationId} stories live right nowGET /inbox/comments/{integrationId}/{mediaId} live comments on a postPOST /inbox/comments/{commentId}/reply reply publiclyPOST /inbox/comments/{integrationId}/{commentId}/hide hide or unhideDELETE /inbox/comments/{integrationId}/{commentId} deleteGET /inbox/{integrationId} list conversationsGET /inbox/{integrationId}/{conversationId} read onePOST /inbox/{integrationId}/send send a messagePOST /inbox/{integrationId}/poll read the inbox nowPOST /inbox/{integrationId}/subscribe-webhook opt the account into Meta's webhookFinding something to attach to
Section titled “Finding something to attach to”GET /inbox/media/{integrationId} lists recent posts. Their id is what a
pose stores as mediaId.
GET /inbox/stories/{integrationId} lists the stories live right now: the
only window in which a template can be
posed on a story.
Live comments
Section titled “Live comments”GET /inbox/comments/{integrationId}/{mediaId} reads the comments on a post.
POST /inbox/comments/{commentId}/reply answers publicly (201):
{ "integrationId": "instagram-123", "message": "Envoyé, check tes DM 📬" }message is 1 to 2,200 characters, Instagram’s cap on a comment.
POST /inbox/comments/{integrationId}/{commentId}/hide hides a comment
({ "hide": true }) or shows it again ({ "hide": false }). A hidden comment
stays visible to its author only; the list of live comments carries
hidden: true on it. DELETE /inbox/comments/{integrationId}/{commentId}
deletes it on Instagram, for good. Both answer { "success": true } (201
for the hide, 200 for the delete).
List conversations
Section titled “List conversations”GET /inbox/{integrationId}?box=needs_reply&limit=25
Conversations are read from the copy of the inbox UseNadia keeps (every message received or sent, kept current by Meta’s webhook and an hourly catch-up), not from Instagram at call time: the list is complete, with no cap.
| Parameter | Description |
|---|---|
box |
The box: needs_reply (the default, somebody waits for an answer), conversations (everything but full auto), auto (only an automation has spoken), thanks (what waits is a thank-you or an emoji), organic (people no automation ever wrote to), read (marked read) or all |
limit |
1 to 200, 25 by default; anything unparseable falls back to 25 |
after |
The cursor of the last conversation of the previous page. Leave it out for the first page |
The answer is still an array. Each conversation carries id (pass it to
Read one conversation), conversationId
(Instagram’s id, null until Instagram has named the thread), personId (the
recipientId of a send), participants, messages.data (the newest message,
for a preview), inbox (the box and when the 24 hour window closes) and
cursor. Fewer rows than limit means it was the last page.
Conversations come back sorted by [box, last message from the person], which
is what keeps an automatic message from pushing a real question down the list.
The boxes are ours: Instagram does not expose its own Primary / General /
Requests folders through the API.
Read one conversation
Section titled “Read one conversation”GET /inbox/{integrationId}/{conversationId}?limit=50
conversationId is the id the list returned: Instagram’s conversation id,
or the person’s id while Instagram has not named the thread yet. Returns the
thread, read from the same copy: messages, attachments, the button templates
the flows sent, and story replies. Newest first, limit at a time (1 to 200,
50 by default); messages.paging.before is there when older ones remain, to
pass back as before.
A message that quotes another one carries reply_to.mid, the id of the
quoted message. Instagram only tells it when the message arrives: only
messages received live and yours carry it, never one caught up by the sync.
Send a message
Section titled “Send a message”POST /inbox/{integrationId}/send
| Field | Type | Required | Description |
|---|---|---|---|
recipientId |
string |
Yes | The person’s Instagram-scoped ID (IGSID) |
text |
string |
Yes | 1 to 1,000 characters |
linkUrl |
string |
No | Absolute URL. Sent as a button |
linkButtonText |
string |
No | ≤ 20, Instagram’s cap |
replyTo |
string |
No | The id of a message of the thread, yours or the person’s: the message quotes it, like Instagram’s « Reply » |
{ "recipientId": "17841400000000000", "text": "Le voilà 👇🏻", "linkUrl": "https://example.com/guide-malte", "linkButtonText": "Le guide Malte"}POST /inbox/{integrationId}/poll reads the 25 most recent conversations now
and hands any message a missed webhook would have delivered to the
automations waiting on that person (a button tapped, an email typed). The
hourly catch-up keeps the inbox current but does not move the automations
along: the webhook is the normal path, this is the catch-up.
It answers:
{ "conversations": 25, "handled": 1 }conversations is how many were read from Meta, handled how many inbound
messages reached a waiting automation.
Subscribe to the webhook
Section titled “Subscribe to the webhook”POST /inbox/{integrationId}/subscribe-webhook opts the account into Meta’s
webhook delivery for comments and messages. That webhook is what makes an
automation fire in seconds; without it, nothing fires until a poll is run by
hand. UseNadia does it itself when a channel connects, when a template is
posed, and at every restart.