Skip to content

Events no Verb emits

Events the payment rail, the clock, the runtime or a state change raises, how each names its Noun, and how a Startup records its own.

Most Events are a Verb completing. A few happen with no caller: money settles, a date passes, work finishes, and each is recorded the same way.

Four sources

Raised byWhenExample
The payment railWhat Stripe reports after a call returnsTransaction.settled, Dispute.opened
The clockA date passing with nothing doneInvoice.overdue, Domain.renewalDue
The runtimeWork finishing or failing with no one callingAction.done, Action.failed
A state changeA record entering a state worth its own nameSubscription.pastDue

Each one names the Noun it is about, so data carries that record as the Event left it. Where it marks a state, the record is in that state when the Event arrives. Extra fields, such as the retry attempt on an overdue Invoice, sit beside the record.

Every one of them moves a lifecycle, wakes a Workflow or feeds a computed property. Dispute.opened puts the Customer at risk, and Transaction.failed reopens the Bill it was paying.

One, delivered

Subscription.pastDue fires when a renewal Invoice goes overdue and the Subscription moves to pastDue. It carries the overdue Invoice beside the Subscription:

FieldTypeRequiredDescription
namestringrequiredSubscription.pastDue.
atdatetimerequiredWhen it happened.
seqintegerIts position in the namespace, one more than the Event before; any seq is a cursor into the history.
datajsonrequiredThe Thing as the Action left it.
data.subscriptionSubscriptionrequiredThe Subscription as the change left it.
data.subscription.statusstringrequiredOne of active, trialing, pastDue, paused, incomplete, or canceled.
data.subscription.renewsAtdatetimeWhen it next renews.
data.subscription.quantityintegerHow many units of the Offer’s Price it holds, such as 12 members; 1 by default.
data.subscription.trialEndsAtdatetimeWhen a trial ends and the first paid period starts.
data.subscription.termEndsAtdatetimeWhen a committed term ends; the Renewal and the renewal notice read it.
data.invoiceInvoicerequiredThe overdue renewal Invoice.
data.previous.statusstringrequiredThe status before this Event: active.
JSON
{
  "$id": "https://api.sb/routewise.dev/events/event_1",
  "$type": "Event",
  "name": "Subscription.pastDue",
  "at": "2026-10-22T12:00:00Z",
  "data": {
    "subscription": {
      "$id": "https://api.sb/routewise.dev/subscriptions/subscription_1",
      "$type": "Subscription",
      "status": "pastDue",
      "renewsAt": "2026-10-22T12:00:00Z",
      "quantity": 1,
      "trialEndsAt": "2026-10-22T12:00:00Z",
      "termEndsAt": "2026-10-22T12:00:00Z"
    },
    "invoice": {
      "$id": "https://api.sb/routewise.dev/invoices/invoice_1",
      "$type": "Invoice",
      "total": {
        "amount": 25,
        "currency": "USD"
      },
      "due": "2026-10-22"
    },
    "previous": {
      "status": "active"
    }
  },
  "seq": 1
}

Your own Events

A Verb a Startup adds emits its own Event, named for it, with no extra work. For what no Verb does, such as a page view or a click, Event.capture records one. It takes calls from a Site, an App or the API:

TypeScript
await $.Event.capture({ name: 'Report.viewed', data: { report: 'renewals-due' } })

A captured Event is a record like any other. A Metric counts it, a Trigger wakes a Workflow on it, and webhooks deliver it.