Skip to content

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.

GET /inbox/media/{integrationId} recent posts
GET /inbox/stories/{integrationId} stories live right now
GET /inbox/comments/{integrationId}/{mediaId} live comments on a post
POST /inbox/comments/{commentId}/reply reply publicly
POST /inbox/comments/{integrationId}/{commentId}/hide hide or unhide
DELETE /inbox/comments/{integrationId}/{commentId} delete
GET /inbox/{integrationId} list conversations
GET /inbox/{integrationId}/{conversationId} read one
POST /inbox/{integrationId}/send send a message
POST /inbox/{integrationId}/poll read the inbox now
POST /inbox/{integrationId}/subscribe-webhook opt the account into Meta's webhook

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.

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).

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.

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.

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.

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.