Skip to content

Webhooks ​

A webhook makes Site Connect call your server whenever a chosen event happens in your workspace. Each call is an HTTP POST with a JSON body and a signature (use an https:// address), so your server can check it really came from Site Connect.

In Site Connect, the event you can subscribe to is tenant.status_changed — your workspace's account status changed, for example from active to past_due or suspended. Use it to alert your office or pause an integration when the account needs attention.

Where to find it

Menu: System › Webhooks · Address: hub.siteconnect.ai/webhooksWho can use it: Admins by default. Other roles need the Webhooks permission: view, create, edit (pause, rotate, replay) or delete — see Roles and permissions. Like API keys, webhooks only work once API access is turned on for your company — if the page stays empty or adding a webhook shows an error, contact support.

Some labels aren't translated yet

A few buttons on this page aren't in English yet, so this guide describes them by where they are.

Add a webhook ​

  1. Go to System › Webhooks (the page is titled Webhooks outbound) and click the blue button with the + at the top right. A dialog opens.
  2. In the URL webhook field, enter your server's address, for example https://example.com/site-connect-webhook. It must be reachable from the internet; private network addresses are refused.
  3. Under Events, click message.created to untick it (it's selected by default), then click tenant.status_changed. Selected events turn blue. The other events in the list belong to features that aren't part of Site Connect — leave them unticked.
  4. Click Subscribe.
  5. A yellow box at the top of the page shows the signing secret (it starts with whsec_). Click Copy and store it on your server.

You only see the secret once

If you lose it, use Rotate (below) to get a new one.

What your server receives ​

Each call carries these headers:

HeaderWhat it is
X-Webhook-SignatureThe signature — see the next section.
X-Webhook-Event-TypeThe event, for example tenant.status_changed.
X-Webhook-IdThe event's ID. It stays the same on retries and replays — use it to ignore duplicates.
X-Webhook-Delivery-IdThe ID of this particular attempt.

The JSON body looks like this:

json
{
  "id": "…",
  "type": "tenant.status_changed",
  "schema_version": 1,
  "tenant_id": "…",
  "aggregate_type": "tenant",
  "aggregate_id": "…",
  "payload": {
    "tenant_id": "…",
    "from": "active",
    "to": "past_due",
    "reason": "…",
    "actor_type": "system",
    "actor_id": null
  },
  "created_at": "2026-10-01T14:05:00.000Z",
  "delivery_id": "…",
  "attempt": 1
}

tenant_id is your workspace's ID. from and to are one of provisioning, active, past_due, suspended, grace_period or terminated. reason may be empty.

Check the signature ​

The X-Webhook-Signature header looks like t=1759327500,v1=5f8a…:

  • t is the time the call was signed, in Unix seconds.
  • v1 is an HMAC-SHA256 of the text <t>.<raw body>, using your signing secret as the key, written in hex.
  • Right after you rotate the secret, a third part v1d=… is added, signed with your old secret.

To verify a call:

  1. Read the raw request body exactly as received — before any JSON parsing.
  2. Reject the call if t is more than 5 minutes away from your server's clock.
  3. Compute the HMAC-SHA256 of t, a dot, and the raw body, with your secret.
  4. Accept the call if it matches v1 (or v1d when you check with the old secret). Use a constant-time comparison.
js
const crypto = require('crypto');

// secrets: your current signing secret, plus the old one during a rotation
function verifySiteConnectWebhook(rawBody, signatureHeader, secrets) {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map((p) => p.trim().split('=')),
  );
  const t = Number(parts.t);
  if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;

  const received = [parts.v1, parts.v1d].filter(Boolean);
  return secrets.some((secret) => {
    const expected = crypto.createHmac('sha256', secret)
      .update(`${t}.${rawBody}`)
      .digest('hex');
    return received.some((sig) => sig.length === expected.length
      && crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expected, 'hex')));
  });
}

Reply quickly — and what happens if you don't ​

  • Reply with any 2xx status within 10 seconds. Do slow work after you've replied.
  • Site Connect doesn't follow redirects. Give the final address.
  • These are tried again: timeouts, network errors, and the statuses 408, 425, 429 and 5xx. Each event gets up to 5 attempts, about 1 minute, 5 minutes, 30 minutes and 2 hours apart. If you reply 429 with a Retry-After header, Site Connect waits that long (up to an hour).
  • Other 4xx replies, such as 400, 401, 403 or 404, are not tried again.
  • Replying 410 Gone switches the webhook off.
  • Each failed attempt adds one to the webhook's Failures count, and a successful delivery resets it to zero. A webhook with 1,000 failures and no success for 3 days is switched off automatically.

Check deliveries and resend ​

The webhook list shows URL, Events, Status, Failures and Last success for each webhook. Dates on this page are shown day first (day/month).

  1. Click Deliveries on a webhook's row. The Delivery log opens with the last 100 attempts. It refreshes every few seconds; click Refresh to update it now.
  2. Each row shows Time, Event, Att. (attempt number), Status, HTTP (your server's reply), Duration and Error. Status is one of succeeded, pending, in_flight, failed, dead_letter (out of attempts) or skipped.
  3. To send a failed attempt again, click Replay on its row. Replay only works while the webhook is Active.

Rotate the signing secret ​

  1. Click Rotate on the webhook's row, then confirm with Rotate.
  2. The new secret appears in the yellow box. Click Copy.
  3. Add the new secret to your server. For the next 7 days each call is signed with both secrets (v1 new, v1d old), so nothing breaks while you switch. After that, only the new secret works.

Pause or delete a webhook ​

  • Pause: click the Active badge on the row. It changes to Disabled and nothing is sent. Click it again to switch the webhook back on.
  • Delete: click the bin icon on the row and confirm. This can't be undone.

Site Connect — run your job sites from one place.