All docs

Developer

Webhooks

Receive Rank Sonar events in your own systems via signed HTTP POSTs.

Webhooks let you receive Rank Sonar events in your own infrastructure. Available on Studio and above.

When webhooks fire

Webhooks fire whenever a matching event is created — same trigger conditions as in-app alerts. Configure which events you want via the dispatch rules.

Registering a webhook endpoint

  1. Visit Settings → Webhooks → New endpoint
  2. URL: an HTTPS URL on your infrastructure (HTTP not allowed)
  3. Events: tick which event types to receive
  4. Click Create
  5. Copy the signing secret (shown once — store it in your secret manager)

Payload format

{
  "id": "evt_01H8Z9X...",
  "type": "rank_change",
  "severity": "warning",
  "workspaceId": "ws_01H8Z9...",
  "appId": "app_01H8Z9...",
  "occurredAt": "2026-05-03T08:14:22Z",
  "payload": {
    "keyword": "meal planner",
    "store": "apple",
    "countryCode": "us",
    "previousRank": 5,
    "newRank": 18,
    "delta": -13
  }
}

The exact payload shape varies by event type. See the per-type schemas in the API reference.

Verifying signatures

Every request includes a X-RankSonar-Signature header containing an HMAC-SHA256 of the raw request body using your endpoint's signing secret.

Verify in Node.js:

import crypto from 'node:crypto';

function verify(rawBody: string, signature: string, secret: string): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected),
  );
}

Reject any request where verification fails. Always use a constant-time comparison (crypto.timingSafeEqual) to prevent timing attacks.

Retry policy

If your endpoint returns a non-2xx response or times out (10s), we retry with exponential backoff:

  • 1 minute later
  • 5 minutes later
  • 30 minutes later
  • 2 hours later
  • 12 hours later

After 5 failed attempts the delivery is marked failed. Failed deliveries are visible in Settings → Webhooks → [endpoint] → Deliveries, where you can manually retry.

Idempotency

Webhook deliveries can be retried, so your handler must be idempotent. Use the event id as a deduplication key — store processed event IDs in your database and skip duplicates.

Common patterns

Forward to your own logging:

export async function POST(req: Request) {
  const body = await req.text();
  const sig = req.headers.get('x-ranksonar-signature') ?? '';
  if (!verify(body, sig, process.env.RANKSONAR_SECRET!)) {
    return new Response('Invalid signature', { status: 401 });
  }
  const event = JSON.parse(body);
  await logToDatadog(event);
  return new Response(null, { status: 204 });
}

Auto-create Linear issues for critical alerts:

if (event.severity === 'critical' && event.type === 'rating_drop') {
  await linearClient.createIssue({
    title: `Rating dropped on ${event.payload.appName}`,
    teamId: process.env.LINEAR_TEAM_ID,
  });
}

See also