> ## 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.

# Alerts

> Monitor ingested content with retriever-powered alerts and real-time notifications

![Alerts: batch ingested documents checked against retriever in parallel, matches trigger webhook, Slack, or email notifications](https://mintcdn.com/mixpeek/pDBzbsnRaRIThJZv/assets/mixpeek-alerts.svg?fit=max\&auto=format\&n=pDBzbsnRaRIThJZv\&q=85\&s=9f6293ecc7fd647de5ac0335782a077f)

Alerts let you attach retriever pipelines to collections so that every ingested document is automatically checked against your search criteria. When matches are found, Mixpeek notifies your configured channels (webhook, Slack, or email) or processes the matching objects into a collection you define.

## How It Works

1. **Create** a retriever that defines your search criteria (e.g., semantic similarity, attribute filters)
2. **Create** an alert referencing that retriever, with notification channels configured
3. **Attach** the alert to a collection via `alert_applications` with input mappings
4. **Ingest** documents — alerts execute automatically during post-processing (Phase 3)
5. **Receive** notifications when matches are found

### Alerting on a Taxonomy-Assigned Category

If your goal is "notify my team when content lands in a specific category" rather than a semantic search, the retriever in step 1 does not need embeddings at all. Taxonomies run in Phase 1, before Alerts in Phase 3 (see [Architecture](#architecture) below), so by the time your alert's retriever executes, any category a taxonomy assigned is already a field on the document. A retriever with a single filter stage is enough:

```json theme={null}
{
  "stage_name": "attribute_filter",
  "stage_type": "filter",
  "config": {
    "stage_id": "attribute_filter",
    "parameters": {
      "field": "metadata.category",
      "operator": "eq",
      "value": "counterfeit"
    }
  }
}
```

Replace `metadata.category` with the `field_path` your taxonomy's `enrichment_fields` writes (see [Taxonomies](https://docs.mixpeek.com/docs/enrichment/taxonomies.md)), and `value` with the category you want to alert on. Use the `in` operator instead of `eq` to alert on more than one category at once. See [Attribute Filter](https://docs.mixpeek.com/docs/retrieval/stages/attribute-filter.md) for the full operator list.

## Architecture

Alerts execute during the post-processing pipeline after document ingestion completes:

| Phase | System | Purpose |
| - | - | - |
| 1 | Taxonomies | Vector-based classification |
| 2 | Clusters | Document grouping |
| **3** | **Alerts** | **Retriever execution + notifications** |
| 4 | Retriever Enrichments | Field write-back |

### Parallel Execution

Within a single alert, document-level retriever calls execute **in parallel** as independent Ray tasks. If a batch ingests 100 documents, all 100 retriever calls fan out simultaneously rather than running sequentially. Results are aggregated after all calls complete, and a single notification is sent if any document produced matches.

Multiple alerts on the same collection execute **sequentially** to avoid race conditions in notification delivery.

## Configuration

### Create an Alert

```python Python theme={null}
from mixpeek import Mixpeek

client = Mixpeek(api_key="your-api-key")

alert = client.alerts.create(
    name="safety_content_monitor",
    description="Detect potentially harmful content on ingestion",
    retriever_id="ret_safety_classifier",
    notification_config={
        "channels": [
            {
                "channel_type": "webhook",
                "config": {"url": "https://your-app.com/webhooks/alerts"}
            },
            {
                "channel_type": "slack",
                "config": {"channel": "#content-alerts"}
            }
        ],
        "include_matches": True,
        "include_scores": True
    },
    enabled=True
)
```

```bash cURL theme={null}
curl -X POST https://api.mixpeek.com/v1/alerts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-Namespace: your-namespace" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "safety_content_monitor",
    "retriever_id": "ret_safety_classifier",
    "notification_config": {
      "channels": [
        {"channel_type": "webhook", "config": {"url": "https://your-app.com/webhooks/alerts"}},
        {"channel_type": "slack", "config": {"channel": "#content-alerts"}}
      ],
      "include_matches": true,
      "include_scores": true
    },
    "enabled": true
  }'
```

### Conditions

`trigger_on` decides what the alert's retriever has to return for the alert to fire. The same condition is applied wherever the alert runs: when a document is written, in the batch pipeline, and on the periodic sweep.

| `trigger_on` | Fires when |
| - | - |
| `results` (default) | the retriever returns at least one row, or at least `min_count` rows when that is set, and the first row meets `threshold` when that is set |
| `no_results` | the retriever returns nothing |

Each run of the retriever is judged on its own rows. A retriever that ends in an aggregate stage returns its groups as rows, so `min_count: 3` means three groups and a `threshold` reads the first group's metrics. A run that errors or comes back degraded gives no verdict, so it neither fires a `no_results` alert nor clears one that is firing.

A `threshold` compares one field of the first row the retriever returns. The field can be a top-level key such as `score` or an aggregate column, a dot path such as `metadata.price`, or a bare name that is looked up in the row's metadata. The operators are `gt`, `gte`, `lt`, `lte`, `eq` and `ne`. A missing field, or a value of a different type, does not fire.

```json theme={null}
{
  "name": "error_burst",
  "retriever_id": "ret_errors_by_service",
  "trigger_on": "results",
  "min_count": 3,
  "notification_config": {"channels": [{"channel_type": "slack", "config": {"channel": "#ops"}}]}
}
```

```json theme={null}
{
  "name": "top_match_is_close",
  "retriever_id": "ret_prohibited_content",
  "threshold": {"field": "score", "operator": "gte", "value": 0.92},
  "notification_config": {"channels": [{"channel_type": "email"}]}
}
```

Sending `min_count` or `threshold` with `trigger_on: no_results` is refused with a 422 that names the field.

### Cooldown and Re-notify

`cooldown_seconds` keeps an alert from notifying again within that many seconds of its last notification. A fire inside the cooldown is recorded with `suppressed: cooldown` and sends nothing. The cooldown is shared by every path, so an alert that fired at ingest stays quiet on the sweep until the cooldown has passed.

`renotify_interval_seconds` applies to the sweep. While the condition stays true, the alert notifies again at this interval, and never sooner than the cooldown. Leave it unset to be told once each time the condition starts to hold.

`last_fired_at` on the alert shows when it last notified.

### Alert Inputs

`inputs` are the retriever inputs the alert runs with. The sweep has no document to map from, so it runs the retriever with exactly these. At ingest, inputs mapped from the document are laid over them, so a document field wins where both set the same key.

This is how a time window carries over to the sweep. A retriever filter on `{{time_ago(minutes=INPUT.window_minutes)}}` with `"inputs": {"window_minutes": 60}` on the alert looks back one hour whichever path runs it.

### Attach to a Collection

Attach alerts to collections via `alert_applications` when creating or updating a collection:

```python Python theme={null}
client.collections.create(
    collection_name="user_uploads",
    source={"type": "bucket", "bucket_ids": [bucket_id]},
    feature_extractor={
        "feature_extractor_name": "text_extractor",
        "version": "v1",
        "input_mappings": {"text": "content"},
    },
    alert_applications=[
        {
            "alert_id": alert.alert_id,
            "execution_mode": "on_ingest",
            "input_mappings": [
                {
                    "input_key": "query_text",
                    "source": {
                        "source_type": "document_field",
                        "path": "content"
                    }
                }
            ]
        }
    ]
)
```

```bash cURL theme={null}
curl -X POST https://api.mixpeek.com/v1/collections \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-Namespace: your-namespace" \
  -H "Content-Type: application/json" \
  -d '{
    "collection_name": "user_uploads",
    "source": {"type": "bucket", "bucket_ids": ["bucket_id"]},
    "feature_extractor": {
      "feature_extractor_name": "text_extractor",
      "version": "v1",
      "input_mappings": {"text": "content"}
    },
    "alert_applications": [
      {
        "alert_id": "alert_id",
        "execution_mode": "on_ingest",
        "input_mappings": [
          {
            "input_key": "query_text",
            "source": {"source_type": "document_field", "path": "content"}
          }
        ]
      }
    ]
  }'
```

### Input Mappings

Input mappings connect document fields to retriever input parameters:

| Source Type | Description | Example |
| - | - | - |
| `document_field` | Extract value from the ingested document using a dot-notation path | `{"source_type": "document_field", "path": "metadata.category"}` |
| `constant` | Pass a fixed value to the retriever | `{"source_type": "constant", "value": "safety_check"}` |

### Execution Modes

| Mode | Behavior |
| - | - |
| `on_ingest` | Execute automatically when documents are ingested (default) |
| `scheduled` | Execute on a schedule (does not trigger on ingest) |
| `on_demand` | Execute only when manually triggered |

## Notification Channels

### Webhook

```json theme={null}
{
  "channel_type": "webhook",
  "config": {"url": "https://your-app.com/webhooks/alerts"}
}
```

The webhook receives a JSON payload with alert details, matched documents, and scores.

### Slack

```json theme={null}
{
  "channel_type": "slack",
  "config": {"channel": "#alerts-channel"}
}
```

### Email

```json theme={null}
{
  "channel_type": "email",
  "config": {"to": ["alerts@your-company.com"]}
}
```

### Batch

The `batch` channel processes the bucket objects behind the matching documents into a collection you define.

```json theme={null}
{
  "channel_type": "batch",
  "config": {"collection_id": "col_alert_frames"}
}
```

After the batch that fired the alert completes, Mixpeek submits one batch per source bucket. Each batch holds the objects whose documents matched. The output documents keep lineage back to those objects. To copy capture fields such as `captured_at` onto the output documents, list them in the target collection's `field_passthrough` (see [Feature extractors](https://docs.mixpeek.com/docs/processing/feature-extractors.md)).

| Key | Required | Description |
| - | - | - |
| `collection_id` | Yes | Collection that receives the processed objects. Mixpeek also accepts `target_collection_id`. |
| `bucket_id` | No | Limits the batch to objects from this bucket. Defaults to the bucket in each matching document's lineage. |
| `dedup_strategy` | No | `skip` (default), `replace`, or `force`. With `skip`, Mixpeek processes an object once, even when it matches again. |

The target collection must meet three conditions:

* It reads from a bucket, and that bucket holds the matched objects.
* It is user-defined. Names that start with `_` or `mxp_` are system collections.
* It differs from the collection the alert watches. Processing back into it would fire the alert again.

Mixpeek validates the config keys when you save the alert. A missing `collection_id`, an unknown key, or an invalid `dedup_strategy` returns a 422. Mixpeek checks the three conditions after the alert fires.

The batch that fired the alert must leave the target out. A batch created without `collection_ids` covers every enabled collection that reads the bucket, the target included. `POST /v1/buckets/{bucket_identifier}/objects?auto_process=true` and `POST /v1/buckets/{bucket_identifier}/objects/batch?auto_process=true` create their batch this way. The target then holds the objects' documents before the alert fires, so with `dedup_strategy: skip` the batch the channel submits skips those objects. To exercise the channel, create the batch with `POST /v1/buckets/{bucket_identifier}/batches` and list the collections to process in `collection_ids`, leaving the target out.

Attach a second alert to the target collection to monitor the processed output. It is an ordinary retriever alert with the same channels as any other.

Each batch task records its origin in `additional_data.triggered_by_alert`. The record holds the alert ID, the execution ID, the matching document IDs, and each object's capture fields (`captured_at`, `segment_index`, `segment_duration_seconds`) when the source is an RTSP feed.

If the batch cannot start, for example because the target collection reads from another collection, Mixpeek sends an `alert.execution.failed` webhook event with the reason. Mixpeek retries transient errors and stops after 3 attempts.

## Monitoring Executions

Track alert execution history to monitor performance and debug issues:

```python Python theme={null}
executions = client.alerts.list_executions(alert_id="alert_id")

for execution in executions.results:
    print(f"Execution {execution.execution_id}: "
          f"triggered={execution.triggered}, "
          f"matches={execution.match_count}, "
          f"duration={execution.duration_ms}ms")
```

```bash cURL theme={null}
curl https://api.mixpeek.com/v1/alerts/ALERT_ID/executions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-Namespace: your-namespace"
```

## Default Alerts

Every new organization gets a set of system alerts, one per built-in condition, across all its namespaces. They email the organization's `notifications_email`, which is set to the signup address when the organization is created and can be changed with `PATCH /v1/organizations`.

| Alert | Fires when | Ships |
| - | - | - |
| Batch failed | An ingestion batch ends in a failed state | enabled |
| Batch stalled | A batch makes no progress past the stall threshold | enabled |
| Batch error rate | The share of failed objects in a batch passes the threshold | enabled |
| Dead-letter queue growth | Sync objects exhaust their retries and land in the dead-letter queue | enabled |
| Provider quota exhausted | A batch hits a hard provider quota | enabled |
| Retriever returning no results | A retriever's recent executions all return zero results | enabled |
| Retriever getting no interactions | A retriever ran at least ten times in the window and none of its results drew an interaction. Quiet in a namespace that reports no interactions | enabled |
| Collection went stale | A collection that holds documents has had the same count for a week | enabled |
| Ingest stalled | A bucket that was receiving objects has received none for a day | enabled |
| Spend reached 80 percent of the monthly budget | Month-to-date spend reaches 80 percent of the monthly budget. Quiet with no budget set | enabled |
| Collection went empty | A collection that held documents drops to zero | disabled |
| Collection drift | A collection's document count drops sharply | disabled |
| Retriever latency spike | A retriever's p95 latency rises well above its baseline | disabled |
| Retriever accuracy regression | A retriever's latest evaluation metric falls below its floor | disabled |
| Retriever alignment regression | A retriever's benchmark ndcg drops sharply from the prior run | disabled |
| Task queue degraded | A processing queue's backlog is not draining | disabled |

List them with `GET /v1/organizations/alerts`, and turn one on or off with `PATCH /v1/organizations/alerts/{alert_identifier}` and `{"enabled": true}` or `false`. Provisioning finds each default by name, so a default you change or disable is not recreated.

Each condition is evaluated by a saved retriever in the namespace, named `system-<condition>` (for example `system-collection-stale`) and tagged `system-alert`, over the system collection that records it. Run it like any retriever to see which collections, batches or retrievers meet the condition now. Mixpeek never overwrites a shipped retriever you have edited.

## Comparison with Other Enrichment Types

| Feature | Taxonomies | Clusters | Alerts | Retriever Enrichments |
| - | - | - | - | - |
| Purpose | Vector-based classification | Document grouping | Notifications on match | Arbitrary retriever pipelines |
| Output | Label + score fields | Cluster assignments | Webhook/Slack/email notifications, or a batch into a collection | Configurable field write-back |
| Phase | 1 | 2 | 3 | 4 |
| Execution | Parallel per document | Batch | Parallel per document | Sequential per document |
| Use cases | Face matching, entity linking | Segmentation, pattern discovery | Content monitoring, safety checks | LLM classification, cross-collection joins |


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