---
title: "Update a webhook"
method: PATCH
path: "/webhooks/{id}"
tags: ["Webhooks"]
---

# Update a webhook

`PATCH /webhooks/{id}`

## Request

- Base URL: `https://optionsbell.com/api/v1`
- URL: `https://optionsbell.com/api/v1/webhooks/{id}`
- Auth: API key in header `X-API-Key`

Partial update: send any subset of `name`, `url`, `tickers`,
`criteria`, `notify_interval_min`, `active_hours`, `is_active`.
`tickers` replaces the whole list; `criteria` keys are merged into
the stored criteria (set a key to `null` to drop that constraint);
`active_hours` needs both `start` and `end`.

Sending a `url` or `is_active: true` **resumes a paused webhook** and
resets `consecutive_failures` to 0. `is_active: false` stops
evaluation without deleting anything. Changing `tickers` does not
reset the baseline: contracts already unusual on a newly added ticker
are delivered on the next evaluation.

## Request body

- WebhookPatch: Any subset of the create fields. Omitted fields are unchanged.
  - `url` string, uri: Same rules as on create. Also resumes a paused webhook.
  - `tickers` string[]: Replaces the whole list.
  - `name` string
  - `criteria` WebhookCriteria: Optional contract filters on top of the always-on base floor (Vol/OI >= 1.5, OI >= 100, premium >= $25,000). `null` (or 0) means no extra constraint. In responses `type` is `c`, `p` or `all`; in requests `call`/`calls` and `put`/`puts` are accepted as well.
    - `type` 'c' | 'p' | 'all': Calls only, puts only, or both.
    - `min_voloi` number, nullable: Vol/OI ratio floor.
    - `min_premium` number, nullable: Estimated premium floor in USD (last_price * volume * 100).
    - `min_iv` number, nullable: Implied volatility floor in percent (60 = 60%).
    - `max_dte` integer, nullable: Max days to expiration (floats are floored).
    - `min_volume` number, nullable: Contract volume floor.
    - `min_oi` number, nullable: Open interest floor.
  - `notify_interval_min` 10 | 30 | 60 | 240 | 480 | 1440
  - `active_hours` WebhookActiveHours: Daily window in which the webhook is evaluated, `HH:MM` 24h in America/New_York. `end` is exclusive; a window may wrap past midnight (e.g. 20:00 -> 04:00). `start` equal to `end` never fires.
    - `start` string, required
    - `end` string, required
  - `is_active` boolean: `true` also resumes a paused webhook; `false` stops evaluation.

## Response `200`

The updated webhook

- Webhook: A registered webhook. Never carries the signing secret.
  - `id` string, required
  - `name` string, required
  - `url` string, uri, required
  - `tickers` string[], required: Upper-cased, de-duplicated. Max 150.
  - `criteria` WebhookCriteria, required: Optional contract filters on top of the always-on base floor (Vol/OI >= 1.5, OI >= 100, premium >= $25,000). `null` (or 0) means no extra constraint. In responses `type` is `c`, `p` or `all`; in requests `call`/`calls` and `put`/`puts` are accepted as well.
    - `type` 'c' | 'p' | 'all': Calls only, puts only, or both.
    - `min_voloi` number, nullable: Vol/OI ratio floor.
    - `min_premium` number, nullable: Estimated premium floor in USD (last_price * volume * 100).
    - `min_iv` number, nullable: Implied volatility floor in percent (60 = 60%).
    - `max_dte` integer, nullable: Max days to expiration (floats are floored).
    - `min_volume` number, nullable: Contract volume floor.
    - `min_oi` number, nullable: Open interest floor.
  - `notify_interval_min` 10 | 30 | 60 | 240 | 480 | 1440, required: Minimum minutes between two deliveries.
  - `active_hours` object, required: Daily window in which the webhook is evaluated, `HH:MM` 24h in America/New_York. `end` is exclusive; a window may wrap past midnight (e.g. 20:00 -> 04:00). `start` equal to `end` never fires.
    - `start` string, required
    - `end` string, required
    - `timezone` 'America/New_York'
  - `is_active` boolean, required: false: not evaluated at all (set via PATCH).
  - `paused_at` string, date-time, nullable, required: Set when the webhook was paused after 20 consecutive failed deliveries; null otherwise. Paused webhooks are still evaluated but not delivered to.
  - `consecutive_failures` integer, required: Failed `unusual_activity` deliveries in a row; reset to 0 by any successful delivery, a successful test, or a PATCH with `url` / `is_active: true`.
  - `last_run_at` string, date-time, nullable, required: Last evaluation (UTC), whether or not anything was delivered.
  - `last_delivered_at` string, date-time, nullable, required: Last successful `unusual_activity` delivery (UTC). The cooldown (`notify_interval_min`) counts from here.
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required

## Other responses

- `400`: Invalid input: `INVALID_PARAM`, `INVALID_URL`, `INVALID_SYMBOLS` - same rules as on create.
- `401`: Missing or invalid API key: `MISSING_API_KEY`, `INVALID_API_KEY_FORMAT`, `INVALID_API_KEY`. No rate-limit headers.
- `403`: Key is valid but has no usable subscription: `NO_ACTIVE_SUBSCRIPTION` (no Personal plan on the key) or `SUBSCRIPTION_INACTIVE` (past due, paused, expired, or canceled past its period end). No rate-limit headers.
- `404`: `NOT_FOUND`: no webhook with this id on your subscription (app-created alerts are not reachable through this API).
- `429`: Rate limit exceeded: `RATE_LIMIT_MINUTE` (60/min) or `RATE_LIMIT_DAY` (4,000/day). Carries both rate-limit header sets and `Retry-After` (seconds until the exhausted window opens again). Rejected requests count against the minute window, so back off instead of retrying immediately.
- `500`: Unexpected server error: `INTERNAL_ERROR`. The body and the X-Request-Id header carry the request id.
- `503`: Authentication backend unreachable: `AUTH_UNAVAILABLE`. Retry later. No rate-limit headers.

---

[API](https://skmtc.dev/optionsbell/apis/optionsbell-options-flow-api.md) · [All operations](https://skmtc.dev/optionsbell/apis/optionsbell-options-flow-api/llms.txt) · [OpenAPI document](https://skmtc.dev/optionsbell/apis/optionsbell-options-flow-api/revisions/42db9bcaa61f?raw)
