# Webhook events reference

> Every webhook event type Qualy can send, when it fires, and what the payload looks like.

This is the catalog of every event type Qualy can deliver to a [webhook endpoint](/docs/webhooks.md). Subscribe to only the events your integration needs — you choose them when you register the endpoint.

---

## Event naming

Every event name follows the pattern `resource.action`, where `action` is one of `create`, `update`, or `delete`:

- `create` — a new object was created.
- `update` — an existing object changed (including status transitions like a payment moving from `due` to `paid-full`).
- `delete` — an object was removed.

A single API request can produce several events. For example, when a customer completes a payment you may receive `transactions.create`, `paymentIntents.update`, `splitIntents.create`, `splitIntents.update`, and `transactions.update`. Events are **not** guaranteed to arrive in order — see [event ordering](/docs/webhooks.md#event-ordering).

---

## Available events

| Event | Fires when |
| --- | --- |
| `contacts.create` | A contact is created. |
| `contacts.update` | A contact's details change. |
| `paymentIntents.create` | A payment intent is created. |
| `paymentIntents.update` | A payment intent changes — most importantly its `status` (e.g. `due` → `paid-full`, `paid-partial`, `overdue`). |
| `paymentIntents.delete` | A payment intent is deleted. |
| `transactions.create` | A transaction is created against a payment intent (a payment attempt begins). |
| `transactions.update` | A transaction changes — most importantly its `status` (e.g. `processing` → `succeeded` or `failed`). |
| `splitIntents.create` | A payment split is created (the portion of a payment owed to a partner). See [payment splits](/docs/guides/creating-payment-splits.md). |
| `splitIntents.update` | A payment split changes. |
| `splitIntents.delete` | A payment split is removed. |
| `partnerships.create` | A partnership is created. |
| `partnerships.update` | A partnership's details change. |

> **Note — Watch status, not just the event**
>
> For payments, the event you almost always care about is a `status` change carried on `paymentIntents.update` and `transactions.update`. Read `data.status` to decide what to do, rather than assuming an `update` means "paid-full".

---

## Payload structure

Every delivery has the same envelope. Note this is **different** from the REST API's `data` envelope — here `data` holds the object that changed, and it sits alongside delivery metadata:

| Field | Description |
| --- | --- |
| `event` | The event type, e.g. `transactions.create`. |
| `data` | The object that changed. Its shape mirrors that resource's REST representation. |
| `version` | API version of the payload, e.g. `v1`. |
| `tenantId` | The Qualy account that generated the event. Useful when one endpoint serves multiple tenants. |
| `messageId` | A unique ID for this delivery. Use it to deduplicate and guard against replays. |

### Example: `transactions.create`

```json
{
  "event": "transactions.create",
  "data": {
    "_id": "653fc551d14abfe63d4fd48b",
    "paymentIntent": "653fc43fd14abee63d4fcb63",
    "amount": 87700,
    "contact": "65351dfbde8f28fb4a757585",
    "transactionType": "charge",
    "method": "ZAI_PAYTO",
    "currency": "AUD",
    "status": "processing",
    "number": 1906,
    "createdAt": "2023-10-30T15:01:37.187Z"
  },
  "version": "v1",
  "tenantId": "eu1-nonhozilnwbeuo1qkdftdkqg",
  "messageId": "8960447655358365"
}
```

### Example: `paymentIntents.update`

A status change from `due` to `paid-full`:

```json
{
  "event": "paymentIntents.update",
  "data": {
    "_id": "653fc43fd14abee63d4fcb63",
    "status": "paid-full",
    "amount": 100000,
    "due": 0,
    "currency": "AUD",
    "contact": "65351dfbde8f28fb4a757585",
    "number": "2216"
  },
  "version": "v1",
  "tenantId": "eu1-nonhozilnwbeuo1qkdftdkqg",
  "messageId": "8960447655358366"
}
```

---

## Handling events

Because a single action fans out into several events and they can arrive out of order, treat webhooks as **signals to reconcile**, not as the source of truth. A robust handler:

1. **Verifies the signature** — see [verifying signatures](/docs/webhooks.md#security--veryfing-signatures).
2. **Deduplicates on `messageId`** — you may receive the same event more than once.
3. **Returns `2xx` quickly** — acknowledge first, then do the work asynchronously.
4. **Re-fetches the object** when it needs the full, current state — for example `GET /v1/payment-intents/{id}` after a `paymentIntents.update`.

See [Setting up webhooks](/docs/webhooks.md) for the endpoint setup, signature verification, retry behavior, and best practices.
