When to use
Use this skill to send on the WhatsApp channel: whatsapp: address formatting,
deciding free-form vs template based on the 24-hour customer-care window, sending
approved templates by ContentSid with ContentVariables, attaching media, and
rendering interactive buttons/lists built through the Content API. It also covers
which conversation pricing category applies.
Not for: onboarding or registering a WhatsApp sender/WABA, display name, or
messaging-tier limits (use twilio-whatsapp-manage-senders), plain SMS/MMS (use
twilio-sms-send-message), or generic channel routing (use twilio-send-message).
Method
- Format both endpoints as
whatsapp:+E164, e.g. whatsapp:+15558675310. For
testing, send from the sandbox number whatsapp:+14155238886 after joining.
- Decision point — is the 24-hour window open? The window opens for 24 hours
from the customer's most recent inbound message. If open, you may send
free-form
Body and/or media. If closed, you MUST send an approved template.
- Free-form send (window open): set
Body and optionally one MediaUrl.
- Template send (window closed, or business-initiated): set
ContentSid (an
approved WhatsApp template) plus ContentVariables (JSON string mapping
placeholder indexes to values). Do not send raw Body when closed — it fails
with 63016.
- Decision point — interactivity: quick-reply buttons, call-to-action buttons,
and list pickers must be defined in the Content template (Content API types
like
twilio/quick-reply, twilio/call-to-action, twilio/list-picker) and
sent via ContentSid; they cannot be injected ad hoc in Body.
- Send through a
MessagingServiceSid (WhatsApp sender attached) or a From
whatsapp: address.
- Set
StatusCallback and watch for sent/delivered/read. WhatsApp adds a
read receipt that SMS lacks.
- Understand billing: business-initiated templates bill per conversation
category (marketing / utility / authentication); user-initiated opens a
service conversation.
Example
from twilio.rest import Client
client = Client(account_sid, auth_token)
# Window closed -> approved template with variables
msg = client.messages.create(
from_="whatsapp:+14155238886",
to="whatsapp:+15558675310",
content_sid="HXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
content_variables='{"1":"Ada","2":"12345"}',
status_callback="https://example.com/twilio/wa-status",
)
print(msg.sid, msg.status)
# Window open -> free-form with media
client.messages.create(
from_="whatsapp:+14155238886",
to="whatsapp:+15558675310",
body="Here is your receipt.",
media_url=["https://example.com/receipt.pdf"],
)
Pitfalls
- Forgetting the
whatsapp: prefix routes the message to SMS or fails; both
To and From need it.
- Sending free-form
Body after the 24h window closes fails with 63016; only a
template will deliver.
- 63007 means the WhatsApp channel/sender was not found — the number is not a
registered WhatsApp sender (see twilio-whatsapp-manage-senders).
- Templates must be pre-approved by Meta; an unapproved or paused template will
be rejected even if the ContentSid exists.
ContentVariables is a JSON string, not an object; malformed JSON drops
substitutions and can send literal {{1}} placeholders.
- Rapid bursts can hit 63018 (WhatsApp rate limit) — throttle to your tier.
- Buttons/lists typed into
Body do not render; they must live in the template.
Output format
Returns a Message resource with sid, status, to/from (whatsapp: prefixed),
num_segments, price/price_unit, and error_code/error_message. Status
callbacks progress queued → sent → delivered → read, or to
undelivered/failed with a WhatsApp error code.
Reference
- Endpoint:
POST https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages.json
- Address format:
whatsapp:+E164. Sandbox sender: whatsapp:+14155238886
(recipient must first join the sandbox with the join keyword).
- Session window: 24 hours from the last inbound customer message. Inside the
window: free-form allowed. Outside: approved template (
ContentSid +
ContentVariables) required.
- Media: single media per WhatsApp message typical; standard document/image/
audio/video types; MMS-style 5 MB ceiling applies to media payloads.
- Interactive content via Content API:
twilio/quick-reply,
twilio/call-to-action, twilio/list-picker, twilio/card.
- Conversation pricing categories: marketing, utility, authentication, service —
business-initiated templates bill by category; user-initiated = service.
- Error codes: 63016 message sent outside the 24h free-form window (template
required), 63007 WhatsApp channel/sender not found, 63018 WhatsApp rate limit
exceeded; plus generic 21211 invalid
To, 30007 filtered.
1---2name: twilio-whatsapp-send-message3description: Send WhatsApp messages via Twilio, honoring the 24-hour session window and using approved Content templates when it is closed.4---56## When to use78Use this skill to send on the WhatsApp channel: `whatsapp:` address formatting,9deciding free-form vs template based on the 24-hour customer-care window, sending10approved templates by `ContentSid` with `ContentVariables`, attaching media, and11rendering interactive buttons/lists built through the Content API. It also covers12which conversation pricing category applies.1314**Not for:** onboarding or registering a WhatsApp sender/WABA, display name, or15messaging-tier limits (use twilio-whatsapp-manage-senders), plain SMS/MMS (use16twilio-sms-send-message), or generic channel routing (use twilio-send-message).1718## Method19201. Format both endpoints as `whatsapp:+E164`, e.g. `whatsapp:+15558675310`. For21 testing, send from the sandbox number `whatsapp:+14155238886` after joining.222. Decision point — is the 24-hour window open? The window opens for 24 hours23 from the customer's most recent inbound message. If open, you may send24 free-form `Body` and/or media. If closed, you MUST send an approved template.253. Free-form send (window open): set `Body` and optionally one `MediaUrl`.264. Template send (window closed, or business-initiated): set `ContentSid` (an27 approved WhatsApp template) plus `ContentVariables` (JSON string mapping28 placeholder indexes to values). Do not send raw `Body` when closed — it fails29 with 63016.305. Decision point — interactivity: quick-reply buttons, call-to-action buttons,31 and list pickers must be defined in the Content template (Content API types32 like `twilio/quick-reply`, `twilio/call-to-action`, `twilio/list-picker`) and33 sent via `ContentSid`; they cannot be injected ad hoc in `Body`.346. Send through a `MessagingServiceSid` (WhatsApp sender attached) or a `From`35 `whatsapp:` address.367. Set `StatusCallback` and watch for `sent`/`delivered`/`read`. WhatsApp adds a37 `read` receipt that SMS lacks.388. Understand billing: business-initiated templates bill per conversation39 category (marketing / utility / authentication); user-initiated opens a40 service conversation.4142## Example4344```python45from twilio.rest import Client46client = Client(account_sid, auth_token)4748# Window closed -> approved template with variables49msg = client.messages.create(50 from_="whatsapp:+14155238886",51 to="whatsapp:+15558675310",52 content_sid="HXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",53 content_variables='{"1":"Ada","2":"12345"}',54 status_callback="https://example.com/twilio/wa-status",55)56print(msg.sid, msg.status)5758# Window open -> free-form with media59client.messages.create(60 from_="whatsapp:+14155238886",61 to="whatsapp:+15558675310",62 body="Here is your receipt.",63 media_url=["https://example.com/receipt.pdf"],64)65```6667## Pitfalls6869- Forgetting the `whatsapp:` prefix routes the message to SMS or fails; both70 `To` and `From` need it.71- Sending free-form `Body` after the 24h window closes fails with 63016; only a72 template will deliver.73- 63007 means the WhatsApp channel/sender was not found — the number is not a74 registered WhatsApp sender (see twilio-whatsapp-manage-senders).75- Templates must be pre-approved by Meta; an unapproved or paused template will76 be rejected even if the ContentSid exists.77- `ContentVariables` is a JSON string, not an object; malformed JSON drops78 substitutions and can send literal `{{1}}` placeholders.79- Rapid bursts can hit 63018 (WhatsApp rate limit) — throttle to your tier.80- Buttons/lists typed into `Body` do not render; they must live in the template.8182## Output format8384Returns a Message resource with `sid`, `status`, `to`/`from` (whatsapp: prefixed),85`num_segments`, `price`/`price_unit`, and `error_code`/`error_message`. Status86callbacks progress `queued` → `sent` → `delivered` → `read`, or to87`undelivered`/`failed` with a WhatsApp error code.8889## Reference9091- Endpoint: `POST https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages.json`92- Address format: `whatsapp:+E164`. Sandbox sender: `whatsapp:+14155238886`93 (recipient must first join the sandbox with the join keyword).94- Session window: 24 hours from the last inbound customer message. Inside the95 window: free-form allowed. Outside: approved template (`ContentSid` +96 `ContentVariables`) required.97- Media: single media per WhatsApp message typical; standard document/image/98 audio/video types; MMS-style 5 MB ceiling applies to media payloads.99- Interactive content via Content API: `twilio/quick-reply`,100 `twilio/call-to-action`, `twilio/list-picker`, `twilio/card`.101- Conversation pricing categories: marketing, utility, authentication, service —102 business-initiated templates bill by category; user-initiated = service.103- Error codes: 63016 message sent outside the 24h free-form window (template104 required), 63007 WhatsApp channel/sender not found, 63018 WhatsApp rate limit105 exceeded; plus generic 21211 invalid `To`, 30007 filtered.