SCSuper ConnectDocumentationOpen the admin panel →

Receive webhooks

Super Connect sends two kinds of signed event to your integration's webhook URL. An auth event reports a connection lifecycle outcome: a token refresh that fails, recovers, or is replaced by reauthorization. A forward event carries a provider webhook that Slack or GitHub posted to Super Connect. Both are signed the same way and share one delivery queue.

This guide shows how to enable delivery, verify it, and handle auth events. For forward events and the provider-side setup, read Receive provider events.

1. Configure the integration

Both fields are required together. The secret is encrypted at rest.

curl https://super-connect.dogar.biz/api/v1/integrations/github -X PUT \
  -H "Authorization: Bearer $SUPER_CONNECT_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "providerId": "github",
    "scopes": "repo read:user",
    "webhookUrl": "https://app.example.com/super-connect/events",
    "webhookSecret": "GENERATE_A_LONG_RANDOM_STRING"
  }'

Omitting clientId and clientSecret on an update keeps the stored values. Set webhookUrl to null to disable delivery and clear the secret.

2. Know the auth payload

{
  "id": "evt_5c5d…",
  "version": "1",
  "type": "auth",
  "operation": "refresh",
  "connectionId": "…",
  "integrationId": "github",
  "provider": "github",
  "endUserId": "user_123",
  "success": false,
  "createdAt": "2026-09-11T20:00:00.000Z",
  "error": {
    "type": "refresh_token_external_error",
    "description": "Token exchange failed (HTTP 400); …"
  }
}

3. Verify the signature

Each request carries two headers:

X-Super-Connect-Webhook-Timestamp: 1757620800
X-Super-Connect-Hmac-Sha256: <hex>

The signature is HMAC-SHA256 over timestamp + "." + rawBody with your webhook secret, hex-encoded. Verify against the raw request body, not a re-serialized object.

import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret: string, headers: Headers, rawBody: string) {
  const timestamp = headers.get("X-Super-Connect-Webhook-Timestamp") ?? "";
  const given = headers.get("X-Super-Connect-Hmac-Sha256") ?? "";
  const skew = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!timestamp || skew > 300) return false;
  const expected = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
  return (
    given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected))
  );
}

Reject anything older than five minutes.

4. Respond and deduplicate

Return any 2xx status quickly. The Worker cancels the response body and does not read it.

Delivery is retried on network errors and 5xx responses after 3 and then 6 seconds, at most three attempts, then marked failed. A 4xx fails immediately. Because of retries you may see the same id twice. Store processed IDs and ignore repeats.

Events for one connection are delivered in order. A stuck event blocks later events for that connection only, never for others.

5. Test locally

Point webhookUrl at a tunnel (for example cloudflared tunnel --url http://localhost:3000) since only HTTPS destinations are accepted. Trigger a failure by revoking the app's access in the provider's account settings and waiting for the next ten-minute cron sweep, or by making a proxy request once the token has expired.