Glossary
Terms as Inbox uses them. Several are ordinary words with a narrow meaning here, and a few are easy to mistake for each other.
Adapter
An implementation of the IntegrationAdapter interface from @~inbox/types, teaching Inbox to speak one
provider's dialect: connect, disconnect, verify a webhook signature, and normalise a provider payload into
NormalizedEvent[]. Everything downstream — threads, deduplication, unread counts, realtime, retries — is
channel-agnostic, so adding a channel is one adapter. Registered today: aggregator,
instagram.native / facebook.native, whatsapp, whatsapp_session, sms, email, livechat, ai.
See Architecture.
Binding
The row (ConnectionBinding) that says which integration serves which capability on one connection,
unique per (connectionId, capability). It is what makes a connection's routing explicit rather than
implied: credentials are always read from the binding, never shared between integrations. A * binding
means one integration serves every capability. Re-point a single capability with
POST /v1/connections/{id}/switch-integration — no re-auth.
Capability
One thing an integration can do for a network: publish, messages, comments, reviews, or
connection (connect and disconnect accounts). A capability nothing serves on a connection returns
422 capability_unavailable, with details.connectIntegrationKey naming what to connect instead.
Connection
A connected account — a WhatsApp number, a Facebook page, an email domain, the LiveChat widget — unique
per (tenantId, network, externalAccountId). Its provider credentials are encrypted at rest. There is no
delete on the normal path: teardown is a non-destructive disconnect, which revokes upstream but keeps
the connection record and all saved threads.
Contact
A person. Globally unique in the estate, not per workspace: InboxContactIdentifier carries
@@unique([kind, value]), so one phone number is one identity everywhere. Facts that are true only within
one workspace — source, engagement, consent — live on InboxContactTenant. See
Data model.
Entry
One message or comment inside a thread, unique per (threadId, externalEntryId). Order entries by
seq (a Postgres bigint sequence), never by createdAt — millisecond ties break arbitrarily.
Flow
An automated conversation: a versioned graph of nodes and edges that a contact traverses one step at a
time. A run is unique per (flowId, dedupeKey), so duplicate enrolment is refused by the database rather
than by application logic.
Handler
Who is currently answering a thread: ai or human (InboxThread.handler). Distinct from takenOver,
which records a human stepping in over an agent — that is a different thing from the thread simply being
assigned to someone.
Integration key
The stable string identifying an integration — aggregator, whatsapp, whatsapp_session, sms,
email, instagram.native, livechat, ai. It is what your code references; you never name a vendor.
It also appears in the inbound webhook path, /webhooks/:integrationKey, which selects the verification
scheme for that delivery.
Operator
A cross-tenant caller. The operator surface (/v1/operator/*) is defined by the absence of the
X-Axis-Tenant header, and is gated by the stats scope — which admin deliberately does not imply.
Mutating operator routes additionally require operator:connections:write. A session caller must also be
a member of the app named by INBOX_OPERATOR_APP_SLUG. See Tenancy.
Participant
The other party on a thread (InboxParticipant), unique per (threadId, externalUserId). Take the row
with isOwner: false for the name and avatar to render.
Segment
A named audience within a workspace, unique per (tenantId, name). Membership is materialised as
explicit InboxSegmentMember rows rather than stored as a query, so a rule-based segment can be layered
on later without changing how anything reads it.
Tenant
A workspace: the scope for essentially everything. Identified on the wire by
X-Axis-Tenant: ws_<accountsUserGroupId>, and keyed internally by the composite (appId, externalRef).
An unrecognised value is silently provisioned, not rejected — the single most common integration
failure. See Tenancy.
Thread
A conversation: a DM exchange or a comment thread, unique per
(connectionId, threadType, externalThreadId). Threads are never created directly — there is no
create-thread endpoint. upsertThread is called only from ingest(), so a thread exists because a
message arrived, either via webhook or via the post-connect backfill.
Ticket
A support session over a thread. A thread is the stable unit of conversation; its open ticket comes and
goes. At most one ticket per thread may be open — enforced by a partial unique index in raw SQL, not
by application logic, so concurrent webhook-driven opens are safe. Native ticket numbers come from the
tenant's ticketPrefix plus ticketSeq, and are monotonic but not gapless.
Warm-up
Pacing a new sender so its volume ramps rather than spikes. A WarmupProfile defines the ramp,
WarmupChannelSetting enables it per (tenant, channel), and ConnectionWarmupState tracks where a
given sender is on that ramp. Channel-agnostic; campaigns consume the projection.
Widget key
The public identifier in the LiveChat routes, /v1/livechat/:widgetKey/*. Those routes are
unauthenticated by design — a browser visitor has no credential — so the tenant is derived from the
connection the widget key resolves to, never from the caller. That derivation is the whole security
boundary for the widget.
Workspace
The human name for a tenant. In Axis Accounts it is a user group, and its id is what the ws_ prefix
namespaces. Accounts owns it; Inbox stores no users and no group membership of its own.