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.