Skip to main content

Overview

plans is a reserved structured attribute on every account. It lets you natively segment and filter both users and accounts by their commercial plan (trial, paid tiers, billing cadence) without ad-hoc queries or manual tagging. Because a user can belong to multiple accounts, plan data is also denormalized onto each user, so user-level filtering stays fast.

The PlanItem contract

An account’s plans is an array of plan objects:
Plan keys are deduplicated per account — sending the same key twice keeps the last occurrence. Values are normalized and validated server-side regardless of the write channel.

A reserved attribute

plans is reserved: it is always written into the typed account.plans field and never into the generic custom-attributes map. An identify or API caller cannot smuggle arbitrary structure into it.
plan (singular string) is a legacy field, now deprecated in favour of plans. It is left untouched for backwards compatibility — new integrations should use plans only.

Setting plans

Plans can be set through two channels:
  • SDK identify — via the account.plans array. See identify.
  • Server API — via the account upsert endpoint, or PATCH /accounts/:id from the workspace.
In all cases, sending plans replaces the account’s plan list. An empty array ([]) clears all plans; omitting plans leaves them unchanged.

Filtering and segmentation

User cache

Each user caches the plan data of every account it belongs to:
  • cache.accounts[].plans — the full plan objects, per associated account.
  • cache.planKeys — the flattened, deduplicated union of plan keys across all of the user’s accounts. This is the field queried for user-level filtering.
Caches are recomputed synchronously on every account write — there is no background sync, so plan filters never read stale data.