Webhooks
Receive push notifications when your watchlist changes
Instead of polling the API for changes, register a webhook URL and TickerDB will POST updates to you after each daily and weekly pipeline run. Subscribe to the events you care about and get structured, field-level diffs delivered automatically.
Event Types
| Event | Description | Default |
|---|---|---|
watchlist.changes | Structured field-level diffs for tickers on your watchlist. Only fires when at least one field has changed. | Enabled on creation |
data.ready | Simple notification that fresh data has been computed and is available via the API. | Opt-in |
Webhook deliveries do not consume your API request quota.
GET — List webhooks
Returns all registered webhooks for your account. The secret field is never included in GET responses.
Response Fields
| Field | Type | Description |
|---|---|---|
webhooks | array | Array of webhook objects |
webhooks[].id | string | Webhook ID |
webhooks[].url | string | Delivery URL |
webhooks[].events | object | Subscribed event configuration |
webhooks[].active | boolean | Whether the webhook is active |
webhooks[].created_at | string | ISO 8601 creation timestamp |
webhooks[].updated_at | string | ISO 8601 last update timestamp |
webhook_count | integer | Number of registered webhooks |
webhook_limit | integer | Max webhooks for your tier |
POST — Register a webhook
Register a new webhook URL. The secret is returned only on creation — save it immediately. Use it to verify webhook signatures.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS URL to receive webhook payloads |
events | object | No | Event subscriptions. Defaults to {"watchlist.changes": true} |
PUT — Update a webhook
Update the URL, event subscriptions, or active status of an existing webhook.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Webhook ID to update |
url | string | No | New HTTPS URL |
events | object | No | Updated event subscriptions |
active | boolean | No | Enable or disable the webhook |
DELETE — Remove a webhook
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Webhook ID to delete |
Verifying Webhook Signatures
Every webhook delivery includes an X-Webhook-Signature header containing an HMAC-SHA256 signature of the request body, signed with your webhook's secret. Always verify this signature before processing the payload.
| Header | Description |
|---|---|
X-Webhook-Signature | HMAC-SHA256 hex digest of the raw request body |
X-Webhook-Event | Event type: watchlist.changes or data.ready |
Content-Type | application/json |
User-Agent | TickerDB-Webhook/2.0 |
Verification — Python
The secret returned from POST /v1/webhooks is a 64-character hex string. Decode it to raw bytes before using it as the HMAC key.
Verification — Node.js
Webhook Payloads
watchlist.changes
Delivered after each pipeline run when at least one field has changed on a watchlist ticker. Contains structured diffs showing exactly which fields changed and their previous/current values.
This payload uses the same changes object as GET /v1/watchlist/changes. Technical fields stay top-level (rsi_zone, macd_state, momentum_direction, divergence_detected, trend_direction, volume_ratio_band, accumulation_state, squeeze_active, extreme_condition, breakout_type). Pro-only stock fundamentals keep a dotted fundamentals.* prefix (fundamentals.valuation_zone, fundamentals.analyst_consensus, fundamentals.earnings_proximity, and related fields). Plus webhooks omit all fundamental diffs. Entitlement is checked again at delivery time, so queued payloads also honor downgrades before delivery.
Webhook deliveries are already scoped to a single run, so unlike the pull endpoint they do not include a ticker_context block.
When a band field changes, the change object includes stability context on Plus Pro tiers: stability, periods_in_current_state, flips_recent, and flips_lookback. These describe the new band value's stability at the time of the change. Not included on Starter tier.
data.ready
Simple notification that fresh data has been computed. Useful for triggering downstream fetches without polling.
Delivery History
Every webhook delivery is logged in the database. You can view delivery status, HTTP response codes, and errors from the dashboard or the API.
Dashboard
Open Dashboard → Webhooks, click history next to any webhook row, and the last 20 deliveries expand inline. Status chips show sent (green), failed (red), pending / delivering / retrying (yellow — queued, in flight, or waiting for an automatic retry after a failed attempt), and no changes / skipped (grey — nothing to deliver: no diff for your watchlist, or the watchlist was empty).
GET — List delivery history
Returns delivery records for your account, sorted by run_date descending.
| Parameter | Type | Description |
|---|---|---|
webhook_id | string | Filter to a single webhook |
limit | integer | Max records to return. Default 50, max 200. |
| Field | Type | Description |
|---|---|---|
deliveries[].id | string | Delivery record ID |
deliveries[].webhook_id | string | Webhook ID |
deliveries[].event_type | string | watchlist.changes or data.ready |
deliveries[].timeframe | string | daily or weekly |
deliveries[].run_date | string | Pipeline run date (YYYY-MM-DD) |
deliveries[].status | string | pending, delivering, retrying, sent, endpoint_error (your endpoint never returned a usable response), failed (delivery could not be attempted on our side), no_changes, or skipped |
deliveries[].attempt_count | integer | Number of delivery attempts made |
deliveries[].http_status | integer | null | Last HTTP response code from your endpoint |
deliveries[].error | string | null | Last error summary (no secrets) |
deliveries[].started_at | string | ISO 8601 timestamp of first attempt |
deliveries[].completed_at | string | null | ISO 8601 timestamp of final attempt |
Retry & Deactivation
Webhook delivery is handled by a Cloudflare Queue consumer. Failed deliveries are marked retrying and retried automatically with exponential backoff — up to 4 attempts total. After all retries are exhausted the delivery is marked endpoint_error (your endpoint never returned a usable response). The separate failed status is reserved for the rare case where the delivery could not be attempted on our side.
| Behaviour | Detail |
|---|---|
| Retry count | 4 attempts total (initial + 3 retries) |
| Backoff | Managed by Cloudflare Queue (exponential, ~30 s base) |
| Timeout | 10 seconds per attempt |
| Auto-deactivation | After 7 consecutive undelivered runs (endpoint_error or failed, with no successful delivery in between) the webhook is automatically paused. Re-enable it from the dashboard or via PUT /v1/webhooks with {"active": true}. |
| Integrity gate | When INTEGRITY_POLICY=block_publish the pipeline skips webhook delivery entirely for that run. No delivery rows are created and no retries are attempted. |
| No-changes skip | For watchlist.changes, if no fields changed for your watchlist tickers, no delivery is attempted for that run. The record appears as no_changes in delivery history; an empty watchlist appears as skipped. |