The Unibox API exposes canonical lead email conversations so connected tools can review chronological messages, find an existing lead's sent campaign steps, send a reply to a step or a separate message, understand answer status, stage and reuse attachments, apply lead actions, and forward messages.
Filter by answer status, campaign, mailbox, conversation classification, reply primary category, and detailed reply subcategory.
Answer status mirrors the current Unibox behavior so API clients and the web inbox stay aligned.
Replies require a current latest-message check, idempotency key, workspace sender validation, and content validation before sending.
All requests use your Organization API Key with the standard bearer token header.
The Commercial strip shows the same full-history counts as Deals. Select Reply needed, Cooling or Gone cold to see the matching threads. The notice shows how many conversations are listed and how many have no thread here. With other filters, it shows the count within those filters instead. API callers use filter[commercial_status] with reply_needed, cooling or gone_cold; MCP callers use commercial_status. Selecting a count clears other filters. Clearing the filter returns to the plain Inbox.
Use filter[latest_message_after] (inclusive) and filter[latest_message_before] (exclusive) with RFC 3339 timestamps to list threads by the time of their latest message. Include the offset of the time zone you mean, so that a day such as 1 October covers 2026-10-01T00:00:00+02:00 to 2026-10-02T00:00:00+02:00. Either bound may be used alone, and both combine with every other filter. In a query string, send a positive offset as %2B02:00 (or use Z for UTC), for example filter[latest_message_after]=2026-10-01T00:00:00%2B02:00.
Observed CRM messages can have an application identity without an independently verified RFC Message-ID. In that case, message_id is an opaque integration-event:<event UUID> token and observed_event_id identifies the event. The optional rfc_message_id is omitted when unknown. Use the returned token for selection and expected_latest_message_id; do not construct email headers from it. Replies use a verified RFC identity or a qualifying recorded ancestor in the same conversation. If neither is available, reply_context.can_reply is false and a normal reply is rejected before sending. CRM observations expose metadata in thread detail. Read the selected accepted CRM message body on demand through the content endpoint below; mailbox messages and attachments keep their existing read paths.
First retrieve thread detail. Integration messages include source_kind, source_provider, observed_event_id, observation_id, and the selected observation_revision. Their body_text is empty and their latest-message body excerpt is omitted; a subject is not a message body. Mailbox bodies are unchanged.
Call GET /api/v1/unibox/threads/{thread_id}/observed-messages/{event_id}/content?observation_id={observation_id}&observation_revision={observation_revision} with the exact selected identifiers. The response contains plain text and sanitized HTML, up to 1 MiB combined, with Cache-Control: no-store. It reads the existing connected HubSpot or Close account and does not store bodies or change reply admission. The same workspace and Unibox access preferences apply.
A missing or retracted accepted source returns 404. A changed source, revision or provider record returns 409; reload thread detail before retrying. A provider or credential failure returns a generic 503 without exposing provider errors or credentials. Body requests and source checks share an eight-second context. Existing credential refresh and token-persistence recovery can take longer. The request does not retain a database transaction during body-provider I/O. MCP clients use unibox_observed_message_content_get with OAuth read.
reply_context.expected_latest_message_id from the list or detail response when calling POST /api/v1/unibox/threads/{thread_id}/reply.GET /api/v1/unibox/compose-leads?search=..., then list the campaign steps actually sent to them with GET /api/v1/unibox/compose-leads/{lead_id}/sent-steps. The selected step supplies its message_id and sender context. Its thread_id is an email thread reference, not a conversation UUID. A new message cannot address a person who is not a stored lead.POST /api/v1/unibox/leads/{lead_id}/compose-context with the selected step's message_id before composing. This creates or finds a canonical conversation for an outbound-only thread without sending email. Use its thread_id UUID to stage attachments and its expected_latest_message_id for the stale-message check.POST /api/v1/unibox/leads/{lead_id}/messages. For a threaded reply, set reply_parent_message_id to the selected step's message_id. For a separate unthreaded conversation, set new_message: true, context_message_id to the selected step's message_id, and an editable subject. The response thread_id identifies the sent conversation. Both modes require the compose context's expected_latest_message_id and an idempotency_key. Omitted to uses the Lead's current stored address; an explicit to must match that address.stop_campaign_steps: true to stop future steps for this Lead in the selected campaign after the manual email is accepted. The selected Lead must be the sole To recipient; other CC and BCC recipients are allowed. A mismatched To is rejected before sending. Omit the choice to continue the campaign. Inspect stop_campaign_steps_applied in the send response: true confirms future steps are prevented; false means the email was sent but the stop could not be applied, so check campaign status before sending again. The same result is returned on idempotent replay. A failed send leaves the campaign unchanged, and a step already handed to its mail provider may still complete. Changing this choice while reusing an idempotency key returns a conflict.unibox_unsubscribed_acknowledgement_required. After showing that status to the user, retry with acknowledge_unsubscribed: true and the same draft. This sends one manual email and does not re-subscribe the Lead or restart the campaign. Bounced or blocked Leads and domain-blocklisted recipients remain ineligible.to_all_addresses and cc_addresses from the original MIME headers. Use the focused message's recipients when composing a group reply, excluding your workspace mailboxes and the primary reply recipient before setting cc.from_address to use the Unibox default sender for the thread. For a selected sent campaign step, its original sender is selected automatically. For ordinary replies, a provided override must be an active sender mailbox in the same workspace.body_text, body_html, or both, with optional staged attachment_ids. HTML is sanitized the same way as in the Unibox composer, and when it is present the plaintext part is derived from it. Simple Unicode text is supported for multilingual replies; inline attachment objects, emoji, and unsupported control characters are rejected. An attachment-only reply may omit both body fields.POST /api/v1/unibox/threads/{thread_id}/reply-attachments using form field file. A file may be up to 10 MB; a draft supports up to five files and 15 MB total. Drafts expire after 24 hours and belong to the API key that staged them. Staging does not send mail.attachments: decoded name, type, size, SHA-256, direction: inbound, message_id (the parent messages[].id campaign UUID, not its RFC Message-ID), inline, opens_inline, blocked and download_path. Up to 50 attachments are listed per message. CID images are grouped separately in the inbox; cryptographic signatures, recognized 1×1 tracking pixels and delivery-report parts are hidden. Bounce messages have no attachments. Sent entries have direction: outbound. A malformed part does not hide valid attachments beside it.GET or POST /api/v1/unibox/threads/{thread_id}/reply-attachments/recent to list or reuse recent sent files, DELETE /api/v1/unibox/threads/{thread_id}/reply-attachments/{attachment_id} to remove your draft, and GET /api/v1/unibox/attachments/{attachment_id} to download a sent attachment.GET /api/v1/unibox/threads/{thread_id} includes each sent message's attachment IDs, names, types, sizes, and SHA-256 hashes. Use these IDs to retrieve files even after they leave the five-item recent list.idempotency_key in the JSON body or an Idempotency-Key header. Reusing the same key with the same request returns the original send response; reusing it with different content returns a conflict.POST /api/v1/unibox/threads/{thread_id}/reply, optionally set rationale: a short note, up to 2,000 characters, on why you wrote the reply this way. It is stored with the sent reply, never sent to recipients, and readable by every API key and MCP user in the workspace. Thread detail for the thread_id in the send response returns it as rationale on that message. A rationale is part of the idempotent request, so changing it while reusing a key returns a conflict. The lead message and forward routes reject it.Call POST /api/v1/unibox/threads/{thread_id}/lead-action with one action: unsubscribe, blocklist_email, or blocklist_domain. Use the canonical thread UUID from the list or detail response. The API key acts in its own workspace. Unsubscribe marks the campaign lead as unsubscribed, records its activity, and blocks its email. The blocklist actions only add the email or domain to the workspace blocklist. Existing active blocks are not duplicated.
POST /api/v1/unibox/threads/{thread_id}/forward with explicit to recipients, the current expected_latest_message_id, and an idempotency_key. A forward uses an unthreaded Fwd: subject and does not change the original thread's answer status.body_text or body_html to up to 20 combined To, Cc, and Bcc recipients. HTML is sanitized. Attachments are not supported.GET and POST /api/v1/reply-snippets, plus PUT and DELETE /api/v1/reply-snippets/{id}, manage the same workspace library used by the Unibox and Settings. A snippet can contain plain text, sanitized HTML, or both. New API writes require the combined body fields to fit 16 KiB both before and after sanitization, within a 32 KiB JSON request; existing UI snippets may be larger. List pages omit body fields above 8 KiB and mark those entries body_omitted: true. Read a legacy body through GET /api/v1/reply-snippets/{id}/content in UTF-8 chunks, carrying the returned content hash on continuation. Your integration must resolve variables against the intended lead and mailbox before calling the reply endpoint. Creating or reading a snippet does not send mail.
Call POST /api/v1/unibox/threads/{thread_id}/classification-impact with proposed_primary_category set to positive, neutral, or negative. The response reports no_credit_impact, refund_on_approval, or charge_on_approval. This read-only preview does not submit a correction or change credits; the effect may change if the conversation or billing state changes before approval.
Reply and forward sends return HTTP 429 details when limited. Current limits are 10 sends per API key per minute, 30 sends per workspace per minute, 10 sends per sender mailbox per minute, and 1 send per thread per 30 seconds. A new email uses a Lead-wide limit of 1 send per 30 seconds across that Lead's conversations instead of the thread limit.
Campaign AI draft history is available through GET /api/v1/campaigns/{campaign_id}/ai-replies/drafts and the MCP tool campaign_ai_drafts_list. Both return the original generated text and its current outcome, newest first. Responses contain up to 100 drafts and a nullable next_offset; pass that value as offset to continue. A null edit_distance means the amount of editing is unknown. Deleted campaigns and deleted leads are excluded. Access is restricted to the selected workspace.
An unchanged AI draft has a moving colour edge. Editing it changes the edge to a blue glow; human writing uses a blue glow within AI replies. Restore AI draft brings back the original text. Your own text is kept when a draft becomes unavailable. Discarded, superseded or stopped drafts become ordinary replies with a notice. Sending or already sent drafts disable Send and show their progress or sender. Review the draft and its reason before sending. Failed drafts show “No draft: write your own”.
Discard draft removes the draft identity immediately, keeps your text and edits, then offers optional reason chips. A reason can be saved once. Restoring a discarded draft only copies its original text into the composer; it remains an ordinary reply until you choose Send or clear the text. To add new Cc or Bcc recipients, discard the AI draft and write your own reply. Stop AI replies on this thread shows “AI replies off for this thread”; choose Turn back on to resume. Stops apply to the conversation or the same campaign and Lead, so they can cover more than one conversation. Stopping keeps your composer text and edits. The AI draft filter offers Any, Ready to send, Needs you and Holding reply.
Preview shows the server-derived envelope and the email delivered by every surface. The server adds quoted history and the eligible product footer, without adding a second personal sign-off. An uncertain delivery says “We can't confirm this sent. It may already have gone out. Open in Unibox.” and cannot be sent again automatically.
AI drafts are sent only after a person chooses Send. Slack, Unibox, API and MCP use the same send checks. The sender comes from the thread; recipients are the Lead and the people already on the Lead’s latest message, excluding your own mailboxes, blocklisted addresses and unsubscribed people. You cannot add recipients or choose another sender for an AI draft. Two people choosing Send at once deliver at most one reply.
If AI replies are switched off, a posted card cannot send. If the Lead booked a call after the draft was created, one-click Send is refused with “Booked a call since this draft. Open in Unibox.” You can still edit and send after reviewing the booking.
In Slack, Send uses the original draft. Edit opens a plain-text editor; invalid formatting keeps the editor open with the reason. Discard requires a reason. A Slack user must match a current workspace member allowed to send; other users see “Ask an approver.” A successful send updates the card with who sent it and the time, and appears in Activity.
API callers use the existing reply endpoint with ai_reply_draft_id; MCP callers use unibox_thread_reply_send with the same field. Omit sender and recipient overrides. To send edited text, also set ai_reply_edited: true and supply body_text. The original draft remains available for comparison. AI draft sends do not accept HTML, attachments, a different parent message or an unsubscribe acknowledgement. API and MCP retain their emoji refusal; Slack and Unibox allow emoji written by a person.
Why you wrote it this way is an optional note that helps future drafts. It is saved with the reply, separately from the email body.
Drafts use plain text and plain links. Unibox flags formatting, literal codes, corrupted characters, control characters and unfilled placeholders as you type. Human edits may include emoji.