Skip to main content
POST
/
v1
/
templates
Crear un template de WhatsApp y enviarlo a Meta para aprobación
curl --request POST \
  --url https://api.keebai.com/v1/templates \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "channel_id": "<string>",
  "name": "welcome_v2",
  "language": "es_CL",
  "components": [
    {
      "text": "<string>",
      "buttons": [
        {
          "text": "<string>",
          "url": "<string>",
          "phone_number": "<string>",
          "example": [
            "<string>"
          ],
          "flow_id": "<string>",
          "coupon_code": "<string>",
          "autofill_text": "<string>",
          "package_name": "<string>",
          "signature_hash": "<string>"
        }
      ],
      "example": {
        "header_text": [
          "<string>"
        ],
        "header_text_named_params": [
          {
            "param_name": "nombre",
            "example": "Lucio"
          }
        ],
        "header_handle": [
          "<string>"
        ],
        "body_text": [
          "<string>"
        ],
        "body_text_named_params": [
          {
            "param_name": "nombre",
            "example": "Lucio"
          }
        ]
      },
      "add_security_recommendation": true,
      "code_expiration_minutes": 123
    }
  ],
  "metadata": {}
}
'
{
  "id": "<string>",
  "name": "<string>",
  "language": "<string>",
  "status": "<string>",
  "category": "<string>",
  "variables": [
    "<string>"
  ],
  "parameter_format": "<string>"
}
Creates a template on the given WhatsApp channel and submits it to Meta for review. The response includes the template’s internal id and the status it landed in. Until Meta approves the template, you can’t use it in POST /v1/messages/template — use GET /v1/templates to check when it becomes APPROVED.

Endpoint

POST https://api.keebai.com/v1/templates

Required scope

templates:create

Headers

HeaderRequiredValue
AuthorizationYesBearer kbai_pk_<token>
Content-TypeYesapplication/json

Body

FieldTypeRequiredDescription
channel_idstringYesObjectId of the WhatsApp channel where the template is created.
namestringYesTemplate name. Lowercase letters, numbers, and underscores only. Max 512.
languagestringYesLanguage code (es, es_CL, en_US, etc.). Max 10.
categorystringYesMARKETING, UTILITY, or AUTHENTICATION.
parameter_formatstringNoNAMED (recommended) or POSITIONAL.
componentsarrayYesTemplate components. Same structure as Meta’s.
metadataobjectNoFree-form metadata attached to the template.

Structure of components[]

FieldTypeDescription
typestringHEADER, BODY, FOOTER, or BUTTONS.
formatstringOnly for HEADER: TEXT, IMAGE, VIDEO, or DOCUMENT.
textstringComponent text. BODY supports {{name}} variables. Max 1024.
buttonsarrayOnly for type: BUTTONS. Each button has type (QUICK_REPLY, URL, PHONE_NUMBER, FLOW, VOICE_CALL, COPY_CODE, OTP) and text (max 25).
exampleobjectVariable examples Meta requires when reviewing (body_text, body_text_named_params, header_text, header_handle).
add_security_recommendationbooleanOnly for authentication templates.
code_expiration_minutesnumberOnly for authentication templates.

Example request

curl -X POST https://api.keebai.com/v1/templates \
  -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": "65f3a1b2c3d4e5f6a7b8c9d0",
    "name": "welcome_v2",
    "language": "es_CL",
    "category": "UTILITY",
    "parameter_format": "NAMED",
    "components": [
      {
        "type": "BODY",
        "text": "Hola {{nombre}}, tu pedido por {{monto}} está confirmado.",
        "example": {
          "body_text_named_params": [
            { "param_name": "nombre", "example": "Lucio" },
            { "param_name": "monto", "example": "$15.000" }
          ]
        }
      }
    ]
  }'

Response

201 Created

{
  "id": "65f3a1b2c3d4e5f6a7b8c9d2",
  "name": "welcome_v2",
  "language": "es_CL",
  "status": "PENDING",
  "category": "UTILITY",
  "parameter_format": "NAMED",
  "variables": ["nombre", "monto"]
}
FieldTypeDescription
idstringInternal ObjectId of the newly created template.
namestringName as it was registered.
languagestringLanguage code.
statusstringInitial status reported by Meta (PENDING or IN_REVIEW right after creation).
categorystringAssigned category.
parameter_formatstringNAMED or POSITIONAL.
variablesstring[]Named variables detected in the body.

400 / 401 / 403 / 404 / 409 / 429

Standard errors. Common cases:
  • 400 BAD_REQUEST: body validation (invalid characters in name, malformed components, missing example when Meta requires it).
  • 403 FORBIDDEN with code: INSUFFICIENT_SCOPE: the PAT does not have templates:create.
  • 404 NOT_FOUND: the channel_id does not belong to your tenant or does not exist.
  • 409 CONFLICT: a template with the same name + language already exists on that channel.
  • 502 UPSTREAM_ERROR: Meta rejected the creation. The details field explains why.
After creating the template you have to wait for Meta to approve it before sending. Poll GET /v1/templates filtering by status until it shows up as APPROVED. Approval usually takes minutes, but can take longer for sensitive categories like MARKETING.

Authorizations

Authorization
string
header
required

Personal Access Token con prefijo kbai_pk_. Generar desde el portal con permiso developer.manage_tokens.

Body

application/json
channel_id
string
required

ObjectId del canal WhatsApp donde se crea el template.

name
string
required

Nombre del template. Solo minúsculas, números y guiones bajos.

Maximum string length: 512
Example:

"welcome_v2"

language
string
required
Maximum string length: 10
Example:

"es_CL"

category
enum<string>
required
Available options:
MARKETING,
UTILITY,
AUTHENTICATION
components
object[]
required
parameter_format
enum<string>
Available options:
NAMED,
POSITIONAL
metadata
object

Response

201 - application/json
id
string
required
name
string
required
language
string
required
status
string
required
category
string
required
variables
string[]
required
parameter_format
string