Event catalog
The full topic catalog in the launch (v1.0) emission. See Overview for the envelope and signing.
Billing-link events#
tenant.billing_linked.v1#
Emitted when a tenant first gains a tenant_billing_profiles row. Signals "this tenant is now a billable entity"; subscribers can create a corresponding CRM record.
1 2 3 4 5 6 7 8 9 | |
partner.billing_linked.v1#
Emitted when a partner first gains legal_name AND seller_country_code set.
1 2 3 4 5 6 7 8 | |
tenant.billing_updated.v1 / partner.billing_updated.v1#
Emitted on subsequent edits to billing-relevant fields. Carries changed_fields[] so subscribers can do selective re-sync.
1 2 3 4 5 6 7 8 9 10 11 | |
Subscription lifecycle events#
subscription.activated.v1#
A single-service subscription becomes active or trialing. Pack subscriptions use pack_subscription.activated.v1 instead.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
subscription.changed.v1#
Plan change, status transition, scheduled cancellation, renewal. The change_kind discriminator tells subscribers what kind of change.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
change_kind values: plan_change, quantity_change, renewal, scheduled_cancellation, scheduled_cancellation_undone, status_change.
When change_kind="scheduled_cancellation", the state is cancelling and pending_cancellation_at is set to the future cancel date.
subscription.cancelled.v1#
The subscription reached terminal cancelled. Either from an immediate admin cancel, or from the reaper flipping cancelling → cancelled at period end. The effective_immediately flag tells subscribers which:
1 2 3 4 5 6 7 8 9 10 | |
terminal_state is cancelled for admin/customer cancellation and expired for time-bound subscriptions that ran their term — both treated the same by most subscribers.
subscription.suspended.v1#
Admin-driven pause, or auto-suspend after sustained past_due. Subscribers map this to a paused state.
1 2 3 4 5 6 7 8 9 | |
subscription.resumed.v1#
Suspended → active.
1 2 3 4 5 6 7 8 | |
subscription.trial_ending.v1#
Fired by the daily trial monitor cron at days_remaining ∈ {7, 3, 1}. Once per (subscription, threshold) — dedup state lives in subscription.metadata_["trial_ending_fired"].
1 2 3 4 5 6 7 8 9 | |
subscription.payment_failed.v1#
A scheduled payment failed. Subscribers typically mirror as payment_status='past_due' on their copy of the subscription.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Pack subscription lifecycle#
pack_subscription.activated.v1#
A pack subscription was created. The pack_includes array enumerates the constituent services — child subscriptions are NOT emitted independently.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
pack_subscription.changed.v1#
(Reserved.) Pack mutations aren't currently exposed via an endpoint — no producer wired up yet. The topic + schema exist for when one lands.
pack_subscription.cancelled.v1#
Pack cancellation. Child subscriptions are torn down implicitly.
1 2 3 4 5 6 7 8 9 10 | |
ID conventions across events#
| Field | Format | Notes |
|---|---|---|
event_id |
UUIDv4 | Per-delivery unique |
tenant_id, partner_id |
tnt_<alphanum> / prt_<alphanum> |
Synced from ScaiKey |
subscription_id, pack_subscription_id, plan_id |
UUIDv4 | DB-internal IDs |
scaicontrol_customer_id |
Equals tenant_id (for owner_kind="tenant") or partner_id (for owner_kind="partner") |
Pass-through, no separate space |
plan_key |
<service_slug>.<plan_slug> |
Stable across re-seeds; pair with plan_id |
service_slug |
[a-z0-9_]+ |
E.g. scaikey, scaivault |
pack_slug |
[a-z0-9_-]+ |
E.g. trial-bundle |
currency |
ISO 4217 | EUR, USD, GBP |
| Timestamps | RFC 3339 UTC | Always +00:00 suffix |
State mapping#
ScaiControl ships eight subscription states; most subscribers normalise to fewer:
| ScaiControl | Typical CRM-side state |
|---|---|
pending |
(not surfaced — sub-second transient) |
trialing |
trial |
active |
active |
past_due |
past_due |
cancelling |
active + pending_cancellation_at field |
cancelled |
cancelled |
expired |
cancelled |
suspended |
paused |