Tracked Sending API¶
Added in Epic HEKA-240 (v0.2.0)
Tracked sending endpoints create MessageRecord entries with wamid linkage for every template sent, enabling delivery status tracking via Meta webhooks.
Individual tracked send¶
Send a single template to one contact within a campaign context.
Request¶
{
"wa_id": "5219671234567",
"template_name": "amate_recordatorio_hoy_v2",
"language": "es_MX",
"parameters": ["4:00 pm", "Luna"],
"request_id": "req-uuid-unique",
"header_location": {
"lat": 16.7370359,
"lng": -92.6376543,
"name": "Escuela Ámate",
"address": "Hermilio López 14-A"
}
}
Response¶
Features¶
- Idempotency — duplicate
request_idreturnsstatus: "duplicate"without re-sending - Rate limiting — respects
WhatsAppRateLimiter(80 msg/s default) - Daily limit — enforces per-phone-number daily template send cap (1,000 default)
- Header variants — supports
header_location,header_media, andbutton_url(mutually exclusive with location) - Error mapping — Meta Graph API errors return HTTP 502 with
SenderErrordetails
Status values¶
| Status | Meaning |
|---|---|
sent |
Template sent, wamid assigned |
duplicate |
Same request_id already sent, returns original wamid |
Batch tracked send¶
Send personalized templates to multiple contacts in one request.
Request¶
{
"template_name": "amate_recordatorio_hoy_v2",
"language": "es_MX",
"header_location": {
"lat": 16.7370359,
"lng": -92.6376543,
"name": "Escuela Ámate",
"address": "Hermilio López 14-A"
},
"dry_run": false,
"contacts": [
{
"wa_id": "5219671234567",
"parameters": ["4:00 pm", "Luna"],
"request_id": "batch-001-luna"
},
{
"wa_id": "5219679876543",
"parameters": ["6:00 pm", "Ana"],
"request_id": "batch-001-ana"
}
]
}
Response¶
{
"results": [
{
"wa_id": "5219671234567",
"request_id": "batch-001-luna",
"status": "sent",
"wamid": "wamid.HBgNNTI..."
},
{
"wa_id": "5219679876543",
"request_id": "batch-001-ana",
"status": "sent",
"wamid": "wamid.HBgNNTI..."
}
],
"summary": {
"total": 2,
"sent": 2,
"failed": 0,
"skipped": 0
}
}
Per-contact status values¶
| Status | Meaning |
|---|---|
sent |
Template sent successfully |
duplicate |
Same request_id already sent |
failed |
Meta Graph API returned an error |
rate_limited |
Rate limiter rejected the send |
daily_limit_reached |
Daily template send cap exceeded |
dry_run |
Preview only, nothing sent |
Features¶
- Per-contact personalization — each contact gets unique
parameters - Error isolation — one contact's failure doesn't affect others
- Dry run — set
dry_run: trueto preview without sending - Circuit breaker — once daily limit is reached, remaining contacts are immediately marked
daily_limit_reachedwithout additional DB queries
Delivery tracking¶
Status webhooks from Meta automatically correlate with tracked sends via wamid. The status lifecycle is:
Query delivery statuses:
statuses = repo.get_statuses_by_wamid("wamid.HBgNNTI...")
# Returns: [StatusRecord(status="sent", ...), StatusRecord(status="delivered", ...)]
The message_statuses table has an index on wamid for efficient lookups.
LOCATION header¶
Templates with LOCATION headers include a map pin in the message. The header is mutually exclusive with IMAGE/VIDEO headers.
{
"header_location": {
"lat": 16.7370359,
"lng": -92.6376543,
"name": "Escuela Ámate",
"address": "Hermilio López 14-A"
}
}
Mutual exclusivity
header_location and header_media cannot be used in the same request. The sender raises ValueError at send time if both are provided.