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

# Article

Canonical editorial article with hybrid taxonomy + tag classification for CMS surfaces.

Maturity not declared 4 traits · 7 contexts · 28 fields [Article.object.yaml](#file) [How objects work](https://oods-foundry.com/guides/objects-and-traits)

Generated from @oods/foundry 0.10.1

## 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.

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

React app · sample records Maturity: not declared `sha256:86b2472ca161…` [Open full size: Article detail screen generated by OODS Foundry in React](https://oods-foundry.com/objects/article/screens/detail-react)

Vue app · sample records Maturity: not declared `sha256:772e8b4298d0…` [Open full size: Article detail screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/article/screens/detail-vue)

React app · sample records Maturity: not declared `sha256:782a7615c558…` [Open full size: Article list screen generated by OODS Foundry in React](https://oods-foundry.com/objects/article/screens/list-react)

Vue app · sample records Maturity: not declared `sha256:1ef838afb69a…` [Open full size: Article list screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/article/screens/list-vue)

React app · sample records Maturity: not declared `sha256:aff2a08ab625…` [Open full size: Article form screen generated by OODS Foundry in React](https://oods-foundry.com/objects/article/screens/form-react)

Vue app · sample records Maturity: not declared `sha256:8322a751c972…` [Open full size: Article form screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/article/screens/form-vue)

React app · sample records Maturity: not declared `sha256:ad18556d6397…` [Open full size: Article timeline screen generated by OODS Foundry in React](https://oods-foundry.com/objects/article/screens/timeline-react)

Vue app · sample records Maturity: not declared `sha256:bf8d7fffe706…` [Open full size: Article timeline screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/article/screens/timeline-vue)

React app · sample records Maturity: not declared `sha256:43c1eb71d613…` [Open full size: Article card screen generated by OODS Foundry in React](https://oods-foundry.com/objects/article/screens/card-react)

Vue app · sample records Maturity: not declared `sha256:d04d93af1370…` [Open full size: Article card screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/article/screens/card-vue)

React app · sample records Maturity: not declared `sha256:214bc34b7a9f…` [Open full size: Article inline screen generated by OODS Foundry in React](https://oods-foundry.com/objects/article/screens/inline-react)

Vue app · sample records Maturity: not declared `sha256:b051ff81828d…` [Open full size: Article inline screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/article/screens/inline-vue)

React app · sample records Maturity: not declared `sha256:bfb431861664…` [Open full size: Article workflow screen generated by OODS Foundry in React](https://oods-foundry.com/objects/article/screens/workflow-react)

Vue app · sample records Maturity: not declared `sha256:2631a1dfd955…` [Open full size: Article workflow screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/article/screens/workflow-vue)

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

3 calls, as the pipeline sent them

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

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

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

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

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

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

3 calls, as the pipeline sent them

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

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

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

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

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

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

3 calls, as the pipeline sent them

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

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

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

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

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

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

3 calls, as the pipeline sent them

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

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

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

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

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

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

3 calls, as the pipeline sent them

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

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

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

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

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

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

3 calls, as the pipeline sent them

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

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

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

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

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

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

3 calls, as the pipeline sent them

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

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

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

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

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

## Fields

| Field | Type | Description |
| - | - | - |
| `label` required | string | Human-readable display name rendered in primary surfaces. |
| `description` | string | Supporting description used in detail and card contexts. |
| `placeholder` | string | Hint copy surfaced in form fields when the label is empty. |
| `status` required | string | Canonical lifecycle state derived from the states parameter.MoreThis is the single source of truth for the entity's current lifecycle position. Consumed by Colorized to resolve visual tokens, and by view extensions to render StatusBadge and StatusTimeline. |
| `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. |
| `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. |
| `classification_metadata` required | ClassificationMetadata | Operational metadata describing mode, storage model, governance rules, and audit timestamps. |
| `categories` | CategoryNode\[] | Ordered taxonomy nodes scoped to the object. |
| `primary_category_id` | string | Identifier of the canonical taxonomy node. |
| `primary_category_path` | string | Human-readable breadcrumb path (Electronics > Mobile > Android). |
| `tags` | Tag\[] | Canonical tag collection after synonym collapse. |
| `tag_count` | number | Number of canonical tags assigned to the object. |
| `tag_preview` | string | Denormalized comma-delimited preview for list renders. |
| `article_id` required | uuid | Primary identifier for the article document. |
| `slug` required | string | Canonical slug rendered in URLs and breadcrumb links. |
| `author_id` required | uuid | Reference to the authoring user. |
| `author_name` | string | Display name of the authoring user. |
| `content_type` required | string (knowledge_base, announcement, release_notes, how_to) | Editorial template driving layout and governance workflows. |
| `excerpt` | string | Summary text used in list + SEO contexts. |
| `body_markdown` required | string | Markdown content rendered in CMS detail views. |
| `hero_media_id` | string | Optional Media object identifier referenced in hero slots. |
| `reading_time_minutes` | number | Estimated reading time derived from body length. |
| `locale` | string | Locale tag determining headline, copy, and SEO metadata. |
| `published_at` | datetime | Timestamp when the article was published live. |

## Traits

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

- [Labelled as ArticleHeadline](https://oods-foundry.com/traits/labelled)
- [Stateful as ArticleWorkflow](https://oods-foundry.com/traits/stateful)
- [Timestampable as ArticleTimestamps](https://oods-foundry.com/traits/timestampable)
- [Classifiable as ArticleClassification](https://oods-foundry.com/traits/classifiable)

## Relationships

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

- [User](https://oods-foundry.com/objects/user), many-to-one, via `author_id`
- [Media](https://oods-foundry.com/objects/media), many-to-one, via `hero_media_id`

## The object file

The Article object definition in YAML: `objects/content/Article.object.yaml`

```
object:
  name: Article
  version: 1.0.0
  domain: content.publication
  description: Canonical editorial article with hybrid taxonomy + tag classification for CMS surfaces.
  tags:
    - content
    - article
    - hybrid
    - classifiable

traits:
  - name: content/Labelled
    alias: ArticleHeadline
  - name: lifecycle/Stateful
    alias: ArticleWorkflow
    parameters:
      states:
        - draft
        - in_review
        - scheduled
        - published
        - archived
      initialState: draft
  - name: lifecycle/Timestampable
    alias: ArticleTimestamps
    parameters:
      recordedEvents:
        - created
        - scheduled
        - published
        - updated
        - archived
      timezone: UTC
  - name: core/Classifiable
    alias: ArticleClassification
    parameters:
      classification_mode: hybrid
      hierarchy_storage_model: materialized_path
      tag_policy: moderated
      max_tags: 24
      require_primary_category: true

schema:
  article_id:
    type: uuid
    required: true
    description: Primary identifier for the article document.
  slug:
    type: string
    required: true
    description: Canonical slug rendered in URLs and breadcrumb links.
    validation:
      pattern: '^[a-z0-9]+(?:-[a-z0-9]+)*$'
  author_id:
    type: uuid
    required: true
    description: Reference to the authoring user.
  author_name:
    type: string
    required: false
    description: Display name of the authoring user.
  content_type:
    type: string
    required: true
    default: knowledge_base
    description: Editorial template driving layout and governance workflows.
    validation:
      enum:
        - knowledge_base
        - announcement
        - release_notes
        - how_to
  excerpt:
    type: string
    required: false
    description: Summary text used in list + SEO contexts.
    validation:
      maxLength: 240
  body_markdown:
    type: string
    required: true
    description: Markdown content rendered in CMS detail views.
  hero_media_id:
    type: string
    required: false
    description: Optional Media object identifier referenced in hero slots.
  reading_time_minutes:
    type: number
    required: false
    description: Estimated reading time derived from body length.
    validation:
      minimum: 1
      maximum: 60
  locale:
    type: string
    required: false
    default: en-US
    description: Locale tag determining headline, copy, and SEO metadata.
  published_at:
    type: datetime
    required: false
    description: Timestamp when the article was published live.

semantics:
  # The one-line summary under the headline in lists (content/Labelled's description, marked as the summary).
  description:
    semantic_type: text.summary
    token_mapping: tokenMap(text.body.*)
    ui_hints:
      component: TextBody
      maxLengthParameter: maxDescriptionLength
  article_id:
    semantic_type: content.article.id
    token_mapping: tokenMap(content.article.id)
  slug:
    semantic_type: content.article.slug
    token_mapping: tokenMap(content.article.slug)
  author_id:
    semantic_type: content.article.author
    token_mapping: tokenMap(identity.user.id)
    ui_hints:
      displayLabelField: author_name
  content_type:
    semantic_type: content.article.type
    token_mapping: tokenMap(content.article.type)
  reading_time_minutes:
    semantic_type: content.article.read_time
    token_mapping: tokenMap(metric.duration.short)
  locale:
    semantic_type: content.article.locale
    token_mapping: tokenMap(content.locale.badge)
  published_at:
    semantic_type: content.article.published_at
    token_mapping: tokenMap(timestamp.published_at)

tokens:
  content.article.id: "var(--sys-text-strong)"
  content.article.slug: "var(--sys-text-muted)"
  content.article.type: "var(--sys-badge-neutral-fg)"
  content.article.read_time: "var(--sys-text-subtle)"
  content.locale.badge: "var(--sys-badge-accent-fg)"
  timestamp.published_at: "var(--sys-text-muted)"

metadata:
  owners:
    - content-platform@oods.systems
    - docs@oods.systems
  steward: publishing@oods.systems
  changelog:
    - version: 1.0.0
      date: "2025-11-18"
      description: Initial article object composing hybrid Classifiable mode for taxonomy + tags.

# Authored type associations; no record join or referential-integrity claim.
relationships:
  - target: User
    via: author_id
    cardinality: many-to-one
    label: Author
  - target: Media
    via: hero_media_id
    cardinality: many-to-one
    label: Hero media

# Eight authored sample help-centre articles about the billing product, written by three editors. Each follows the
# editorial lifecycle from draft (its initial state); only published and archived articles carry a publication date, and
# the heroes are sample Media assets.
samples:
  - label: September 2026 release notes
    description: What changed in billing this month
    slug: september-2026-release-notes
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e01
    author_name: Elena Park
    content_type: release_notes
    excerpt: A preview of usage-based pricing, smarter payment retries and faster invoice search.
    body_markdown: |
      ## Payment retries
      Failed card payments now retry on the schedule you choose, and each retry is listed on the invoice.

      ## Invoice search
      Search finds invoices by number, customer or amount in under a second.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c03
    reading_time_minutes: 5
    locale: en-US
    primary_category_id: updates-release-notes
    primary_category_path: Updates > Release notes
    tag_preview: releases
    status: published
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-09-22T09:00:00Z', reason: Drafted from the release checklist }
      - { title: In review, from: draft, to: in_review, at: '2026-09-26T15:00:00Z', reason: Sent to product review }
      - { title: Published, from: in_review, to: published, at: '2026-09-29T16:00:00Z', reason: Published with the release }
    published_at: '2026-09-29T16:00:00Z'
    created_at: '2026-09-22T09:00:00Z'
    updated_at: '2026-09-29T16:00:00Z'
    last_event: published
    last_event_at: '2026-09-29T16:00:00Z'
  - label: How prorations work when plans change
    description: Why an upgrade adds a prorated charge
    slug: how-prorations-work
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e02
    author_name: Marcus Hale
    content_type: knowledge_base
    excerpt: When a customer upgrades mid-cycle, they pay the difference for the days left in the period.
    body_markdown: |
      An upgrade takes effect straight away. The customer is charged the new price minus the old one, for the days left
      in the current billing period, and the next renewal is at the new price.

      A downgrade takes effect at the end of the period, so no credit is issued.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c01
    reading_time_minutes: 6
    locale: en-US
    primary_category_id: billing-plans
    primary_category_path: Billing > Plans
    tag_preview: prorations, upgrades
    status: published
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-06-22T10:00:00Z', reason: Requested by the support team }
      - { title: In review, from: draft, to: in_review, at: '2026-07-01T10:00:00Z', reason: Sent to the billing lead }
      - { title: Published, from: in_review, to: published, at: '2026-07-08T14:00:00Z', reason: Approved and published }
    published_at: '2026-07-08T14:00:00Z'
    created_at: '2026-06-22T10:00:00Z'
    updated_at: '2026-07-08T14:00:00Z'
    last_event: published
    last_event_at: '2026-07-08T14:00:00Z'
  - label: Set up automatic payment retries
    description: Recover failed payments on a schedule
    slug: set-up-automatic-payment-retries
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e03
    author_name: Ines Duarte
    content_type: how_to
    excerpt: Choose how often a declined card is retried and when the customer is reminded.
    body_markdown: |
      1. Open Settings, then Payments.
      2. Under Retries, choose up to four attempts over 21 days.
      3. Turn on reminder emails so the customer can update their card before the last retry.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c02
    reading_time_minutes: 4
    locale: en-US
    primary_category_id: billing-payments
    primary_category_path: Billing > Payments
    tag_preview: retries, dunning
    status: published
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-08-05T09:00:00Z', reason: Drafted for the retries launch }
      - { title: In review, from: draft, to: in_review, at: '2026-08-12T09:00:00Z', reason: Sent to the support lead }
      - { title: Published, from: in_review, to: published, at: '2026-08-19T13:00:00Z', reason: Approved and published }
    published_at: '2026-08-19T13:00:00Z'
    created_at: '2026-08-05T09:00:00Z'
    updated_at: '2026-08-19T13:00:00Z'
    last_event: published
    last_event_at: '2026-08-19T13:00:00Z'
  - label: Introducing usage-based billing
    description: Charge for what customers use
    slug: introducing-usage-based-billing
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e01
    author_name: Elena Park
    content_type: announcement
    excerpt: Meter API calls or seats, include an allowance in each plan and bill the overage automatically.
    body_markdown: |
      Usage-based billing arrives on October 6. Each plan can include an allowance, and anything above it is billed at
      the rate you set, on the same invoice as the subscription.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c04
    reading_time_minutes: 3
    locale: en-US
    primary_category_id: updates-announcements
    primary_category_path: Updates > Announcements
    tag_preview: usage, launch
    status: scheduled
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-09-01T09:00:00Z', reason: Drafted for the October launch }
      - { title: In review, from: draft, to: in_review, at: '2026-09-15T09:00:00Z', reason: Sent to product marketing }
      - { title: Scheduled, from: in_review, to: scheduled, at: '2026-09-24T16:00:00Z', reason: Scheduled for October 6 }
    published_at: null
    created_at: '2026-09-01T09:00:00Z'
    updated_at: '2026-09-24T16:00:00Z'
    last_event: scheduled
    last_event_at: '2026-09-24T16:00:00Z'
  - label: Refund a duplicate charge
    description: Find and refund a charge made twice
    slug: refund-a-duplicate-charge
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e03
    author_name: Ines Duarte
    content_type: how_to
    excerpt: Spot two charges for the same period and refund one of them in full.
    body_markdown: |
      Open the customer's payments and look for two charges on consecutive days for the same amount. Open the later one
      and choose Refund; the refund appears on the invoice and in the customer's payment history.
    hero_media_id: null
    reading_time_minutes: 3
    locale: en-US
    primary_category_id: billing-payments
    primary_category_path: Billing > Payments
    tag_preview: refunds
    status: in_review
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-09-17T11:00:00Z', reason: Written after a support ticket }
      - { title: In review, from: draft, to: in_review, at: '2026-09-25T11:00:00Z', reason: Sent to the support lead }
    published_at: null
    created_at: '2026-09-17T11:00:00Z'
    updated_at: '2026-09-25T11:00:00Z'
    last_event: updated
    last_event_at: '2026-09-25T11:00:00Z'
  - label: Invoice numbers, tax IDs and legal names
    description: What each invoice must show, by region
    slug: invoice-numbers-tax-ids
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e02
    author_name: Marcus Hale
    content_type: knowledge_base
    excerpt: The details an invoice needs in the US, the UK and the EU, and where to set them.
    body_markdown: |
      Every invoice shows a unique number, your legal name and address, and the customer's. In the UK and the EU it also
      shows your VAT number and, for business customers, theirs.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c06
    reading_time_minutes: 7
    locale: en-GB
    primary_category_id: billing-invoices
    primary_category_path: Billing > Invoices
    tag_preview: invoices, tax
    status: draft
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-09-08T08:00:00Z', reason: Drafted for the tax settings page }
      - { title: In review, from: draft, to: in_review, at: '2026-09-16T08:00:00Z', reason: Sent to the finance team }
      - { title: Changes requested, from: in_review, to: draft, at: '2026-09-18T08:00:00Z', reason: Needs the EU VAT section }
    published_at: null
    created_at: '2026-09-08T08:00:00Z'
    updated_at: '2026-09-18T08:00:00Z'
    last_event: updated
    last_event_at: '2026-09-18T08:00:00Z'
  - label: Moving from the Pro plan
    description: Pro customers moved to Business in 2026
    slug: moving-from-the-pro-plan
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e01
    author_name: Elena Park
    content_type: announcement
    excerpt: The Pro plan closed to new customers; existing Pro subscriptions moved to Business at the same price.
    body_markdown: |
      Pro subscriptions moved to the Business plan at their next renewal, at the Pro price for the first year. Nothing
      changed for customers already on Business.
    hero_media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c07
    reading_time_minutes: 2
    locale: en-US
    primary_category_id: updates-announcements
    primary_category_path: Updates > Announcements
    tag_preview: plans
    status: archived
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2025-10-01T09:00:00Z', reason: Drafted with the pricing change }
      - { title: In review, from: draft, to: in_review, at: '2025-10-06T09:00:00Z', reason: Sent to product marketing }
      - { title: Published, from: in_review, to: published, at: '2025-10-09T14:00:00Z', reason: Published with the pricing change }
      - { title: Archived, from: published, to: archived, at: '2026-04-30T09:00:00Z', reason: Migration complete }
    published_at: '2025-10-09T14:00:00Z'
    created_at: '2025-10-01T09:00:00Z'
    updated_at: '2026-04-30T09:00:00Z'
    last_event: archived
    last_event_at: '2026-04-30T09:00:00Z'
  - label: Pause a subscription for the off-season
    description: Stop billing without losing your data
    slug: pause-a-subscription
    author_id: 8b2e4d6f-1a3c-4e5b-9d7f-2c4e6a8b0e03
    author_name: Ines Duarte
    content_type: how_to
    excerpt: Pause billing for up to three months and resume from the same place.
    body_markdown: |
      Open the subscription and choose Pause. Billing stops at the end of the current period, and nothing is deleted.
      Choose Resume at any time to start the next period straight away.
    hero_media_id: null
    reading_time_minutes: 3
    locale: en-US
    primary_category_id: billing-subscriptions
    primary_category_path: Billing > Subscriptions
    tag_preview: subscriptions
    status: published
    state_history:
      - { title: Drafted, from: null, to: draft, at: '2026-05-11T10:00:00Z', reason: Requested by seasonal customers }
      - { title: In review, from: draft, to: in_review, at: '2026-05-19T10:00:00Z', reason: Sent to the support lead }
      - { title: Published, from: in_review, to: published, at: '2026-05-26T12:00:00Z', reason: Approved and published }
    published_at: '2026-05-26T12:00:00Z'
    created_at: '2026-05-11T10:00:00Z'
    updated_at: '2026-05-26T12:00:00Z'
    last_event: published
    last_event_at: '2026-05-26T12:00:00Z'
```
