Skip to main content

Template-only Asia channels: the send playbook

Four of Orbit’s Asia messaging channels — WeChat, Zalo, KakaoTalk Alimtalk, and anything routed through Meta (Messenger, Instagram) — reject free-form text until it rides an approved template or a Meta messaging_type tag. LINE and Telegram accept a free-form body. Pick wrong and the send fails at validation with 422 VALIDATION_ERROR before it ever reaches the provider. This playbook is the one conceptual source the per-channel pages link back to: which channels demand a template, how the same request body moves across template channels with only the path swapped, what the 422 branch means, one receiver for all inbound traffic, and how to bulk-send templates through campaigns.

1. Which channels demand a template

For Messenger and Instagram, “template” is a Meta concept, not an Orbit pre-approval: outside the 24-hour customer-service window the send must carry a Meta messaging tag (for example POST_UPDATE or ACCOUNT_UPDATE) via the messaging_type metadata, and the recipient is only reachable if that tag’s policy allows it. See the per-channel pages for the exact tag surface.

2. Decision tree: one body, swap the channel

The template-only channels share the same request body shape. The only differences are the endpoint path and what to holds:
  1. Recipient is a phone number (E.164 digits), template lives on Zalo? → /messages/zalo.
  2. Recipient is a phone number, template lives on KakaoTalk, any recipient (follower or not)? → /messages/kakao with an approved Alimtalk code (via a campaign — see §5), or with metadata.kakao_message_type: "friendtalk" plus a body when the recipient added your channel as a friend.
  3. Recipient is a WeChat openid captured from an inbound follow? → /messages/wechat.
  4. You hold no approved template at all → you are on the wrong channel; switch to LINE or Telegram and send a free-form body, or get a template approved first.
Because the body never changes, a cross-channel sanity test is a single curl with only the path swapped:
(WeChat needs an openid in to, so run its swap with a real follower id — the shape point stands: to, template_name, template_params is the whole contract for every template-only channel.) KakaoTalk’s free-form variant is the same endpoint with metadata selecting the type:

3. Why a template send returns 422

Template-only channels validate before the provider is contacted. Two distinct rejections:
  • 422 VALIDATION_ERROR with no template_name — ZNS and OA messaging are template-only, so a body-only send is refused at the API. This is the optimistic gate: the 422 carries no provider round trip, so it costs nothing and returns immediately.
  • CHANNEL_NOT_CONFIGURED (503) — the channel has no connected credentials and no platform default; fix this first, template questions come after.
When the API-side check passes, the provider can still refuse the template: on WeChat and Zalo the response surfaces as MESSAGE_SEND_FAILED (502) with the upstream errcode — an unapproved or mistyped template name is the most common cause. Avoid it the same way the WhatsApp template recipe does: list your approved templates (GET /api/v1/templates), copy the exact approved name, and treat the approved-name cache as your source of truth rather than retyping names at each call site. (Meta’s equivalent rejection is WHATSAPP_TEMPLATE_NOT_FOUND when the name or language.code does not match an approved template.) The error branch, per channel — the same pattern the per-channel pages document:
Fix on all three: pass an approved template_name (or, for KakaoTalk, either the approved Alimtalk code or a friendtalk body). The full per-channel error tables live on the Zalo, WeChat, and KakaoTalk pages.

4. One receiver for all inbound traffic

Inbound replies from every Asia channel arrive on the same message.received webhook with the normalized envelope — subscribe once under Settings → Webhooks and route on data.channel. The full envelope shape is documented under Normalized inbound event envelope; the receiver below is the whole handler for WeChat, Zalo, KakaoTalk, LINE, and Telegram.
Store data.from on your contact record — on WeChat it is the follower’s openid, and you need it as to for every outbound template send.

5. Bulk send via campaigns

Looping POST /messages/<channel> per recipient works, but the campaigns API is the intended bulk path and the only path for KakaoTalk Alimtalk (the direct endpoint only carries Friendtalk). Submit one campaign and the fan-out is one templated send per recipient, reusing the same template_name / template_params contract as the direct endpoints:
The campaign starts as a draft; launch it with POST /api/v1/campaigns/{id}/send and each recipient’s delivery advances to queued from there. For WeChat and Zalo the same pattern holds — swap channel and the approved template name. Full parameter reference: Campaigns API.

6. Per-channel references

This playbook covers the shared template contract once. For per-channel quirks — credential fields, rate limits, provider-specific error codes, KakaoTalk’s Friendtalk/Alimtalk split, WeChat’s H5 deep-link — go to the channel page:
  • Zalo — ZNS templates, E.164 recipients
  • WeChat — OA templates, openid recipients, optional metadata.url deep-link
  • KakaoTalk — Alimtalk vs Friendtalk, message-type metadata
  • LINE — free-form bodies, profile sender
  • Telegram — free-form bodies
Run the onboarding order (credentials → webhook → first send) once per channel with the APAC onboarding playbook; use this page as the send-time contract reference that onboarding links to.