VitrinaAPI

WhatsApp templates

The catalog of Meta-approved message shapes for writing outside the 24-hour window, and how Vitrina fills each variable.

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-templatesList WhatsApp templates
POST/whatsapp-templatesSubmit 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-bindingsRe-run the binder over every slot ("volver a sugerir")
PATCH/whatsapp-templates/{id}/param-bindingSet what fills each {{variable}} when Vitrina sends this template
PATCH/whatsapp-templates/{id}/param-metaSet the agent-facing label/example for each {{variable}}
PATCH/whatsapp-templates/{id}/usageRe-file the template under another category
GET/whatsapp-templates/categoriesList the workspace’s template categories
POST/whatsapp-templates/categoriesAdd 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/orderReorder the template categories
POST/whatsapp-templates/syncForce a reconcile of a channel's templates against Meta
PATCH/whatsapp-templates/usageRe-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.

ParameterInTypeRequiredConstraints
messaging_account_idqueryuuidno
statusqueryPENDING \APPROVED \REJECTED \
curl https://api.vitrinadev.com/api/v1/whatsapp-templates \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (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
  }
}

Answers: 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.

Body

FieldTypeRequiredConstraints
messaging_account_iduuidyes
namestringyesmín. 1, máx. 512, patrón ^[a-z0-9_]+$
languagestringyespatrón ^[a-z]{2}(_[A-Z]{2})?$
categoryMARKETING \UTILITY \AUTHENTICATION
body_textstringyesmín. 1, máx. 1024
header_textstring | null—mín. 1, máx. 60
footer_textstring | null—mín. 1, máx. 60
buttonsany[]—
param_metaobject—
usagestring—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"
  }'

Example response (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"
  }
}

Answers: 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.

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

Answers: 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.

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

Example response (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"
  }
}

Answers: 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.

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

Example response (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"
  }
}

Answers: 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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
param_bindingobjectyes
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"
      }
    }
  }'

Example response (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"
  }
}

Answers: 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).

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
param_metaobjectyes
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"
      }
    }
  }'

Example response (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"
  }
}

Answers: 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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
usagestringyesmí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"
  }'

Example response (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"
  }
}

Answers: 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"

Example response (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
    }
  ]
}

Answers: 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.

Body

FieldTypeRequiredConstraints
namestringyesmín. 1, máx. 80
hidden_from_customersboolean—
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
  }'

Example response (201)

{
  "data": {
    "id": "0199a3c2-7b10-7000-8000-00000000c007",
    "key": "cobranza",
    "name": "Cobranza",
    "position": 7,
    "hidden_from_customers": false,
    "system": false
  }
}

Answers: 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).

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

Answers: 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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
namestring—mín. 1, máx. 80
hidden_from_customersboolean—
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"
  }'

Example response (200)

{
  "data": {
    "id": "0199a3c2-7b10-7000-8000-00000000c001",
    "key": "reminder",
    "name": "Recordatorios de cita",
    "position": 1,
    "hidden_from_customers": false,
    "system": true
  }
}

Answers: 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.

Body

FieldTypeRequiredConstraints
idsuuid[]yes
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"
    ]
  }'

Example response (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
    }
  ]
}

Answers: 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.

Body

FieldTypeRequiredConstraints
messaging_account_iduuidyes
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"
  }'

Example response (200)

{
  "data": {
    "checked": 4,
    "updated": 1,
    "created": 0
  }
}

Answers: 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.

Body

FieldTypeRequiredConstraints
idsuuid[]yes
usagestringyesmí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"
  }'

Example response (200)

{
  "data": {
    "updated": 1
  }
}

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

On this page