---
id: webhooks
slug: /apis/webhooks
title: 'Webhooks (api/webhook)'
description: 'Subscribe to asset created and changed events and have Agility push them to your system instead of polling.'
sidebar_label: 'Overview'
sidebar_position: 1
tags: ['api', 'webhooks', 'events', 'integration']

draft: false
---

A **webhook subscription** tells Agility to send an HTTP request to a URL you control whenever
something you care about happens. It is the push alternative to polling
[`query.v1`](/apis/query-v1) on a timer.

Subscriptions can be as broad or as narrow as you like: every Story status change in the
instance, or only status changes on Stories in one project that are owned by one person.

## How It Fits Together

A subscription has two halves:

1. **Where to deliver** — the `url`, an optional `authorizationHeader`, an optional `secret` for
   payload signing, and a `webhookId` you choose to identify it.
2. **What to deliver** — an `eventTypes` array. Each entry describes a class of change using the
   same vocabulary as a query: `from`, `attributes`, `filter`, `with`, `userContext` and
   `select`.

When a matching change happens, Agility `POST`s a JSON body describing the event — who did it,
what changed, and a projection of the asset shaped by your `select`.

## Operations

| Operation                                        | Endpoint                              |
| ------------------------------------------------ | ------------------------------------- |
| [Create a subscription](/apis/webhooks/create)    | `POST ~/api/webhook`                  |
| [Query subscriptions](/apis/webhooks/query)       | `GET ~/api/webhook`                   |
| [Update a subscription](/apis/webhooks/update)    | `POST ~/api/webhook/:id`              |
| [Delete a subscription](/apis/webhooks/delete)    | `POST ~/api/webhook/delete/:id`       |
| [Check delivery status](/apis/webhooks/status)    | `GET ~/api/webhook/status/:id`        |

Supporting reference:

- [Event types](/apis/webhooks/event-types) — how to describe what you want to be notified about
- [Webhook payload](/apis/webhooks/payload) — what arrives at your endpoint, and how to verify it

:::warning These are `POST` endpoints, including delete
There is no `PUT`, `PATCH` or `DELETE` verb anywhere in this API. Update is a `POST` to the
subscription URL, and **delete is a `POST` to `.../api/webhook/delete/:id`** — the id is in the
path *after* the word `delete`. This is unusual, it is deliberate, and sending
`DELETE ~/api/webhook/:id` will get you a `405`. See [delete](/apis/webhooks/delete).
:::

## Permissions

Every endpoint in this API requires the **`ManageConfig`** privilege — the same administrative
privilege that governs instance configuration. Webhook subscriptions are an instance-level
integration surface, not a per-user one.

## Instance Limits and Configuration

| Limit                                | Default   | Effect                                                              |
| ------------------------------------- | --------- | -------------------------------------------------------------------- |
| Maximum **enabled** subscriptions     | unlimited | Creating past the limit fails.                                        |
| Delivery retry attempts               | `5`       | Attempts per delivery — see [retries and automatic disabling](#retries-and-automatic-disabling). |

These are instance configuration, adjustable by an administrator on
on-premise installations.

:::note The "limit of 5 subscriptions" is out of date
The original reference stated that "in the first release of webhook subscriptions, your instance
can have up to 5 enabled webhook subscriptions at a time". That restriction has since been
lifted: `MaxActiveSubscriptions` now defaults to unlimited and the cap only applies where an
administrator has explicitly configured one.

If you do hit a configured cap, creation fails with a message naming the limit. Check the
setting rather than assuming the number is 5. The unrelated `5` that *is* still real is the
retry count.
:::

Webhooks can also be disabled instance-wide. When they are, every endpoint in this section
returns `404` rather than an explanatory error — a blanket `404` from a correctly-formed request
usually means the feature is switched off, not that your subscription is missing.

## Delivery Semantics

Understand these before you build against webhooks:

- **Non-`200` means failure.** Responding with anything other than `200 OK` counts as a failed
  delivery. Return `200` as soon as you have durably accepted the payload; do your processing
  afterwards.
- **Repeated failure eventually disables the subscription** — through the retry and
  probation model [below](#retries-and-automatic-disabling). If a webhook has "stopped
  working", check its [`retryCount`](/apis/webhooks/query) and its
  [delivery receipts](/apis/webhooks/status) first.
- **Order is not guaranteed.** Events may arrive out of order. Every event carries a
  `sequenceId` so you can reorder them yourself. Do not infer ordering from arrival time.
- **Delivery is at-least-once in effect.** A retry after a response you actually processed will
  redeliver the event. Make your handler idempotent, keyed on `webhookId` plus `sequenceId`.
- **Your endpoint must be reachable from the Agility instance.** For cloud-hosted instances that
  means publicly routable, or reachable through whatever egress path your instance is configured
  with.

## Retries and Automatic Disabling

Two separate mechanisms, often conflated:

**Retries happen per delivery.** Each delivery is attempted up to **5 times**
(default), with exponential backoff starting at **5 seconds** — so the gaps
run 5, 10, 20, 40, 80 seconds. A successful delivery resets the retry count
to zero.

**Disabling happens on probation.** When a delivery exhausts its retries:

1. The **first** exhausted delivery puts the subscription on **probation**
   (default **12 hours**) and sends a failure-warning email to the owner.
2. A **successful** delivery during probation clears it — with a recovery
   email. Nothing was lost but the failed deliveries themselves.
3. A delivery that exhausts its retries **after the probation window has
   elapsed** — meaning the endpoint has been failing for the whole window —
   **disables the subscription**, with a final notification email.

A disabled subscription stops producing [receipts](/apis/webhooks/status);
fix the receiver, then re-enable via [update](/apis/webhooks/update). The
retry count, backoff quantum and probation length are configurable on
on-premise instances — ask your administrator if your instance behaves
differently from the defaults above.

## Related Topics

- [Event types](/apis/webhooks/event-types)
- [Webhook payload](/apis/webhooks/payload)
- [Activity stream](/apis/activity-stream) — the pull equivalent, for reading history
- [Query API (query.v1)](/apis/query-v1) — the `select` and `filter` vocabulary
- [API conventions](/apis/conventions)
