Skip to main content
You never write flow JSON yourself, but knowing what a screen can hold makes your requests to Paige far more precise. This page is the reference: how screens fit together, then every component that can go on one.

How a screen works

A flow is an ordered set of screens — at most 10. Each screen has an ID, a title, and a single-column stack of components that render top to bottom. Screens don’t advance on their own. The Footer — the button pinned to the bottom of the screen — is what moves things along:
  • A Footer with a navigate action opens another screen in the same flow, optionally handing it values from this one.
  • A Footer with a complete action ends the flow and submits everything to your bot. At least one screen in every flow must end this way.
Navigation is forward-only. There’s no need for a back button — WhatsApp provides one.

Conditional rendering

If and Switch let one screen show different things to different people. Both render on the same screen surface — the branches aren’t separate screens, they’re alternative contents of the one screen. That has two consequences worth remembering:
  • A field name used in one branch counts as a duplicate if it’s reused in another branch on that screen. See Every input on a screen needs its own name.
  • A Footer inside a branch is a real Footer for that render path.
Exactly one Footer per render path. Not one per screen — one per path a person can actually see.
This screen has no Footer at all. The list rows do the navigating themselves, so a NavigationList must be the only thing on its screen — no Footer, no headings, no inputs. Move anything else to its own screen.

Text

A bold heading displayed at the top of a screen or section. Use it to label each screen clearly. Max 80 characters.
A smaller heading. Use it to break a longer screen into labelled sections. Max 80 characters.
Regular body text for instructions or descriptions. Set markdown: true for bold, italics, lists, and links. Max 4096 characters.
Small print — a disclaimer, a helper note, or a static label sitting above a value. Also supports markdown: true. Max 4096 characters.
Full markdown rendering, including tables. Accepted, but not recommended — it currently causes rendering errors in WhatsApp’s flow runtime, so Paige is told to use TextBody with markdown: true instead. Reach for it only when you need something TextBody genuinely can’t do, such as a markdown table, and expect trouble.

Inputs

Each of these registers a field name that appears in the submitted data.
A single-line text field — names, email addresses, reference numbers. Needs a name and a label of 20 characters or fewer. Set input-type to one of text, number, email, password, passcode, or phone to get the right keyboard and validation. For anything longer than a line, use TextArea.
A multi-line text box for notes, descriptions, and free-form comments. Needs a name and a label of 20 characters or fewer.
Pick exactly one, with every option visible at once. Best for a short list where seeing all the choices matters. Max 20 options.
Pick several from a visible list. Max 20 options — for a longer list, split it across two screens.
Multi-select shown as tappable tags rather than a list — good for interests and preferences. Between 2 and 20 options, and max-selected-items caps how many can be chosen.
A consent tick box — marketing permission, terms acceptance. Label max 120 characters, and at most 5 per screen.

Dates

A native date selector. The chosen date comes back as a YYYY-MM-DD string. Label max 40 characters. min-date, max-date, and unavailable-dates let you fence off dates that can’t be picked.
A full calendar view. mode: "single" picks one date; mode: "range" picks a start and an end, and the label becomes a pair. Use it for stays and multi-day bookings where seeing the month matters. Label max 40 characters.
A single image on the screen — a product shot, a map, a logo. src is either an https URL or raw base64 with no data:image/...;base64, prefix. Max 3 per screen, and keep each under about 300 KB.
Between 1 and 3 swipeable images in one block. Use it to show variants of the same thing without spending three separate screens.
Lets someone attach photos from their camera or gallery — proof of damage, a meter reading, a receipt. Needs a name and a label. Only one per screen, and it can’t share a screen with a DocumentPicker.
The same idea for files — a signed contract, a PDF quote. allowed-mime-types restricts what can be attached. Needs a name and a label, one per screen, and no PhotoPicker alongside it.

Structure and logic

A container that groups a screen’s inputs together with the Footer that submits them. It’s plumbing rather than something people see — Paige adds it where it’s needed.
Shows one set of components when a condition is true and another when it isn’t — for example, only asking for a company name if someone said they’re a business. The condition has to resolve to a true/false answer.
The same idea for more than two outcomes: one case per value, plus an optional default. This is how a single screen replaces several near-identical ones, and how a screen sends different people to different next screens — each case carries its own Footer.

Limits at a glance

Paige checks all of these before a flow ever reaches Meta, so hitting one shows up as a clear message rather than a rejection.