Agent skill
Wiza Webhooks Skill
Receive and authenticate Wiza webhooks. Use when setting up a Wiza webhook endpoint, verifying the x-auth-key header, debugging 401s, or handling individual_reveal results for finished (200) and failed (400) lookups.
Install this skill
npx skills add hookdeck/webhook-skills --skill wiza-webhooks
Wiza is a B2B contact data platform. When a contact lookup (an individual reveal) completes, Wiza POSTs the result as JSON to your webhook URL. Webhooks are not signed. Every request carries an x-auth-key header containing a SHA-256 hash of your Wiza API key, and your endpoint authenticates the request by recomputing that hash and comparing.
When to Use This Skill
- How do I receive Wiza webhooks?
- How do I verify the
x-auth-keyheader on a Wiza webhook? - Does Wiza sign webhook payloads? (No: it sends a hash of your API key)
- How do I handle
individual_revealresults, finished or failed? - Why does my Wiza webhook endpoint return 401 after I rotated the API key?
Verification (core)
const crypto = require('crypto');
function verifyWizaRequest(headers, apiKey = process.env.WIZA_API_KEY) {
if (!apiKey) return false; // never accept when unconfigured
const received = headers['x-auth-key']; // lowercase hex SHA-256 of the API key
if (!received) return false;
const expected = crypto.createHash('sha256').update(apiKey, 'utf8').digest('hex');
const a = Buffer.from(received, 'utf8');
const b = Buffer.from(expected, 'utf8');
return a.length === b.length && crypto.timingSafeEqual(a, b); // lengths first: it throws otherwise
}
- Header:
x-auth-key: <lowercase hex SHA-256 of your API key>(header name is case-insensitive). Compare the whole value; nothing is stripped or prefixed. - Python:
hmac.compare_. Pass bytes, neverdigest(received.encode(), expected.encode()) str, which raisesTypeErroron non-ASCII input. - Return
401on a missing header or mismatch,400for a body that isn't a JSON object,200once handled. Non-2xx responses are retried up to 3 times. - Read the body raw (
express.raw({ type: '*/*' })) and authenticate before parsing it. - This is a static shared credential. It is the same on every request. It authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS and rotate the credential if it leaks. Rotating the Wiza API key changes the header value, so update your configuration at the same time.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
The payload is { status: { code, message }, type, data }; there is no top-level id (the reveal's id is data.id). The type documented for webhooks is individual_reveal; status.code says how the lookup ended.
type | status.code | Meaning |
|---|---|---|
individual_reveal | 200 | The reveal finished; data holds the result |
individual_reveal | 400 | The reveal failed |
Environment Variables
WIZA_API_KEY=your-wiza-api-key # Settings > API
The examples hash this key and compare the digest with x-auth-key.
Using Hookdeck
Enter the lowercase hex SHA-256 hash of your Wiza API key, as sent in x-auth-key, not the API key itself. Compute it with:
printf %s "$WIZA_API_KEY" | sha256sum
# or
node -e "console.log(require('crypto').createHash('sha256').update(process.env.WIZA_API_KEY).digest('hex'))"
Local Development
Wiza can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 wiza --path /webhooks/wiza
Use the printed URL as the webhook URL in Wiza.
Resources
- references/overview.md - What Wiza webhooks are, payload shape and delivery
- references/setup.md - Set the webhook URL and compute the hash for Hookdeck
- references/verification.md - x-auth-key check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests