Skip to content

Glossary

Beacon's shared vocabulary — its ubiquitous language. This is the one place we agree on what a word means, so the same concept doesn't end up named three ways across passes. Define each term once here, keep the definition to a sentence or two, and link to the page that owns the concept; the depth lives there, not here.

If you're new to Beacon, read it as a story rather than an A–Z list. An Organization (a customer) buys a Plan, which gives it a Subscription with some number of Seats. When the org changes plan mid-cycle it's charged a prorated difference. When a payment fails, the subscription goes past due and dunning tries to recover it before it's canceled. Separately, the org sends messages to its customers; each send produces a write-once delivery event recording whether it arrived.

flowchart LR
  org[Organization] -->|subscribes to| plan[Plan]
  plan -->|grants| sub[Subscription]
  sub -->|measured in| seat[Seat]
  sub -->|failed payment| pastdue[Past due]
  pastdue -->|recover or cancel| dunning[Dunning]
  org -->|sends| msg[Message]
  msg -->|records| de[Delivery event]

The terms below run business → technical: the first line is the plain-language meaning anyone can use; anything deeper is for engineers and links to the canonical page. Two distinctions show up again and again, so keep them straight:

  • Accounts vs. Billing. Who exists and how many seats they have is accounts truth (accounts-service); what they pay for and are entitled to is billing truth (billing-service). Each domain owns its own facts and reads the other's over an API — neither caches the other as long-term truth.
  • Beacon's customer vs. their customers. An Organization is Beacon's customer. The people that org sends messages to are its customers — Beacon never models them as accounts. "Customer-messaging SaaS" is exactly this two-level relationship.

For a hover tooltip on a term anywhere in the docs, also add a one-line *[term]: short def to includes/abbreviations.md (auto-appended to every page).

Campaign message

A message sent to many recipients as a batch — a newsletter or announcement — as opposed to a one-off transactional message triggered by a single event. Both flow through the same send pipeline in messaging-service and both produce delivery events; the distinction is why the send happened, not how. See: messaging-service.

Channel

How a message physically leaves Beacon — the transport. Email is the live channel today; SMS and push are part of the messaging domain's remit but, in practice, only legacy sms delivery events exist and no new ones are written. Recorded as the channel field on each delivery event. See: Delivery event.

SMS is legacy-only

channel: "sms" rows survive from the retired SMS feature; treat email as the only channel current code produces. Details on Delivery event.

Delivery event

A write-once record of one message send attempt and its outcome — delivered, failed, or bounced — owned by messaging-service and stored in MongoDB (messaging.delivery_events). High-volume and append-only: there's one document per attempt and it's never updated. Because the collection is schemaless, older documents lack fields that were added later (provider_id, region, attempts), so every consumer must code defensively. See: Delivery event.

Dunning

The automated retry-and-notify process that runs after a payment fails, while a subscription sits in past due, trying to recover the charge before the subscription is canceled. It's the grace period between "your card was declined" and "your account is off." Driven by billing-service. See: Subscription (the past_due state).

Event

An asynchronous message published to RabbitMQ that one service emits and another reacts to — Beacon's way of decoupling domains. The canonical example: billing-service emits subscription.upgraded, and messaging-service consumes it to send a confirmation, then emits message.delivered / message.failed of its own. Events carry the what-happened, not commands; a consumer chooses how (or whether) to react. Billing's HTTP surface is specified in OpenAPI; the event surface is specified in AsyncAPI. See: messaging-service, billing-service.

Invoice

A bill generated for a subscription — what the org owes for a billing period, including any prorated adjustments from a mid-cycle plan change. Invoices are owned by billing-service and are the one relationship in the Subscription model backed by a real database foreign key (one subscription → many invoices). A failed invoice payment is what pushes a subscription to past due and into dunning. See: Subscription.

Message

Something an organization sends to its own customers through Beacon — the product's whole reason to exist. A message is either transactional (triggered by one event) or a campaign (sent to many at once), goes out over a channel, and leaves behind a delivery event recording what happened. Templates and scheduling for messages live in messaging-service (PostgreSQL); the send itself goes through an external provider. The messaging flow starts at "send" and ends at "delivered/failed." See: messaging-service.

Organization

A customer account — the top-level tenant that users belong to and that holds a subscription. It's the identity boundary every other domain keys off: a subscription, a seat, and a delivery event all carry an org_id. Owned by accounts-service (Java/Spring on MySQL, accounts.organizations), which is the sole writer — billing and messaging read org data over the API and must not cache it as long-term truth. An org's status (active / suspended) is a DB-enforced enum; a suspended org can still sign in but can't send. See: Organization.

Past due

The state a subscription enters when its latest payment fails (status = past_due). It's not yet canceled: it kicks off dunning, and from there it either reverts to active if payment recovers or is canceled if dunning gives up. The legal states and transitions are enforced in application code (Billing::Subscription::StateMachine), not by the database. See: Subscription.

Plan

The package an organization pays for — its price, billing interval, seat limit, and feature set. A subscription references exactly one plan via plan_id, and a plan's seat limit is what flows over to the org's seat_limit when the plan changes. Owned by billing-service. See: Plan.

Proration

Charging the prorated difference when a plan changes mid-cycle, so the org pays only for what it actually used at each price — the credit-and-recharge that makes a plan upgrade fair within a billing period. The amount is computed in billing-service (Billing::Proration), not by the database or the payment provider; the contract exposes the result but not the formula. See: Plan upgrade.

Customers sometimes expect upgrades to be free until renewal

The help center has said an upgrade is free until the next cycle, but the code charges the prorated difference immediately. Code wins. Tracked on Plan upgrade.

Seat

One paid-for user slot on a subscription. A subscription buys some number of seats; changing that count is billed prorated. Keep two counts apart:

  • Seats — how many the org has paid for (seats, a plain column).
  • Seats in use — how many are actually assigned to users (seats_in_use, view-derived from active_subscriptions_v, not a stored column).

The rule that seats in use can't exceed seats is enforced in application code, not the database. "Seats in use" is what drives the near-the-limit warnings admins see. See: Subscription.

Subscription

What an organization is currently paying for: a plan, a billing cycle, and a lifecycle status. It's the authority on what a customer is entitled to — owned by billing-service (billing.subscriptions, partly view-backed) and read by other services as the source of truth for entitlement. Its status moves through trialing → active → past_due ↔ active → canceled, with transitions enforced in app code rather than the database. Each subscription links to one org (org_id, cross-store, no foreign key), one plan (plan_id, app-only), and many invoices (a real DB foreign key). See: Subscription.

Suspended

An organization status (the other being active) set by an admin for non-payment or abuse. A suspended org can still sign in but can't send messages: messaging-service checks org status before each send and rejects when it's suspended. It's reversible — unsuspend flips the status back to active and deletes nothing. Distinct from a canceled subscription, which is a billing state. See: Suspend an organization.

Tenant

The isolation boundary that keeps one customer's data separate from another's — in Beacon, the Organization. When you see org_id threaded through a subscription, a seat, or a delivery event, that's the tenant key tying a row back to the customer it belongs to. accounts-service is the tenant/identity layer every other domain reads from. See: Organization.

Transactional message

A message triggered by a single event for a single recipient — a receipt, a confirmation, a password reset — as opposed to a bulk campaign. The plan-upgrade confirmation is the canonical example: billing emits subscription.upgraded, and messaging-service renders and sends the transactional confirmation. See: messaging-service.

User

A person inside an organization — part of the accounts/identity layer, owned by accounts-service (one org → many users, a real DB foreign key). Assigning a user consumes a seat; a user is not a customer the org sends messages to. The dedicated User page isn't written yet. See: accounts-service.

Confidence: 85/100 · Last reviewed: 2026-06-14 · Status: active