# Operator Events

Operator events let you subscribe to lifecycle events from your Outpost deployment, including delivery failures, destination disabling, retry exhaustion, and subscription changes.

## Configuration

Enable operator events by specifying topics and configuring a sink.

### Topics

### Managed
Configure operator event topic subscriptions through [Hookdeck Monitoring settings](https://dashboard.hookdeck.com/settings/project/monitoring) or Config API.

### Self-Hosted
Set `OPERATION_EVENTS_TOPICS` to a comma-separated list of topics, or `*` for all topics:
```
OPERATION_EVENTS_TOPICS=*

```

Available topics:

| Topic | Trigger |
| --- | --- |
| `alert.destination.consecutive_failure` | Consecutive failure count reaches 50%, 70%, 90%, or 100% of `ALERT_CONSECUTIVE_FAILURE_COUNT` |
| `alert.destination.disabled` | Destination auto-disabled at 100% failure threshold |
| `alert.attempt.exhausted_retries` | Delivery exhausts all retry attempts (deduplicated per event+destination) |
| `tenant.subscription.updated` | Destination created/updated/deleted and tenant topics or destination count changed |

### Managed
Operator event delivery and topic selection are managed through [Hookdeck Monitoring settings](https://dashboard.hookdeck.com/settings/project/monitoring).

### Self-Hosted
If `OPERATION_EVENTS_TOPICS` is empty or unset, operator events are disabled. If topics are configured but no sink is set, Outpost fails to start.

### Sinks

Configure exactly one sink to receive events.

### Managed
Configure sinks for operator event forwarding in [Hookdeck Monitoring settings](https://dashboard.hookdeck.com/settings/project/monitoring).

### Self-Hosted
Four sink types are supported:#### HTTPSends events as `POST` requests to a URL. If `OPERATION_EVENTS_HTTP_SIGNING_SECRET` is set, requests are signed with HMAC-SHA256.
```
OPERATION_EVENTS_HTTP_URL=https://example.com/outpost-events
OPERATION_EVENTS_HTTP_SIGNING_SECRET=your-secret

```

The HTTP sink sends signatures in `X-Outpost-Signature`:* Format: `v0=<hex>`
* Algorithm: HMAC-SHA256 over the raw JSON bodyVerification example:
```python
import hashlib
import hmac

expected = hmac.new(signing_secret.encode(), body, hashlib.sha256).hexdigest()
assert signature_header == f"v0={expected}"

```

#### AWS SQS
```
OPERATION_EVENTS_AWS_SQS_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789/outpost-events
OPERATION_EVENTS_AWS_SQS_ACCESS_KEY_ID=AKIA...
OPERATION_EVENTS_AWS_SQS_SECRET_ACCESS_KEY=...
OPERATION_EVENTS_AWS_SQS_REGION=us-east-1
OPERATION_EVENTS_AWS_SQS_ENDPOINT=          # optional, for local dev

```

#### GCP Pub/Sub
```
OPERATION_EVENTS_GCP_PUBSUB_PROJECT_ID=my-project
OPERATION_EVENTS_GCP_PUBSUB_TOPIC_ID=outpost-events
OPERATION_EVENTS_GCP_PUBSUB_CREDENTIALS={"type":"service_account",...}

```

#### RabbitMQ
```
OPERATION_EVENTS_RABBITMQ_SERVER_URL=amqp://guest:guest@localhost:5672
OPERATION_EVENTS_RABBITMQ_EXCHANGE=outpost-events

```

## Event Envelope

All operator events share this envelope:

```json
{
  "id": "unique-event-id",
  "topic": "alert.destination.consecutive_failure",
  "time": "2025-06-01T12:00:00Z",
  "deployment_id": "my-deployment",
  "tenant_id": "tenant_123",
  "data": {}
}

```

| Field | Description |
| --- | --- |
| `id` | Unique event identifier |
| `topic` | Event topic |
| `time` | ISO 8601 timestamp |
| `deployment_id` | Deployment ID (if configured) |
| `tenant_id` | Tenant associated with the event (if applicable) |
| `data` | Topic-specific payload |

## Event Payloads

### `alert.destination.consecutive_failure`

Emitted when a destination reaches 50%, 70%, 90%, or 100% of `ALERT_CONSECUTIVE_FAILURE_COUNT`.

```json
{
  "tenant_id": "tenant_123",
  "event": {},
  "attempt": {},
  "destination": {
    "id": "des_456",
    "tenant_id": "tenant_123",
    "type": "webhook",
    "topics": ["order.created"],
    "disabled_at": null
  },
  "consecutive_failures": {
    "current": 50,
    "max": 100,
    "threshold": 50
  }
}

```

### `alert.destination.disabled`

Emitted when a destination is auto-disabled after reaching the 100% threshold (only when `ALERT_AUTO_DISABLE_DESTINATION=true`).

```json
{
  "tenant_id": "tenant_123",
  "destination": {
    "id": "des_456",
    "tenant_id": "tenant_123",
    "type": "webhook",
    "topics": ["order.created"],
    "disabled_at": "2025-06-01T12:00:00Z"
  },
  "disabled_at": "2025-06-01T12:00:00Z",
  "reason": "consecutive_failure",
  "event": {},
  "attempt": {}
}

```

### `alert.attempt.exhausted_retries`

Emitted when a delivery exhausts all retry attempts. Deduplicated per event+destination within `ALERT_EXHAUSTED_RETRIES_WINDOW_SECONDS`.

```json
{
  "tenant_id": "tenant_123",
  "event": {},
  "attempt": {},
  "destination": {
    "id": "des_456",
    "tenant_id": "tenant_123",
    "type": "webhook",
    "topics": ["order.created"],
    "disabled_at": null
  }
}

```

### `tenant.subscription.updated`

Emitted when destination changes affect tenant-level subscribed topics or destination count.

```json
{
  "tenant_id": "tenant_123",
  "topics": ["order.created", "order.updated"],
  "previous_topics": ["order.created"],
  "destinations_count": 3,
  "previous_destinations_count": 2
}

```

## Delivery Guarantees

`alert.*` topics are delivered with an at-least-once guarantee. For other topics (e.g. `tenant.subscription.updated`), delivery is on a best-effort basis with up to 3 attempts. Consumers should deduplicate using the event `id`.

## Related Configuration

### Managed
Alert threshold and destination auto-disable behavior are managed through [Hookdeck Monitoring settings](https://dashboard.hookdeck.com/settings/project/monitoring).

### Self-Hosted
| Config | Description | Default |
| --- | --- | --- |
| `ALERT_CONSECUTIVE_FAILURE_COUNT` | Number of consecutive failures before the 100% threshold | `100` |
| `ALERT_AUTO_DISABLE_DESTINATION` | Auto-disable destinations at the 100% threshold | `false` |
| `ALERT_EXHAUSTED_RETRIES_WINDOW_SECONDS` | Deduplication window for exhausted retry alerts (seconds) | `3600` |