WhatsApp templates
Outside the 24-hour window WhatsApp only accepts templates: pre-approved message shapes with variable slots. Meta reviews each one.
Submit for approval
Section titled “Submit for approval”curl -X POST https://api.neike.dev/api/templates/submit \ -H "x-neike-api-key: whk_live_…" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "nk_dvzobn7m4h4y", "template_data": { "name": "order_ready", "language": "en", "category": "UTILITY", "components": [ { "type": "BODY", "text": "Hi {{1}}, your order {{2}} is ready." } ] } }'template_data is passed through to Meta in their own format, so anything their
template API accepts works here. Names are lowercase with underscores, and
category is UTILITY, MARKETING or AUTHENTICATION — picking the wrong one
is the most common rejection reason.
Approval is asynchronous. Status changes arrive on your webhook, and templates are listed with their current state:
curl -X POST https://api.neike.dev/api/templates/list \ -H "x-neike-api-key: whk_live_…" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "nk_dvzobn7m4h4y" }'The submit response includes category when Meta reports it. Each listed template
carries rejected_reason, quality_score, previous_category and
correct_category when Meta provides them, passed through as Meta returns them.
If the channel has no WABA attached, submit, list and edit answer with
externalCode: 400 and a clear message.
Edit a template that already exists in the channel’s WABA. Meta replaces the whole
components array, and the template goes back to review.
curl -X POST https://api.neike.dev/api/templates/edit \ -H "x-neike-api-key: whk_live_…" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "nk_dvzobn7m4h4y", "template_name": "order_ready", "language": "en", "components": [ { "type": "BODY", "text": "Hi {{1}}, order {{2}} is ready for pickup." } ] }'Send at least one of components, category or message_send_ttl_seconds. Only
APPROVED, REJECTED and PAUSED templates can be edited, and the category of
an APPROVED template cannot change.
The template is found by template_name inside the channel’s WABA. language is
required when the name exists in several languages, and it is the full code that
list returns (for example es_AR), not the base code the webhook carries. template_id is optional and
only confirms the match: it must be the id of the template found by name.
{ "externalCode": 200, "success": true, "id_external": "123456789", "name": "order_ready", "language": "en", "status": "PENDING"}Errors keep HTTP 200 and arrive in externalCode and message: 404 when the
template is not in the WABA, 400 when language is missing or template_id
does not match.
Templates belong to the WABA, not to the channel. With an API key (or MCP grant) limited
to specific channels, editing requires the scope to cover ALL the workspace channels that share
that WABA. If any is missing, the call returns the same 404 “Canal no encontrado” as a
channel that does not exist. Creating, listing and media-handle only require the channel.
Keys without a channel restriction and the console are unaffected.
Header media
Section titled “Header media”A template with an image, video or PDF header needs a media handle in
example.header_handle. Get it from a public URL:
curl -X POST https://api.neike.dev/api/templates/media-handle \ -H "x-neike-api-key: whk_live_…" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "nk_dvzobn7m4h4y", "url": "https://cdn.example.com/banner.jpg" }'{ "externalCode": 200, "header_handle": "4::aW1h…" }The file must be image/jpeg, image/png, video/mp4 or application/pdf, up to
16 MB. Anything else answers externalCode: 400.
Status webhook
Section titled “Status webhook”Template status changes are sent to POST {your_url}/chat/updateTemplateStatus
with the x-webhook-token header (when you configured a secret), like every other
webhook.
{ "metadata": { "event_type": "message_template_status_update", "template_id": "1848392017465", "template_name": "order_ready", "language": "en", "status": "APPROVED", "reason": null, "category": "UTILITY", "waba_id": "102290129340398", "channel_ids": ["nk_dvzobn7m4h4y"] }}waba_id is the WABA that owns the template. channel_ids lists your active
channels on that WABA, so you know which one to use to edit or resubmit it.
category is present only when Meta sends it, and language is normalized to the
base code (es_AR arrives as es).
The namespace
Section titled “The namespace”Sending a template needs your WABA’s message template namespace. You get it when the channel is connected — it is part of the payload the embedded signup returns, and it is shown on the channel in the console.
Delete
Section titled “Delete”curl -X DELETE https://api.neike.dev/api/templates/delete \ -H "x-neike-api-key: whk_live_…" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "nk_dvzobn7m4h4y", "template_name": "order_ready" }'Deleting is permanent on Meta’s side and the name cannot be reused immediately.
Templates belong to the WABA, not to the channel. With an API key (or MCP grant) limited
to specific channels, deleting requires the scope to cover ALL the workspace channels that share
that WABA. If any is missing, the call returns the same 404 “Canal no encontrado” as a
channel that does not exist. Creating, listing and media-handle only require the channel.
Keys without a channel restriction and the console are unaffected.