VitrinaAPI

WhatsApp Flows

Structured, multi-screen forms a WhatsApp conversation can open, built and published from Vitrina.

Beta
This may change at any time, with a changelog entry and a notice to recent callers — see versioning.

Download the full API pública projection: openapi.json.

Use WhatsApp templates and Flows explains this resource in prose, with runnable examples.

MethodPathWhat it does
GET/whatsapp-flowsLive list of a channel's WhatsApp Flows (composer picker)
GET/whatsapp-flows/capabilitiesWhat this workspace can build into a WhatsApp Flow
GET/whatsapp-flows/managedList flows built in the Vitrina flow builder
POST/whatsapp-flows/managedCreate a flow in the builder
DELETE/whatsapp-flows/managed/{id}Delete a draft flow
GET/whatsapp-flows/managed/{id}Fetch one managed flow
PATCH/whatsapp-flows/managed/{id}Update a flow (drafts and published flows, in place)
POST/whatsapp-flows/managed/{id}/convert-to-jsonConvert a flow to raw Flow JSON authoring
POST/whatsapp-flows/managed/{id}/deprecateDeprecate a published flow
POST/whatsapp-flows/managed/{id}/duplicateDuplicate a flow as a new draft
GET/whatsapp-flows/managed/{id}/flow-jsonGet a flow's current Flow JSON
GET/whatsapp-flows/managed/{id}/previewGet (or refresh) a flow preview link
POST/whatsapp-flows/managed/{id}/publishPublish a flow
PATCH/whatsapp-flows/managed/{id}/settingsUpdate a flow's agent settings
POST/whatsapp-flows/managed/{id}/test-sendSend a flow to a phone for testing
POST/whatsapp-flows/managed/data-previewPreview the live data of one flow screen
POST/whatsapp-flows/syncReconcile a channel's managed flows against Meta

GET /whatsapp-flows

Live list of a channel's WhatsApp Flows (composer picker)

A live proxy read from Meta via the bridge — includes a flow authored outside Vitrina that has not been synced into the builder yet. Each row also carries the agent settings of its local copy (managed_id, key, description, ai_enabled and the default message_*), all null when there is none. messaging_account_id is required; there is no tenant-wide list.

ParameterInTypeRequiredConstraints
messaging_account_idqueryuuidyes
curl https://api.vitrinadev.com/api/v1/whatsapp-flows \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "1200300400500600",
      "name": "Agenda tu visita",
      "status": "PUBLISHED",
      "categories": [
        "APPOINTMENT_BOOKING"
      ],
      "managed_id": "e3e3e3e3-0000-4000-8000-000000000001",
      "key": "agenda_tu_visita",
      "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
      "ai_enabled": true,
      "message_body": "Elige el día y la hora que te acomoden para tu visita 👇",
      "message_cta": "Agendar visita",
      "message_header": null,
      "message_footer": null
    }
  ],
  "meta": {
    "total": 1
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

GET /whatsapp-flows/capabilities

What this workspace can build into a WhatsApp Flow

Discovery for anyone authoring a flow (UI, API, MCP): the Flow JSON and data API versions, the authoring modes, the builder palette (components — every kind with its props, types and bounds, read off the same schema create/update validate with), the live-data data_sources with their params and whether THIS workspace can answer them (booking engine + vertical aware; available: false carries the reason), the context_keys a field can be prefilled from (generic keys + the workspace's custom contact/conversation attributes), the completion_modes and whether booking at submit is possible with this workspace's engine, and the limits. Labels follow the workspace vertical (subject_label: vehicle, property, product, treatment, …). messaging_account_id is required.

ParameterInTypeRequiredConstraints
messaging_account_idqueryuuidyes
curl https://api.vitrinadev.com/api/v1/whatsapp-flows/capabilities \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "flow_json_version": "7.3",
    "data_api_version": "4.0",
    "authoring_modes": [
      "builder",
      "json"
    ],
    "vertical": "healthcare",
    "subject_label": "Tratamiento",
    "booking_engine": {
      "key": "native-clinic",
      "can_book": true,
      "booking_link_only": false
    },
    "components": [
      {
        "kind": "date_picker",
        "props": {
          "name": {
            "type": "string",
            "required": true,
            "max": 60,
            "min": 1
          },
          "label": {
            "type": "string",
            "required": true,
            "max": 40,
            "min": 1
          },
          "required": {
            "type": "boolean",
            "required": false
          },
          "helper_text": {
            "type": "string",
            "required": false,
            "max": 80
          },
          "source": {
            "type": "object",
            "required": false
          }
        }
      }
    ],
    "data_sources": [
      {
        "source": "available_slots",
        "kind": "options",
        "label": "Horas disponibles",
        "description": "Free hours on one day (`date` required) from the booking engine, after the booking-notice policy.",
        "params": [
          {
            "name": "date",
            "required": true
          },
          {
            "name": "service",
            "required": false
          },
          {
            "name": "professional",
            "required": false
          }
        ],
        "available": true,
        "reason": null
      },
      {
        "source": "vehicles",
        "kind": "options",
        "label": "Tratamiento",
        "description": "Items of the workspace inventory (today: the vehicle stock); `query` searches, `branch` narrows.",
        "params": [
          {
            "name": "branch",
            "required": false
          },
          {
            "name": "query",
            "required": false
          }
        ],
        "available": false,
        "reason": "this workspace's inventory is not exposed as a flow source yet"
      }
    ],
    "context_keys": [
      {
        "key": "subject",
        "label": "Tratamiento",
        "source": "conversation"
      },
      {
        "key": "contact.name",
        "label": "Nombre del contacto",
        "source": "contact"
      },
      {
        "key": "contact_attr:prevision",
        "label": "Previsión",
        "source": "contact_attr"
      }
    ],
    "completion_modes": [
      {
        "mode": "book_then_agent",
        "label": "Agendar y que el agente confirme",
        "description": "The appointment is booked (or held for a deposit) when the form is submitted; the agent then confirms and continues.",
        "books_at_submit": true,
        "available": true,
        "reason": null
      }
    ],
    "limits": {
      "max_screens": 10,
      "max_components_per_screen": 49,
      "max_images_per_screen": 3,
      "image_max_bytes": 307200,
      "builder_flow_json_max_bytes": 1000000,
      "raw_flow_json_max_bytes": 10000000
    }
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

GET /whatsapp-flows/managed

List flows built in the Vitrina flow builder

messaging_account_id is required. Health — pushed by Meta’s flows webhook: health_status is blocked / throttled (Meta stopped delivering the flow — sends fail with 132068 / 132069), degraded (an endpoint alert is open: error rate, p90 latency, availability, or the Flow JSON version is about to freeze) or healthy. active_alerts holds the open alerts keyed error_rate:endpoint / error_rate:client / latency / availability / version_expiry (plus status, carrying Meta’s reason, while throttled or blocked); each alert is \{ type, source, state, value, threshold, window, meta_event, message, details, occurred_at \}. last_alert is the most recent health event of any kind, recoveries included; health_updated_at when it was applied.

ParameterInTypeRequiredConstraints
messaging_account_idqueryuuidyes
curl https://api.vitrinadev.com/api/v1/whatsapp-flows/managed \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "e3e3e3e3-0000-4000-8000-000000000001",
      "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
      "meta_flow_id": "1200300400500600",
      "name": "Agenda tu visita",
      "categories": [
        "APPOINTMENT_BOOKING"
      ],
      "status": "PUBLISHED",
      "authoring": "builder",
      "builder_state": {
        "screens": [
          {
            "title": "¿Cuándo te acomoda?",
            "footer_label": "Continuar",
            "components": [
              {
                "kind": "heading",
                "text": "Agenda tu visita"
              },
              {
                "kind": "date_picker",
                "name": "fecha",
                "label": "Fecha preferida",
                "required": true
              }
            ]
          }
        ]
      },
      "flow_json": null,
      "validation_errors": null,
      "preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
      "preview_expires_at": "2026-09-15T19:20:00.000Z",
      "key": "agenda_tu_visita",
      "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
      "ai_enabled": true,
      "message_body": "Elige el día y la hora que te acomoden para tu visita 👇",
      "message_cta": "Agendar visita",
      "message_header": null,
      "message_footer": null,
      "completion_mode": "agent",
      "field_map": {},
      "confirmation_message": null,
      "health_status": "degraded",
      "active_alerts": {
        "latency": {
          "type": "latency",
          "source": null,
          "state": "activated",
          "value": 8000,
          "threshold": 7000,
          "window": "30m",
          "meta_event": "ENDPOINT_LATENCY",
          "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
          "details": {
            "p50_latency": 500,
            "requests_count": 34
          },
          "occurred_at": "2026-09-15T18:10:00.000Z"
        }
      },
      "last_alert": {
        "type": "latency",
        "source": null,
        "state": "activated",
        "value": 8000,
        "threshold": 7000,
        "window": "30m",
        "meta_event": "ENDPOINT_LATENCY",
        "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
        "details": {
          "p50_latency": 500,
          "requests_count": 34
        },
        "occurred_at": "2026-09-15T18:10:00.000Z"
      },
      "health_updated_at": "2026-09-15T18:10:02.000Z",
      "created_at": "2026-01-10T13:00:00.000Z",
      "updated_at": "2026-09-15T18:20:00.000Z"
    }
  ],
  "meta": {
    "total": 1
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /whatsapp-flows/managed

Create a flow in the builder

builder_state is the ordered screen list; the Flow JSON is generated and uploaded to Meta as part of this call, and validation_errors reports what Meta rejected, if anything. Always created DRAFT.

Authoring — send exactly one of builder_state (the builder: ordered screens of palette components, Flow JSON generated by Vitrina; see GET /whatsapp-flows/capabilities for the palette, data sources and limits of this workspace) or flow_json (advanced: a raw Meta Flow JSON, string or object, uploaded as-is — Meta is the validator and its findings come back as validation_errors; the flow is authoring: "json" from then on). A raw Flow JSON that declares data_api_version gets the workspace data endpoint, but Vitrina's data route only serves builder flows for now — such a flow answers its customers with a clear error instead of data.

Body

FieldTypeRequiredConstraints
messaging_account_iduuidyes
namestringyesmín. 1, máx. 200
categoriesSIGN_UP \SIGN_IN \APPOINTMENT_BOOKING \
builder_stateobject—
flow_jsonWhatsappFlowJsonInput—
descriptionstring—máx. 1000
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-flows/managed \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "name": "Agenda tu visita",
    "categories": [
      "APPOINTMENT_BOOKING"
    ],
    "builder_state": {
      "screens": [
        {
          "title": "¿Cuándo te acomoda?",
          "footer_label": "Continuar",
          "components": [
            {
              "kind": "heading",
              "text": "Agenda tu visita"
            },
            {
              "kind": "date_picker",
              "name": "fecha",
              "label": "Fecha preferida",
              "required": true
            }
          ]
        }
      ]
    },
    "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora."
  }'

Example response (201)

{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "meta_flow_id": "1200300400500600",
    "name": "Agenda tu visita",
    "categories": [
      "APPOINTMENT_BOOKING"
    ],
    "status": "DRAFT",
    "authoring": "builder",
    "builder_state": {
      "screens": [
        {
          "title": "¿Cuándo te acomoda?",
          "footer_label": "Continuar",
          "components": [
            {
              "kind": "heading",
              "text": "Agenda tu visita"
            },
            {
              "kind": "date_picker",
              "name": "fecha",
              "label": "Fecha preferida",
              "required": true
            }
          ]
        }
      ]
    },
    "flow_json": null,
    "validation_errors": null,
    "preview_url": null,
    "preview_expires_at": null,
    "key": "agenda_tu_visita",
    "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
    "ai_enabled": false,
    "message_body": null,
    "message_cta": null,
    "message_header": null,
    "message_footer": null,
    "completion_mode": "agent",
    "field_map": {},
    "confirmation_message": null,
    "health_status": "degraded",
    "active_alerts": {
      "latency": {
        "type": "latency",
        "source": null,
        "state": "activated",
        "value": 8000,
        "threshold": 7000,
        "window": "30m",
        "meta_event": "ENDPOINT_LATENCY",
        "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
        "details": {
          "p50_latency": 500,
          "requests_count": 34
        },
        "occurred_at": "2026-09-15T18:10:00.000Z"
      }
    },
    "last_alert": {
      "type": "latency",
      "source": null,
      "state": "activated",
      "value": 8000,
      "threshold": 7000,
      "window": "30m",
      "meta_event": "ENDPOINT_LATENCY",
      "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
      "details": {
        "p50_latency": 500,
        "requests_count": 34
      },
      "occurred_at": "2026-09-15T18:10:00.000Z"
    },
    "health_updated_at": "2026-09-15T18:10:02.000Z",
    "created_at": "2026-01-10T13:00:00.000Z",
    "updated_at": "2026-09-15T18:20:00.000Z"
  }
}

Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429

DELETE /whatsapp-flows/managed/{id}

Delete a draft flow

Only a DRAFT flow can be deleted (409 otherwise) — deprecate a published one instead.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X DELETE https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429

GET /whatsapp-flows/managed/{id}

Fetch one managed flow

The flow row, including its builder_state (the screen/component tree) and its status on Meta. Health — pushed by Meta’s flows webhook: health_status is blocked / throttled (Meta stopped delivering the flow — sends fail with 132068 / 132069), degraded (an endpoint alert is open: error rate, p90 latency, availability, or the Flow JSON version is about to freeze) or healthy. active_alerts holds the open alerts keyed error_rate:endpoint / error_rate:client / latency / availability / version_expiry (plus status, carrying Meta’s reason, while throttled or blocked); each alert is \{ type, source, state, value, threshold, window, meta_event, message, details, occurred_at \}. last_alert is the most recent health event of any kind, recoveries included; health_updated_at when it was applied.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "meta_flow_id": "1200300400500600",
    "name": "Agenda tu visita",
    "categories": [
      "APPOINTMENT_BOOKING"
    ],
    "status": "PUBLISHED",
    "authoring": "builder",
    "builder_state": {
      "screens": [
        {
          "title": "¿Cuándo te acomoda?",
          "footer_label": "Continuar",
          "components": [
            {
              "kind": "heading",
              "text": "Agenda tu visita"
            },
            {
              "kind": "date_picker",
              "name": "fecha",
              "label": "Fecha preferida",
              "required": true
            }
          ]
        }
      ]
    },
    "flow_json": null,
    "validation_errors": null,
    "preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
    "preview_expires_at": "2026-09-15T19:20:00.000Z",
    "key": "agenda_tu_visita",
    "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
    "ai_enabled": true,
    "message_body": "Elige el día y la hora que te acomoden para tu visita 👇",
    "message_cta": "Agendar visita",
    "message_header": null,
    "message_footer": null,
    "completion_mode": "agent",
    "field_map": {},
    "confirmation_message": null,
    "health_status": "degraded",
    "active_alerts": {
      "latency": {
        "type": "latency",
        "source": null,
        "state": "activated",
        "value": 8000,
        "threshold": 7000,
        "window": "30m",
        "meta_event": "ENDPOINT_LATENCY",
        "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
        "details": {
          "p50_latency": 500,
          "requests_count": 34
        },
        "occurred_at": "2026-09-15T18:10:00.000Z"
      }
    },
    "last_alert": {
      "type": "latency",
      "source": null,
      "state": "activated",
      "value": 8000,
      "threshold": 7000,
      "window": "30m",
      "meta_event": "ENDPOINT_LATENCY",
      "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
      "details": {
        "p50_latency": 500,
        "requests_count": 34
      },
      "occurred_at": "2026-09-15T18:10:00.000Z"
    },
    "health_updated_at": "2026-09-15T18:10:02.000Z",
    "created_at": "2026-01-10T13:00:00.000Z",
    "updated_at": "2026-09-15T18:20:00.000Z"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

PATCH /whatsapp-flows/managed/{id}

Update a flow (drafts and published flows, in place)

Works on a DRAFT and on a PUBLISHED (or throttled / blocked) flow: the Flow JSON is re-uploaded to the SAME Meta flow, which Meta returns to DRAFT until POST /whatsapp-flows/managed/\{id\}/publish publishes it again — status mirrors what Meta reports after the edit. A DEPRECATED flow is final (409).

A builder flow takes builder_state; a raw-JSON flow (authoring: "json") takes flow_json and never a builder_state — convert a builder or external flow first with POST /whatsapp-flows/managed/\{id\}/convert-to-json.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
namestring—mín. 1, máx. 200
categoriesSIGN_UP \SIGN_IN \APPOINTMENT_BOOKING \
builder_stateobject—
flow_jsonWhatsappFlowJsonInput—
curl -X PATCH https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Agenda tu visita al showroom"
  }'

Example response (200)

{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "meta_flow_id": "1200300400500600",
    "name": "Agenda tu visita al showroom",
    "categories": [
      "APPOINTMENT_BOOKING"
    ],
    "status": "PUBLISHED",
    "authoring": "builder",
    "builder_state": {
      "screens": [
        {
          "title": "¿Cuándo te acomoda?",
          "footer_label": "Continuar",
          "components": [
            {
              "kind": "heading",
              "text": "Agenda tu visita"
            },
            {
              "kind": "date_picker",
              "name": "fecha",
              "label": "Fecha preferida",
              "required": true
            }
          ]
        }
      ]
    },
    "flow_json": null,
    "validation_errors": null,
    "preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
    "preview_expires_at": "2026-09-15T19:20:00.000Z",
    "key": "agenda_tu_visita",
    "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
    "ai_enabled": true,
    "message_body": "Elige el día y la hora que te acomoden para tu visita 👇",
    "message_cta": "Agendar visita",
    "message_header": null,
    "message_footer": null,
    "completion_mode": "agent",
    "field_map": {},
    "confirmation_message": null,
    "health_status": "degraded",
    "active_alerts": {
      "latency": {
        "type": "latency",
        "source": null,
        "state": "activated",
        "value": 8000,
        "threshold": 7000,
        "window": "30m",
        "meta_event": "ENDPOINT_LATENCY",
        "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
        "details": {
          "p50_latency": 500,
          "requests_count": 34
        },
        "occurred_at": "2026-09-15T18:10:00.000Z"
      }
    },
    "last_alert": {
      "type": "latency",
      "source": null,
      "state": "activated",
      "value": 8000,
      "threshold": 7000,
      "window": "30m",
      "meta_event": "ENDPOINT_LATENCY",
      "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
      "details": {
        "p50_latency": 500,
        "requests_count": 34
      },
      "occurred_at": "2026-09-15T18:10:00.000Z"
    },
    "health_updated_at": "2026-09-15T18:10:02.000Z",
    "created_at": "2026-01-10T13:00:00.000Z",
    "updated_at": "2026-09-15T18:20:00.000Z"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /whatsapp-flows/managed/{id}/convert-to-json

Convert a flow to raw Flow JSON authoring

One-way. The flow becomes authoring: "json": a builder flow keeps the Flow JSON the generator produced (its builder_state is dropped — there is no reverse parser), and a flow created outside Vitrina takes its current flow.json downloaded from Meta, which is what makes it editable here (PATCH with flow_json). Nothing is uploaded and the status is untouched; settings, completion and templates keep working. Already json → returned unchanged.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id>/convert-to-json \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "meta_flow_id": "1200300400500600",
    "name": "Agenda tu visita",
    "categories": [
      "APPOINTMENT_BOOKING"
    ],
    "status": "PUBLISHED",
    "authoring": "json",
    "builder_state": null,
    "flow_json": "{\n  \"version\": \"7.3\",\n  \"screens\": [\n    {\n      \"id\": \"AGENDA\",\n      \"title\": \"Agenda tu visita\",\n      \"terminal\": true,\n      \"layout\": {\n        \"type\": \"SingleColumnLayout\",\n        \"children\": [\n          {\n            \"type\": \"Form\",\n            \"name\": \"form\",\n            \"children\": [\n              {\n                \"type\": \"DatePicker\",\n                \"name\": \"fecha\",\n                \"label\": \"Fecha preferida\",\n                \"required\": true\n              },\n              {\n                \"type\": \"Footer\",\n                \"label\": \"Enviar\",\n                \"on-click-action\": {\n                  \"name\": \"complete\",\n                  \"payload\": {\n                    \"fecha\": \"${form.fecha}\"\n                  }\n                }\n              }\n            ]\n          }\n        ]\n      }\n    }\n  ]\n}",
    "validation_errors": null,
    "preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
    "preview_expires_at": "2026-09-15T19:20:00.000Z",
    "key": "agenda_tu_visita",
    "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
    "ai_enabled": true,
    "message_body": "Elige el día y la hora que te acomoden para tu visita 👇",
    "message_cta": "Agendar visita",
    "message_header": null,
    "message_footer": null,
    "completion_mode": "agent",
    "field_map": {},
    "confirmation_message": null,
    "health_status": "degraded",
    "active_alerts": {
      "latency": {
        "type": "latency",
        "source": null,
        "state": "activated",
        "value": 8000,
        "threshold": 7000,
        "window": "30m",
        "meta_event": "ENDPOINT_LATENCY",
        "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
        "details": {
          "p50_latency": 500,
          "requests_count": 34
        },
        "occurred_at": "2026-09-15T18:10:00.000Z"
      }
    },
    "last_alert": {
      "type": "latency",
      "source": null,
      "state": "activated",
      "value": 8000,
      "threshold": 7000,
      "window": "30m",
      "meta_event": "ENDPOINT_LATENCY",
      "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
      "details": {
        "p50_latency": 500,
        "requests_count": 34
      },
      "occurred_at": "2026-09-15T18:10:00.000Z"
    },
    "health_updated_at": "2026-09-15T18:10:02.000Z",
    "created_at": "2026-01-10T13:00:00.000Z",
    "updated_at": "2026-09-15T18:20:00.000Z"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429 · 502

POST /whatsapp-flows/managed/{id}/deprecate

Deprecate a published flow

Stops it being offered as new; conversations already inside it are unaffected.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id>/deprecate \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "meta_flow_id": "1200300400500600",
    "name": "Agenda tu visita",
    "categories": [
      "APPOINTMENT_BOOKING"
    ],
    "status": "DEPRECATED",
    "authoring": "builder",
    "builder_state": {
      "screens": [
        {
          "title": "¿Cuándo te acomoda?",
          "footer_label": "Continuar",
          "components": [
            {
              "kind": "heading",
              "text": "Agenda tu visita"
            },
            {
              "kind": "date_picker",
              "name": "fecha",
              "label": "Fecha preferida",
              "required": true
            }
          ]
        }
      ]
    },
    "flow_json": null,
    "validation_errors": null,
    "preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
    "preview_expires_at": "2026-09-15T19:20:00.000Z",
    "key": "agenda_tu_visita",
    "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
    "ai_enabled": true,
    "message_body": "Elige el día y la hora que te acomoden para tu visita 👇",
    "message_cta": "Agendar visita",
    "message_header": null,
    "message_footer": null,
    "completion_mode": "agent",
    "field_map": {},
    "confirmation_message": null,
    "health_status": "degraded",
    "active_alerts": {
      "latency": {
        "type": "latency",
        "source": null,
        "state": "activated",
        "value": 8000,
        "threshold": 7000,
        "window": "30m",
        "meta_event": "ENDPOINT_LATENCY",
        "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
        "details": {
          "p50_latency": 500,
          "requests_count": 34
        },
        "occurred_at": "2026-09-15T18:10:00.000Z"
      }
    },
    "last_alert": {
      "type": "latency",
      "source": null,
      "state": "activated",
      "value": 8000,
      "threshold": 7000,
      "window": "30m",
      "meta_event": "ENDPOINT_LATENCY",
      "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
      "details": {
        "p50_latency": 500,
        "requests_count": 34
      },
      "occurred_at": "2026-09-15T18:10:00.000Z"
    },
    "health_updated_at": "2026-09-15T18:10:02.000Z",
    "created_at": "2026-01-10T13:00:00.000Z",
    "updated_at": "2026-09-15T18:20:00.000Z"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /whatsapp-flows/managed/{id}/duplicate

Duplicate a flow as a new draft

Copies the flow's builder_state (or a raw-JSON flow’s flow_json) and its agent and completion settings (except the AI switch) into a brand-new DRAFT flow (a new meta_flow_id), leaving the original untouched. name defaults to the original’s name with a suffix.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
namestring—mín. 1, máx. 200
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id>/duplicate \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Agenda tu visita (copia)"
  }'

Example response (201)

{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "meta_flow_id": "1200300400500600",
    "name": "Agenda tu visita (copia)",
    "categories": [
      "APPOINTMENT_BOOKING"
    ],
    "status": "DRAFT",
    "authoring": "builder",
    "builder_state": {
      "screens": [
        {
          "title": "¿Cuándo te acomoda?",
          "footer_label": "Continuar",
          "components": [
            {
              "kind": "heading",
              "text": "Agenda tu visita"
            },
            {
              "kind": "date_picker",
              "name": "fecha",
              "label": "Fecha preferida",
              "required": true
            }
          ]
        }
      ]
    },
    "flow_json": null,
    "validation_errors": null,
    "preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
    "preview_expires_at": "2026-09-15T19:20:00.000Z",
    "key": "agenda_tu_visita_copia",
    "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
    "ai_enabled": false,
    "message_body": "Elige el día y la hora que te acomoden para tu visita 👇",
    "message_cta": "Agendar visita",
    "message_header": null,
    "message_footer": null,
    "completion_mode": "agent",
    "field_map": {},
    "confirmation_message": null,
    "health_status": "degraded",
    "active_alerts": {
      "latency": {
        "type": "latency",
        "source": null,
        "state": "activated",
        "value": 8000,
        "threshold": 7000,
        "window": "30m",
        "meta_event": "ENDPOINT_LATENCY",
        "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
        "details": {
          "p50_latency": 500,
          "requests_count": 34
        },
        "occurred_at": "2026-09-15T18:10:00.000Z"
      }
    },
    "last_alert": {
      "type": "latency",
      "source": null,
      "state": "activated",
      "value": 8000,
      "threshold": 7000,
      "window": "30m",
      "meta_event": "ENDPOINT_LATENCY",
      "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
      "details": {
        "p50_latency": 500,
        "requests_count": 34
      },
      "occurred_at": "2026-09-15T18:10:00.000Z"
    },
    "health_updated_at": "2026-09-15T18:10:02.000Z",
    "created_at": "2026-01-10T13:00:00.000Z",
    "updated_at": "2026-09-15T18:20:00.000Z"
  }
}

Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429

GET /whatsapp-flows/managed/{id}/flow-json

Get a flow's current Flow JSON

Whatever its authoring: a raw-JSON flow answers its stored flow_json (source: "stored"), a builder flow the generator output its last save uploaded ("generated"), and a flow created outside Vitrina the flow.json asset downloaded from Meta ("meta", through the bridge). Read-only.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id>/flow-json \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "authoring": "json",
    "source": "stored",
    "flow_json": "{\n  \"version\": \"7.3\",\n  \"screens\": [\n    {\n      \"id\": \"AGENDA\",\n      \"title\": \"Agenda tu visita\",\n      \"terminal\": true,\n      \"layout\": {\n        \"type\": \"SingleColumnLayout\",\n        \"children\": [\n          {\n            \"type\": \"Form\",\n            \"name\": \"form\",\n            \"children\": [\n              {\n                \"type\": \"DatePicker\",\n                \"name\": \"fecha\",\n                \"label\": \"Fecha preferida\",\n                \"required\": true\n              },\n              {\n                \"type\": \"Footer\",\n                \"label\": \"Enviar\",\n                \"on-click-action\": {\n                  \"name\": \"complete\",\n                  \"payload\": {\n                    \"fecha\": \"${form.fecha}\"\n                  }\n                }\n              }\n            ]\n          }\n        ]\n      }\n    }\n  ]\n}"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429 · 502

GET /whatsapp-flows/managed/{id}/preview

Get (or refresh) a flow preview link

Read-only and side-effect free by default — answers the cached preview URL. ?refresh=true forces a fresh one through the bridge when the cached one has expired.

ParameterInTypeRequiredConstraints
idpathuuidyes
refreshqueryboolean | nullno
curl https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id>/preview \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
    "preview_expires_at": "2026-09-15T19:20:00.000Z",
    "status": "PUBLISHED",
    "validation_errors": null
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /whatsapp-flows/managed/{id}/publish

Publish a flow

Refused while validation_errors is non-empty. A published flow can still be edited in place (PATCH) and published again.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id>/publish \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "meta_flow_id": "1200300400500600",
    "name": "Agenda tu visita",
    "categories": [
      "APPOINTMENT_BOOKING"
    ],
    "status": "PUBLISHED",
    "authoring": "builder",
    "builder_state": {
      "screens": [
        {
          "title": "¿Cuándo te acomoda?",
          "footer_label": "Continuar",
          "components": [
            {
              "kind": "heading",
              "text": "Agenda tu visita"
            },
            {
              "kind": "date_picker",
              "name": "fecha",
              "label": "Fecha preferida",
              "required": true
            }
          ]
        }
      ]
    },
    "flow_json": null,
    "validation_errors": null,
    "preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
    "preview_expires_at": "2026-09-15T19:20:00.000Z",
    "key": "agenda_tu_visita",
    "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
    "ai_enabled": true,
    "message_body": "Elige el día y la hora que te acomoden para tu visita 👇",
    "message_cta": "Agendar visita",
    "message_header": null,
    "message_footer": null,
    "completion_mode": "agent",
    "field_map": {},
    "confirmation_message": null,
    "health_status": "degraded",
    "active_alerts": {
      "latency": {
        "type": "latency",
        "source": null,
        "state": "activated",
        "value": 8000,
        "threshold": 7000,
        "window": "30m",
        "meta_event": "ENDPOINT_LATENCY",
        "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
        "details": {
          "p50_latency": 500,
          "requests_count": 34
        },
        "occurred_at": "2026-09-15T18:10:00.000Z"
      }
    },
    "last_alert": {
      "type": "latency",
      "source": null,
      "state": "activated",
      "value": 8000,
      "threshold": 7000,
      "window": "30m",
      "meta_event": "ENDPOINT_LATENCY",
      "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
      "details": {
        "p50_latency": 500,
        "requests_count": 34
      },
      "occurred_at": "2026-09-15T18:10:00.000Z"
    },
    "health_updated_at": "2026-09-15T18:10:02.000Z",
    "created_at": "2026-01-10T13:00:00.000Z",
    "updated_at": "2026-09-15T18:20:00.000Z"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

PATCH /whatsapp-flows/managed/{id}/settings

Update a flow's agent settings

The Vitrina-only settings: key (lowercase slug, unique per WhatsApp number — skills and knowledge documents name the flow by it, in backticks), description (when the AI agent should send it), ai_enabled (the agent may send it while it is PUBLISHED) and the default interactive message (message_body, message_cta, optional message_header / message_footer). Every field is optional; null or a blank string clears one.

None of it is sent to Meta, so this works on a flow in any status — a published flow, and one created outside Vitrina, included.

Completion (ADR 0120): completion_mode — agent (the answers become a customer message and the AI agent answers; the default), book_then_agent (a form with an available-hours field books the chosen hour at submit, then the agent confirms), book_only (books at submit and sends confirmation_message; no AI turn unless the booking failed) or automation_only (no AI turn; only events, webhooks and automations). field_map maps form fields to name, email, rut, phone or attr:&lt;contact attribute key&gt; (replaces the whole map). confirmation_message takes \{\{field\}\} placeholders by field name and \{\{cita\}\} for the booked hour; blank = the default text.

409 flow_key_taken — another flow on the same number uses the key. 422 flow_ai_incomplete — ai_enabled without a description, message body and button label (checked after merging with what the flow already has); flow_completion_mode_unsupported — a booking mode on a form without an available-hours field; flow_field_map_invalid — the map names a field the form lacks.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
keystring—patrón ^[a-z0-9][a-z0-9_]{0,63}$
descriptionstring | null—máx. 1000
ai_enabledboolean—
message_bodystring | null—máx. 1024
message_ctastring | null—máx. 30
message_headerstring | null—máx. 60
message_footerstring | null—máx. 60
completion_modeagent \book_then_agent \book_only \
field_mapobject—
confirmation_messagestring | null—máx. 1024
curl -X PATCH https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id>/settings \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
    "ai_enabled": true,
    "message_body": "Elige el día y la hora que te acomoden para tu visita 👇",
    "message_cta": "Agendar visita"
  }'

Example response (200)

{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "meta_flow_id": "1200300400500600",
    "name": "Agenda tu visita",
    "categories": [
      "APPOINTMENT_BOOKING"
    ],
    "status": "PUBLISHED",
    "authoring": "builder",
    "builder_state": {
      "screens": [
        {
          "title": "¿Cuándo te acomoda?",
          "footer_label": "Continuar",
          "components": [
            {
              "kind": "heading",
              "text": "Agenda tu visita"
            },
            {
              "kind": "date_picker",
              "name": "fecha",
              "label": "Fecha preferida",
              "required": true
            }
          ]
        }
      ]
    },
    "flow_json": null,
    "validation_errors": null,
    "preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
    "preview_expires_at": "2026-09-15T19:20:00.000Z",
    "key": "agenda_tu_visita",
    "description": "Cuando el cliente quiere agendar una visita al showroom y todavía no ha dado fecha ni hora.",
    "ai_enabled": true,
    "message_body": "Elige el día y la hora que te acomoden para tu visita 👇",
    "message_cta": "Agendar visita",
    "message_header": null,
    "message_footer": null,
    "completion_mode": "agent",
    "field_map": {},
    "confirmation_message": null,
    "health_status": "degraded",
    "active_alerts": {
      "latency": {
        "type": "latency",
        "source": null,
        "state": "activated",
        "value": 8000,
        "threshold": 7000,
        "window": "30m",
        "meta_event": "ENDPOINT_LATENCY",
        "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
        "details": {
          "p50_latency": 500,
          "requests_count": 34
        },
        "occurred_at": "2026-09-15T18:10:00.000Z"
      }
    },
    "last_alert": {
      "type": "latency",
      "source": null,
      "state": "activated",
      "value": 8000,
      "threshold": 7000,
      "window": "30m",
      "meta_event": "ENDPOINT_LATENCY",
      "message": "Flow endpoint latency has reached the p90 threshold in the last 30 minutes.",
      "details": {
        "p50_latency": 500,
        "requests_count": 34
      },
      "occurred_at": "2026-09-15T18:10:00.000Z"
    },
    "health_updated_at": "2026-09-15T18:10:02.000Z",
    "created_at": "2026-01-10T13:00:00.000Z",
    "updated_at": "2026-09-15T18:20:00.000Z"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 422 · 429

POST /whatsapp-flows/managed/{id}/test-send

Send a flow to a phone for testing

Sends the flow — a DRAFT included (mode: draft, its latest draft) — to phone, with a real session behind it: submitting it runs the flow's completion (booking, events, confirmation) like a customer's. When the phone has a live conversation on the flow's WhatsApp number the session is tied to it, so the reply lands there. WhatsApp only delivers inside the phone's open 24-hour window with the number — write to it first; Meta's refusal comes back as a 502 with a Spanish message saying what to do.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
phonestringyesmín. 8, máx. 24, patrón ^\+?[0-9 ()-]{8,24}$
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/<id>/test-send \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+56912345678"
  }'

Example response (200)

{
  "data": {
    "external_id": "wamid.HBgLNTY5MTIzNDU2NzgVAgARGBI",
    "conversation_id": "0190a3b2-5c1d-7e4f-8a9b-0c1d2e3f4a5c",
    "mode": "draft",
    "session": true
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 422 · 429

POST /whatsapp-flows/managed/data-preview

Preview the live data of one flow screen

For a flow with live data (a Dropdown / Radio / Checkbox with a source, or a DatePicker with available_dates): the screen data the data endpoint would answer for screen_index given answers so far — &lt;field&gt;__options lists, &lt;field&gt;__min / __max / __unavailable dates, carried answers and &lt;field&gt;__value (the selection a refresh keeps). Works on an unsaved builder_state and reads real availability, but never books or writes anything. error_message is the short customer-facing text WhatsApp would show (e.g. no hours left that day).

Body

FieldTypeRequiredConstraints
messaging_account_iduuidyes
builder_stateobjectyes
screen_indexintegeryes≥ 0, ≤ 10
answersobject—por defecto {}
contextobject—
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-flows/managed/data-preview \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "screen_index": 1,
    "answers": {
      "sucursal": "0190a3b2-5c1d-7e4f-8a9b-0c1d2e3f4a5b",
      "fecha": "2026-10-06"
    },
    "builder_state": {
      "screens": [
        {
          "title": "Tu visita",
          "components": [
            {
              "kind": "dropdown",
              "name": "sucursal",
              "label": "Sucursal",
              "required": true,
              "options": [],
              "source": {
                "id": "branches"
              }
            }
          ]
        },
        {
          "title": "Fecha y hora",
          "components": [
            {
              "kind": "date_picker",
              "name": "fecha",
              "label": "Fecha",
              "required": true,
              "source": {
                "id": "available_dates",
                "params": {
                  "branch": {
                    "field": "sucursal"
                  }
                }
              }
            },
            {
              "kind": "dropdown",
              "name": "hora",
              "label": "Hora",
              "required": true,
              "options": [],
              "source": {
                "id": "available_slots",
                "params": {
                  "date": {
                    "field": "fecha"
                  },
                  "branch": {
                    "field": "sucursal"
                  }
                }
              }
            }
          ]
        }
      ]
    }
  }'

Example response (200)

{
  "data": {
    "data": {
      "sucursal": "0190a3b2-5c1d-7e4f-8a9b-0c1d2e3f4a5b",
      "fecha__min": "2026-10-05",
      "fecha__max": "2026-10-19",
      "fecha__unavailable": [
        "2026-10-11",
        "2026-10-18"
      ],
      "fecha__value": "2026-10-06",
      "hora__options": [
        {
          "id": "2026-10-06T13:30:00.000Z",
          "title": "mar 6 oct 10:30"
        },
        {
          "id": "2026-10-06T14:30:00.000Z",
          "title": "mar 6 oct 11:30"
        }
      ],
      "hora__value": ""
    }
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /whatsapp-flows/sync

Reconcile a channel's managed flows against Meta

The most destructive call on this router: local rows Meta no longer reports are deleted. Reports \{ checked, updated, removed \}.

Body

FieldTypeRequiredConstraints
messaging_account_iduuidyes
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-flows/sync \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001"
  }'

Example response (200)

{
  "data": {
    "checked": 3,
    "updated": 1,
    "removed": 0
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

On this page