VitrinaAPI

Use WhatsApp templates and Flows

Meta-approved message shapes, and the structured forms a WhatsApp conversation can open.

Writing to a contact outside the 24-hour window needs a Meta-approved template. Opening a form inside the conversation needs a Flow. Both are configuration catalogs for the WhatsApp channel. Neither sends anything on its own: both are consumed from a conversation's message-send surface.

Templates

A template (whatsapp_template) is the Meta-approved shape for writing to a contact outside the 24-hour window. You create it here and it syncs against Meta. There's no approval webhook, so status (PENDING → APPROVED | REJECTED | …) is reconciled by a periodic poll. POST /whatsapp-templates/sync forces an on-demand reconcile.

Reading and writing need campaigns:read / campaigns:write.

curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-templates \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
    "name": "seguimiento_consulta",
    "language": "es_CL",
    "category": "MARKETING",
    "body_text": "Hola {{1}}, {{2}} sigue disponible. ¿Seguimos adelante?",
    "footer_text": "Equipo de atención"
  }'

name is lowercase letters, digits and underscores only, which is Meta's constraint. language looks like es, es_CL or en_US. The response is 201 with status: "PENDING", because approval is asynchronous.

param_meta holds the label and example for each {{variable}}, for whoever is composing. param_binding says what fills each variable: a contact field, a custom attribute, the lead's interest, a literal, or "the person types it". Both are edited separately, with PATCH /whatsapp-templates/{id}/param-meta and PATCH /whatsapp-templates/{id}/param-binding. Neither touches Meta or re-triggers approval: they describe how Vitrina fills an already-approved body, not the body itself.

{
  "data": {
    "id": "e2e2e2e2-0000-4000-8000-000000000001",
    "name": "seguimiento_consulta",
    "language": "es_CL",
    "status": "APPROVED",
    "param_binding": {
      "1": { "source": "contact_field", "field": "name" },
      "2": { "source": "lead_interest" }
    }
  }
}

POST /whatsapp-templates/{id}/infer-bindings re-suggests a binding for every {{variable}}. The merge rule never overwrites a slot a person bound by hand (origin: "manual"), so re-running it can't lose a workspace's own choice.

Flows

A Flow (whatsapp_flow) is a structured, multi-screen form that opens inside the conversation: booking, sign-up, a short survey.

There are two surfaces. GET /whatsapp-flows is a live mirror of what Meta reports for a channel. It's what the composer's picker shows, and it includes a Flow authored outside Vitrina. /whatsapp-flows/managed/* is the builder: local rows with builder_state as the source of truth, meaning ordered screens from a component palette. Saving regenerates the Flow JSON and uploads it to Meta.

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 }
        ]
      }]
    }
  }'
{
  "data": {
    "id": "e3e3e3e3-0000-4000-8000-000000000001",
    "meta_flow_id": "1200300400500600",
    "name": "Agenda tu visita",
    "status": "DRAFT",
    "validation_errors": null
  }
}

meta_flow_id is the id Meta assigns on upload. We don't generate it, so it isn't a uuid. validation_errors is Meta's verdict on the last upload, and it blocks publish while non-empty.

Trap

PATCH and DELETE on a published Flow answer 409

Meta makes it immutable on publish. POST /whatsapp-flows/managed/{id}/duplicate is the way out: it always creates a fresh DRAFT copy, ready to keep editing. A row with builder_state: null was created outside Vitrina and is read-only here.

On this page