Plantillas de WhatsApp
El catálogo de mensajes pre-aprobados por Meta para escribir fuera de la ventana de 24 horas, y cómo Vitrina llena cada variable.
Descarga la proyección completa de la API pública: openapi.json.
Usar plantillas y Flows de WhatsApp explica este recurso en prosa, con ejemplos ejecutables.
| Método | Ruta | Qué hace |
|---|---|---|
GET | /whatsapp-templates | List WhatsApp templates |
POST | /whatsapp-templates | Submit a WhatsApp template to Meta |
DELETE | /whatsapp-templates/{id} | Delete a WhatsApp template |
GET | /whatsapp-templates/{id} | Fetch one WhatsApp template |
POST | /whatsapp-templates/{id}/infer-bindings | Re-run the binder over every slot ("volver a sugerir") |
PATCH | /whatsapp-templates/{id}/param-binding | Set what fills each {{variable}} when Vitrina sends this template |
PATCH | /whatsapp-templates/{id}/param-meta | Set the agent-facing label/example for each {{variable}} |
PATCH | /whatsapp-templates/{id}/usage | Re-file the template under another category |
GET | /whatsapp-templates/categories | List the workspace’s template categories |
POST | /whatsapp-templates/categories | Add a template category |
DELETE | /whatsapp-templates/categories/{id} | Delete a template category |
PATCH | /whatsapp-templates/categories/{id} | Rename a template category or change its visibility |
PUT | /whatsapp-templates/categories/order | Reorder the template categories |
POST | /whatsapp-templates/sync | Force a reconcile of a channel's templates against Meta |
PATCH | /whatsapp-templates/usage | Re-file several templates under one category |
GET /whatsapp-templates
List WhatsApp templates
Optionally narrowed to one messaging_account_id and/or status. Every template the workspace has created, mirrored from Meta.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
messaging_account_id | query | uuid | no | |
status | query | PENDING \ | APPROVED \ | REJECTED \ |
curl https://api.vitrinadev.com/api/v1/whatsapp-templates \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"id": "e2e2e2e2-0000-4000-8000-000000000001",
"messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
"name": "seguimiento_stock",
"language": "es_CL",
"category": "MARKETING",
"body_text": "Hola {{1}}, el {{2}} que consultaste sigue disponible. ¿Seguimos viéndolo?",
"header_text": null,
"footer_text": "Autos del Valle",
"params": [
"1",
"2"
],
"param_meta": {
"1": {
"label": "Nombre del contacto",
"example": "María"
},
"2": {
"label": "Modelo consultado",
"example": "Chevrolet Onix 2023"
}
},
"param_binding": {
"1": {
"source": "contact_field",
"field": "name"
},
"2": {
"source": "lead_interest"
}
},
"components": null,
"status": "APPROVED",
"rejection_reason": null,
"managed_for": null,
"managed_key": null,
"usage": "follow_up",
"synced_at": "2026-09-15T18:20:00.000Z",
"created_at": "2026-01-10T13:00:00.000Z",
"updated_at": "2026-09-15T18:20:00.000Z"
}
],
"meta": {
"total": 1
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /whatsapp-templates
Submit a WhatsApp template to Meta
name is lowercase letters, digits and underscores only (Meta’s constraint); language looks like es, es_CL or en_US. Answers 201 with status: "PENDING" — approval is asynchronous, reconciled by the worker poll or by POST /whatsapp-templates/sync.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
messaging_account_id | uuid | sí | |
name | string | sí | mín. 1, máx. 512, patrón ^[a-z0-9_]+$ |
language | string | sí | patrón ^[a-z]{2}(_[A-Z]{2})?$ |
category | MARKETING \ | UTILITY \ | AUTHENTICATION |
body_text | string | sí | mín. 1, máx. 1024 |
header_text | string | null | — | mín. 1, máx. 60 |
footer_text | string | null | — | mín. 1, máx. 60 |
buttons | any[] | — | |
param_meta | object | — | |
usage | string | — | mín. 1, máx. 40, patrón ^[a-z0-9_]+$ |
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_stock",
"language": "es_CL",
"category": "MARKETING",
"body_text": "Hola {{1}}, el {{2}} que consultaste sigue disponible. ¿Seguimos viéndolo?",
"footer_text": "Autos del Valle"
}'Ejemplo de respuesta (201)
{
"data": {
"id": "e2e2e2e2-0000-4000-8000-000000000001",
"messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
"name": "seguimiento_stock",
"language": "es_CL",
"category": "MARKETING",
"body_text": "Hola {{1}}, el {{2}} que consultaste sigue disponible. ¿Seguimos viéndolo?",
"header_text": null,
"footer_text": "Autos del Valle",
"params": [
"1",
"2"
],
"param_meta": {
"1": {
"label": "Nombre del contacto",
"example": "María"
},
"2": {
"label": "Modelo consultado",
"example": "Chevrolet Onix 2023"
}
},
"param_binding": {
"1": {
"source": "contact_field",
"field": "name"
},
"2": {
"source": "lead_interest"
}
},
"components": null,
"status": "PENDING",
"rejection_reason": null,
"managed_for": null,
"managed_key": null,
"usage": "follow_up",
"synced_at": null,
"created_at": "2026-01-10T13:00:00.000Z",
"updated_at": "2026-09-15T18:20:00.000Z"
}
}Responde: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /whatsapp-templates/{id}
Delete a WhatsApp template
Removes the local mirror; does not withdraw it from Meta.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
curl -X DELETE https://api.vitrinadev.com/api/v1/whatsapp-templates/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Responde: 204 · 400 · 401 · 403 · 404 · 409 · 429
GET /whatsapp-templates/{id}
Fetch one WhatsApp template
The local mirror row, including Meta’s status, the param labels/examples set on it, and its bindings.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
curl https://api.vitrinadev.com/api/v1/whatsapp-templates/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"id": "e2e2e2e2-0000-4000-8000-000000000001",
"messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
"name": "seguimiento_stock",
"language": "es_CL",
"category": "MARKETING",
"body_text": "Hola {{1}}, el {{2}} que consultaste sigue disponible. ¿Seguimos viéndolo?",
"header_text": null,
"footer_text": "Autos del Valle",
"params": [
"1",
"2"
],
"param_meta": {
"1": {
"label": "Nombre del contacto",
"example": "María"
},
"2": {
"label": "Modelo consultado",
"example": "Chevrolet Onix 2023"
}
},
"param_binding": {
"1": {
"source": "contact_field",
"field": "name"
},
"2": {
"source": "lead_interest"
}
},
"components": null,
"status": "APPROVED",
"rejection_reason": null,
"managed_for": null,
"managed_key": null,
"usage": "follow_up",
"synced_at": "2026-09-15T18:20:00.000Z",
"created_at": "2026-01-10T13:00:00.000Z",
"updated_at": "2026-09-15T18:20:00.000Z"
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /whatsapp-templates/{id}/infer-bindings
Re-run the binder over every slot ("volver a sugerir")
Suggests a binding for every \{\{variable\}\}, not just the unbound ones. The merge rule never overwrites a slot a person already bound by hand (origin: "manual"), so re-running this can never lose the workspace’s own choices.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-templates/<id>/infer-bindings \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"id": "e2e2e2e2-0000-4000-8000-000000000001",
"messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
"name": "seguimiento_stock",
"language": "es_CL",
"category": "MARKETING",
"body_text": "Hola {{1}}, el {{2}} que consultaste sigue disponible. ¿Seguimos viéndolo?",
"header_text": null,
"footer_text": "Autos del Valle",
"params": [
"1",
"2"
],
"param_meta": {
"1": {
"label": "Nombre del contacto",
"example": "María"
},
"2": {
"label": "Modelo consultado",
"example": "Chevrolet Onix 2023"
}
},
"param_binding": {
"1": {
"source": "contact_field",
"field": "name"
},
"2": {
"source": "lead_interest"
}
},
"components": null,
"status": "APPROVED",
"rejection_reason": null,
"managed_for": null,
"managed_key": null,
"usage": "follow_up",
"synced_at": "2026-09-15T18:20:00.000Z",
"created_at": "2026-01-10T13:00:00.000Z",
"updated_at": "2026-09-15T18:20:00.000Z"
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /whatsapp-templates/{id}/param-binding
Set what fills each {{variable}} when Vitrina sends this template
Local-only, like param-meta: describes how Vitrina fills an ALREADY-APPROVED body, so there is no Meta round-trip and no re-approval. Unknown slot keys and malformed entries are dropped rather than failing the whole request.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
param_binding | object | sí |
curl -X PATCH https://api.vitrinadev.com/api/v1/whatsapp-templates/<id>/param-binding \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"param_binding": {
"1": {
"source": "contact_field",
"field": "name"
}
}
}'Ejemplo de respuesta (200)
{
"data": {
"id": "e2e2e2e2-0000-4000-8000-000000000001",
"messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
"name": "seguimiento_stock",
"language": "es_CL",
"category": "MARKETING",
"body_text": "Hola {{1}}, el {{2}} que consultaste sigue disponible. ¿Seguimos viéndolo?",
"header_text": null,
"footer_text": "Autos del Valle",
"params": [
"1",
"2"
],
"param_meta": {
"1": {
"label": "Nombre del contacto",
"example": "María"
},
"2": {
"label": "Modelo consultado",
"example": "Chevrolet Onix 2023"
}
},
"param_binding": {
"1": {
"source": "contact_field",
"field": "name"
},
"2": {
"source": "lead_interest"
}
},
"components": null,
"status": "APPROVED",
"rejection_reason": null,
"managed_for": null,
"managed_key": null,
"usage": "follow_up",
"synced_at": "2026-09-15T18:20:00.000Z",
"created_at": "2026-01-10T13:00:00.000Z",
"updated_at": "2026-09-15T18:20:00.000Z"
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /whatsapp-templates/{id}/param-meta
Set the agent-facing label/example for each {{variable}}
Local-only — no Meta round-trip, no re-approval. Purely descriptive metadata for the composer (Seguimientos).
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
param_meta | object | sí |
curl -X PATCH https://api.vitrinadev.com/api/v1/whatsapp-templates/<id>/param-meta \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"param_meta": {
"1": {
"label": "Nombre del contacto",
"example": "María"
}
}
}'Ejemplo de respuesta (200)
{
"data": {
"id": "e2e2e2e2-0000-4000-8000-000000000001",
"messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
"name": "seguimiento_stock",
"language": "es_CL",
"category": "MARKETING",
"body_text": "Hola {{1}}, el {{2}} que consultaste sigue disponible. ¿Seguimos viéndolo?",
"header_text": null,
"footer_text": "Autos del Valle",
"params": [
"1",
"2"
],
"param_meta": {
"1": {
"label": "Nombre del contacto",
"example": "María"
},
"2": {
"label": "Modelo consultado",
"example": "Chevrolet Onix 2023"
}
},
"param_binding": {
"1": {
"source": "contact_field",
"field": "name"
},
"2": {
"source": "lead_interest"
}
},
"components": null,
"status": "APPROVED",
"rejection_reason": null,
"managed_for": null,
"managed_key": null,
"usage": "follow_up",
"synced_at": "2026-09-15T18:20:00.000Z",
"created_at": "2026-01-10T13:00:00.000Z",
"updated_at": "2026-09-15T18:20:00.000Z"
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /whatsapp-templates/{id}/usage
Re-file the template under another category
What the template is FOR, operator-facing — distinct from Meta's billing category. usage is the KEY of one of the workspace’s template categories (GET /whatsapp-templates/categories); an unknown key answers 422. Inferred from the name at create/sync, editable here. Local-only: no Meta round-trip, no re-approval. Templates in a category with hidden_from_customers are hidden from customer-facing send pickers.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
usage | string | sí | mín. 1, máx. 40, patrón ^[a-z0-9_]+$ |
curl -X PATCH https://api.vitrinadev.com/api/v1/whatsapp-templates/<id>/usage \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"usage": "follow_up"
}'Ejemplo de respuesta (200)
{
"data": {
"id": "e2e2e2e2-0000-4000-8000-000000000001",
"messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001",
"name": "seguimiento_stock",
"language": "es_CL",
"category": "MARKETING",
"body_text": "Hola {{1}}, el {{2}} que consultaste sigue disponible. ¿Seguimos viéndolo?",
"header_text": null,
"footer_text": "Autos del Valle",
"params": [
"1",
"2"
],
"param_meta": {
"1": {
"label": "Nombre del contacto",
"example": "María"
},
"2": {
"label": "Modelo consultado",
"example": "Chevrolet Onix 2023"
}
},
"param_binding": {
"1": {
"source": "contact_field",
"field": "name"
},
"2": {
"source": "lead_interest"
}
},
"components": null,
"status": "APPROVED",
"rejection_reason": null,
"managed_for": null,
"managed_key": null,
"usage": "follow_up",
"synced_at": "2026-09-15T18:20:00.000Z",
"created_at": "2026-01-10T13:00:00.000Z",
"updated_at": "2026-09-15T18:20:00.000Z"
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /whatsapp-templates/categories
List the workspace’s template categories
The operator-facing buckets templates are filed under (a template’s usage is one of these keys), ordered by position, each with the number of the workspace’s templates filed there. Every workspace starts with seven seeded (system: true) categories: first_contact, reminder, follow_up, update, aftercare, internal (hidden from customers) and other.
curl https://api.vitrinadev.com/api/v1/whatsapp-templates/categories \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"id": "0199a3c2-7b10-7000-8000-00000000c000",
"key": "first_contact",
"name": "Primer contacto",
"position": 0,
"hidden_from_customers": false,
"system": true,
"template_count": 2
},
{
"id": "0199a3c2-7b10-7000-8000-00000000c001",
"key": "reminder",
"name": "Recordatorios",
"position": 1,
"hidden_from_customers": false,
"system": true,
"template_count": 4
},
{
"id": "0199a3c2-7b10-7000-8000-00000000c005",
"key": "internal",
"name": "Equipo",
"position": 5,
"hidden_from_customers": true,
"system": true,
"template_count": 1
}
]
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /whatsapp-templates/categories
Add a template category
Appended at the end of the list. The immutable key is derived from name (deaccented, [a-z0-9_], ≤40 chars) with a numeric suffix when taken.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
name | string | sí | mín. 1, máx. 80 |
hidden_from_customers | boolean | — |
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-templates/categories \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Cobranza",
"hidden_from_customers": false
}'Ejemplo de respuesta (201)
{
"data": {
"id": "0199a3c2-7b10-7000-8000-00000000c007",
"key": "cobranza",
"name": "Cobranza",
"position": 7,
"hidden_from_customers": false,
"system": false
}
}Responde: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /whatsapp-templates/categories/{id}
Delete a template category
Its templates are re-filed under other in the same transaction. internal and other cannot be deleted (400).
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
curl -X DELETE https://api.vitrinadev.com/api/v1/whatsapp-templates/categories/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Responde: 204 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /whatsapp-templates/categories/{id}
Rename a template category or change its visibility
Seeded categories can be renamed and hidden too; the internal category must stay hidden from customers (400 when unhiding it). The key never changes.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
name | string | — | mín. 1, máx. 80 |
hidden_from_customers | boolean | — |
curl -X PATCH https://api.vitrinadev.com/api/v1/whatsapp-templates/categories/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Recordatorios de cita"
}'Ejemplo de respuesta (200)
{
"data": {
"id": "0199a3c2-7b10-7000-8000-00000000c001",
"key": "reminder",
"name": "Recordatorios de cita",
"position": 1,
"hidden_from_customers": false,
"system": true
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /whatsapp-templates/categories/order
Reorder the template categories
ids must list every category of the workspace exactly once (422 otherwise); positions are rewritten to the array order. Answers the reordered list.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
ids | uuid[] | sí |
curl -X PUT https://api.vitrinadev.com/api/v1/whatsapp-templates/categories/order \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"0199a3c2-7b10-7000-8000-00000000c001",
"0199a3c2-7b10-7000-8000-00000000c000",
"0199a3c2-7b10-7000-8000-00000000c005"
]
}'Ejemplo de respuesta (200)
{
"data": [
{
"id": "0199a3c2-7b10-7000-8000-00000000c000",
"key": "first_contact",
"name": "Primer contacto",
"position": 0,
"hidden_from_customers": false,
"system": true,
"template_count": 2
},
{
"id": "0199a3c2-7b10-7000-8000-00000000c001",
"key": "reminder",
"name": "Recordatorios",
"position": 1,
"hidden_from_customers": false,
"system": true,
"template_count": 4
},
{
"id": "0199a3c2-7b10-7000-8000-00000000c005",
"key": "internal",
"name": "Equipo",
"position": 5,
"hidden_from_customers": true,
"system": true,
"template_count": 1
}
]
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /whatsapp-templates/sync
Force a reconcile of a channel's templates against Meta
Fetches every template Meta reports for the messaging account and updates local status/rejection_reason/components. Use this right after submitting a change on Meta’s side instead of waiting for the periodic poll.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
messaging_account_id | uuid | sí |
curl -X POST https://api.vitrinadev.com/api/v1/whatsapp-templates/sync \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"messaging_account_id": "e4e4e4e4-0000-4000-8000-000000000001"
}'Ejemplo de respuesta (200)
{
"data": {
"checked": 4,
"updated": 1,
"created": 0
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /whatsapp-templates/usage
Re-file several templates under one category
Bulk form of PATCH /whatsapp-templates/\{id\}/usage: 1–200 template ids, one category key (422 when it is not one of the workspace’s). Ids that are not the workspace’s are ignored; updated counts the rows that actually changed. Local-only.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
ids | uuid[] | sí | |
usage | string | sí | mín. 1, máx. 40, patrón ^[a-z0-9_]+$ |
curl -X PATCH https://api.vitrinadev.com/api/v1/whatsapp-templates/usage \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"e2e2e2e2-0000-4000-8000-000000000001"
],
"usage": "follow_up"
}'Ejemplo de respuesta (200)
{
"data": {
"updated": 1
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429