# Reference Architecture: Multi-Channel Delivery

> Source: https://agilitycms.com/docs/developers/reference-architecture-multi-channel

Use this architecture when the same content has to reach more than a website: a mobile app, email campaigns, in-store kiosks, digital signage or another system. Editors work in one place, and each channel takes the content in the way that suits it.

Each channel uses one of three delivery patterns:

| Pattern | How it works | Best for |
| --- | --- | --- |
| **Pull** | The channel calls the Content Fetch API or GraphQL API when it needs content, through a cache | Websites, apps with a backend, email sent from a server |
| **Push** | A webhook tells the channel's system that content changed, and that system pulls or rebuilds | Static builds, email templates, search indexes, other platforms |
| **Local copy** | Content Sync copies content into a store the channel owns, and keeps it current | Kiosks and screens that must work offline, very high read volume, data platforms |

See [Publishing to Multiple Destinations](/docs/overview/publishing-to-multiple-destinations) for the idea, and [Content Sync Explained](/docs/developers/content-sync-explained) for when to choose Sync over Fetch or GraphQL.

For channel-by-channel advice on reading content and modeling it to work in every channel, see [Beyond the Website](/docs/developers/beyond-the-website).

## Components

| Component | Owned by | Role |
| --- | --- | --- |
| Agility instance | Agility (you configure it) | Shared content lists, plus a sitemap per channel that has pages or screens |
| Content Fetch and GraphQL APIs | Agility | Pull access for every channel |
| Webhooks | Agility | Push notifications on save, publish, unpublish and workflow events |
| Website | You | As in the [marketing website](/docs/developers/reference-architecture-marketing-site) architecture |
| App backend (recommended) | You | Reads from Agility, caches, and serves the app the shape it needs |
| Mobile app | You | Reads from your backend, or directly from the Fetch API with a fetch key |
| Email platform | Your vendor | Pulls content at send time, or receives it from your code when a webhook fires |
| Kiosk or screen player | You | Reads from a local store kept current by Content Sync |

## Deciding where content goes

Editors need to know which channel each item reaches. Pick one approach per content type:

- **Structure:** separate content lists or a separate sitemap per channel, when an item belongs to one channel only. A sitemap can represent a website, an app or digital signage. See [Sitemaps](/docs/developers/sitemaps).
- **A destination field:** a field where editors pick the channels, when the same item can go to several. Each channel filters on it.
- **Code and process:** routing rules in each channel's code, agreed between teams.

These are described in detail in [Publishing to Multiple Destinations](/docs/overview/publishing-to-multiple-destinations).

## Data flow by channel

| Channel | Pattern | How it reads | How it learns about changes |
| --- | --- | --- | --- |
| Website | Pull | Server-side Fetch or GraphQL, cached with tags | Webhook clears tags |
| Mobile app | Pull | From your backend, or the Fetch API with a fetch key | Your backend's cache is cleared by webhook; the app refreshes on launch or on a push notification you send |
| Email | Pull or push | Your sending code fetches the content when it builds the message | A webhook on publish can update a template or trigger a send in your email platform |
| Kiosk or signage | Local copy | Reads its own store, filled by `@agility/content-sync` | A webhook or a schedule runs an incremental sync from the stored token |
| Search or data platform | Push or local copy | Indexes from webhooks, or syncs | Webhooks for single items, Sync for bulk and recovery |

## Keys and security per channel

- **Fetch keys only read published content.** A key compiled into an app or a kiosk can be extracted, so treat everything it can read as public. A fetch key reads all published content in the instance.
- **Never put a preview key or a Management API token in an app, a kiosk or a browser.** Preview keys read unpublished content.
- **An app backend lets you rotate keys** without shipping a new app version, and lets you shape and cache responses for the app.

## Caching

| Channel | Cache | Freshness |
| --- | --- | --- |
| Website | Data cache and CDN | Seconds after publish, through webhooks |
| App backend | Data cache | Seconds after publish, through webhooks |
| Mobile app | On the device | Until the app next refreshes |
| Email | None needed: content is read once at send time | Whatever was published at send time |
| Kiosk | Its local store | Until the next sync |

## Preview

Agility's **Preview** button opens the preview deployment registered for a sitemap. That suits the website and any channel you can render in a browser. For the others:

- **App:** run a preview build or a preview mode of your backend that reads with the preview key.
- **Email:** send test messages built from preview content.
- **Kiosk and screens:** point a test player at a store synced with the preview API type and a preview key.

## Failure modes

| What goes wrong | Effect | Design for it |
| --- | --- | --- |
| A field is removed or renamed in a model | Older app versions still in use break | Change models additively. Add fields, stop using old ones, remove them only when no shipped version reads them |
| An app ships with a preview key | Unpublished content is readable by anyone who extracts it | Fetch keys only in clients, preview through your backend |
| A kiosk loses its network | Nothing, if it reads from its local store | Content Sync to a local store. Sync again when the network returns |
| A webhook is missed | One channel stays on old content | Turn on webhook retries, and run a scheduled sync or rebuild as a safety net |
| Many channels hit the API at the same moment | `429` responses | Put channels behind caches or a backend, and sync rather than poll. See [Handle Rate Limits and Outages Gracefully](/docs/developers/handle-rate-limits-and-outages) |
| An email is built from unpublished content | Drafts reach customers | Build emails with the fetch key and the `fetch` API type only |

## Related

- [Reference Architectures](/docs/developers/reference-architectures)
- [Publishing to Multiple Destinations](/docs/overview/publishing-to-multiple-destinations)
- [Content Sync Explained](/docs/developers/content-sync-explained)
- [Webhook Events and Payload Reference](/docs/developers/webhook-events-and-payloads)
- [Building a Content Hub](/docs/overview/building-a-content-hub)
