> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mixpeek.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Respond to Mixpeek events without polling

Webhooks notify your systems when ingestion, enrichment, or retrieval events occur. The Engine writes events to MongoDB, the work scheduler dispatches them, and Mixpeek delivers HTTP POST requests (or channel-specific messages) until acknowledged.

## Event Flow

```
Engine → MongoDB (webhook_events) → work scheduler → delivery task → HTTP POST → Your endpoint
```

* Events persist until the worker receives a 2xx response.
* Retries use exponential backoff; failures remain in MongoDB for inspection.
* Delivery includes cache scopes so you can invalidate selectively.

## Common Event Types

| Event | Trigger | Payload Highlights |
| - | - | - |
| `collection.documents.written` | Engine finishes writing documents to [MVS](https://mixpeek.com/mvs) | `collection_id`, `document_ids`, `index_signature` |
| `object.created` | Object registered in a bucket | `bucket_id`, `object_id`, metadata snapshot |
| `documents.updated.batch` | A batch of documents finished updating | `collection_id`, `document_ids` |
| `cluster.execution.completed` | Clustering run finished | `cluster_id`, `run_id`, artifact URIs |
| `taxonomy.updated` | Taxonomy definition changed | `taxonomy_id` |
| `resource.executed` | Any primitive finished a run: a retriever, taxonomy, cluster, collection, storage connection, alert or trigger | `resource_type`, `resource_id`, `execution_id`, `duration_ms`, `cost_credits`, `output_count`, `feature_uris`, `model_versions`, `dropped_documents`, `pinned_revision` |
| `resource.execution_failed` | The same run failed | the same fields, with `error_class` set |

Use `/api-reference/webhooks/list-webhooks` to see the full catalog.

To receive runs of some primitives only, add `resource_types` to the webhook,
for example `"resource_types": ["retriever"]` with
`"event_types": ["resource.executed", "resource.execution_failed"]`. The filter
applies to every event type: for the per-kind types the resource type is the
first part of the name (`cluster` in `cluster.execution.completed`).

A batch is how a collection runs, so a finished batch sends one
`resource.executed` event per collection it wrote to, with `resource_type`
`collection`, the collection's id as `resource_id` and the batch id as
`execution_id`. The batch's credits are split evenly across those events, so
they add up to the batch's charge. Earlier events named the
batch itself (`resource_type` `batch`, the batch id as `resource_id`); to
receive batch runs now, filter on `"resource_types": ["collection"]`.
A sync run also sends an event for the storage connection it read through
(`resource_type` `storage_connection`).

A retriever's `resource.executed` payload carries `pinned_revision.config`: the
exact `stages`, `collection_ids` and `input_schema` that ran. Sending them to
`POST /v1/retrievers/execute` (with `collection_ids` as `collection_identifiers`)
and the same inputs reruns that version of the retriever.

> [!NOTE]
>
> The same events are readable as an ordered, resumable log via the
> [Change Feed](https://docs.mixpeek.com/docs/operations/change-feed.md). Webhooks push; the change feed lets
> you pull, which is what a consumer that can be offline needs.

## Create a Webhook

```bash theme={null}
curl -sS -X POST "$MP_API_URL/v1/organizations/webhooks" \
  -H "Authorization: Bearer $MP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_name": "prod-notifications",
    "event_types": [
      "collection.documents.written",
      "cluster.execution.completed"
    ],
    "channels": [
      {
        "channel": "webhook",
        "configs": {
          "url": "https://hooks.example.com/mixpeek",
          "headers": { "X-Mixpeek-Secret": "super-secret"},
          "signing_secret": "a-long-random-string",
          "timeout": 10
        }
      }
    ]
  }'
```

Mixpeek also supports Slack, email, and SMS channels via channel-specific configs.

## Payload Structure

```json theme={null}
{
  "event_id": "evt_123",
  "event_type": "collection.documents.written",
  "occurred_at": "2025-10-28T10:03:22Z",
  "namespace_id": "ns_prod",
  "subject": {
    "type": "collection",
    "id": "col_products"
  },
  "metadata": {
    "document_count": 100,
    "collection_id": "col_products",
    "index_signature": "sig_xyz789"
  },
  "cache_scope": {
    "scope": "collection",
    "collection_id": "col_products"
  }
}
```

Use `event_id` for deduplication and store payloads for auditing.

## Security & Reliability

* Require HTTPS endpoints; reject plaintext URLs.
* Every delivery names its event in two headers: `X-Mixpeek-Event` (the event type, such as `alert.triggered`) and `X-Mixpeek-Event-Id` (the event's id, the same on a retry, so you can drop duplicates). The body is the event payload. A header you set on the webhook with the same name replaces ours.
* Set `signing_secret` and verify the `X-Mixpeek-Signature` header on every delivery (below). A static header such as `X-Mixpeek-Secret` also works, but it can be replayed.
* Respond quickly (\<10s). Offload heavy work to background jobs and return `200`.
* Use idempotent handlers; Mixpeek may retry on failure or timeout.
* Monitor webhook delivery with your logging pipeline; correlate by `event_id`.

### Verify a delivery

When a webhook channel has a `signing_secret`, every delivery carries:

```
X-Mixpeek-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256>
```

The HMAC is computed over the string `<t>.` followed by the raw request body, keyed by your secret.
Recompute it over the raw bytes you received (before any JSON parsing), compare in constant time, and
reject a timestamp more than 300 seconds from your clock so a captured delivery cannot be replayed.

```python theme={null}
import hashlib, hmac, time

def verify(raw_body: bytes, header: str, secret: str, tolerance_s: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    t, sig = parts.get("t", ""), parts.get("v1", "")
    if not t.isdigit() or abs(time.time() - int(t)) > tolerance_s:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)
```

The secret is stored encrypted and is never returned: API reads show `redacted` in its place.

## Operational Tips

1. Subscribe only to the events you need to reduce noise.
2. Combine webhook notifications with the Tasks API for full status context.
3. Use cache scopes to invalidate retriever caches efficiently (`collection`, `namespace`, or `key`).
4. Store webhook definitions in infrastructure-as-code so environments stay consistent.
5. Alert on sustained non-2xx responses and fix the endpoint quickly. After 30 failed deliveries in a row spanning at least two hours, Mixpeek pauses the webhook (`is_active: false`, with `paused_reason`) and alerts your organization; any successful delivery resets the count. Update the webhook to turn it back on.
6. Use a real endpoint. Create and update refuse URLs whose host can never receive a delivery, such as `example.com` and `.test` or `.invalid` names.

## Inbound Webhooks (from upstream providers)

In addition to **outbound** webhooks (Mixpeek → your endpoint), Mixpeek can receive **inbound** webhooks from external systems to trigger ingestion and lifecycle events.

### Sync lifecycle webhooks

Some sync integrations support inbound webhooks for **lifecycle events** (delete, update) on previously synced assets:

| Provider | Endpoint | Events handled | Use case |
| - | - | - | - |
| **Mux** | `POST /v1/webhooks/mux/{connection_id}` | `video.asset.deleted`, `video.asset.updated` | Cascade-delete bucket objects when a Mux asset is deleted. Re-evaluate metadata filters when asset metadata changes. See [Mux integration → Webhooks](https://docs.mixpeek.com/docs/integrations/object-storage/mux.md#webhooks-delete-and-update). |

These webhooks share common properties:

* Scoped to a single connection by URL path (no Authorization header needed).
* Signatures verified with `hmac.compare_digest` and provider-specific HMAC schemes.
* Unhandled events acknowledged with `200` and `handled: false` so retries stop cleanly.

## References

* [Create Webhook](https://docs.mixpeek.com/docs/api-reference/webhooks/create-webhook.md)
* [List Webhooks](https://docs.mixpeek.com/docs/api-reference/webhooks/list-webhooks.md)
* [Delete Webhook](https://docs.mixpeek.com/docs/api-reference/webhooks/delete-webhook.md)
* [Mux cascade-delete webhook](https://docs.mixpeek.com/docs/integrations/object-storage/mux.md#cascade-delete-via-webhooks)
* [Tasks](https://docs.mixpeek.com/docs/processing/tasks.md) – track the jobs that trigger webhook events
* [Security](https://docs.mixpeek.com/docs/operations/security.md) – authentication, tenancy, and secret management


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.