Skip to main content

Webhooks

Webhooks let your client (or any HTTPS endpoint) get notified when something changes on a user's Shuuka account, instead of polling the MCP server.

Webhooks are part of the Shuuka MCP surface — manage them through the webhooks:read and webhooks:write scopes.

Subscribe​

{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "subscribe_webhook",
"arguments": {
"url": "https://my-app.example/shuuka/webhook",
"events": ["report.created", "verification.granted", "link.click_threshold"],
"description": "Brand-safety pipeline"
}
}
}

Response:

{
"id": "whk_...",
"url": "https://my-app.example/shuuka/webhook",
"events": [...],
"secret": "shk_whk_..."
}

The secret is shown once. Store it. Use tools/call with list_my_webhooks to enumerate, and delete_webhook to remove.

Event payload​

Every delivery is a JSON POST with these headers:

X-Shuuka-Event: report.created
X-Shuuka-Event-Id: evt_...
X-Shuuka-Delivery-Id: dlv_...
X-Shuuka-Signature: t={timestamp},v1={hmac_sha256}
Content-Type: application/json

Body:

{
"id": "evt_...",
"type": "report.created",
"created_at": "2026-05-02T14:00:00Z",
"data": {
/* event-specific shape */
}
}

Verifying signatures​

The X-Shuuka-Signature header is t=<unix-timestamp>,v1=<hex-hmac-sha256>.

Compute:

v1 = hex( hmac_sha256(secret, t + "." + raw_body) )

Reject any request where:

  • t is more than 5 minutes in the past or future.
  • v1 does not match.

Node.js example:

const crypto = require('crypto');

function verify(req, secret) {
const sig = req.headers['x-shuuka-signature'] || '';
const parts = Object.fromEntries(sig.split(',').map(p => p.split('=')));
const t = parseInt(parts.t, 10);
if (!t || Math.abs(Date.now()/1000 - t) > 300) return false;

const expected = crypto
.createHmac('sha256', secret)
.update(`${t}.${req.rawBody}`)
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1 || '')
);
}

Retries​

  • Failed deliveries (HTTP 4xx that aren't 410, all 5xx, network errors) retry with exponential backoff: 1m, 5m, 30m, 2h, 12h, 24h.
  • HTTP 410 Gone is treated as a permanent unsubscribe — the webhook is auto-disabled.
  • After 6 failures over 24 hours the webhook is disabled. The user sees it in Settings → Webhooks with a "Re-enable" button.

Event types​

EventWhen it fires
report.createdA fake-account report is filed against the owner.
report.resolvedA fake-account report is closed (confirmed or rejected).
verification.grantedA verification request transitions to verified.
verification.revokedA previously granted verification is revoked.
link.click_thresholdA link crosses the user-configured click threshold.
link.createdA new link or Smart Route is created.
link.updatedA link's destination or settings change.
link.deletedA link is deleted.
theme.appliedThe user applies a new theme.
identity.score_changedThe user's identity score crosses a threshold.
risk.score_changedThe user's risk score crosses a threshold.
plan.changedThe user upgrades, downgrades, or cancels a plan.

data shapes follow the same schema as the corresponding MCP tool's structuredContent.

Delivery log​

Use list_webhook_deliveries (under webhooks:read) or open Settings → Webhooks → Deliveries to see every attempt with status, timestamps, request body, and response body. Logs retain 30 days.

Plan availability​

Webhooks are available on all plans. Free users are subject to the same monthly visit traffic gate that applies to other Pro features — over the threshold, deliveries pause until next month or an upgrade.

Next​