> ## Documentation Index
> Fetch the complete documentation index at: https://docs.masivo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# News Feed

> Persistent in-app cards resolved from live templates for eligible customers.

News Feed cards are persistent in-app content. Unlike overlay in-app messages, a card remains eligible until its assignment expires or the customer dismisses it.

## How News Feed works

Cards are assigned to one customer, to an audience, or globally. The Storefront API resolves those assignments against the active template every time the feed is requested.

```mermaid theme={null}
flowchart LR
  T[Active template] --> A[Journey or scheduled assignment]
  A --> E{Eligible now?}
  E -- No --> Skip[Omitted]
  E -- Yes --> F[GET customer news feed]
  F --> R[Resolve live template content]
  R --> UI[Render in the app]
  UI --> I[Log shown, clicked, or dismissed]
  I --> S[Customer read state]
```

Audience assignments are evaluated against current audience membership. If a customer leaves the audience, that assignment no longer makes the card eligible. Editing an active template changes the content returned for every existing assignment because assignments store a template reference rather than a content copy.

## Card lifecycle

```mermaid theme={null}
stateDiagram-v2
  [*] --> Assigned: Journey or scheduled publication
  Assigned --> Visible: Eligible feed request
  Visible --> Visible: shown or clicked
  Visible --> Dismissed: dismissed
  Assigned --> Expired: expires_at reached
  Dismissed --> Visible: newer assignment created
  Expired --> [*]
  Dismissed --> [*]
```

| State     | Stored signal                         | Behavior                                                    |
| --------- | ------------------------------------- | ----------------------------------------------------------- |
| Assigned  | `news_feed_by_customer` row           | Targets a customer, audience, or the whole account          |
| Visible   | Active assignment and active template | Returned by `GET /customers/{id}/news-feed`                 |
| Shown     | `shown_at`                            | Records an impression without removing the card             |
| Clicked   | `clicked_at`                          | Records a click without removing the card                   |
| Dismissed | `dismissed_at`                        | Hidden until a newer assignment of the same template exists |
| Expired   | `expires_at` is in the past           | Excluded from the feed and later deleted by cleanup         |

Tracking emits `NEWS_FEED_ASSIGNED`, `NEWS_FEED_SHOWN`, `NEWS_FEED_CLICKED`, and `NEWS_FEED_DISMISSED` events.

## Configuration

| Field                               | Type             | Allowed values                              | Description                                        |
| ----------------------------------- | ---------------- | ------------------------------------------- | -------------------------------------------------- |
| `name`                              | `string`         | Non-empty                                   | Internal template name                             |
| `status`                            | `string`         | Account status values                       | Only `ACTIVE` templates are returned               |
| `description`                       | `string`         | Optional                                    | Internal description                               |
| `brand_id`                          | `string \| null` | Optional                                    | Restricts the card to a brand                      |
| `campaign_id`                       | `uuid \| null`   | Optional                                    | Associates analytics with a campaign               |
| `card_type`                         | `string`         | `classic`, `captioned_image`, `image_only`  | Controls which content fields the editor requires  |
| `content_title`                     | `string`         | Required for classic and captioned image    | Card title                                         |
| `content_body`                      | `string`         | Required for classic and captioned image    | Card message                                       |
| `content_image_url`                 | URL              | Required for captioned image and image only | Card image or classic icon                         |
| `content_link_url`                  | URL              | Optional                                    | Destination opened by the card                     |
| `content_link_text`                 | `string`         | Optional                                    | Link label                                         |
| `content_pinned`                    | `boolean`        | `true`, `false`                             | Places the card before unpinned cards              |
| `priority`                          | `integer`        | Optional                                    | Orders cards within the pinned group               |
| `expiration_days`                   | `integer`        | `1`–`30`                                    | Duration calculated from assignment creation       |
| `team_ids`                          | `string[]`       | Up to 20 ids                                | Limits dashboard access to teams                   |
| `news_feed_default_expiration_days` | `integer`        | `1`–`30`                                    | Account fallback when a template has no expiration |

The public feed endpoint accepts `limit` from 1 to 50 for each request; there is no separate account-level feed size setting.

## Eligibility and ordering

The feed combines three assignment branches:

1. Direct customer assignments, including journey actions and test sends.
2. Global assignments with no customer or audience target.
3. Audience assignments when the customer is currently a member.

Duplicate branches for the same template are collapsed. A non-expiring assignment keeps the combined card non-expiring. Cards are ordered by pinned state, numeric priority, and newest assignment time.

## Operations and cleanup

`POST /api/cron/marketing_automation/news_feed/cleanup` deletes expired assignments in batches of 5,000. It also deletes stale read-state rows after a seven-day grace period when no direct, global, or current-audience assignment still covers the customer and template.

Template expiration changes enqueue a batched recomputation of `expires_at` for existing assignments.

## Comparison with in-app messages

|                      | News Feed                             | In-app messages                        |
| -------------------- | ------------------------------------- | -------------------------------------- |
| Surface              | Persistent feed                       | Overlay, banner, modal, or custom code |
| Storage              | Assignment references a live template | Per-customer message rows              |
| Audience membership  | Resolved live                         | Materialized at delivery time          |
| Dismissal            | Hidden until a newer assignment       | Message state becomes dismissed        |
| Public fetch         | `/customers/{id}/news-feed`           | `/customers/{id}/inapp-messages`       |
| Interaction endpoint | Uses template id                      | Uses message id                        |
