Skip to main content
This guide walks through the four kinds of outbound WhatsApp messages and the read receipt, when to use each, and how the 24-hour window shapes your choices. For exact field tables and error codes, see the Karibu WhatsApp API reference.
All send endpoints return the standard envelope and are async-friendly: HTTP 200 (status: "sent") means the provider accepted immediately; HTTP 202 (status: "pending") means it was queued. Treat both as success and rely on delivery webhooks for the final state. Correlate using data.message_id.

Choosing a message type

You want to…UseNeeds an open window?
Reply within an active customer-care threadsend-textYes
Start a conversation or message outside the windowsend-templateNo (opens/refreshes it)
Send an invoice, photo, or videosend-mediaYes
Offer quick-reply buttons or a listsend-interactiveYes
Acknowledge a message the customer sent youmark-readn/a

Text, media, and interactive (window-gated)

These three require an open 24-hour window by default. If you’re not sure the window is open, send a template first (see below) or expect WINDOW_CLOSED (HTTP 422). You may pass check_window: false to skip the check, but only when you are certain the window is open.
curl -X POST "https://karibu.briq.tz/v1/whatsapp/messages/send-text" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sender_id": "SENDER_UUID",
    "recipient": "255712345678",
    "body": "Hello from Briq! Your order has shipped."
  }'
body is 1-4096 characters. sender_id is optional - omit it to use the workspace’s first active sender.

Templates (not window-gated)

Templates are the only way to message someone outside the 24-hour window, so they’re how you start a conversation. A successful template send opens or refreshes the window, after which freeform messages become deliverable.
curl -X POST "https://karibu.briq.tz/v1/whatsapp/messages/send-template" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sender_id": "SENDER_UUID",
    "template_name": "order_update",
    "recipient": "255712345678",
    "variables": { "1": "Asha", "2": "TZ-90211" },
    "callback_data": "order-90211"
  }'
sender_id is required here and must own the named, approved template. Provide every required variable - missing keys cause TEMPLATE_PARAM_COUNT_MISMATCH. See Templates for how to discover a template’s params.

Handling a closed window

A robust pattern: try the freeform send, and if you get WINDOW_CLOSED, fall back to a template.
import requests

BASE = "https://karibu.briq.tz"
HEADERS = {"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"}

def send_text_or_template(recipient: str, text: str):
    r = requests.post(f"{BASE}/v1/whatsapp/messages/send-text",
                      headers=HEADERS,
                      json={"sender_id": "SENDER_UUID", "recipient": recipient, "body": text})
    body = r.json()
    if r.status_code in (200, 202):
        return body["data"]["message_id"]
    codes = {e["code"] for e in (body.get("errors") or [])}
    if "WINDOW_CLOSED" in codes:
        # Re-open the window with an approved template instead.
        r = requests.post(f"{BASE}/v1/whatsapp/messages/send-template",
                          headers=HEADERS,
                          json={"sender_id": "SENDER_UUID", "template_name": "order_update",
                                "recipient": recipient, "variables": {"1": text}})
        return r.json()["data"]["message_id"]
    r.raise_for_status()

Marking inbound messages as read

When a customer messages you, send a read receipt so they see the blue ticks. Only inbound messages belonging to a sender in your workspace can be marked read. This endpoint always responds synchronously with HTTP 200.
curl -X POST "https://karibu.briq.tz/v1/whatsapp/messages/wamid.HBgLMjU1NzEyMzQ1Njc4FQIAEhgU/read" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json"
Marking an outbound message returns INVALID_MESSAGE (422); a message outside your workspace returns FORBIDDEN (403).

Tips

  • Digits-only E.164 recipients (e.g. 255712345678). A leading + is stripped automatically.
  • Respect the rate limit (60 req/min per key by default). On 429, honor the Retry-After header and back off.
  • Don’t treat 202 as failure. The absence of provider_message_id just means the send was queued.
  • Correlate via data.message_id so each delivery webhook attaches to the right message in your system.

Next: Senders & conversations | Templates | Delivery events