Core concepts
Understand workspaces, sending identities, emails, threads, events, and customer spaces.
A workspace contains your API keys, sending identities, messages, and configuration. Use the resources below to configure where mail comes from, work with conversations, and handle delivery outcomes.
The two halves
Everything here divides cleanly. On one side are the objects that establish who you can send as: a domain you publish DNS for, or a mailbox a customer authorized. On the other are the objects that carry what you sent and what came back: emails, threads, and the events they emit.
You need exactly one thing from the first half before anything in the second half works.
Identity
Domain and Mailbox — the two ways an address becomes yours to send from. Pick one; most products end up with both.
Traffic
Email, Thread, Attachment — the messages themselves, and the conversation each one belongs to.
Identity
Domain
A domain you control the DNS for. Creating one returns the records to publish — SPF, DKIM, DMARC, a return path, and an MX record if you want to receive on it — and the domain can send once its DKIM identity is verified. Receiving also requires the inbound MX record.
A domain does not have to be yours. Publishing the records at a customer’s registrar makes their domain send through you, which is how a product sends as support@theircompany.com.
dom_… → verified_at, region, records[]
Domains
Each record, what diagnostics reports when one won’t resolve, and regions.
Mailbox
A Gmail or Outlook account a customer authorized over OAuth. No DNS, no verification — they click through a consent screen and the mailbox is live. In exchange you get one address rather than a whole domain, and the authorization can expire.
mbx_… → address, provider, app_id, state
Mailboxes
The connect flow, ref for matching a mailbox to your user, and reauth.
Traffic
One message, inbound or outbound, with its addresses, bodies, headers, and attachments. Sending returns one; receiving one fires a webhook that names it.
An email’s status reports acceptance, not delivery — queued, sending, sent, scheduled. What actually happened at the far end arrives later, as events.
eml_… → thread_id, status, suppressed[], pacing
Thread
The conversation an email belongs to, and the reason this API exists. A reply is matched onto its thread by RFC headers first, then by quoted content — never by subject line alone, which is what makes unrelated “Re: Hi” messages collide elsewhere.
Because the thread is a real object, replying takes the thread ID and your text. The sender, the recipients, the subject, and the In-Reply-To/References headers are inferred from what’s already on the thread.
thr_… → subject, mailbox, messages[]
Threads
How matching decides, and what threads.reply fills in for you.
Attachment
A file on an email. Outbound, you pass base64 content. Inbound, you get metadata plus a preparation field — AI Inbx extracts a received PDF to Markdown so an agent can read it without you running a parser.
att_… → filename, content_type, size, preparation
Delivery outcomes
Event and webhook endpoint
Everything that happens after acceptance is an event: delivered, bounced, complained, opened, clicked, unsubscribed, plus the inbound email.received that starts most applications. A webhook endpoint is a URL subscribed to some of them.
Subscribe to webhooks for delivery outcomes as they occur. You can also retrieve an email and inspect its events array. A provider accepting a message does not establish that it reached the recipient’s inbox.
whk_… → url, subscriptions[], secret
evt_… → type, created_at, data
Webhooks
Endpoints, subscriptions, retries, and replay.
Event types
Each event type and its payload fields.
Sending controls
Two objects exist only to stop a send that shouldn’t happen.
Suppression
An address that must not be mailed, on a named list. Bounces and complaints create these automatically; unsubscribes and your own calls add more. Every send is checked against the list named by suppression_key, plus the org-wide * list.
Suppression is why a send can succeed with recipients missing — read suppressed on the response.
sup_… → address, key, scope, reason
Pacing rule
A ceiling on how fast a lane sends: a window, a rate, a spread. A held message comes back with a non-null pacing object naming the rule and projecting when it will go.
Transactional mail shouldn’t queue behind a campaign, so a send can set pacing.skip.
pace_… → window, rate, spread
Suppressions
Lists, scopes, and one-click unsubscribe.
Pacing
Lanes, holds, and releasing a queue by hand.
Grouping by customer
Space
Optional. A group inside the workspace — one per customer, typically — that owns domains and mailboxes, and with them every email and thread that went through those. Pacing rules and suppression lists can be put in a space too, and then reach that space alone. API keys and webhook endpoints are workspace-wide.
Mail is never told its space: an email is in the space of the domain or mailbox it went through, and a reply is in its thread’s. Any space-aware resource created without a space belongs to the workspace itself and reads space_id: null.
spc_… → name
Spaces
When you need one, and a platform walkthrough end to end.
A message, through all of it
You send
POST /emails checks the from address against a verified domain or an
active mailbox, checks each recipient against the suppression lists,
and asks the pacing rules whether the lane has room.
An email and a thread exist
You get an email back with a thread_id — a new thread if nothing
matched, an existing one if thread_id was passed. suppressed lists who was
dropped; pacing is non-null if the message is held.
Events arrive
email.sent, then email.delivered — or email.bounced, which writes a
suppression so the next send to that address never leaves.
They reply
Their message is matched onto the same thread and arrives as
email.received, carrying the thread_id and a category that tells you
how the message was classified. Classification is a signal for your application, not proof of sender identity.
You reply
threads.reply with the thread ID and your text. Every header that makes it a
real reply is filled in from the thread.
Everything has an ID
Prefixed, opaque, stable, safe to store. The prefix is part of the ID — an ID of the wrong type is a 404, never a silent match against another resource.
Conventions
The full prefix table, pagination, timestamps, request IDs, and idempotency.