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

# Subscribing to Notifications

> Create and manage webhook subscriptions to receive push notifications for job state changes

Webhook subscriptions let you receive HTTP POST notifications when jobs change state, eliminating the need to poll the jobs API. This guide walks through creating, receiving, and managing webhook subscriptions.

<Info>
  For background on how webhooks work and when to use them, see [Webhooks](/concepts/webhooks/overview). For complete API details, see the [Webhooks API Reference](/api-reference/webhooks/list-webhook-subscriptions).
</Info>

## Prerequisites

* An API token with Webhooks `read` and `write` [permissions](/reference/security/permissions)
* An HTTPS endpoint ready to receive POST requests
* Familiarity with [job types](/reference/architecture/job-types) and [job states](/guides/sdk/tracking-jobs#job-states)

## What you'll learn

* How to create a job webhook subscription with filters
* How to receive and verify webhook events
* How to list, inspect, and archive subscriptions

***

## Creating a job webhook subscription

Use the `POST /webhooks` endpoint to create a subscription. Set the `type` to `webhook_subscription_jobs` and provide your endpoint URL.

<Steps>
  ### Choose your filters

  Decide which job events you want to receive. You can filter by job IDs, job types, job states, or any combination.

  | Scenario                         | Filters                                                    |
  | -------------------------------- | ---------------------------------------------------------- |
  | All completed materialized views | `job_types: ["materialize-view"]`, `states: ["completed"]` |
  | Track a specific job             | `job_ids: ["<job-id>"]`                                    |
  | All failed jobs                  | `states: ["failed"]`                                       |
  | All state changes (no filter)    | Omit `job_ids`, `job_types`, and `states`                  |

  ### Create the subscription

  ```bash theme={null}
  curl -X POST https://api.narrative.io/webhooks \
    -H "Authorization: Bearer $NIO_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "type": "webhook_subscription_jobs",
      "url": "https://your-app.example.com/webhooks/narrative",
      "name": "completed_matviews",
      "job_types": ["materialize-view"],
      "states": ["completed", "failed"]
    }'
  ```

  ### Store the secret

  The response includes a `secret` field — a UUID used to verify that incoming requests are from Narrative. Store this value securely.

  ```json theme={null}
  {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "secret": "8b6dba8c-9a8b-4a3a-8469-9dc7a33c3e17",
    "status": "active",
    "companyId": 42,
    "jobTypes": ["materialize-view"],
    "jobStates": ["completed", "failed"],
    "url": "https://your-app.example.com/webhooks/narrative",
    "createdAt": "2025-04-29T16:12:45.123456",
    "updatedAt": "2025-04-29T16:12:45.123456"
  }
  ```
</Steps>

***

## Receiving events

When a job matches your subscription's filters, Narrative sends an HTTP POST to your URL with a JSON payload.

### Example event payload

```json theme={null}
{
  "context": {
    "companyId": "42"
  },
  "eventTimestamp": "2025-04-29T16:15:30.456789",
  "eventType": {
    "value": "job_state_change"
  },
  "idempotencyKey": {
    "value": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  },
  "payload": {
    "value": {
      "jobId": "d35a12c1-7a2f-46b9-8d60-9e0a2a84c205",
      "jobType": "materialize-view",
      "state": "completed"
    }
  }
}
```

Your endpoint should return a `2xx` status code to acknowledge receipt.

***

## Handling idempotency

Each event includes an `idempotencyKey` — a unique identifier for that specific delivery. If Narrative delivers the same event more than once (for example, due to a network retry), the idempotency key remains the same.

Use the idempotency key to detect duplicates:

1. When you receive an event, check if you've already processed that `idempotencyKey`
2. If yes, return `200 OK` without processing again
3. If no, process the event and store the key

***

## Managing subscriptions

### List all subscriptions

```bash theme={null}
curl https://api.narrative.io/webhooks \
  -H "Authorization: Bearer $NIO_API_TOKEN"
```

Returns an array of all webhook subscriptions for your company, including both `active` and `archived` subscriptions.

### Get a specific subscription

```bash theme={null}
curl https://api.narrative.io/webhooks/{webhook_subscription_id} \
  -H "Authorization: Bearer $NIO_API_TOKEN"
```

### Archive a subscription

Archiving stops event delivery but retains the subscription record.

```bash theme={null}
curl -X DELETE https://api.narrative.io/webhooks/{webhook_subscription_id} \
  -H "Authorization: Bearer $NIO_API_TOKEN"
```

<Warning>
  Archiving is permanent — there is no way to reactivate an archived subscription. Create a new subscription if you need to resume notifications.
</Warning>

***

## Best practices

| Practice                  | Why                                                                                                                     |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **Respond quickly**       | Return a `2xx` response immediately, then process the event asynchronously. Slow responses may cause delivery timeouts. |
| **Store the secret**      | Save the `secret` from the creation response. You cannot retrieve it later.                                             |
| **Implement idempotency** | Use the `idempotencyKey` to deduplicate events in case of retries.                                                      |
| **Use HTTPS**             | Webhook URLs must use HTTPS to protect payloads in transit.                                                             |
| **Filter narrowly**       | Subscribe only to the job types and states you need. This reduces noise and processing overhead.                        |

***

## Related content

<CardGroup cols={2}>
  <Card title="Webhooks" icon="bell" href="/concepts/webhooks/overview">
    How webhooks work and when to use them
  </Card>

  <Card title="Webhook Event Reference" icon="list-check" href="/reference/webhooks/event-reference">
    Complete field reference for subscription requests and responses
  </Card>

  <Card title="Tracking Job Status" icon="spinner" href="/guides/sdk/tracking-jobs">
    Poll-based alternative for monitoring job progress
  </Card>

  <Card title="API Keys" icon="key" href="/account-settings/api-keys">
    Create and manage API tokens for webhook access
  </Card>
</CardGroup>
