# Webhooks

Register a URL and PakDataHub will **POST to it when a series you care about gets new data**, so you don't have to poll.

**Plan limits (active webhooks):** Free 0 - Developer 3 - Pro 50 - Business unlimited.

## `POST /v1/webhooks` (key required) / session

| Field | Type | Notes |
|---|---|---|
| `url` | string | Required, `https://` (or `http://`) |
| `series_id` | string | Subscribe to one series |
| `module` | string | ...or to every series in a module (e.g. `forex`) |

At least one of `series_id` or `module` is required.

```bash
curl -X POST -H "X-API-Key: pk_live_xxx" -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hooks/pakdata", "series_id": "rates.kibor.3m"}' \
  "https://api.pakdatahub.com/v1/webhooks"
```

```json
{ "success": true, "id": "3f1c...", "signing_secret": "whsec_...",
  "note": "store this signing secret now - it is shown only once" }
```

## `GET /v1/webhooks` - `DELETE /v1/webhooks/{id}`

List your webhooks, or delete one.

## The delivery

When an ingestion run adds or changes data for a matching series, you receive:

```http
POST /hooks/pakdata HTTP/1.1
Content-Type: application/json
X-PakData-Signature: 5d41402abc4b2a76b9719d911017c592...
```

```json
{ "event": "series.updated",
  "delivered_at": "2026-09-24T09:12:03Z",
  "series": [ { "series_id": "rates.kibor.3m",
                "latest": { "date": "2026-09-24", "value": 11.75, "dims": { "side": "offer" } } } ] }
```

- `latest` is the newest stored observation of each matched series. Fetch the full update with [`/v1/series/{id}`](https://pakdatahub.com/docs/api-series.md).
- It fires on **incremental** updates only, never for historical backfills.
- Delivery is best-effort with a short timeout. Respond with 2xx quickly and do the work asynchronously.

## Verifying the signature

`X-PakData-Signature` is the hex HMAC-SHA256 of the **raw request body**, keyed with your `signing_secret`:

```python
import hmac, hashlib

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header)
```

```js
import crypto from "node:crypto";
const ok = crypto.timingSafeEqual(
  Buffer.from(crypto.createHmac("sha256", secret).update(rawBody).digest("hex")),
  Buffer.from(req.headers["x-pakdata-signature"]));
```

---
Source: https://pakdatahub.com/docs/api-webhooks - PakDataHub docs index: https://pakdatahub.com/docs/llms.txt
