Receive and verify webhooks
Accept signed JSON events into a durable inbox before acknowledging them. Deduplicate deliveries and process accepted events separately.
Register a JSON destination
Register a public HTTPS endpoint in the dashboard, choose its events, and keep the signing secret on the receiving server. Up to five endpoints can be registered per account. Choose JSON for a programmatic receiver: Slack and Discord destinations receive formatted messages instead.
Deploy behind HTTPS and send test.ping from the dashboard. Redirects are not followed. Return 2xx within 10 seconds. The signing secret is distinct from an API key; management uses the signed-in dashboard, not a new API-key management endpoint.
Envelope and verification
| Field/header | Contract |
|---|---|
| id | Delivery identifier in the JSON body. Deduplicate this value; correlate payment/finding IDs separately. |
| type | Event name. Retain unknown future types without guessing their business effect. |
| created_at | ISO envelope timestamp, not the source timestamp of every record. |
| data | Event-specific object with optional/additional fields. |
| monocrawl-delivery / monocrawl-event | Delivery ID and event name in headers. |
| monocrawl-signature | t=<Unix seconds>,v1=<HMAC-SHA256 hex>. The signed bytes are timestamp + dot + original request body. |
Verify before JSON parsing with a constant-time digest comparison. The example rejects timestamps outside five minutes; synchronize server clocks. Parsing and re-stringifying changes signed bytes. A valid signature proves possession of the secret, not the truth of source content.
Run the receiver example
# Supply MONOCRAWL_WEBHOOK_SECRET through your secret manager. # INBOX_DIR must be on persistent storage when deployed. INBOX_DIR=./webhook-inbox PORT=3001 node webhook-receiver.mjs
The example listens on loopback behind your HTTPS reverse proxy at /webhooks/monocrawl. It verifies raw bytes, bounds the body, validates the envelope and atomically stores one file per delivery ID before returning 202. Duplicates return 200 without replacing the saved event. Storage failures return 503 for retry. The secret is never logged.
This is an inbox receiver, not a worker. Your application must process persisted events and record completion. For multiple instances use shared durable storage with a unique delivery-ID constraint. Apply the business update and processed-event record in one transaction, or use an idempotent queue. A file alone cannot make an external side effect exactly once.
Retries, duplicates and rotation
Delivery is at-least-once. Successive retry delays are 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours, then delivery is marked failed. Scheduling adds time. Ten consecutive failed deliveries auto-disable the destination. Fix it, inspect history and re-enable; do not assume that re-enabling replays every failed event.
A lost 2xx can cause another attempt after successful storage. There is no ordering guarantee. Payment or finding IDs can recur across event types; business effects may need a separate domain-level deduplication key.
Rotation is immediate. Prepare the receiver, rotate in the dashboard, update the secret promptly and test. There is no documented old-secret grace period. Reconcile missed business state from Billing or monitor history.