WhatsApp Flows
Structured, multi-screen forms a WhatsApp conversation can open, built and published from Vitrina.
Download the full API pública projection: openapi.json.
Use WhatsApp templates and Flows explains this resource in prose, with runnable examples.
| Method | Path | What it does |
|---|---|---|
GET | /whatsapp-flows | Live list of a channel's WhatsApp Flows (composer picker) |
GET | /whatsapp-flows/managed | List flows built in the Vitrina flow builder |
POST | /whatsapp-flows/managed | Create 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 draft flow |
POST | /whatsapp-flows/managed/{id}/deprecate | Deprecate a published flow |
POST | /whatsapp-flows/managed/{id}/duplicate | Duplicate a flow as a new draft |
GET | /whatsapp-flows/managed/{id}/preview | Get (or refresh) a flow preview link |
POST | /whatsapp-flows/managed/{id}/publish | Publish a flow |
POST | /whatsapp-flows/sync | Reconcile 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, which the /managed/* builder never sees. messaging_account_id is required; there is no tenant-wide list.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
messaging_account_id | query | uuid | yes |
curl https://api.vitrinadev.com/api/v1/whatsapp-flows \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": [
{
"id": "e3e3e3e3-0000-4000-8000-000000000001",
"name": "Agenda tu visita",
"status": "PUBLISHED",
"categories": [
"APPOINTMENT_BOOKING"
]
}
],
"meta": {
"total": 1
}
}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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
messaging_account_id | query | uuid | yes |
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",
"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
}
]
}
]
},
"validation_errors": null,
"preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
"preview_expires_at": "2026-09-15T19:20:00.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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
messaging_account_id | uuid | yes | |
name | string | yes | mín. 1, máx. 200 |
categories | SIGN_UP \ | SIGN_IN \ | APPOINTMENT_BOOKING \ |
builder_state | object | yes |
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
}
]
}
]
}
}'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",
"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
}
]
}
]
},
"validation_errors": null,
"preview_url": null,
"preview_expires_at": null,
"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
A published flow is immutable on Meta’s side — this is a 409 once status is PUBLISHED. POST /whatsapp-flows/managed/\{id\}/duplicate is the way to keep iterating: it always creates a fresh DRAFT copy.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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",
"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
}
]
}
]
},
"validation_errors": null,
"preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
"preview_expires_at": "2026-09-15T19:20:00.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 draft flow
A published flow is immutable on Meta’s side — this is a 409 once status is PUBLISHED. POST /whatsapp-flows/managed/\{id\}/duplicate is the way to keep iterating: it always creates a fresh DRAFT copy.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | — | mín. 1, máx. 200 |
categories | SIGN_UP \ | SIGN_IN \ | APPOINTMENT_BOOKING \ |
builder_state | object | — |
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",
"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
}
]
}
]
},
"validation_errors": null,
"preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
"preview_expires_at": "2026-09-15T19:20:00.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}/deprecate
Deprecate a published flow
Stops it being offered as new; conversations already inside it are unaffected.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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",
"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
}
]
}
]
},
"validation_errors": null,
"preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
"preview_expires_at": "2026-09-15T19:20:00.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
The escape hatch for an immutable PUBLISHED flow: copies its builder_state 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | — | 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",
"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
}
]
}
]
},
"validation_errors": null,
"preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
"preview_expires_at": "2026-09-15T19:20:00.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}/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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
refresh | query | boolean | null | no |
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. Publishing is one-way — see duplicate for the way to keep editing afterwards.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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",
"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
}
]
}
]
},
"validation_errors": null,
"preview_url": "https://business.facebook.com/wa/manage/flows/1200300400500600/preview/",
"preview_expires_at": "2026-09-15T19:20:00.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/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
| Field | Type | Required | Constraints |
|---|---|---|---|
messaging_account_id | uuid | yes |
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