Logo just-scan-it
Developers · 3 min read

Webhook Integration Guide

Table of Contents

Webhook Integration Guide

Just Scan It can send webhook deliveries for these events:

  • scan
  • first_scan
  • form_submit
  • file_download

Create a webhook

You can create webhooks in three places:

  1. Dashboard: Account -> Webhooks
  2. API: POST /api/v1/webhooks
  3. MCP: create_webhook

Each webhook belongs to a team and subscribes to one or more events.

Event payload shape

All deliveries use this top-level JSON envelope:

{
  "event": "scan",
  "timestamp": "2026-04-20T10:00:00+00:00",
  "data": {
    "qr_code_id": 42
  }
}

The data object depends on the event:

  • scan: QR code, scan id, device, country, resolved URL, referer and UTM fields
  • first_scan: same payload as scan, but only for the first successful scan
  • form_submit: landing page id, title, slug, recipient email and submitted form fields
  • file_download: shared file id, QR code id, slug, title, scan id, device, country and download URL

Signature verification

Each delivery includes an X-Webhook-Signature header.

The signature is computed as:

hash_hmac('sha256', raw_json_body, webhook_secret)

PHP

$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $payload, $secret);

if (! hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Invalid signature');
}

Node.js

import crypto from 'node:crypto';

const expected = crypto
  .createHmac('sha256', secret)
  .update(rawBody)
  .digest('hex');

if (! crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
  throw new Error('Invalid signature');
}

Python

import hmac
import hashlib

expected = hmac.new(
    secret.encode("utf-8"),
    raw_body,
    hashlib.sha256,
).hexdigest()

if not hmac.compare_digest(expected, signature):
    raise ValueError("Invalid signature")

Retry behavior

Webhook deliveries retry up to 3 attempts with exponential backoff:

  1. after 1 minute
  2. after 5 minutes
  3. after 30 minutes

The dashboard and admin area both show response codes, response bodies and retry state.

Testing a webhook

Use one of these options:

  • Dashboard action: Send test delivery
  • API endpoint: POST /api/v1/webhooks/{id}/test

Test deliveries use event type test and include a small payload with test: true.

API endpoints

  • GET /api/v1/webhooks
  • POST /api/v1/webhooks
  • PUT /api/v1/webhooks/{id}
  • DELETE /api/v1/webhooks/{id}
  • GET /api/v1/webhooks/{id}/deliveries
  • POST /api/v1/webhooks/{id}/test

Required scopes:

  • webhooks:read
  • webhooks:write

MCP tools

These MCP tools are available when enabled on the API key page:

  • create_webhook
  • list_webhooks
  • delete_webhook