Skip to main content

Overview

Every call to POST /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

_id
string
Unique message ID (MongoDB ObjectId)
message
string
Body text
messageType
string
"imessage" | "sms" | "email". Defaults to "imessage" when omitted from the API.
status
string
Current lifecycle state. See Status lifecycle below.
fromLineId
string
Line used to send
fromLinePhone
string
Denormalized phone from the line
fromLineEmail
string
Denormalized email from the line
recipientPhone
string
Recipient phone number
recipientEmail
string
Recipient email address
recipientName
string
Display name
leadId
string
Linked lead (if any)
workspaceId
string
Clerk organization ID
createdByUserId
string
Clerk user ID who created the message
scheduledDate
string
ISO 8601 timestamp — when to send
batchId
string
Grouping identifier
stepIndex
number
Campaign step index (0 = step 1)
settings
object
Time window settings:
sentAt
string
When actually sent
deliveredAt
string
When delivery confirmed via device callback
readAt
string
When read (incoming messages only)
externalMessageId
string
device message ID
errorMessage
string
Error details when failed or fallback
checkedAddresses
string[]
Addresses checked for iMessage availability (on fallback)
sendFallbackSmsOnFailed
boolean
When true + message fails, Tuco sends fallback SMS if Twilio is configured. Default: false.
campaignId
string
Campaign reference
hsContactId
string
HubSpot contact ID
hsPortalId
string
HubSpot portal ID
ghlContactId
string
GHL contact ID
ghlLocationId
string
GHL location ID
ghlConversationMessageId
string
GHL conversation message ID
createdAt
string
Creation timestamp
updatedAt
string
Last update timestamp

Status lifecycle

sent vs delivered: sent means the provider accepted the message. delivered means the recipient’s device confirmed receipt — this can arrive seconds or minutes later via a device callback.

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 using POST /api/messages. Unibox groups messages by recipient, so the new message appears in the existing thread.

Where messages appear