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

# POST /v1/flows

> Create a WhatsApp Flow: registers it at Meta and uploads its Flow JSON. The Flow starts as a draft.

Creates a WhatsApp Flow on the channel's WhatsApp Business Account and uploads the Flow JSON as its asset. The Flow starts in `draft` — [publish it](/dev/endpoints/flows-publish) before sending it to real recipients.

## Endpoint

```
POST https://api.keebai.com/v1/flows
```

## Required scope

`flows:write`

## Body

| Field             | Type       | Required | Description                                                                                                                                          |
| ----------------- | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channel_id`      | `string`   | No       | Keebai channel id of the WhatsApp channel that owns the Flow. Only needed when the project has more than one active WhatsApp channel.                |
| `phone_number_id` | `string`   | No       | Alias for `channel_id`: the numeric Meta `phone_number_id`.                                                                                          |
| `name`            | `string`   | Yes      | Flow name, unique per channel. Up to 100 characters.                                                                                                 |
| `description`     | `string`   | No       | Up to 500 characters.                                                                                                                                |
| `categories`      | `string[]` | No       | Any of `OTHER`, `SIGN_UP`, `SIGN_IN`, `APPOINTMENT_BOOKING`, `LEAD_GENERATION`, `CONTACT_US`, `CUSTOMER_SUPPORT`, `SURVEY`. Defaults to `["OTHER"]`. |
| `flow_data`       | `object`   | Yes      | Flow JSON as defined by Meta: `version`, `screens`, `routing_model`.                                                                                 |
| `metadata`        | `object`   | No       | Free-form metadata stored alongside the Flow in Keebai.                                                                                              |

## Example request

```bash curl theme={null}
curl -X POST https://api.keebai.com/v1/flows \
  -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "reserva-mesa",
    "description": "Reserva de mesa en dos pasos",
    "categories": ["APPOINTMENT_BOOKING"],
    "flow_data": {
      "version": "7.0",
      "screens": [
        {
          "id": "WELCOME",
          "title": "Reservar mesa",
          "terminal": true,
          "data": {},
          "layout": {
            "type": "SingleColumnLayout",
            "children": [
              {
                "type": "Form",
                "name": "reserva",
                "children": [
                  { "type": "TextInput", "name": "nombre", "label": "Nombre", "required": true },
                  { "type": "DatePicker", "name": "fecha", "label": "Fecha", "required": true },
                  {
                    "type": "Footer",
                    "label": "Reservar",
                    "on-click-action": {
                      "name": "complete",
                      "payload": { "nombre": "${form.nombre}", "fecha": "${form.fecha}" }
                    }
                  }
                ]
              }
            ]
          }
        }
      ]
    }
  }'
```

## Response

### 201 Created

```json theme={null}
{
  "id": "66a1f2b3c4d5e6f708192a3b",
  "channel_id": "665f1a2b3c4d5e6f70819234",
  "name": "reserva-mesa",
  "description": "Reserva de mesa en dos pasos",
  "meta_flow_id": "1234567890123456",
  "categories": ["APPOINTMENT_BOOKING"],
  "status": "draft",
  "flow_data": { "version": "7.0", "screens": [] },
  "metadata": {},
  "created_at": "2026-07-27T14:31:07.221Z",
  "updated_at": "2026-07-27T14:31:07.221Z"
}
```

`meta_flow_id` is what you pass as `flow_id` when sending a message of type `flow` through [`POST /v1/messages`](/dev/endpoints/messages-send).

### 400 Bad Request

The channel is not WhatsApp, has no Meta credentials, or the Flow JSON was rejected by Meta.

### 409 Conflict

A Flow with that name already exists on the channel.


## OpenAPI

````yaml POST /v1/flows
openapi: 3.0.0
info:
  title: Keebai Public API
  description: Superficie pública REST autenticada con Personal Access Tokens (PAT).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.keebai.com
    description: Production
security: []
tags: []
paths:
  /v1/flows:
    post:
      tags:
        - flows
      summary: Create a WhatsApp Flow
      description: >-
        Registers the Flow at Meta and uploads its Flow JSON. The Flow starts in
        draft: publish it before sending it to real recipients.
      operationId: PublicFlowsController_create
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateFlowDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowResponseDto'
      security:
        - PAT: []
components:
  schemas:
    CreateFlowDto:
      type: object
      properties:
        channel_id:
          type: string
          example: 665f1a2b3c4d5e6f70819234
          description: >-
            Keebai channel id. Optional when the tenant has exactly one active
            channel of the given type; required to disambiguate otherwise.
        phone_number_id:
          type: string
          example: '100000000000001'
          description: >-
            WhatsApp-only alias for channel_id: the Meta phone_number_id of the
            sending number. Ignored on other channels.
        name:
          type: string
          example: reserva-mesa
          maxLength: 100
        description:
          type: string
          maxLength: 500
        categories:
          type: array
          default:
            - OTHER
          items:
            type: string
            enum:
              - OTHER
              - SIGN_UP
              - SIGN_IN
              - APPOINTMENT_BOOKING
              - LEAD_GENERATION
              - CONTACT_US
              - CUSTOMER_SUPPORT
              - SURVEY
        flow_data:
          type: object
          description: >-
            Flow JSON as defined by Meta (version, screens, routing_model).
            Uploaded as the Flow asset on create.
          example:
            version: '7.0'
            screens:
              - id: WELCOME
                title: Reserva
                terminal: true
                layout:
                  type: SingleColumnLayout
                  children: []
        metadata:
          type: object
          description: Free-form metadata stored alongside the Flow in Keebai.
      required:
        - name
        - flow_data
    FlowResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Keebai flow id.
        channel_id:
          type: string
          description: Keebai channel this Flow belongs to.
        name:
          type: string
        description:
          type: string
          nullable: true
        meta_flow_id:
          type: string
          nullable: true
          description: >-
            Meta Flow id. Use it as `flow_id` when sending a message of type
            `flow`. Null until Meta accepts the Flow.
        categories:
          type: array
          items:
            type: string
            enum:
              - OTHER
              - SIGN_UP
              - SIGN_IN
              - APPOINTMENT_BOOKING
              - LEAD_GENERATION
              - CONTACT_US
              - CUSTOMER_SUPPORT
              - SURVEY
        status:
          type: string
          enum:
            - draft
            - published
            - archived
        flow_data:
          type: object
          description: Flow JSON currently stored in Keebai.
        metadata:
          type: object
        created_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
          nullable: true
      required:
        - id
        - channel_id
        - name
        - categories
        - status
  securitySchemes:
    PAT:
      scheme: bearer
      bearerFormat: kbai_pk_<hex>
      type: http
      description: >-
        Personal Access Token con prefijo `kbai_pk_`. Generar desde el portal
        con permiso `developer.manage_tokens`.

````