Overview
Every call toPOST /api/messages creates a message document in Tuco. This
page describes the shape of that document, the lifecycle it moves through, and
how delivery works under the hood.
Messages sent via API appear in Unibox alongside manually sent and incoming
messages. Same recipient = same thread.
Message object
string
Unique message ID (MongoDB ObjectId)
string
Body text
string
"imessage" | "sms" | "email". Defaults to "imessage" when omitted from the API.string
Current lifecycle state. See Status lifecycle below.
string
Line used to send
string
Denormalized phone from the line
string
Denormalized email from the line
string
Recipient phone number
string
Recipient email address
string
Display name
string
Linked lead (if any)
string
Clerk organization ID
string
Clerk user ID who created the message
string
received for inbound (a reply from the lead), sent for outbound. Inbound messages are where lead-sent photos live.Attachment fields (photos & files)
Attachment fields (photos & files)
string[]
Present only when the message has attachments. Public, signed, 30-day URLs —
GET each one (no auth header) to download the real image/file bytes with the correct Content-Type. This is how you retrieve a photo a lead sent you: the raw attachmentUrls below point at private storage and return 403 on their own.string[]
Filenames aligned 1:1 with
attachmentDownloadUrls (e.g. ["IMG_1380.jpeg"]).string[]
Raw storage URLs (private for inbound / Tuco-hosted). Kept for reference — use
attachmentDownloadUrls to fetch bytes.Scheduling & settings fields
Scheduling & settings fields
Delivery tracking fields
Delivery tracking fields
Error & fallback fields
Error & fallback fields
Integration fields
Integration fields
string
Creation timestamp
string
Last update timestamp
Status lifecycle
Pre-send checks (why a message stays queued)
These checks run before a message is sent. None of them cause an API error — the message is accepted and waits.You can check why a message is still queued by looking at your Loki/Grafana
logs for events like
individual.message.validation_failed,
message.queued_limit, individual.message.device_gap_not_met,
or individual.message.gap_not_met.Retries
The “app sync fallback” path only runs when the BullMQ queue is unreachable
(e.g. Redis down). In normal operation, the worker handles all sends.
Replying via API
There is no special “reply” endpoint. To reply to a conversation, send another message to the same recipient usingPOST /api/messages. Unibox groups
messages by recipient, so the new message appears in the existing thread.