Scan webhooks
Receive every scan of a code as a signed JSON POST, within a second of it happening.
A scan webhook is an https endpoint of yours that receives one scan.created event per scan. Set it per code under Tracking & Advanced settings, Send scans to a webhook, or for many codes at once through the API. Scan webhooks are included with the Business plan.
Delivery
POST /hooks/kuikcode HTTP/1.1
Content-Type: application/json
User-Agent: KuikCode-Webhook/1.0
X-KuikCode-Event: scan.created
X-KuikCode-Signature: sha256=3f1c...
{
"event": "scan.created",
"timestamp": "2026-09-06T14:02:11.482Z",
"code": { "id": "Ab3dE9xYz", "name": "Spring menu" },
"scan": {
"scanId": "01J9...",
"country": "US",
"region": "NJ",
"city": "Montclair",
"deviceType": "mobile",
"os": "iOS",
"browser": "Safari",
"language": "en",
"timezone": "America/New_York",
"destination": "https://example.com/spring-menu",
"matchedRule": null
}
}code.id is the code's nine-character short code. matchedRule names the Smart Rule that decided the scan, or is null. A test fired from the dashboard or the API carries an extra "test": true.
Deliveries are sent once, right after the redirect, with a ten-second timeout. There are no retries, so answer quickly with any 2xx and do your processing afterwards. A code can feed up to five endpoints.
Verify the signature
Each endpoint has a secret, shown after the first save as Copy secret and returned by the API. The signature is sha256= followed by the hex HMAC-SHA256 of the raw request body.
import { createHmac, timingSafeEqual } from "node:crypto"
export function verify(rawBody, header, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex")
return header?.length === expected.length && timingSafeEqual(Buffer.from(expected), Buffer.from(header))
}Compute the HMAC over the body bytes exactly as received, before parsing JSON.
Endpoint rules
- https only, on a public hostname. Private hosts, IP literals,
localhostand URLs with credentials are refused when you save. - One endpoint per URL per workspace. Pointing another code at the same URL connects it to the existing endpoint and secret.
- Send test in the editor, or
POST /webhooks/{id}/test, fires a sample event and reports the status your server returned.
Managing endpoints through the API
GET /webhooks, POST /webhooks with the codes to connect, PATCH to change the URL or the exact set of connected codes, DELETE to remove. See the reference.