# 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

```json
{
  "type": "email.delivered",
  "created_at": "2026-10-23T08:15:00.000Z",
  "data": {
    "email_id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
    "from": "Shop <hello@shop.vn>",
    "to": [
      "an@example.com"
    ],
    "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](https://hatang.dev/docs/guides/getting-started/webhooks.md).

## Email events

| Event | When it fires |
|---|---|
| [`email.sent`](https://hatang.dev/docs/reference/api/webhook-email-sent) | The email was accepted by the sending infrastructure. |
| [`email.delivered`](https://hatang.dev/docs/reference/api/webhook-email-delivered) | The recipient's mail server accepted the email. |
| [`email.delivery_delayed`](https://hatang.dev/docs/reference/api/webhook-email-delivery-delayed) | Delivery is being retried (for example the recipient's server is temporarily refusing mail). |
| [`email.bounced`](https://hatang.dev/docs/reference/api/webhook-email-bounced) | The email bounced. `data.bounce.type` is `Permanent` (the address is suppressed) or `Transient`. |
| [`email.complained`](https://hatang.dev/docs/reference/api/webhook-email-complained) | The recipient marked the email as spam. The address is suppressed. |
| [`email.opened`](https://hatang.dev/docs/reference/api/webhook-email-opened) | The recipient opened the email (open tracking on). Fired once per open. |
| [`email.clicked`](https://hatang.dev/docs/reference/api/webhook-email-clicked) | The recipient clicked a tracked link. `data.click.link` is the original URL. |
| [`email.failed`](https://hatang.dev/docs/reference/api/webhook-email-failed) | The email could not be sent. `data.failed.reason` says why. |
| [`email.received`](https://hatang.dev/docs/reference/api/webhook-email-received) | An email arrived at one of your receiving domains. Fetch the body and attachments with the Receiving API. |
| [`contact.created`](https://hatang.dev/docs/reference/api/webhook-contact-created) | A contact was added (API, dashboard, import or CRM sync). |
| [`contact.updated`](https://hatang.dev/docs/reference/api/webhook-contact-updated) | A contact's name, properties or subscription changed. |
| [`contact.deleted`](https://hatang.dev/docs/reference/api/webhook-contact-deleted) | A contact was deleted. |
| [`domain.created`](https://hatang.dev/docs/reference/api/webhook-domain-created) | A sending domain was added. |
| [`domain.updated`](https://hatang.dev/docs/reference/api/webhook-domain-updated) | A domain changed, including its verification status. |
| [`domain.deleted`](https://hatang.dev/docs/reference/api/webhook-domain-deleted) | A domain was removed. |

## Support events

| Event | When it fires |
|---|---|
| [`chat.conversation.created`](https://hatang.dev/docs/reference/api/webhook-chat-conversation-created) | A new conversation started on any channel. |
| [`chat.conversation.assigned`](https://hatang.dev/docs/reference/api/webhook-chat-conversation-assigned) | A conversation was assigned (or unassigned). |
| [`chat.conversation.resolved`](https://hatang.dev/docs/reference/api/webhook-chat-conversation-resolved) | A conversation was marked resolved. |
| [`chat.conversation.closed`](https://hatang.dev/docs/reference/api/webhook-chat-conversation-closed) | A conversation was closed. |
| [`chat.conversation.reopened`](https://hatang.dev/docs/reference/api/webhook-chat-conversation-reopened) | A resolved or closed conversation was reopened. |
| [`chat.conversation.handoff`](https://hatang.dev/docs/reference/api/webhook-chat-conversation-handoff) | The AI assistant handed the conversation to your team. |
| [`chat.conversation.tagged`](https://hatang.dev/docs/reference/api/webhook-chat-conversation-tagged) | A tag was added to a conversation; one event per added tag. |
| [`chat.message.created`](https://hatang.dev/docs/reference/api/webhook-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`](https://hatang.dev/docs/reference/api/webhook-chat-visitor-identified) | A visitor was identified (`identify` from the widget or an SDK). |
| [`chat.order.created`](https://hatang.dev/docs/reference/api/webhook-chat-order-created) | An order was created from the inbox or the API. |
| [`chat.order.updated`](https://hatang.dev/docs/reference/api/webhook-chat-order-updated) | An order changed (status, items, shipping). |
| [`chat.order.paid`](https://hatang.dev/docs/reference/api/webhook-chat-order-paid) | An order was paid. |
| [`chat.flow.webhook`](https://hatang.dev/docs/reference/api/webhook-chat-flow-webhook) | A flow or workflow reached a Webhook step. |
| [`chat.campaign.lead`](https://hatang.dev/docs/reference/api/webhook-chat-campaign-lead) | A visitor submitted a lead form. |
| [`chat.ticket.created`](https://hatang.dev/docs/reference/api/webhook-chat-ticket-created) | A support ticket was created. |
| [`chat.ticket.updated`](https://hatang.dev/docs/reference/api/webhook-chat-ticket-updated) | A ticket changed. |
| [`chat.ticket.resolved`](https://hatang.dev/docs/reference/api/webhook-chat-ticket-resolved) | A ticket was resolved. |
| [`chat.ticket.breached`](https://hatang.dev/docs/reference/api/webhook-chat-ticket-breached) | A ticket missed an SLA target. `clock` says which one. |
| [`chat.rating.created`](https://hatang.dev/docs/reference/api/webhook-chat-rating-created) | A customer rated a conversation (CSAT). |
| [`chat.zns.sent`](https://hatang.dev/docs/reference/api/webhook-chat-zns-sent) | A Zalo ZNS message was delivered to Zalo. |
| [`chat.zns.failed`](https://hatang.dev/docs/reference/api/webhook-chat-zns-failed) | A Zalo ZNS message failed. `error` has the reason. |

## Payments events

| Event | When it fires |
|---|---|
| [`payment.created`](https://hatang.dev/docs/reference/api/webhook-payment-created) | A payment request was created. |
| [`payment.succeeded`](https://hatang.dev/docs/reference/api/webhook-payment-succeeded) | The full amount arrived. |
| [`payment.partially_paid`](https://hatang.dev/docs/reference/api/webhook-payment-partially-paid) | Less than the full amount arrived. |
| [`payment.overpaid`](https://hatang.dev/docs/reference/api/webhook-payment-overpaid) | More than the requested amount arrived. |
| [`payment.expired`](https://hatang.dev/docs/reference/api/webhook-payment-expired) | The payment expired before it was paid. |
| [`payment.canceled`](https://hatang.dev/docs/reference/api/webhook-payment-canceled) | The payment was canceled. |
| [`transaction.received`](https://hatang.dev/docs/reference/api/webhook-transaction-received) | Money arrived in a linked bank account (matched or not). |
| [`bank_connection.activated`](https://hatang.dev/docs/reference/api/webhook-bank-connection-activated) | A bank account was linked and confirmed. |
| [`bank_connection.revoked`](https://hatang.dev/docs/reference/api/webhook-bank-connection-revoked) | A bank link was removed. |
| [`bank_connection.needs_reauth`](https://hatang.dev/docs/reference/api/webhook-bank-connection-needs-reauth) | The bank needs the account holder to confirm the link again. |
| [`invoice.draft_created`](https://hatang.dev/docs/reference/api/webhook-invoice-draft-created) | A draft e-invoice was created. |
| [`invoice.draft_updated`](https://hatang.dev/docs/reference/api/webhook-invoice-draft-updated) | A draft e-invoice changed. |
| [`invoice.draft_deleted`](https://hatang.dev/docs/reference/api/webhook-invoice-draft-deleted) | A draft e-invoice was deleted. |
| [`invoice.issued`](https://hatang.dev/docs/reference/api/webhook-invoice-issued) | An e-invoice was issued by the provider. |
| [`invoice.tax_accepted`](https://hatang.dev/docs/reference/api/webhook-invoice-tax-accepted) | The tax authority accepted the e-invoice. |
| [`invoice.tax_rejected`](https://hatang.dev/docs/reference/api/webhook-invoice-tax-rejected) | The tax authority rejected the e-invoice. |
| [`invoice.canceled`](https://hatang.dev/docs/reference/api/webhook-invoice-canceled) | An e-invoice was canceled. |
| [`invoice.replaced`](https://hatang.dev/docs/reference/api/webhook-invoice-replaced) | An e-invoice was replaced by a new one. |
| [`invoice.adjusted`](https://hatang.dev/docs/reference/api/webhook-invoice-adjusted) | An adjustment invoice was issued. |
| [`invoice.failed`](https://hatang.dev/docs/reference/api/webhook-invoice-failed) | Issuing an e-invoice failed. |
| [`invoice.conflict`](https://hatang.dev/docs/reference/api/webhook-invoice-conflict) | The invoice changed on the provider's side and conflicts with Ha Tang's copy. |

Source: https://hatang.dev/docs/reference/webhooks
