Hooks
Subscribe a URL to scan and lead events, and unsubscribe it.
Plan: Pro and up (webhooks).
These endpoints let your own code subscribe a URL to events and remove it again. For a webhook you set up by hand, use the Webhooks card on the Integrations page instead. Both send the same webhook payloads.
Subscribe
POST /api/v1/hooks
| Body field | Required | Notes |
|---|---|---|
url | Yes | Where to send events. Must be a public https URL on the standard port, with a hostname rather than an IP address |
event | Yes | One of scan.created, lead.created, scan.threshold |
codeId | No | Only send events for this code. Leave out or null for every code |
threshold | For scan.threshold | Fire once when total human scans reach this number, from 1 to 100,000,000 |
curl -X POST https://dynaqr.app/api/v1/hooks \
-H "Authorization: Bearer qrm_live_your_key" \
-H "Content-Type: application/json" \
-d '{"url": "https://hooks.example.com/qr-leads", "event": "lead.created"}'
Returns 201:
{ "id": "3e9a1c5b-7d2f-4b8e-a0c1-d2e3f4a5b6c7", "secret": "whsec_…" }
Keep the id to unsubscribe. A workspace can have up to 10 webhooks in total, counting the ones set up in the dashboard, and the 11th returns 402 PLAN_LIMIT. A URL that isn't allowed, or a scan.threshold hook without a threshold, returns 409 INVALID_STATE. A codeId that isn't in the workspace returns 404 NOT_FOUND.
The response includes the hook's signing secret (whsec_…). It is returned only once, so store it to verify signatures.
Unsubscribe
DELETE /api/v1/hooks/{id}
Returns 204 with no body, or 404 NOT_FOUND if the hook doesn't exist.
A hook created here is also switched off automatically when its URL answers 410 Gone.