Webhook events
Ha Tang sends one HTTP POST per event to each webhook endpoint subscribed to it. The body is always the same envelope; data depends on the event. Each event below links to its full payload schema and an example.
Envelope
{
"type": "email.delivered",
"created_at": "2026-10-23T08:15:00.000Z",
"data": {
"email_id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
"to": [
],
"subject": "Your order",
"created_at": "2026-10-23T08:14:58.000Z"
}
}
Headers
| Header | Meaning |
|---|---|
svix-id / webhook-id | Unique id of the delivery. The same id is reused for retries and replays: de-duplicate on it. |
svix-timestamp / webhook-timestamp | Unix seconds when the delivery was signed. Reject anything more than 5 minutes off. |
svix-signature / webhook-signature | v1,<base64> signatures, space-separated (two during a secret rotation). |
Answer with any 2xx within 15 seconds. Other answers and timeouts are retried after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours. You can replay any delivery from the dashboard or with POST /webhooks/deliveries/{id}/replay.
Set-up, signature verification code and testing are in the Webhooks guide.
Email events
| Event | When it fires |
|---|---|
email.sent | The email was accepted by the sending infrastructure. |
email.delivered | The recipient's mail server accepted the email. |
email.delivery_delayed | Delivery is being retried (for example the recipient's server is temporarily refusing mail). |
email.bounced | The email bounced. data.bounce.type is Permanent (the address is suppressed) or Transient. |
email.complained | The recipient marked the email as spam. The address is suppressed. |
email.opened | The recipient opened the email (open tracking on). Fired once per open. |
email.clicked | The recipient clicked a tracked link. data.click.link is the original URL. |
email.failed | The email could not be sent. data.failed.reason says why. |
email.received | An email arrived at one of your receiving domains. Fetch the body and attachments with the Receiving API. |
contact.created | A contact was added (API, dashboard, import or CRM sync). |
contact.updated | A contact's name, properties or subscription changed. |
contact.deleted | A contact was deleted. |
domain.created | A sending domain was added. |
domain.updated | A domain changed, including its verification status. |
domain.deleted | A domain was removed. |
Support events
| Event | When it fires |
|---|---|
chat.conversation.created | A new conversation started on any channel. |
chat.conversation.assigned | A conversation was assigned (or unassigned). |
chat.conversation.resolved | A conversation was marked resolved. |
chat.conversation.closed | A conversation was closed. |
chat.conversation.reopened | A resolved or closed conversation was reopened. |
chat.conversation.handoff | The AI assistant handed the conversation to your team. |
chat.conversation.tagged | A tag was added to a conversation; one event per added tag. |
chat.message.created | A public message was posted by the customer, an agent, the AI assistant or a flow. Internal notes do not fire it. |
chat.visitor.identified | A visitor was identified (identify from the widget or an SDK). |
chat.order.created | An order was created from the inbox or the API. |
chat.order.updated | An order changed (status, items, shipping). |
chat.order.paid | An order was paid. |
chat.flow.webhook | A flow or workflow reached a Webhook step. |
chat.campaign.lead | A visitor submitted a lead form. |
chat.ticket.created | A support ticket was created. |
chat.ticket.updated | A ticket changed. |
chat.ticket.resolved | A ticket was resolved. |
chat.ticket.breached | A ticket missed an SLA target. clock says which one. |
chat.rating.created | A customer rated a conversation (CSAT). |
chat.zns.sent | A Zalo ZNS message was delivered to Zalo. |
chat.zns.failed | A Zalo ZNS message failed. error has the reason. |
Payments events
| Event | When it fires |
|---|---|
payment.created | A payment request was created. |
payment.succeeded | The full amount arrived. |
payment.partially_paid | Less than the full amount arrived. |
payment.overpaid | More than the requested amount arrived. |
payment.expired | The payment expired before it was paid. |
payment.canceled | The payment was canceled. |
transaction.received | Money arrived in a linked bank account (matched or not). |
bank_connection.activated | A bank account was linked and confirmed. |
bank_connection.revoked | A bank link was removed. |
bank_connection.needs_reauth | The bank needs the account holder to confirm the link again. |
invoice.draft_created | A draft e-invoice was created. |
invoice.draft_updated | A draft e-invoice changed. |
invoice.draft_deleted | A draft e-invoice was deleted. |
invoice.issued | An e-invoice was issued by the provider. |
invoice.tax_accepted | The tax authority accepted the e-invoice. |
invoice.tax_rejected | The tax authority rejected the e-invoice. |
invoice.canceled | An e-invoice was canceled. |
invoice.replaced | An e-invoice was replaced by a new one. |
invoice.adjusted | An adjustment invoice was issued. |
invoice.failed | Issuing an e-invoice failed. |
invoice.conflict | The invoice changed on the provider's side and conflicts with Ha Tang's copy. |