Agent skill
Amplitude Webhooks Skill
Receive and authenticate Amplitude webhooks (the Webhook streaming destination). Use when setting up an Amplitude webhook endpoint, checking the secret header you add to the destination, debugging 401s, or handling forwarded events and user property updates.
Install this skill
npx skills add hookdeck/webhook-skills --skill amplitude-webhooks
Amplitude's Webhook destination (Events and User Properties, on the Plus, Growth and Enterprise plans) streams events and user updates to a URL you choose. Requests are not signed. Every request carries two preset headers, Content-Type: application/json and User-Agent: Amplitude/Webhook/1.0, plus up to five custom headers you define. Add a secret as one of those headers and have your endpoint compare it with the value you stored.
When to Use This Skill
- How do I receive Amplitude webhooks?
- Does Amplitude sign webhook payloads? (No: use a secret header)
- How do I authenticate requests from an Amplitude Webhook destination?
- How do I handle forwarded Amplitude events and user property updates?
- Why does my Amplitude webhook endpoint return 401?
Verification (core)
const crypto = require('crypto');
// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
const ha = crypto.createHash('sha256').update(String(a)).digest();
const hb = crypto.createHash('sha256').update(String(b)).digest();
return crypto.timingSafeEqual(ha, hb);
}
function verifyAmplitudeRequest(
headers,
secret = process.env.AMPLITUDE_WEBHOOK_SECRET,
headerName = process.env.AMPLITUDE_WEBHOOK_HEADER || 'Authorization'
) {
if (!secret) return false; // never accept when unconfigured
const received = headers[headerName.toLowerCase()]; // Node lowercases header names
return typeof received === 'string' && received !== '' && safeEqual(received, secret);
}
- The header name is whatever you typed in the destination's header rows, so read it from
AMPLITUDE_WEBHOOK_HEADER(defaultAuthorization) and look it up case-insensitively. AMPLITUDE_WEBHOOK_SECRETis the exact, whole header value, including anyBearerprefix you typed. Compare the whole value; do not strip or parse a prefix.- Return
401for a missing or mismatched header,400for a body that isn't a JSON object,200once handled. - Read the body raw (
express.raw({ type: '*/*' })) and authenticate before parsing it. - In Python, use
hmac.compare_digeston bytes (.encode()both sides); passingstrraises on non-ASCII input.
Security note: a static shared credential authenticates the sender, not the body, and gives no replay protection (there is no signature and no timestamp). Use HTTPS, and rotate the secret in the destination and in your environment if it leaks.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Payloads
Amplitude does not use a fixed list of webhook event names. The default payloads are:
| Payload | Format | Identified by |
|---|---|---|
| Forwarded event | Amplitude event format | event_type is your own event name (for example Button Clicked), plus user_id, event_time, event_properties, user_properties, ... |
| User properties update | Amplitude user format | user_id and/or device_id plus user_properties, sent when Amplitude receives an event or an Identify API call |
An Apache FreeMarker template can emit any JSON instead, which changes these shapes. The examples handle the default format; adapt the dispatch if you use a template.
Environment Variables
AMPLITUDE_WEBHOOK_HEADER=Authorization # name of the header you added in Amplitude
AMPLITUDE_WEBHOOK_SECRET=your-secret-value # its exact, whole value (including any "Bearer ")
Local Development
Amplitude can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 amplitude --path /webhooks/amplitude
Use the printed URL as the webhook URL in Amplitude.
Resources
- references/overview.md - What the Webhook destination sends, payload shapes, retries
- references/setup.md - Create the sync, add the secret header, enable forwarding
- references/verification.md - Secret header check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests