Replying to customers
Your operators answer end customers from your panel, not ours. A conversation arrives on your webhook, your agent types a reply, and you post it back through this API. The message reaches the customer on whichever channel the conversation belongs to, so the same call covers Instagram and Telegram.
Send a text reply
Every event carries a conversation_id. That is all you need to reply.
external_id is your idempotency key. Send the same one twice and you get the original message
back rather than a duplicate, so retrying after a timeout is safe.
A status of queued means the channel is rate limited and we will deliver the message when
capacity frees. Nothing is discarded.
Send a file or a voice note
Two steps: upload the file, then send a message referencing it.
It is two steps rather than one so that a large upload failing mid-transfer can be retried on its own, without re-sending the message.
conversation_id is required at upload. An attachment belongs to the channel it will be sent
on, so we need the destination before the file can be stored. One upload serves one conversation
— to send the same file to two conversations, upload it twice.
1. Upload
2. Send it
attachments comes back empty on the send response, even when the message carried one. Use
the id you got from the upload rather than expecting it echoed here. The file is on the message
— the message webhook event delivers it with its attachments populated.
Size is a plan dimension, checked per file at upload:
The usual alternative spellings are accepted too (image/jpg, video/avi, audio/m4a, audio/wave, audio/x-wav, audio/vnd.wave). This is exactly what Instagram delivers: GIF, WebP and other formats are refused at upload, rather than accepted and then failing when you send.
Every plan with API access sends all four types — the difference between tiers is size, not which kinds of file you may send. If your agreement sets different caps, those apply instead.
A file type outside this list, or a file over your plan’s cap, is rejected with
422 validation_failed before it is stored; for the size cap, details carries kind, cap_mb
and size_bytes. A kind of file your plan does not include is 403 attachment_not_in_plan.
Unsent uploads are discarded after 24 hours.
Full limits, and how they relate to Instagram’s own, are on Rate limits.
A voice note is just an attachment with type: voice. Upload the audio file the same way; we
infer the type from the media type, or you can pass type=voice explicitly.
Show a typing indicator
Call this when your agent starts typing, so the customer sees the same cue they would in any chat app.
The indicator expires by itself after a few seconds. Send it again while the agent keeps typing rather than sending a stop.
Build an operator queue
Filter conversations by status to find the ones waiting for a human.
unread_count is the number of customer messages since the last reply from an operator, the AI or
an agent: what is still waiting for an answer.
You do not have to poll this. The chat webhook event fires the moment a conversation enters
operator_attention, so use the event to update your queue and this endpoint to rebuild it after
a restart.
Close a conversation
Closing is a context boundary, not a deletion. The transcript stays and remains readable. What
changes is that the next conversation with that customer starts without the earlier history as
AI context. See context_reset.