# Webhooks API reference

> Canonical HTML: https://screenrig.ai/docs/api/webhooks
> Source SHA-256: e0c997a2ab813502f0d99a41d2ed1d3d6b26dbaf1a3d7e514c1cce0278ff8422

Webhooks: signed event delivery. This group has 9 operations, each with its authentication, parameters, request body fields, request template and responses. The [API overview](https://screenrig.ai/docs/api.md) covers services, authentication, requests and retries, problem codes and versioning.

Replace uppercase placeholders. Save a schema-valid JSON body in request.json when required. Cookie examples use a local cookie jar from an authenticated session.

## Webhooks: signed event delivery

### GET /api/webhooks

**List Webhooks**

Lists the project's webhook notifications in the webhook shape, oldest first (at most 50); GET /api/notifications lists every channel. The signing secret is never returned here. Requires the project capability; a reports-only credential is refused 403 forbidden, because the response includes receiver URLs.


Operation: listWebhooks.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/webhooks'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The project's webhooks. | WebhookList (application/json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/webhooks

**Create Webhook**

Creates a webhook that receives the project's own durable events as signed HTTPS POSTs. url must be https on a public Internet host: an IP literal or every current DNS answer of the name must be public (private, loopback, link-local, CGNAT, IPv6 outside 2000::/3, documentation, and other reserved ranges are refused with webhook_url_rejected).

The port must be 443 (the default) or 8443, else webhook_url_rejected. The host is resolved and checked again before every delivery and the connection is dialed to exactly the checked address, so a DNS answer that changes later cannot redirect deliveries. event_types lists exact event types (screen.online) or prefixes ending in .* (screen.*).

An organization has at most 50 notifications across every channel (webhook_limit_reached). The server generates the signing secret and returns it only in this answer and in rotate-secret; store it. An exact retry with the same Idempotency-Key returns this same answer, secret included, for twenty-four hours.

Delivery starts with the first event after notification.created.


Operation: createWebhook.

- Authentication: projectBearer
- Request body: WebhookWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| url | required | string | https URL on a public Internet host, port 443 (the default) or 8443. No userinfo or fragment. maxLength: 2048 format: "uri" |
| event_types | required | WebhookEventTypes | Exact event types such as screen.online, or prefixes ending in .* such as screen.* (every type starting with screen.). notification.test is never fanned out by a subscription; it is sent only by POST /api/webhooks/{id}/test. |
| enabled | optional | boolean | default: true |
| description | optional | string | maxLength: 200 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/webhooks'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 201 | Created. secret is shown only here and on rotate-secret. | WebhookWithSecret (application/json); headers: Cache-Control |
| 400 | invalid_request or webhook_url_rejected. | Problem (application/problem+json) |
| 409 | webhook_limit_reached, idempotency_mismatch, or resource_conflict when the project belongs to no organization. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/webhooks/{id}

**Get Webhook**

Requires the project capability; a reports-only credential is refused 403 forbidden, because the response includes receiver URLs.


Operation: getWebhook.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/webhooks/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | One webhook. | Webhook (application/json); headers: ETag |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### PATCH /api/webhooks/{id}

**Update Webhook**

Changes any of url, event_types, enabled, and description (an empty description clears it). A new url is checked like create.

Setting enabled true on a disabled webhook clears its failure state and starts delivery at the event that records the change; events from while it was disabled are not replayed, and deliveries still pending from before the disable are failed with webhook_disabled. Setting enabled false fails its pending deliveries with webhook_disabled.

Appends notification.updated. If-Match guards the revision.


Operation: updateWebhook.

- Authentication: projectBearer
- Request body: WebhookPatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| url | optional | string | maxLength: 2048 format: "uri" |
| event_types | optional | WebhookEventTypes | Exact event types such as screen.online, or prefixes ending in .* such as screen.* (every type starting with screen.). notification.test is never fanned out by a subscription; it is sent only by POST /api/webhooks/{id}/test. |
| enabled | optional | boolean |  |
| description | optional | string | An empty string clears the description. maxLength: 200 |

#### Request template

~~~sh
curl --request PATCH --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/webhooks/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Updated webhook. | Webhook (application/json); headers: ETag |
| 400 | invalid_request or webhook_url_rejected. | Problem (application/problem+json) |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### DELETE /api/webhooks/{id}

**Delete Webhook**

Deletes the webhook and appends notification.deleted. Its pending deliveries are failed with webhook_deleted; its delivery log is no longer addressable and expires with event retention.


Operation: deleteWebhook.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/webhooks/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 204 | Deleted. | no body; headers: Cache-Control |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/webhooks/{id}/rotate-secret

**Rotate Webhook Secret**

Replaces the signing secret and returns the new one; the old secret stops signing at once: every attempt that signs after this commits uses the new secret, and at most one attempt per webhook already in flight may still arrive signed with the old one, so accept both briefly. Appends notification.updated with details.changed [secret].

An exact retry with the same Idempotency-Key returns the same new secret for twenty-four hours.


Operation: rotateWebhookSecret.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/webhooks/RESOURCE_ID/rotate-secret'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The webhook and its new secret. | WebhookWithSecret (application/json); headers: Cache-Control |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/webhooks/{id}/test

**Test Webhook**

Appends a notification.test project event and queues one delivery of it to this webhook only (no other webhook receives notification.test). It works on a disabled webhook, is attempted once without retries, is metered like any delivery, and never changes the webhook's failure state. Poll the deliveries log for its outcome.


Operation: testWebhook.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/webhooks/RESOURCE_ID/test'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | The queued test delivery. | WebhookDelivery (application/json); headers: RateLimit-Policy, RateLimit |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/webhooks/{id}/deliveries

**List Webhook Deliveries**

The webhook's delivery log, newest event first. One row per (webhook, event); retries update the row and GET .../deliveries/{delivery_id}/attempts lists its most recent attempts. Rows are retained like events (the plan's event retention, at least the 24-hour retry window). Pass next_cursor back as before for the next page.


Operation: listWebhookDeliveries.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| before | query | optional | next_cursor of the previous deliveries page. |
| limit | query | optional |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/webhooks/RESOURCE_ID/deliveries'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | One page of deliveries. | WebhookDeliveryList (application/json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/webhooks/{id}/deliveries/{delivery_id}/attempts

**List Webhook Delivery Attempts**

One delivery's attempts, newest first, the most recent 20, kept with the delivery row and for as long. Each has its attempt number, when it finished, the receiver's HTTP status (null when none answered), the error class (null on success) and its duration. Response bodies and headers are never kept. A delivery recorded before attempts were kept lists none.

A delivery that is not this webhook's, or a webhook of another project, is not_found.


Operation: listWebhookDeliveryAttempts.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| delivery_id | path | required | The id of one of the webhook's deliveries. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/webhooks/RESOURCE_ID/deliveries/DELIVERY_ID/attempts'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The delivery's kept attempts. | WebhookDeliveryAttemptList (application/json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |


## Sitemap

See the [public documentation sitemap](https://screenrig.ai/sitemap.md).
