Live inbox
The inbox is a complete messenger inside the console: every private chat of every connected account — Telegram, Instagram, Facebook — in one list, with whole messages including photos, voice notes and files, live updates, and history from before the account was added. The one rule that surprises people is the read state: reading here does not mark anything read for the customer.
Why nothing is marked read#
Reading a conversation in Oqim never marks it read on Telegram, Instagram or Facebook. The customer does not see a tick or a “seen”. That is deliberate: an owner who scans their messages from the console should not silently tell a hundred customers that their message has been read and answered.
- Oqim contains no call that marks messages read passively — a source-level check fails the build if one is ever added, and the integration tests assert that ingesting, listing, opening and downloading send zero read receipts.
- The Mark as read control in the thread is the only way to send a receipt on the platform, and it tells you that it does. It needs
ai:operate. - Receipt calls are best-effort: if the platform refuses, the API answers
409, and if the account is unreachable,503with nothing sent.
The chat list#
Every account's private chats are in one list. Each row carries the person's avatar, the channel glyph and name, the last message with its time, the account it belongs to and, where the customer wrote last and nobody answered, since when it has been waiting.
- A file in the last message is shown by its kind, not as a bare attachment id: photo, video, voice, audio, document, sticker, animation, video note, location or contact.
- Personal chats are listed for owners and admins (
accounts:manage) and hidden from other roles. The AI never sees them — see Past conversations and privacy. - Group chats and channels are not part of the inbox.
Whole messages#
A message keeps more than its text: its files, the message it replies to, who it was forwarded from, when it was edited or deleted, the reactions on it, and its location or contact card where the platform sent one.
Files: photos, videos, voice and documents#
Every file is stored in Oqim's own storage — local disk or S3 — and served through an authorized API route. A platform URL or file reference is never handed to the browser.
- When a file downloads. Telegram: photos, voice notes and stickers up to a few megabytes are fetched as the message arrives, and everything else the first time somebody opens it; the worker holds the sessions, and an expired file reference is refreshed and retried. Instagram and Facebook: Meta's attachment URLs expire, so those files are downloaded immediately while the message is processed.
- Caps and statuses. 50 MB per file by default (
INBOX_MAX_FILE_MB). A file moves throughPENDING,STORED,TOO_LARGE,UNAVAILABLEandFAILED; the thread shows the status rather than a broken attachment. - Streaming.
GET /ai/conversations/{id}/attachments/{attachment_id}streams the file with its content type and supports HTTP Range, so audio and video can seek. While the file is still downloading it answers202with{status: "PENDING"}and queues the work; when it finishes,ai.conversation.attachment_readyfires.?thumb=1returns the thumbnail. - Files from imported history are not downloaded during the import; they download the first time somebody views them.
Sending files#
Replying with a file is two steps: upload it, then send it. POST /ai/conversations/{id}/uploads takes one file and returns an upload_id (unused uploads expire after 24 hours), and POST /ai/conversations/{id}/reply sends either text, or attachments, or both — a reply needs at least one of the two.
- The reply gate is unchanged by files: the consent rules, the reply window and the platform's own window are checked when the reply is queued and again when it is sent. A file counts as a message.
- A reply may quote an earlier message where the channel supports it; otherwise it is sent as a plain reply and the thread notes that.
- Instagram and Facebook text is plain: no Telegram formatting is applied.
History from before the account was added#
Imports are resumable, obey flood control, and never touch a chat's read state. What arrives is inert — it can never make the AI reply. That is covered in Past conversations and privacy.
Live updates#
- The console subscribes to
GET /api/v1/events/stream. A new message (ai.conversation.message) appends to the open thread and re-sorts the list in place; edits, deletions, reactions and Oqim-side read changes arrive asai.conversation.message_updated; a finished download arrives asai.conversation.attachment_ready. - Those payloads carry identifiers and states only — never the message text. Anything sensitive stays in the console's own fetch, so a webhook endpoint never receives it.
- Polling is the fallback when the stream is unavailable: a few seconds for the open thread, about fifteen for the list.
Endpoints#
All under /api/v1. Everything on a conversation needs ai:read or ai:operate; in a personal chat the attachment route additionally needs accounts:manage. See the API reference for bodies and responses.