Message templates

Create an official WABA template through Gateway API — POST /templates/. Use lowercase [a-z0-9_] for name. body_text is required; header_text/footer_text are optional. channel_id goes in the request body. The response is always PENDING: Meta’s APPROVED/REJECTED decision arrives asynchronously in the templates stream or a later GET /templates/ response:

curl -X POST https://dialogs.tehprof.kz/api/v1/gw/templates/ \
  -H "Authorization: Bearer gw_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": 32,
    "name": "order_shipped",
    "language": "en",
    "category": "UTILITY",
    "body_text": "Order {{1}} shipped, courier {{2}}.",
    "footer_text": "TehProf.kz"
  }'

201 { "template_id": "123", "status": "PENDING" }

List templates — GET /templates/?channel_id=. From the synced mirror, no live Meta call. Each status is APPROVED | PENDING | REJECTED | PAUSED | DISABLED — only APPROVED can be sent via template:

curl "https://dialogs.tehprof.kz/api/v1/gw/templates/?channel_id=32" \
  -H "Authorization: Bearer gw_xxx"

{
  "templates": [
    {
      "template_id": "123",
      "name": "order_shipped",
      "language": "en",
      "status": "APPROVED",
      "category": "UTILITY",
      "rejection_reason": "",
      "quality_rating": "GREEN",
      "components": [ { "type": "BODY", "text": "Order {{1}} shipped, courier {{2}}." } ]
    }
  ]
}

Edit a template — PATCH /templates/<id>/. Trap: Meta replaces components WHOLESALE. An edit without body_text (header_text/footer_text alone) silently changes nothing, while body_text without header/footer WIPES the existing HEADER and FOOTER. The rule: when you edit text, send all three text fields together. name and language are immutable; an edit re-triggers review, the verdict arrives on the templates stream. channel_id in the body:

curl -X PATCH https://dialogs.tehprof.kz/api/v1/gw/templates/123/ \
  -H "Authorization: Bearer gw_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": 32,
    "body_text": "Order {{1}} delivered.",
    "header_text": "TehProf.kz",
    "footer_text": "Thank you"
  }'

200 { "template_id": "123", "status": "APPROVED" }

Delete — DELETE /templates/<id>/?channel_id=. Note: channel_id is in the query here, not the body (unlike PATCH). The response is 204 with no body. A deleted APPROVED template’s name is reserved for 30 days — “delete then re-create” is not an edit strategy, use PATCH:

curl -X DELETE "https://dialogs.tehprof.kz/api/v1/gw/templates/123/?channel_id=32" \
  -H "Authorization: Bearer gw_xxx"

204 No Content

Template health — GET /templates/<id>/health/?channel_id=. A live pre-flight at Meta: a template can fail AFTER the 201 (e.g. the WABA has no billing), so ask health BEFORE sending rather than judging by the code. can_send: available | limited | blocked; when not available, reasons carries the error text and fix per entity (the phone number, the WABA, the business, the app, the template):

curl "https://dialogs.tehprof.kz/api/v1/gw/templates/123/health/?channel_id=32" \
  -H "Authorization: Bearer gw_xxx"

200 {
  "can_send": "blocked",
  "reasons": [
    {
      "code": "141006",
      "description": "There is an error with the payment method...",
      "solution": "Add a new payment method to the account.",
      "entity": "WABA"
    }
  ]
}