Skip to main content

Scoping cards to dynamic screens

Sometimes you do not know up front how many screens will need cards. A banking app, for example, lets a customer open any number of accounts, each with its own screen, created at runtime. You want each account screen to show only the cards relevant to that account - but you cannot create a stream container per account in the Workbench due to the account's dynamic nature.

The solution is to use a single stream container for all of these screens and let each screen ask for just the cards that belong to it, by filtering on a unique identifier - such as the account number - that the card also carries.

The pattern at a glance

  1. On the Action Flow: include a custom variable (for example account_id) populated with the identifier of the account the cards in the Action Flow relate to.
  2. In the app: embed the same stream container on every account screen, and apply an SDK card filter that matches account_id to the account that screen is showing.

The same identifier appears on both sides: the Action Flow declares which account the cards in it are for, and the screen asks only for cards matching the account in front of the customer. One container serves an unlimited, ever-changing set of screens, and no Workbench changes are needed when a customer opens or closes an account.

Why one container

Stream containers are configured in the Workbench, so there is a fixed, finite set of them. They suit stable surfaces (a notification centre, a dashboard), not entities that are dynamically created. Trying to map one container to each account does not scale and is impossible to set up in advance.

A single container avoids all of this:

  • It scales to any number of screens. The container ID is just a value you pass to the SDK; embedding it on a new account screen requires no new configuration.
  • Scoping happens client-side. Each screen decides what to show by applying a filter, so adding, removing or switching accounts is purely an app concern.
  • Configuration stays in one place. The card limit and theme are set once on the single container.

Step 1: Put the identifier on the card

Action Flows carry custom variables whose values are set on each card sent in that Action Flow. Add a variable that holds the account identifier (for example account_id) and populate it for each card sent.

The value typically comes from the Action Flow's trigger payload or context - whatever data identifies the account the card is about. The important thing is that the value you put in account_id is exactly the same identifier your app uses for that account, so the two sides line up.

Use a stable, exact identifier

Filters match values exactly, so use a canonical identifier such an account ID or account number rather than a display name that might be formatted differently in the app and on the card.

Step 2: Filter the container on each screen

Embed the same container on every account screen, and apply a filter that matches the card's account_id variable to the account that screen is displaying. Filters are applied client-side when you embed the container, so the same container shows a different, scoped set of cards on each screen.

// On the screen for account "1234567"
let filters = [
AACCardListFilter.filter(byCardsEqualTo: .byVariableName("account_id", string: "1234567"))
]
var config = ContainerConfiguration()
config.filters = filters
StreamContainer(
containerId: "<stream container id>",
configuration: config
)

When the customer navigates to a different account, embed the container with a filter for that account's identifier instead. Because there is only one container, you only ever change the filter, never the container.

See Filtering cards for the full set of filter values and operators, and Creating cards for how to attach the variable.

Combining with other filters

You can apply more than one filter at once - for example, scope to an account and only show high-priority cards:

let filters = [
AACCardListFilter.filter(byCardsEqualTo: .byVariableName("account_id", string: "1234567")),
AACCardListFilter.filter(byCardsIn: [.byPriority(1), .byPriority(2)])
]
var config = ContainerConfiguration()
config.filters = filters
StreamContainer(
containerId: "<stream container id>",
configuration: config
)

The card count APIs respect the same filters, so a per-account badge will reflect only the cards scoped to that account.

A note on enforcement

This pattern scopes what each screen displays. Every card still lives in the one container's stream; the filter simply narrows what a given screen shows. That is exactly what you want for per-account screens, where the same customer is entitled to see all of their accounts' cards, just organised by screen.

If instead you need to ensure a card is never delivered to a customer at all (for example, for consent or entitlement reasons) enforce that when sending, not with a client-side filter. See Consent and message-type preferences.