[Objects](https://oods-foundry.com/objects) / Subscription

# Subscription

Canonical subscription record composing lifecycle, billing, and cancellation semantics.

beta 6 traits · 7 contexts · 37 fields [Subscription.object.yaml](#file) [How objects work](https://oods-foundry.com/guides/objects-and-traits)

Generated from @oods/foundry 0.10.1

This object is marked beta.

## Screens

Every screen below was generated from this object by OODS Foundry 0.10.1, in React and Vue, and shows the calls that made it and its content hash. Each runs as its generated app runs, in this site's brand, Aquex, in the theme you choose, with the sample records this object's own file carries. A value no record gives shows as a neutral one, such as an empty value or "Not recorded"; OODS Foundry invents none.

[Subscription detail screen generated by OODS Foundry in React](https://oods-foundry.com/objects/subscription/screens/detail-react)

React app · sample records Maturity: beta `sha256:dde1d1ded91c…` [Open full size: Subscription detail screen generated by OODS Foundry in React](https://oods-foundry.com/objects/subscription/screens/detail-react)

Vue app · sample records Maturity: beta `sha256:5f818219f92b…` [Open full size: Subscription detail screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/subscription/screens/detail-vue)

React app · sample records Maturity: beta `sha256:1a4469913d0d…` [Open full size: Subscription list screen generated by OODS Foundry in React](https://oods-foundry.com/objects/subscription/screens/list-react)

Vue app · sample records Maturity: beta `sha256:b952f0e283b5…` [Open full size: Subscription list screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/subscription/screens/list-vue)

React app · sample records Maturity: beta `sha256:4ec092f76727…` [Open full size: Subscription form screen generated by OODS Foundry in React](https://oods-foundry.com/objects/subscription/screens/form-react)

Vue app · sample records Maturity: beta `sha256:c610e0e03690…` [Open full size: Subscription form screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/subscription/screens/form-vue)

React app · sample records Maturity: beta `sha256:a4abb916a117…` [Open full size: Subscription timeline screen generated by OODS Foundry in React](https://oods-foundry.com/objects/subscription/screens/timeline-react)

Vue app · sample records Maturity: beta `sha256:9d1e00f71b4c…` [Open full size: Subscription timeline screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/subscription/screens/timeline-vue)

React app · sample records Maturity: beta `sha256:866a723c1228…` [Open full size: Subscription card screen generated by OODS Foundry in React](https://oods-foundry.com/objects/subscription/screens/card-react)

Vue app · sample records Maturity: beta `sha256:2fde60af3a38…` [Open full size: Subscription card screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/subscription/screens/card-vue)

React app · sample records Maturity: beta `sha256:7fca51ae717b…` [Open full size: Subscription inline screen generated by OODS Foundry in React](https://oods-foundry.com/objects/subscription/screens/inline-react)

Vue app · sample records Maturity: beta `sha256:389f15e66ae1…` [Open full size: Subscription inline screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/subscription/screens/inline-vue)

React app · sample records Maturity: beta `sha256:a406565b9f17…` [Open full size: Subscription workflow screen generated by OODS Foundry in React](https://oods-foundry.com/objects/subscription/screens/workflow-react)

Vue app · sample records Maturity: beta `sha256:b9ad19b282e9…` [Open full size: Subscription workflow screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/subscription/screens/workflow-vue)

What `design_compose` reported for every screen here:

- `OODS-V121` Object 'Subscription' has maturity 'beta' — composed output may change.
- `OODS-V117` Subscription refines 11 fields its traits define, and its own definitions are used: lifecycle/Stateful (status); lifecycle/Cancellable (cancel_at_period_end, cancellation_reason, cancellation_requested_at); financial/Billable (amount, currency, billing_interval, current_period_start, current_period_end, current_period_progress, next_payment_due_at).

The calls that made the Subscription detail apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Subscription",
  "context": "detail",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-1ab339c8",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-1ab339c8",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:dde1d1ded91c3a9f489ea363826a798de88c849366ffc8cdc0c9ec38dbcabf21`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, charts/, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:5f818219f92bbe3a0267ea4a82d419b7b7ffc0e71f4940004d165adc3b77b33f`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, charts/, main.ts, oods-brand-aquex.css

The calls that made the Subscription list apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Subscription",
  "context": "list",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-b1183dc3",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-b1183dc3",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:1a4469913d0d7e8a40c141b1eeb6f439096cf3521c687b20becc05d18ce44294`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:b952f0e283b5d5ea4a8ad98b10992e2f01e0aaec2c17c176d70f5be30caa2855`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

The calls that made the Subscription form apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Subscription",
  "context": "form",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-3d5240da",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-3d5240da",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:4ec092f767279a592addf1db8bf6792733165512e0605207e084ece7cdb55379`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:c610e0e03690b21a000908fc2eaf0a0a350a5754efec406ee0fb3cd18d4f9eb8`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

The calls that made the Subscription timeline apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Subscription",
  "context": "timeline",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-ae55b946",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-ae55b946",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:a4abb916a117de60f7ef389509811db421a54423d5c6599230b1d50eba3cf88c`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:9d1e00f71b4cbc7af13e0a52e60ecc43f689b2f4b7b986500633327455a7be09`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

The calls that made the Subscription card apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Subscription",
  "context": "card",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-a7a13ca7",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-a7a13ca7",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:866a723c122818699f0fa176f3241a8d621288a23692561416fbd3c27d0f838c`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:2fde60af3a3855e2ee0d09fbee81ad4349842cdeaa5f881c8d7d4b7ebaf209a4`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

For this screen it also reported:

- `OODS-V119` No view_extensions found for context "inline" in object "Subscription". Available contexts: list, detail, form, card, timeline, dashboard

The calls that made the Subscription inline apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Subscription",
  "context": "inline",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-a26901a2",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-a26901a2",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:7fca51ae717b5049c2c4a0a2f312867fe6a81d55a8222bb03201c0e714f96ce8`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:389f15e66ae10d50db17caa6700e16fb5a543e309164a81bf4ee12c401d035c8`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

For this screen it also reported:

- `OODS-V121` as above, 4 times in all
- `OODS-V117` as above, 4 times in all

The calls that made the Subscription workflow apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Subscription",
  "context": "workflow",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-4c19d781",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-4c19d781",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:a406565b9f1764f67e9fe11c63c049c52fe64b6d4fec7f004af76fd8eb09cabd`. Its `src/` folder: App.tsx, actions.ts, app.css, application.ts, chart-assets.ts, charts/, main.tsx, oods-brand-aquex.css, sample-data.ts, screens/, ssr.tsx, store.ts

The Vue app's content hash: `sha256:b9ad19b282e9aa006f59866afceede8f31b968db05625c7481d133f554884c62`. Its `src/` folder: App.vue, actions.ts, app.css, application.ts, chart-assets.ts, charts/, main.ts, oods-brand-aquex.css, sample-data.ts, screens/, ssr.ts, store.ts

## Fields

| Field | Type | Description |
| - | - | - |
| `status` required | string (future, trialing, active, paused, pending_cancellation, past_due, unpaid, terminated) | Current lifecycle status of the subscription. |
| `state_history` | StateTransition\[] | Chronological log of state transitions.MoreEach entry records the before/after states, timestamp, and (when governance is enabled) the actor, reason, and transition metadata. Rendered by StatusTimeline in the detail and timeline views. Entry structure: - from: string (previous state) - to: string (new state) - timestamp: ISO 8601 datetime - actor_id: string (user/system who triggered the transition, optional) - reason: string (human-readable justification, required when requireTransitionReason is true) - transition_metadata: Record\<string, unknown> (arbitrary context, optional) |
| `allowed_transitions` | string\[] | Materialized list of valid next states from the current status, computed from the transitionRules parameter.MoreWhen transitionRules is null (open model), this contains all states except the current one. Used by StatusSelector to disable invalid options and by StatusBadge to indicate available paths. |
| `cancel_at_period_end` required | boolean | Whether the subscription will cancel at the natural billing period end. |
| `cancellation_reason` | string | Free-form explanation captured during cancellation workflows. |
| `cancellation_reason_code` | string | Structured reason code chosen from the allowedReasons parameter. |
| `cancellation_requested_at` | datetime | Timestamp when cancellation was initiated. |
| `created_at` required | datetime | Timestamp recording when the entity was first created. |
| `updated_at` | datetime | Timestamp for the most recent modification, when available. |
| `last_event` required | string | Lifecycle event associated with the most recent timestamp mutation. |
| `last_event_at` | datetime | Timestamp for the lifecycle event captured in last_event. |
| `amount` required | integer | Recurring price expressed in minor units (e.g., cents). |
| `currency` required | string | ISO 4217 currency code used for billing. |
| `billing_interval` | string | Recurrence cadence (monthly, yearly, quarterly, etc.). |
| `payment_status` | string (pending, succeeded, failed, retrying, refunded) | Outcome of the most recent collection attempt.MoreDistinct from subscription lifecycle status — a subscription can be "active" with payment_status "retrying". |
| `payment_method_type` | string (card, ach, wire, invoice, other) | Payment instrument category used for collection. |
| `proration_amount` | integer | Prorated credit or charge in minor currency units generated by a mid-cycle plan change.MorePositive values are charges; negative values are credits. Only populated when the supportProration parameter is true. |
| `proration_date` | integer | Unix timestamp (epoch SECONDS) at which proration is calculated.MorePinned when an upcoming-invoice PREVIEW is requested and MUST be passed identically on commit so the committed charge equals the previewed amount. The preview is read-only and does NOT mutate the subscription. Stored as an integer (not a datetime) so the value round-trips byte-identically between preview and commit — an ISO string normalization could shift it and desync the previewed vs charged amount. Only populated when supportProration is true. Source: docs.stripe.com/billing/subscriptions/prorations. |
| `last_payment_at` | datetime | Timestamp of the most recent successful payment. |
| `next_payment_due_at` | datetime | Timestamp when the next payment attempt should occur. |
| `current_period_start` required | datetime | Start timestamp of the current billing cycle. |
| `current_period_end` required | datetime | End timestamp of the current billing cycle. |
| `current_period_progress` | number | Decimal progress (0-1) through the active billing cycle. |
| `is_archived` required | boolean | Flag indicating whether the entity is currently archived (soft-deleted). |
| `archived_at` | datetime? | Timestamp for when the entity entered the archived state. |
| `restored_at` | datetime? | Timestamp for when the entity was most recently restored from an archived state. |
| `archive_reason` | string | Human-readable narrative describing why the entity was archived. |
| `archived_by` | string | User ID or system identifier of the actor who archived this entity.MoreSet to "system" for automated/policy-driven archival. Supports audit trail queries like "show all entities archived by user X" or "show all auto-archived entities". |
| `archive_metadata` | object | Structured metadata about the archival action for compliance and audit purposes.MoreProperties: - method: "manual" \| "automated" \| "policy" — how the archive was triggered - compliance_tags: string\[] — regulatory labels (e.g., \["GDPR", "SOX", "HIPAA"]) - retention_policy_id: string — reference to the retention policy that triggered archival - original_status: string — the Stateful status before archival - related_entity_count: number — count of related entities also archived (cascade) |
| `restoration_metadata` | object | Metadata about the most recent restoration action.MoreProperties: - restored_by: string — user ID or "system" - restored_fields: string\[] — when partial restore, which fields were restored - restoration_reason: string — why the entity was restored - restored_from_snapshot: boolean — whether restored from a point-in-time snapshot |
| `subscription_id` required | string | Primary identifier used across billing and lifecycle systems. |
| `plan_name` required | string | Human-readable plan label shown in headers. |
| `plan_code` | string | Internal plan code or price identifier. |
| `plan_interval` | string | Billing interval descriptor (monthly, yearly, etc.). |
| `customer_name` | string | Customer or account name associated with the subscription. |
| `customer_email` | email | Billing contact email address. |
| `payment_history` | PaymentRecord\[] | Recorded payments, oldest first, each with its time and amount in minor units.MoreDrawn by the payment chart. |

## Traits

Each trait adds fields and behaviour. Every object with the trait gets the same.

- [Stateful as SubscriptionLifecycle](https://oods-foundry.com/traits/stateful)
- [Cancellable as SubscriptionCancellation](https://oods-foundry.com/traits/cancellable)
- [Timestampable as BillingCycleTimeline](https://oods-foundry.com/traits/timestampable)
- [Billable as SubscriptionBilling](https://oods-foundry.com/traits/billable)
- [Archivable as SubscriptionArchive](https://oods-foundry.com/traits/archivable)
- [MarkBar](https://oods-foundry.com/traits/mark-bar)

## Relationships

These arrows show declared relationships between object types. They don't join live records.

- [Plan](https://oods-foundry.com/objects/plan), many-to-one, via `plan_code`

## The object file

The Subscription object definition in YAML: `objects/core/Subscription.object.yaml`

```
object:
  name: Subscription
  version: 2.0.0
  domain: core.billing
  description: Canonical subscription record composing lifecycle, billing, and cancellation semantics.
  tags:
    - billing
    - lifecycle
    - monetization

traits:
  - name: lifecycle/Stateful
    alias: SubscriptionLifecycle
    parameters:
      states:
        - future
        - trialing
        - active
        - paused
        - pending_cancellation
        - past_due
        - unpaid
        - terminated
      initialState: future
  - name: lifecycle/Cancellable
    alias: SubscriptionCancellation
    parameters:
      allowCancellationAfterStart: true
      requireReason: false
  - name: lifecycle/Timestampable
    alias: BillingCycleTimeline
    parameters:
      recordedEvents:
        - billing_cycle_started
        - billing_cycle_completed
        - payment_received
        - cancellation_requested
      timezone: UTC
  - name: financial/Billable
    alias: SubscriptionBilling
    parameters:
      defaultCurrency: usd
      billingIntervals:
        - monthly
        - yearly
      minorUnits: 100
      # A plan change mid-cycle is charged at once as a prorated payment (Northwind Traders' upgrade to Business).
      supportProration: true
      prorationBehavior: always_invoice
  - name: lifecycle/Archivable
    alias: SubscriptionArchive
    parameters:
      gracePeriodDays: 30
      retainHistory: true
      restoreWindowDays: 30

  # One bar per recorded payment (s220-m01, #2461): an area over a steady price filled its plot as one block.
  - name: viz/MarkBar
    parameters:
      title: Payment amounts
      description: Payments recorded for this subscription, in its currency.
      chart:
        chartType: bar
        source: payment-events
        dateFields: [last_payment_at, next_payment_due_at]
        amountField: amount
        minorUnits: 100
        currencyField: currency

schema:
  subscription_id:
    type: string
    required: true
    description: Primary identifier used across billing and lifecycle systems.
  plan_name:
    type: string
    required: true
    description: Human-readable plan label shown in headers.
  plan_code:
    type: string
    required: false
    description: Internal plan code or price identifier.
  plan_interval:
    type: string
    required: false
    description: Billing interval descriptor (monthly, yearly, etc.).
  customer_name:
    type: string
    required: false
    description: Customer or account name associated with the subscription.
  customer_email:
    type: email
    required: false
    description: Billing contact email address.
  status:
    type: string
    required: true
    description: Current lifecycle status of the subscription.
    validation:
      enum:
        - future
        - trialing
        - active
        - paused
        - pending_cancellation
        - past_due
        - unpaid
        - terminated
  cancel_at_period_end:
    type: boolean
    required: true
    description: Whether the subscription will cancel at the natural billing period end.
  cancellation_reason:
    type: string
    required: false
    description: Free-form explanation captured during cancellation workflows.
  cancellation_requested_at:
    type: datetime
    required: false
    description: Timestamp when cancellation was initiated.
  amount:
    type: integer
    required: true
    description: Recurring price expressed in minor units (e.g., cents).
  currency:
    type: string
    required: true
    description: ISO 4217 currency code used for billing.
  billing_interval:
    type: string
    required: false
    description: Recurrence cadence (monthly, yearly, quarterly, etc.).
  current_period_start:
    type: datetime
    required: true
    description: Start timestamp of the current billing cycle.
  current_period_end:
    type: datetime
    required: true
    description: End timestamp of the current billing cycle.
  current_period_progress:
    type: number
    required: false
    description: Decimal progress (0-1) through the active billing cycle.
  next_payment_due_at:
    type: datetime
    required: false
    description: Timestamp when the next payment attempt should occur.
  payment_history:
    type: PaymentRecord[]
    required: false
    description: Recorded payments, oldest first, each with its time and amount in minor units. Drawn by the payment chart.

semantics:
  subscription_id:
    semantic_type: billing.subscription.id
    token_mapping: tokenMap(billing.subscription.id)
  plan_name:
    semantic_type: billing.plan.name
    token_mapping: tokenMap(billing.plan.name)
  plan_code:
    semantic_type: billing.plan.code
    token_mapping: tokenMap(billing.plan.code)
  plan_interval:
    semantic_type: billing.plan.interval
    token_mapping: tokenMap(billing.plan.interval)
  # The one-line summary under the plan's name in lists: whose subscription it is.
  customer_name:
    semantic_type: text.summary
    token_mapping: tokenMap(customer.account.name)
  customer_email:
    semantic_type: customer.account.email
    token_mapping: tokenMap(customer.account.email)
  status:
    semantic_type: billing.subscription.status
    token_mapping: tokenMap(billing.subscription.status.*)
  cancel_at_period_end:
    semantic_type: billing.subscription.cancel_at_period_end
    token_mapping: tokenMap(billing.subscription.cancel_at_period_end)
  cancellation_reason:
    semantic_type: billing.subscription.cancellation_reason
    token_mapping: tokenMap(billing.subscription.cancellation_reason)
  cancellation_requested_at:
    semantic_type: billing.subscription.cancellation_requested_at
    token_mapping: tokenMap(billing.subscription.cancellation_requested_at)
  amount:
    semantic_type: billing.plan.amount
    token_mapping: tokenMap(billing.plan.amount)
    ui_hints:
      component: CurrencyAmount
      currencyField: currency
      minorUnitsParameter: minorUnits
  currency:
    semantic_type: billing.plan.currency
    token_mapping: tokenMap(billing.plan.currency)
  billing_interval:
    semantic_type: billing.subscription.interval
    token_mapping: tokenMap(billing.subscription.interval)
  current_period_start:
    semantic_type: billing.subscription.current_period_start
    token_mapping: tokenMap(billing.subscription.current_period_start)
  current_period_end:
    semantic_type: billing.subscription.current_period_end
    token_mapping: tokenMap(billing.subscription.current_period_end)
  current_period_progress:
    semantic_type: billing.subscription.current_period_progress
    token_mapping: tokenMap(billing.subscription.current_period_progress)
  last_payment_at:
    semantic_type: billing.subscription.last_payment_at
    token_mapping: tokenMap(billing.subscription.last_payment_at)
  next_payment_due_at:
    semantic_type: billing.subscription.next_payment_due_at
    token_mapping: tokenMap(billing.subscription.next_payment_due_at)

tokens:
  billing.subscription.status.future: "var(--semantic-info)"
  billing.subscription.status.trialing: "var(--semantic-info)"
  billing.subscription.status.active: "var(--semantic-success)"
  billing.subscription.status.paused: "var(--semantic-warning)"
  billing.subscription.status.pending_cancellation: "var(--semantic-info)"
  billing.subscription.status.past_due: "var(--semantic-danger)"
  billing.subscription.status.unpaid: "var(--semantic-danger)"
  billing.subscription.status.terminated: "var(--semantic-neutral)"
  billing.subscription.interval.monthly: "var(--text-subtle)"
  billing.plan.amount: "var(--text-strong)"

metadata:
  owners:
    - billing@oods.systems
    - lifecycle@oods.systems
  steward: design@oods.systems
  maturity: beta
  changelog:
    - version: 2.0.0
      date: "2026-06-24"
      description: >-
        Converge status onto the canonical Stripe-literal extend-8 state set.
        Replace the consolidated `delinquent` with `past_due` (retries ongoing →
        grace access) + `unpaid` (retries exhausted → access revoked). BREAKING:
        `delinquent` is removed from the status enum. Source: docs.stripe.com/api/subscriptions/object.
    - version: 1.0.0
      date: "2025-10-21"
      description: Initial subscription composition integrating State, Cancellation, Billing, and Timeline traits.

# Authored type associations; no record join or referential-integrity claim.
relationships:
  - target: Plan
    via: plan_code
    cardinality: many-to-one
    label: Plan code

# Eight authored sample subscriptions, one coherent record each: plan, price, billing period, payments, payment status,
# events, history and cancellation agree, and each customer is a sample Organization. Payments vary: Northwind Traders
# upgrades from Team to Business with a prorated charge, Lindqvist Bakery is refunded a duplicate charge, and
# Tidewater Co-op pays a yearly price by bank transfer. Progress is authored so a period's position does not depend on
# the day a screen is opened. The state history starts in the lifecycle's initial state (future) and follows the
# subscription transitions.
samples:
  - subscription_id: sub_northwind_business
    plan_name: Business
    plan_code: business-monthly
    plan_interval: monthly
    billing_interval: monthly
    customer_name: Northwind Traders
    customer_email: billing@northwind.example
    status: active
    state_history:
      - { title: Trial started, from: future, to: trialing, at: '2026-02-16T15:00:00Z', reason: Started a 14-day Team trial }
      - { title: Trial converted, from: trialing, to: active, at: '2026-03-02T15:00:00Z', reason: First Team payment collected }
    amount: 14900
    currency: USD
    payment_history:
      - { at: '2026-03-02T15:00:00Z', amount: 4900 }
      - { at: '2026-04-02T15:00:00Z', amount: 4900 }
      - { at: '2026-05-02T15:00:00Z', amount: 4900 }
      - { at: '2026-06-02T15:00:00Z', amount: 4900 }
      - { at: '2026-06-16T15:00:00Z', amount: 5333 }
      - { at: '2026-07-02T15:00:00Z', amount: 14900 }
      - { at: '2026-08-02T15:00:00Z', amount: 14900 }
      - { at: '2026-09-02T15:00:00Z', amount: 14900 }
    proration_amount: 5333
    payment_status: succeeded
    payment_method_type: card
    last_payment_at: '2026-09-02T15:00:00Z'
    next_payment_due_at: '2026-10-02T15:00:00Z'
    current_period_start: '2026-09-02T15:00:00Z'
    current_period_end: '2026-10-02T15:00:00Z'
    current_period_progress: 0.93
    cancel_at_period_end: false
    cancellation_reason: null
    cancellation_requested_at: null
    created_at: '2026-02-16T15:00:00Z'
    updated_at: '2026-09-02T15:00:00Z'
    last_event: payment_received
    last_event_at: '2026-09-02T15:00:00Z'
  - subscription_id: sub_northwind_analytics
    plan_name: Analytics add-on
    plan_code: analytics-addon-monthly
    plan_interval: monthly
    billing_interval: monthly
    customer_name: Northwind Traders
    customer_email: billing@northwind.example
    status: paused
    state_history:
      - { title: Started, from: future, to: active, at: '2026-04-10T15:00:00Z', reason: Analytics add-on purchased }
      - { title: Paused, from: active, to: paused, at: '2026-08-20T14:00:00Z', reason: Paused during the ERP migration }
    amount: 3900
    currency: USD
    payment_history:
      - { at: '2026-04-10T15:00:00Z', amount: 3900 }
      - { at: '2026-05-10T15:00:00Z', amount: 3900 }
      - { at: '2026-06-10T15:00:00Z', amount: 3900 }
      - { at: '2026-07-10T15:00:00Z', amount: 3900 }
      - { at: '2026-08-10T15:00:00Z', amount: 3900 }
    payment_status: succeeded
    payment_method_type: card
    last_payment_at: '2026-08-10T15:00:00Z'
    next_payment_due_at: null
    current_period_start: '2026-08-10T15:00:00Z'
    current_period_end: '2026-09-10T15:00:00Z'
    current_period_progress: 1
    cancel_at_period_end: false
    cancellation_reason: null
    cancellation_requested_at: null
    created_at: '2026-04-10T15:00:00Z'
    updated_at: '2026-09-10T15:00:00Z'
    last_event: billing_cycle_completed
    last_event_at: '2026-09-10T15:00:00Z'
  - subscription_id: sub_lindqvist_starter
    plan_name: Starter
    plan_code: starter-monthly
    plan_interval: monthly
    billing_interval: monthly
    customer_name: Lindqvist Bakery
    customer_email: finance@lindqvist.example
    status: active
    state_history:
      - { title: Started, from: future, to: active, at: '2026-01-15T08:00:00Z', reason: Subscribed to the Starter plan }
      - { title: Payment failed, from: active, to: past_due, at: '2026-05-15T08:00:00Z', reason: Card expired; retrying }
      - { title: Recovered, from: past_due, to: active, at: '2026-05-17T08:00:00Z', reason: New card charged }
    amount: 1900
    currency: EUR
    payment_history:
      - { at: '2026-01-15T08:00:00Z', amount: 1900 }
      - { at: '2026-02-15T08:00:00Z', amount: 1900 }
      - { at: '2026-03-15T08:00:00Z', amount: 1900 }
      - { at: '2026-04-15T08:00:00Z', amount: 1900 }
      - { at: '2026-05-17T08:00:00Z', amount: 1900 }
      - { at: '2026-06-15T08:00:00Z', amount: 1900 }
      - { at: '2026-07-15T08:00:00Z', amount: 1900 }
      - { at: '2026-08-15T08:00:00Z', amount: 1900 }
      - { at: '2026-08-16T08:00:00Z', amount: 1900 }
      - { at: '2026-08-19T11:30:00Z', amount: -1900 }
      - { at: '2026-09-15T08:00:00Z', amount: 1900 }
    payment_status: succeeded
    payment_method_type: card
    last_payment_at: '2026-09-15T08:00:00Z'
    next_payment_due_at: '2026-10-15T08:00:00Z'
    current_period_start: '2026-09-15T08:00:00Z'
    current_period_end: '2026-10-15T08:00:00Z'
    current_period_progress: 0.5
    cancel_at_period_end: false
    cancellation_reason: null
    cancellation_requested_at: null
    created_at: '2026-01-15T08:00:00Z'
    updated_at: '2026-09-15T08:00:00Z'
    last_event: payment_received
    last_event_at: '2026-09-15T08:00:00Z'
  - subscription_id: sub_blueharbor_team
    plan_name: Team
    plan_code: team-monthly
    plan_interval: monthly
    billing_interval: monthly
    customer_name: Blue Harbor Studio
    customer_email: accounts@blueharbor.example
    status: trialing
    state_history:
      - { title: Scheduled, from: null, to: future, at: '2026-09-19T10:00:00Z', reason: Trial starts once a card is verified }
      - { title: Trial started, from: future, to: trialing, at: '2026-09-21T09:00:00Z', reason: Card verified; 14-day trial started }
    amount: 4900
    currency: USD
    payment_history: []
    payment_status: pending
    payment_method_type: card
    last_payment_at: null
    next_payment_due_at: '2026-10-05T09:00:00Z'
    current_period_start: '2026-09-21T09:00:00Z'
    current_period_end: '2026-10-05T09:00:00Z'
    current_period_progress: 0.64
    cancel_at_period_end: false
    cancellation_reason: null
    cancellation_requested_at: null
    created_at: '2026-09-19T10:00:00Z'
    updated_at: '2026-09-21T09:00:00Z'
    last_event: billing_cycle_started
    last_event_at: '2026-09-21T09:00:00Z'
  - subscription_id: sub_kestrel_team
    plan_name: Team
    plan_code: team-monthly
    plan_interval: monthly
    billing_interval: monthly
    customer_name: Kestrel Print Co.
    customer_email: hello@kestrelprint.example
    status: past_due
    state_history:
      - { title: Trial started, from: future, to: trialing, at: '2026-01-20T10:30:00Z', reason: Started a 14-day Team trial }
      - { title: Trial converted, from: trialing, to: active, at: '2026-02-03T10:00:00Z', reason: First payment collected }
      - { title: Payment failed, from: active, to: past_due, at: '2026-09-03T10:00:00Z', reason: Card declined; retrying }
    amount: 4900
    currency: GBP
    payment_history:
      - { at: '2026-02-03T10:00:00Z', amount: 4900 }
      - { at: '2026-03-03T10:00:00Z', amount: 4900 }
      - { at: '2026-04-03T10:00:00Z', amount: 4900 }
      - { at: '2026-05-03T10:00:00Z', amount: 4900 }
      - { at: '2026-06-03T10:00:00Z', amount: 4900 }
      - { at: '2026-07-03T10:00:00Z', amount: 4900 }
      - { at: '2026-08-03T10:00:00Z', amount: 4900 }
    payment_status: retrying
    payment_method_type: card
    last_payment_at: '2026-08-03T10:00:00Z'
    next_payment_due_at: '2026-10-01T10:00:00Z'
    current_period_start: '2026-09-03T10:00:00Z'
    current_period_end: '2026-10-03T10:00:00Z'
    current_period_progress: 0.9
    cancel_at_period_end: false
    cancellation_reason: null
    cancellation_requested_at: null
    created_at: '2026-01-20T10:30:00Z'
    updated_at: '2026-09-03T10:00:00Z'
    last_event: billing_cycle_started
    last_event_at: '2026-09-03T10:00:00Z'
  - subscription_id: sub_tidewater_enterprise
    plan_name: Enterprise
    plan_code: enterprise-annual
    plan_interval: yearly
    billing_interval: yearly
    customer_name: Tidewater Co-op
    customer_email: treasurer@tidewater.example
    status: active
    state_history:
      - { title: Started, from: future, to: active, at: '2024-10-30T14:00:00Z', reason: Annual Enterprise agreement signed }
      - { title: Invoice overdue, from: active, to: past_due, at: '2025-11-14T14:00:00Z', reason: Renewal invoice unpaid after 15 days }
      - { title: Recovered, from: past_due, to: active, at: '2025-11-20T02:00:00Z', reason: Paid by bank transfer }
    amount: 1200000
    currency: USD
    payment_history:
      - { at: '2024-10-30T14:00:00Z', amount: 1200000 }
      - { at: '2025-11-20T02:00:00Z', amount: 1200000 }
    payment_status: succeeded
    payment_method_type: wire
    last_payment_at: '2025-11-20T02:00:00Z'
    next_payment_due_at: '2026-10-30T14:00:00Z'
    current_period_start: '2025-10-30T14:00:00Z'
    current_period_end: '2026-10-30T14:00:00Z'
    current_period_progress: 0.92
    cancel_at_period_end: false
    cancellation_reason: null
    cancellation_requested_at: null
    created_at: '2024-10-30T14:00:00Z'
    updated_at: '2025-11-20T02:00:00Z'
    last_event: payment_received
    last_event_at: '2025-11-20T02:00:00Z'
  - subscription_id: sub_marlow_starter
    plan_name: Starter
    plan_code: starter-monthly
    plan_interval: monthly
    billing_interval: monthly
    customer_name: Marlow Yoga
    customer_email: studio@marlowyoga.example
    status: terminated
    state_history:
      - { title: Started, from: future, to: active, at: '2026-01-09T10:15:00Z', reason: Subscribed to the Starter plan }
      - { title: Cancellation requested, from: active, to: pending_cancellation, at: '2026-06-20T09:00:00Z', reason: Closing the studio in July }
      - { title: Ended, from: pending_cancellation, to: terminated, at: '2026-07-09T10:15:00Z', reason: Cancelled at the end of the billing period }
    amount: 1900
    currency: USD
    payment_history:
      - { at: '2026-01-09T10:15:00Z', amount: 1900 }
      - { at: '2026-02-09T10:15:00Z', amount: 1900 }
      - { at: '2026-03-09T10:15:00Z', amount: 1900 }
      - { at: '2026-04-09T10:15:00Z', amount: 1900 }
      - { at: '2026-05-09T10:15:00Z', amount: 1900 }
      - { at: '2026-06-09T10:15:00Z', amount: 1900 }
    payment_status: succeeded
    payment_method_type: card
    last_payment_at: '2026-06-09T10:15:00Z'
    next_payment_due_at: null
    current_period_start: '2026-06-09T10:15:00Z'
    current_period_end: '2026-07-09T10:15:00Z'
    current_period_progress: 1
    cancel_at_period_end: true
    cancellation_reason: Closing the studio in July
    cancellation_requested_at: '2026-06-20T09:00:00Z'
    created_at: '2026-01-09T10:15:00Z'
    updated_at: '2026-07-09T10:15:00Z'
    last_event: billing_cycle_completed
    last_event_at: '2026-07-09T10:15:00Z'
  - subscription_id: sub_summit_business
    plan_name: Business
    plan_code: business-monthly
    plan_interval: monthly
    billing_interval: monthly
    customer_name: Summit Outfitters
    customer_email: ap@summit.example
    status: pending_cancellation
    state_history:
      - { title: Trial started, from: future, to: trialing, at: '2026-04-23T12:30:00Z', reason: Started a 14-day Business trial }
      - { title: Trial converted, from: trialing, to: active, at: '2026-05-07T12:00:00Z', reason: First payment collected }
      - { title: Cancellation requested, from: active, to: pending_cancellation, at: '2026-09-21T16:40:00Z', reason: Moving to an annual contract elsewhere }
    amount: 14900
    currency: USD
    payment_history:
      - { at: '2026-05-07T12:00:00Z', amount: 14900 }
      - { at: '2026-06-07T12:00:00Z', amount: 14900 }
      - { at: '2026-07-07T12:00:00Z', amount: 14900 }
      - { at: '2026-08-07T12:00:00Z', amount: 14900 }
      - { at: '2026-09-07T12:00:00Z', amount: 14900 }
    payment_status: succeeded
    payment_method_type: card
    last_payment_at: '2026-09-07T12:00:00Z'
    next_payment_due_at: null
    current_period_start: '2026-09-07T12:00:00Z'
    current_period_end: '2026-10-07T12:00:00Z'
    current_period_progress: 0.77
    cancel_at_period_end: true
    cancellation_reason: Moving to an annual contract elsewhere
    cancellation_requested_at: '2026-09-21T16:40:00Z'
    created_at: '2026-04-23T12:30:00Z'
    updated_at: '2026-09-21T16:40:00Z'
    last_event: cancellation_requested
    last_event_at: '2026-09-21T16:40:00Z'
```
