Skip to main content
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.
For background on how webhooks work and when to use them, see Webhooks. For complete API details, see the Webhooks API Reference.

Prerequisites

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.
1
Choose your filters
2
Decide which job events you want to receive. You can filter by job IDs, job types, job states, or any combination.
3
ScenarioFiltersAll completed materialized viewsjob_types: ["materialize-view"], states: ["completed"]Track a specific jobjob_ids: ["<job-id>"]All failed jobsstates: ["failed"]All state changes (no filter)Omit job_ids, job_types, and states
4
Create the subscription
5
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"]
  }'
6
Store the secret
7
The response includes a secret field — a UUID used to verify that incoming requests are from Narrative. Store this value securely.
8
{
  "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"
}

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

{
  "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

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

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.
curl -X DELETE https://api.narrative.io/webhooks/{webhook_subscription_id} \
  -H "Authorization: Bearer $NIO_API_TOKEN"
Archiving is permanent — there is no way to reactivate an archived subscription. Create a new subscription if you need to resume notifications.

Best practices

PracticeWhy
Respond quicklyReturn a 2xx response immediately, then process the event asynchronously. Slow responses may cause delivery timeouts.
Store the secretSave the secret from the creation response. You cannot retrieve it later.
Implement idempotencyUse the idempotencyKey to deduplicate events in case of retries.
Use HTTPSWebhook URLs must use HTTPS to protect payloads in transit.
Filter narrowlySubscribe only to the job types and states you need. This reduces noise and processing overhead.