# Write a Flow with any AI

> A copy-ready prompt that gets ChatGPT, Claude, Gemini or any AI to write WhatsApp Flow JSON that works in Avox, with a worked example and fixes.

Source: https://docs.theavoxai.com/flows/write-with-ai

A Flow is described by one **Flow JSON** document: a list of screens, the fields on each screen,
and what the button at the bottom of each screen does. Avox's editor takes that JSON as it is, so
any AI chat tool can write it for you if you give it the right rules.

This page gives you a prompt with those rules built in, a worked example, and the steps to put the
result into Avox. You don't need to know JSON to use it.

## Plain or live

There are two kinds of Flow. The prompt below writes the first kind; [Live Flows with an
AI](#live-flows-with-an-ai) covers the second.

- **A plain Flow.** Every screen, question and choice is written into the Flow JSON. The customer
  fills it in, taps the last button, and their answers come back to Avox. There's nothing to host
  and no code to run, so anyone can make one, with or without an AI.
- **A live Flow.** Some screens read or save your [Tables](https://docs.theavoxai.com/tables) while the customer fills them
  in, such as free time slots, seats left or a price. Avox's own endpoint answers, with nothing to
  host: see [Live Flows and the Avox endpoint](https://docs.theavoxai.com/flows/live-endpoint).

If you're not sure which you need, start plain. Most lead forms, surveys, feedback and support
forms don't need live data, and you can start from a premade live Flow when you do.

## The prompt

Copy this prompt into ChatGPT, Claude, Gemini or any other AI chat. Fill in the five lines under
"About my business" first; leave everything under "Rules" exactly as it is.

```text
You write WhatsApp Flow JSON for a business. Reply with ONLY the JSON:
no explanation before or after, no markdown code fences, no comments.

About my business:
- Business: [what you do and where, e.g. "a dental clinic in Pune"]
- What to collect: [e.g. name, phone number, which treatment, preferred day]
- Screens: [e.g. "screen 1: contact details, screen 2: what they need",
  or "you decide, at most 3 screens"]
- Button on the last screen: [e.g. "Send"]
- Language for all text: [e.g. English]

Rules (follow every one):
1. The top level has exactly two keys: "version": "6.0" and "screens".
   This Flow has no server, so never add "data_api_version",
   "routing_model", "data_endpoint_uri", or any "data_exchange" action.
2. Every screen has:
   - "id": capital letters and underscores only (e.g. CONTACT_DETAILS),
     unique in the Flow, and never the word SUCCESS
   - "title": a short title shown at the top of the screen
   - "data": {}
   - "layout": {"type": "SingleColumnLayout", "children": [...]}
3. A screen's layout "children" may start with a "TextHeading" and a
   "TextBody" (each with a "text"), followed by exactly one
   {"type": "Form", "name": "form", "children": [...]}.
   The Form holds every input on that screen and ends with exactly one
   "Footer" (the button).
4. Inputs you may use: "TextInput" (with "input-type": "text", "number",
   "email" or "phone"), "TextArea", "Dropdown", "RadioButtonsGroup",
   "CheckboxGroup", "DatePicker". Never ask for a password: every
   answer is shown to the business team.
   Every input has:
   - "name": lowercase letters and underscores, unique in the whole Flow
   - "label": the question shown to the customer
   - "required": true or false
   "Dropdown", "RadioButtonsGroup" and "CheckboxGroup" also have
   "data-source": a list of {"id": "...", "title": "..."}.
5. Keep text short: labels of "TextInput", "TextArea" and "Dropdown"
   20 characters or fewer, of "RadioButtonsGroup" and "CheckboxGroup"
   30 or fewer, of "DatePicker" 40 or fewer; choice titles 30 or fewer,
   Footer labels 35 or fewer, headings 80 or fewer. Put a longer
   question in the input's "helper-text" (80 or fewer).
6. On every screen except the last, the Footer is:
   {"type": "Footer", "label": "Next", "on-click-action":
     {"name": "navigate", "next": {"type": "screen", "name": "<ID OF THE NEXT SCREEN>"}, "payload": {}}}
7. The last screen has "terminal": true and "success": true. Its Footer's
   "on-click-action" is {"name": "complete", "payload": {...}}, and the
   payload MUST list EVERY input in the whole Flow, on every screen, one
   key per input, the key being the input's name. Only what is in this
   payload is sent back; an input left out is lost. Use:
   - "${form.<name>}" for inputs on the last screen
   - "${screen.<SCREEN_ID>.form.<name>}" for inputs on earlier screens
   Before you reply, check every input name appears in this payload.
8. Output must be valid JSON: double quotes only, no trailing commas.
```

> **Tip**
>
> When the AI replies, check that the answer starts with `{` and ends with `}`. If it added a
> sentence or wrapped the JSON in a code block, copy only the part from the first `{` to the last
> `}`.

### Why the rules are there

Each rule matches something Avox or Meta checks:

| Rule | Why |
| --- | --- |
| Version 6.0 | The Flow JSON version Avox's own blank and premade Flows use. Avox's Errors panel flags a Flow with no version. |
| No endpoint keys | Endpoint settings only belong in a Flow whose server is set up. Without one, Meta can't run those steps. |
| Screen ids | Avox flags a screen with no id or a repeated id. Meta reserves the word SUCCESS for itself. |
| One Form with a Footer | Avox flags a screen with no Form, a Form with no Footer, and a Footer with no action. Without these the button does nothing. |
| Short text | Meta limits how long labels and titles can be. **Validate** tells you if one is too long. |
| navigate, then complete | `navigate` moves to the next screen. `complete` closes the Flow and sends the answers. |
| Every input in the payload | Only what's in the `complete` payload comes back to Avox, where each answer shows under its question in the Inbox, on the contact's page and in the Flow's Responses. An input left out is lost. Avox's **Errors** panel warns about each one, and **Send all answers** adds them for you. |
| No passwords | Answers are stored in Avox and shown to your team. Avox warns about a password sent back into the chat. |

## A worked example

Filled in for a small business that wants call-back requests:

```text
- Business: a home cleaning company in Leeds
- What to collect: full name, phone, email (optional), which service,
  best time to call, anything else they want to tell us
- Screens: screen 1: contact details, screen 2: what they need
- Button on the last screen: Send
- Language for all text: English
```

A good answer looks like this:

```json
{
  "version": "6.0",
  "screens": [
    {
      "id": "CONTACT_DETAILS",
      "title": "Request a call",
      "data": {},
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          { "type": "TextHeading", "text": "How can we reach you?" },
          { "type": "TextBody", "text": "We call back within one working day." },
          {
            "type": "Form",
            "name": "form",
            "children": [
              { "type": "TextInput", "name": "full_name", "label": "Full name", "input-type": "text", "required": true },
              { "type": "TextInput", "name": "phone", "label": "Phone number", "input-type": "phone", "required": true },
              { "type": "TextInput", "name": "email", "label": "Email", "input-type": "email", "required": false },
              {
                "type": "Footer",
                "label": "Next",
                "on-click-action": {
                  "name": "navigate",
                  "next": { "type": "screen", "name": "SERVICE_DETAILS" },
                  "payload": {}
                }
              }
            ]
          }
        ]
      }
    },
    {
      "id": "SERVICE_DETAILS",
      "title": "What you need",
      "terminal": true,
      "success": true,
      "data": {},
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          { "type": "TextHeading", "text": "What can we help with?" },
          {
            "type": "Form",
            "name": "form",
            "children": [
              {
                "type": "Dropdown",
                "name": "service",
                "label": "Service",
                "required": true,
                "data-source": [
                  { "id": "regular", "title": "Regular cleaning" },
                  { "id": "deep", "title": "Deep cleaning" },
                  { "id": "end_of_tenancy", "title": "End of tenancy" }
                ]
              },
              {
                "type": "RadioButtonsGroup",
                "name": "best_time",
                "label": "Best time to call",
                "required": true,
                "data-source": [
                  { "id": "morning", "title": "Morning" },
                  { "id": "afternoon", "title": "Afternoon" },
                  { "id": "evening", "title": "Evening" }
                ]
              },
              { "type": "TextArea", "name": "details", "label": "Anything else?", "required": false },
              {
                "type": "Footer",
                "label": "Send",
                "on-click-action": {
                  "name": "complete",
                  "payload": {
                    "full_name": "${screen.CONTACT_DETAILS.form.full_name}",
                    "phone": "${screen.CONTACT_DETAILS.form.phone}",
                    "email": "${screen.CONTACT_DETAILS.form.email}",
                    "service": "${form.service}",
                    "best_time": "${form.best_time}",
                    "details": "${form.details}"
                  }
                }
              }
            ]
          }
        ]
      }
    }
  ]
}
```

And this is how it looks on a phone. It's the same preview the Flow editor shows next to your
JSON; tap the button to move between the two screens:

When a customer taps **Send**, WhatsApp sends Avox one reply holding `full_name`, `phone`,
`email`, `service`, `best_time` and `details`. WhatsApp sends the `id` of each choice (for
example `deep`); Avox shows it under its question with the choice's title ("Deep cleaning") in
the Inbox, on the contact's page and in the Flow's [Responses](https://docs.theavoxai.com/flows#log-and-responses).

## Put it into Avox

**1. Open the Flow wizard**

Go to **Flows** and click **Create Flow**.

**2. Start from scratch**

Under **Start from scratch**, pick the category that fits best (for the example above, **Lead
generation**) and click **Continue**.

**3. Name it**

Enter a **Flow name** and check the **Categories**, then click **Next**.

**4. Paste the JSON**

Click inside the **Flow content Editor (JSON)**, select everything in it (Ctrl+A, or Cmd+A on a
Mac) and paste the AI's answer over it. Leave **Live data** off.

**5. Run**

Click **Run** (or press Ctrl+Enter, Cmd+Enter on a Mac). It's an instant check in your browser:
the phone preview redraws from your JSON, and **Previewing** above the phone lets you look at
each screen. "Invalid JSON" means the text isn't JSON yet; see the table below.

**6. Validate**

Click **Validate** to run Meta's own check. It needs a connected WhatsApp number and doesn't
publish anything.

**7. Fix anything in Errors**

Open **Errors** in the footer. It lists every problem in one numbered list: Avox's own checks
and Meta's findings, then warnings in amber. If it says an answer is never sent back, click
**Send all answers**. When it says "No errors - Meta's happy with this JSON." you're ready.

**8. Create and publish**

Click **Create & publish**. The Flow goes to Meta and shows as **Published** on the Flows page.
From there, an automation's **Use Flows** action sends it (see [Actions](https://docs.theavoxai.com/automation/actions)).

> **Tip**
>
> The fastest fix for anything in **Errors**: copy the whole list, paste it back into the same AI
> chat, and write "Fix these errors and reply with the full corrected JSON only." Paste the new
> answer over the old one and click **Run** and **Validate** again.

Changing a Flow later works the same way: ask the AI for the change ("add a question about budget
to the second screen, keep all the rules"), then paste, Run and Validate again.

## Common mistakes and fixes

| What you see | What it means | Fix |
| --- | --- | --- |
| "Invalid JSON" with a reason | The text isn't valid JSON. Usually the AI added a sentence, a code fence, a comment, or a comma after the last item in a list. | Copy only from the first `{` to the last `}`. Or paste the error back to the AI and ask for valid JSON only. |
| Missing a top-level "version" field. | The JSON has no version. | Add `"version": "6.0",` as the first line inside the outer braces. |
| This Flow has no screens defined yet. | There's no `screens` list, or it's empty. | Ask the AI to follow rule 2 and rewrite the Flow. |
| Screen id "..." is used more than once | Two screens share an id. | Give each screen its own id, and update any `navigate` that points to it. |
| no "Form" found in its layout. | A screen has its inputs outside a Form, or no Form at all. | Put the screen's inputs and its Footer inside one Form (rule 3). |
| missing a "Footer" | A screen has no button. | Add a Footer as the last item in that screen's Form. |
| its Footer has no "on-click-action" | The button doesn't do anything. | Use `navigate` on every screen but the last, and `complete` on the last (rules 6 and 7). |
| Meta says a label or title is too long | A label is over Meta's limit. | Shorten it (rule 5). Put longer explanations in a TextBody above the Form. |
| Meta says a screen can't be found | A `navigate` points to a screen id that doesn't exist, often a typo. | Make `next.name` match the next screen's `id` exactly, capitals included. |
| Meta mentions a data endpoint, `data_exchange` or `routing_model` | The AI wrote a live Flow. | Remove those parts and use `navigate` and `complete` instead (rule 1), or turn on **Live data** and connect its screens to tables (see [Live Flows with an AI](#live-flows-with-an-ai)). |
| The answer to "..." is never sent back, so you won't see it in Avox. | The `complete` payload is empty or leaves that input out ([AVX-3455](https://docs.theavoxai.com/error-codes/avx-3455)). | Click **Send all answers** in **Errors**, or list every input in the payload (rule 7). |
| The Flow works, but answers are missing in Avox | The `complete` payload is empty or leaves fields out. | List every input in the payload (rule 7), or click **Send all answers**. |

If **Create & publish** fails with "Meta rejected the Flow's content", nothing was published: click
**Validate**, fix what **Errors** shows, and publish again. See [Publishing](https://docs.theavoxai.com/flows#publishing).

## Live Flows with an AI

You don't need the AI for the live parts. Ask it for a plain Flow with the screens you want, then
let Avox make it live:

**1. Ask for the screens**

Use the prompt above, and add under "What to collect" which screen shows a list from your data,
for example: "screen 2: a Dropdown named slot whose choices come from my data". The AI may then
write `"data-source": "${data.slots}"`; that's fine.

**2. Paste it and turn on Live data**

Paste the JSON, open **Live data**, and turn on **This Flow uses live data**.

**3. Connect tables**

On each screen that needs data, click **Connect a table**: for example **Show rows** from your
Time slots table into `slots` when SLOT opens, and **Book a slot or seat** when it's submitted.

**4. Add live parts**

Click **Add live parts**. Avox adds `data_api_version`, the `routing_model`, the data each screen
declares (with examples from your rows), turns the buttons that need data into `data_exchange`,
and sends every answer along. Then **Run**, **Validate** and publish.

## Related

- [WhatsApp Flows](https://docs.theavoxai.com/flows): the Flows page, the wizard, drafts and statuses.
- [How WhatsApp Flows Work](https://docs.theavoxai.com/whatsapp/flows): Flow JSON, screens, components and actions on
  Meta's side.
- [Live Flows and the Avox endpoint](https://docs.theavoxai.com/flows/live-endpoint): Flows that read and save your tables,
  or ask your own server.
