Skip to main content

24-Hour Messaging Window

WhatsApp Business API enforces a 24-hour customer service window that determines what types of messages you can send. Understanding this rule is essential for building reliable WhatsApp integrations.

How It Works​

The 24-hour window opens when a customer sends you a message. During this window, you can send any message type without a template. When the window closes, you can only send pre-approved template messages.

Customer sends a message
|
v
24-hour window OPENS
|
|-- You can send: text, media, interactive, reactions, etc.
|
24 hours later...
|
v
24-hour window CLOSES
|
|-- You can ONLY send: approved template messages

Rules at a Glance​

Window StatusAllowed MessagesHow to Send
Open (customer messaged within 24h)All types: text, media, interactive, reactionsPOST /api/v1/messages
Closed (no customer message in 24h)Template messages onlyPOST /api/v1/whatsapp-templates/{id}/send

What Reopens the Window​

The 24-hour window restarts (resets to a full 24 hours) every time a customer sends you a message. This includes:

  • Text messages
  • Media messages (images, documents, audio, video)
  • Button/list reply selections
  • Reactions
warning

Messages from your side (outbound) do not reopen the window. Only inbound messages from the customer reset the timer.

Handling the Window in Your Integration​

Strategy 1: Respond to Inbound Messages​

The simplest approach. When you receive a message.received webhook event, the window is guaranteed to be open:

# Webhook handler -- window is open
def handle_message_received(event):
conversation_id = event["data"]["conversation_id"]

# Safe to send any message type (reply to the existing conversation)
requests.post(
f"{BASE_URL}/messages",
headers=HEADERS,
json={
"conversation_id": conversation_id,
"text": "Thanks for reaching out! How can I help?",
"message_type": "text",
},
)
note

The text-message body field is text (a top-level string). There is no content wrapper — sending nested content is silently ignored.

Strategy 2: Use Templates for Outbound Initiation​

When you need to reach out to a customer proactively (order updates, reminders, notifications), always use a template:

# Proactive outreach -- always use a template
def send_order_update(contact_id, order_id, delivery_date):
requests.post(
f"{BASE_URL}/whatsapp-templates/tpl_order_update/send",
headers=HEADERS,
json={
"contact_id": contact_id,
# Positional template: ordered list fills {{1}}, {{2}}, ...
# (For a NAMED template, pass a dict keyed by parameter name instead.)
"variable_values": [order_id, delivery_date],
},
)

Strategy 3: Graceful Fallback​

Try sending a free-form message first. If it fails because the window is closed, fall back to a template:

def send_message_with_fallback(contact_id, channel_id, text, template_id, template_vars):
# Try free-form message first
response = requests.post(
f"{BASE_URL}/messages",
headers=HEADERS,
json={
"contact_id": contact_id,
"channel_id": channel_id,
"text": text,
"message_type": "text",
},
)

if response.status_code == 422:
# Window closed -- fall back to template
response = requests.post(
f"{BASE_URL}/whatsapp-templates/{template_id}/send",
headers=HEADERS,
json={
"contact_id": contact_id,
# template_vars: list (positional) or dict (named/numeric keys)
"variable_values": template_vars,
},
)

return response.json()
danger

Do not rely on the fallback strategy for time-sensitive notifications. If the window is closed, the initial request fails and you lose time on the retry. For proactive messaging, always start with a template.

Common Scenarios​

ScenarioWindow StatusRecommended Approach
Customer asks a questionOpenReply with text, media, or interactive message
Customer taps a button replyOpen (refreshed)Continue the conversation freely
Sending order confirmationUnknownUse a template message
Following up after 2 daysClosedUse a template message
Sending a marketing promotionClosedUse a MARKETING template
Sending an OTP codeUnknownUse an AUTHENTICATION template

Error When Window Is Closed​

If you send a non-template message with POST /api/v1/messages outside the window, nothing is sent and you receive 400:

{
"detail": {
"error": "WHATSAPP_WINDOW_EXPIRED",
"message": "WhatsApp 24-hour messaging window has expired. Please use a template message to re-engage this contact."
}
}

The same applies to conversations that started from a Click-to-WhatsApp ad: a free entry point window only waives Meta's fees and does not extend the time for free-form messages (see Pricing Inside the Window). If the contact has never written to you, error is WHATSAPP_NO_CUSTOMER_MESSAGE. Branch on detail.error, not on the message text.

The free-form rich-message endpoints (/messages/send/interactive, /send/contacts, /send/sticker) run the same check and return the same 400 errors, before anything is sent. /messages/send/carousel is not checked: a WhatsApp carousel is an approved template, which may be sent outside the window.

Pricing Inside the Window​

An open window does not mean the message is free. Meta charges per delivered message. Each WhatsApp number gets 1,000 free service messages (free-form, non-template replies inside the 24-hour window) per month; the allowance resets monthly and does not roll over. From the 1,001st service message in a month, Meta charges each one at the utility rate for the recipient's country, set by Meta. Since October 1, 2026, utility templates sent inside the open window are charged as well. Replies inside a free entry point window and inbound messages stay free of Meta fees. The window opens when you reply within 24 hours to a user who wrote in from a Click-to-WhatsApp ad, and it can stay open for up to 7 days. Only ads opened in the WhatsApp app on Android or iOS open it. Meta Business Agent messages are still charged. Meta bills these fees directly through your WhatsApp Business Account; they are not on your SendSeven invoice.

Next Steps​