Skip to content

Stop asking. We will tell you.

Delivered, read, failed, replied to, unsubscribed. 7 events, signed, and retried for about a day if your server is having an afternoon.

Set an address in Settings → API and press Send test event. Polling GET /messages/{id} works and is the wrong way round — a delivery receipt arrives when it arrives.

What you get told

Every event, and when it fires.

The one worth wiring first is contact.opted_out. It arrives the moment somebody replies STOP, which is the moment your own system should stop trying.

message.sent
message.delivered
message.read
message.failed
A message sent through the API changes state
message.received
Somebody writes to your number
contact.opted_out
Somebody replies STOP, or is marked opted out in talkv
contact.opted_in
Somebody who had opted out writes START
broadcast.finished
A broadcast has sent everything and its figures have settled
template.approved
template.rejected
Meta decides on a template
webhook.test
You press Send test event in Settings → API

The body

One shape, every time.

Switch on type, read data, and ignore a type you do not recognise — so a new event can never break the handler you already have.

Fields

id string required
type string required
message.sent · message.delivered · message.read · message.failed · message.received · contact.opted_out · contact.opted_in · broadcast.finished · template.approved · template.rejected · webhook.test
created_at date-time required
data object required
message.* → {message: Message}; message.received → {message: {id, from, type, text, received_at}, contact: Contact}; contact.* → {contact: Contact}; broadcast.finished → {broadcast: Broadcast}; template.* → {template: Template, reason}.
A delivery event json
{
  "id": "evt_01JB8Z3T6Q0K7V9X2M4N5P6R7S",
  "type": "message.delivered",
  "created_at": "2026-10-04T10:15:09+05:30",
  "data": { "message": { "id": 812, "to": "+919825012345", "status": "delivered" } }
}

Getting it right

Three mistakes, in the order people make them.

  • Answer in ten seconds

    Return any 2xx and do the work afterwards, on your own queue. Anything slower is a failure as far as the retry schedule is concerned.

  • Expect it twice

    Ignore an event whose id you have already seen. Retries and redeliveries are normal; an order marked shipped twice is your bug, not ours.

  • Verify the raw bytes

    Compute the HMAC before parsing the JSON. Re-encoding the body changes the bytes, and then the signature can never match.

Checking the signature

Signed with Talkv-Signature: t=<unix time>,v1=<hex HMAC-SHA256 of "t.raw body"> using the workspace's webhook secret. Answer 2xx within 10 seconds; anything else is retried for about a day. Events can repeat — use id.

The secret is in Settings → API, beside the address. If it may have been seen, New secret replaces it — and the old one stops working at once.

Verify, in PHP php
[$t, $v1] = sscanf($_SERVER['HTTP_TALKV_SIGNATURE'], 't=%d,v1=%s');
$expected = hash_hmac('sha256', $t.'.'.file_get_contents('php://input'), $secret);
$valid = hash_equals($expected, $v1) && abs(time() - $t) < 300;

Questions

Asked while wiring it up.

How do I verify a talkv webhook signature?

Every request carries a Talkv-Signature header of the form t=<unix time>,v1=<hex>. Compute the HMAC-SHA256 of the timestamp, a full stop, and the raw request body, keyed with the webhook secret from Settings → API. Compare it in constant time and reject any timestamp more than five minutes old.

What happens if my endpoint is down?

talkv retries after about 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours, then stops. Answer with any 2xx within 10 seconds and do the work afterwards — a slow endpoint is treated as a failed one.

Can the same webhook event arrive twice?

Yes, by design. Use the event id to ignore repeats. Every event carries one, it never changes between retries, and events usually arrive within a minute of the thing they describe.

Do I get a webhook for every message in a broadcast?

No. Message events cover messages you sent through the API one at a time. A broadcast sends one broadcast.finished event when its figures have settled, and GET /broadcasts/{id}/recipients has the per-person detail.

Every endpoint, every field, one page.

API reference
Three colleagues gathered round a laptop in a glass-walled meeting room.

Your customers are waiting. Say hello today.

₹50 credit to start and no monthly fee. Your number and your chats stay yours.