---
title: "Create a webhook subscription (one-time reveal of the signing secret)"
method: POST
path: "/api/v1/webhooks"
tags: ["Webhooks"]
---

# Create a webhook subscription (one-time reveal of the signing secret)

`POST /api/v1/webhooks`

## Request

- Base URL: `https://api.hub-equity.com`
- URL: `https://api.hub-equity.com/api/v1/webhooks`
- Auth: HTTP bearer

Subscribe an https endpoint to `filing.ingested` (a filing whose values are served, filed within the last 45 days) and `restatement.material` (that filing restates an earlier value beyond the materiality wall: medium or high). Optionally scoped to a list of entities; otherwise every entity your workspace can see. Enterprise plan (scope `webhooks.manage`): 401 anonymous, 403 otherwise. At most 10 active subscriptions per workspace (409). Every delivery is a `POST` from 178.104.6.216 of a compact JSON body with sorted keys (`{"id", "event", "created_at", "data"}`) and four headers: `X-Hub-Equity-Event`, `X-Hub-Equity-Delivery` (uuid), `X-Hub-Equity-Timestamp` (unix seconds) and `X-Hub-Equity-Signature: v1=<hex>` where hex is HMAC-SHA256(secret, `<timestamp>.<raw body>`); for 24 hours after a secret rotation the header carries two signatures, `v1=<new>,v1=<old>`, and any match verifies. Verify on the raw bytes with a constant-time comparison and reject a timestamp more than 5 minutes from your clock. Delivery is at-least-once and unordered: deduplicate on `X-Hub-Equity-Delivery`. Answer 2xx within 10 seconds; anything else is retried after 1 min, 5 min, 30 min, 6 h and 24 h, except a 4xx (other than 408 and 429), which is final. Twenty consecutive failed attempts pause the subscription; resume it with `PATCH {"is_active": true}`. Events cover filings ingested after the subscription with a filing date under 45 days; to catch up after an outage, read `GET /entities/changes?since=`.

`filing.ingested` data: `{entity_id, entity_name, filing_id, form_type, fiscal_year, period_end, filing_date, ingested_at, viewer_url}`. `restatement.material` data: the same plus `restatements: [{hub_concept_code, hub_label, fiscal_year, original_value, restated_value, delta_pct, materiality}]` (materiality `medium` or `high`).

## Request body

- WebhookCreateRequest
  - `name` string, required: Human-readable label
  - `url` string, required: https URL of your receiver, on a public host
  - `events` string[]: Events to receive; at least one
  - `entity_ids` string[], nullable: Entity UUIDs to watch (at most 500, a snapshot). Absent or empty: every entity your workspace can see.

## Response `201`

Subscription created; `secret` is shown only here

- ApiResponseWebhookCreatedResponse
  - `success` boolean, required
  - `data` WebhookCreatedResponse
    - `id` string, uuid, required
    - `name` string, required
    - `url` string, required
    - `events` string[], required
    - `entity_ids` string[], nullable
    - `is_active` boolean, required
    - `disabled_reason` string, nullable
    - `consecutive_failures` integer
    - `last_delivery_at` string, date-time, nullable
    - `last_delivery_status` integer, nullable
    - `created_at` string, date-time, required
    - `secret` string, required: The signing secret (`whsec_...`). Shown ONCE, at creation or rotation: store it now. `POST /webhooks/{id}/rotate-secret` replaces it; the previous one keeps signing for 24 hours.
  - `error` ApiError: API error details.
    - `code` string, required
    - `message` string, required
    - `details` object, nullable
  - `meta` ApiMeta: API response metadata. Pagination fields (``page`` / ``per_page`` / ``total``) are populated on collection endpoints only. They are omitted from the payload on single-resource endpoints.
    - `timestamp` string, date-time
    - `request_id` string, nullable
    - `page` integer, nullable
    - `per_page` integer, nullable
    - `total` integer, nullable
  - `links` ApiLinks: HATEOAS-style pagination cursors. Host-relative path + query links for a collection page. Present on collection endpoints, ``null`` fields where a page doesn't exist (``next`` on the last page, ``prev`` on the first). The whole ``links`` object is omitted from single-resource responses (see ``ApiResponse`` serializer).
    - `self` string, nullable
    - `first` string, nullable
    - `prev` string, nullable
    - `next` string, nullable
    - `last` string, nullable

## Other responses

- `403`: The workspace's plan does not include webhooks
- `409`: The workspace already has 10 active subscriptions
- `422`: URL not https or not on a public host, unknown event

---

[API](https://skmtc.dev/hub-equity/apis/hub-equity-api.md) · [All operations](https://skmtc.dev/hub-equity/apis/hub-equity-api/llms.txt) · [OpenAPI document](https://skmtc.dev/hub-equity/apis/hub-equity-api/revisions/e8eeda5dc13e?raw)
