Webhooks
Webhooks let Statalog push data to your own systems the moment something significant happens — a goal conversion, a traffic spike, or an unexpected traffic drop. Instead of polling the API, your server receives an HTTP POST request with a JSON payload in real time.
Available events
| Event | When it fires |
|---|---|
goal.converted |
A visitor completes a goal you have configured in Statalog |
traffic.spike |
Yesterday's visitor count exceeds your configured threshold above the rolling average |
traffic.drop |
Yesterday's visitor count falls below your configured threshold under the rolling average |
Traffic spike and drop events are evaluated once per day, shortly after midnight, when the previous day's totals are finalised.
Creating a webhook
- Go to Account → Webhooks and click Add Webhook.
- Enter a name for the webhook (e.g. "Slack goal alerts" or "CRM signup sync").
- Enter the endpoint URL — the HTTPS URL of the server that will receive POST requests. The endpoint must be publicly reachable.
- Select one or more events to subscribe to.
- Click Create Webhook.
Immediately after creation, a signing secret is displayed. Copy it now — it is shown only once. You will use it to verify that incoming requests genuinely originate from Statalog.
Payload formats
goal.converted
{
"event": "goal.converted",
"site_id": "ST-A1B2C3",
"goal": {
"id": 17,
"name": "Signup"
},
"monetary_value": 49.00,
"visitor_id": "anon_7f3a2c",
"session_id": "sess_9d1e4b",
"url": "https://example.com/thank-you",
"timestamp": "2025-04-24T14:32:08Z"
}
monetary_value is null if no value is configured for the goal. visitor_id and session_id are opaque anonymous identifiers scoped to the current day's session — they cannot be used to identify or track individuals.
traffic.spike and traffic.drop
{
"event": "traffic.spike",
"site_id": "ST-A1B2C3",
"visitors_yesterday": 4820,
"visitors_average": 2100,
"change_pct": 129.5,
"timestamp": "2025-04-24T00:15:00Z"
}
visitors_average is the 30-day rolling daily average. change_pct is the percentage difference from that average.
Signature verification
Every webhook request includes two custom headers:
X-Statalog-Signature—sha256=<hmac>where the HMAC is computed over the raw request body using your signing secret as the keyX-Statalog-Event— the event name, e.g.goal.converted
Always verify the signature before acting on the payload. This confirms the request came from Statalog and the body was not tampered with in transit.
Verifying in PHP
<?php
function verifyStatalogSignature(string $payload, string $signatureHeader, string $secret): bool
{
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
return hash_equals($expected, $signatureHeader);
}
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_STATALOG_SIGNATURE'] ?? '';
$secret = getenv('STATALOG_WEBHOOK_SECRET');
if (! verifyStatalogSignature($payload, $signature, $secret)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
// Handle the event
if ($event['event'] === 'goal.converted') {
// e.g. add a row to your CRM
}
http_response_code(200);
Verifying in Node.js
import crypto from 'crypto';
function verifySignature(payload, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
// Use timingSafeEqual to prevent timing attacks
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader);
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
}
// Express example
app.post('/webhooks/statalog', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-statalog-signature'];
const secret = process.env.STATALOG_WEBHOOK_SECRET;
if (!verifySignature(req.body, signature, secret)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
console.log('Received event:', event.event);
res.sendStatus(200);
});
Use express.raw() (not express.json()) so the body is available as a Buffer before JSON parsing — the HMAC must be computed over the raw bytes, not a re-serialised object.
Retry logic
If your endpoint does not return a 2xx response within 10 seconds, Statalog treats the delivery as failed and retries. The retry schedule is:
- Immediate first attempt
- Second attempt after 60 seconds
- Third attempt after 120 seconds
After three failed attempts, the delivery is dropped. The webhook log in Account → Webhooks → [webhook name] shows the delivery history and response codes for each attempt, which is useful for debugging.
Testing your endpoint
Click Send Test on any webhook to send a {"event":"test"} payload to your endpoint. The test payload is not signed — use it only to verify that your server is reachable and returns 200.
Use cases
Slack alerts on goal conversions — post a message to a Slack channel every time a "Purchase" or "Signup" goal fires. Parse the payload in a small serverless function and call the Slack Incoming Webhook URL.
CRM sync on signups — when a "Signup" goal converts, create a contact in HubSpot, Salesforce, or Pipedrive with the timestamp and page URL from the payload.
Zapier and Make.com triggers — use Zapier's "Webhooks by Zapier" trigger or Make.com's "Custom Webhook" module as the endpoint URL. The JSON payload is automatically parsed and available as fields in your automation. See Zapier & Make for workflow examples.
Traffic monitoring — send a Slack or PagerDuty alert when a traffic.drop event fires so your team knows immediately if something is wrong with your site.
FAQ
What happens if my server is down? Statalog retries three times over approximately two minutes. If all three attempts fail, the delivery is dropped and logged. There is no dead-letter queue or manual replay — if you need to process missed events, query the REST API for the data directly.
Are webhooks synchronous? No. Webhooks are queued asynchronously. The visitor's pageview or goal conversion is recorded immediately; the webhook delivery happens in the background via the queue worker. This means a slow or offline webhook endpoint never affects the visitor's experience or the accuracy of your analytics data.
Can I subscribe the same endpoint to multiple events?
Yes. When creating the webhook, simply select all the events you want. Each delivery will include the X-Statalog-Event header so your server can route accordingly.