Agent skill
Agntix Webhooks Skill
Receive and verify Agntix webhooks (voice and chat AI-agent platform, delivered by Svix). Use when setting up Agntix webhook handlers, debugging svix-signature verification, or handling the session.created and session.ended events with post-call analysis.
Install this skill
npx skills add hookdeck/webhook-skills --skill agntix-webhooks
Agntix (agntix.ai) is a voice and chat AI-agent platform. It sends an HTTPS POST with a signed JSON body when a session starts and when it ends (the latter with the post-call analysis). Delivery is by Svix, which follows Standard Webhooks and signs with the svix-* header names: svix-id, svix-timestamp and svix-signature.
The body is a flat envelope (no data wrapper): { "id": "<session UUID>", "eventType": "session.ended", "type": "VOICE", "platform": "PHONE", "direction": "OUTBOUND", "state": "CLOSED", "agentId": "...", "createdAt": "...", "metadata": { "sessionVariables": { ... } } }. The event name is the top-level eventType. The top-level type is the session kind (VOICE or CHAT), not the event name. id is the session UUID, which Agntix tells you to use as the idempotency key. session.ended adds lastActivityAt, endedAt, analysis and recordingUrl.
When to Use This Skill
- How do I receive Agntix webhooks?
- How do I verify Agntix webhook signatures (
svix-signature)? - How do I handle
session.createdandsession.endedevents and the post-callanalysis? - How do I get the Agntix signing secret (Developer > Add Endpoint)?
- Why is my Agntix webhook signature verification failing, or rejecting Hookdeck retries?
Verification (core)
Signed content is {svix-id}.{svix-timestamp}.{raw body}. Svix signs it with HMAC-SHA256 using the base64-decoded secret (the part after whsec_), base64-encodes the result, and sends one or more space-separated v1,<signature> entries. Use the official svix library, which compares in constant time. Read the svix-* headers, and when svix-id is absent fall back to the webhook-* headers (webhook-id, webhook-timestamp, webhook-signature), as Hookdeck does.
import { Webhook } from 'svix'; // svix 2.x is ESM-only
const prefix = req.headers['svix-id'] ? 'svix' : 'webhook';
const id = req.headers[`${prefix}-id`];
const timestamp = req.headers[`${prefix}-timestamp`];
const signature = req.headers[`${prefix}-signature`];
// rawBody: the raw Buffer/string, never re-serialized JSON
new Webhook(process.env.AGNTIX_WEBHOOK_SECRET).verify(rawBody, {
'svix-id': id, 'svix-timestamp': timestamp, 'svix-signature': signature,
'webhook-id': id, 'webhook-timestamp': timestamp, 'webhook-signature': signature,
}); // throws on a bad signature or a timestamp outside the window
const event = JSON.parse(rawBody.toString('utf8')); // parse only after verify passes
from svix.webhooks import Webhook, WebhookVerificationError
wh = Webhook(os.environ["AGNTIX_WEBHOOK_SECRET"]) # "whsec_..."
wh.verify(raw_body, {"svix-id": id, "svix-timestamp": ts, "svix-signature": sig,
"webhook-id": id, "webhook-timestamp": ts, "webhook-signature": sig})
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Each endpoint has its own signing secret, shown for the endpoint in the dashboard's Developer section (Svix). Agntix doesn't print the secret's form, but Svix secrets start with whsec_. Keep the full value in AGNTIX_WEBHOOK_SECRET. Handlers fail closed: with the variable unset (or not a whsec_ value) they return 500 instead of accepting anything. A missing header or bad signature gets a 400.
Read the raw body: Agntix's docs say to verify on "the raw request body, not a parsed JSON object". With Express use express.raw({ type: '*/*' }) so any content type works and the exact bytes are verified.
Hookdeck retries and replays hit the 5-minute window
The svix library always enforces a fixed 5-minute timestamp window, and it can't be configured. Hookdeck retries and replays resend the original svix-timestamp, so the library rejects any retry or replay that happens more than 5 minutes after the original delivery. If you verify behind Hookdeck (or replay old events), use the manual verifier in references/verification.md, where the window can be dropped or widened. Deduplicate on the envelope id instead of relying on the timestamp.
Common Event Types
Two events are delivered today (the list may not be exhaustive). The name is eventType in the JSON body, and the examples handle both:
| Event | Triggered When |
|---|---|
session.created | A session is initiated (for outbound calls, before pickup) |
session.ended | A session closes (hangup, timeout, TTL or manual), with analysis and recordingUrl |
session.updated and knowledge_store.updated are documented as planned: they exist only as in-app SSE signals and are not delivered as webhooks. Return 2xx for any event you don't handle.
Delivery is at least once and expects a 2xx within 15 seconds; failures retry with exponential backoff for up to 24 hours. Both events carry the same session id, so deduplicate on id plus eventType, return 2xx, then process asynchronously. The webhook is summary-grade: fetch the full transcript from the API when you need it. See references/overview.md.
Environment Variables
# Signing secret of the webhook endpoint (Svix format, starts with whsec_)
AGNTIX_WEBHOOK_SECRET=whsec_xxxxx
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 agntix --path /webhooks/agntix
Add the tunnel URL under Developer > + Add Endpoint (see references/setup.md), subscribe to session.created and session.ended, then place a test call or chat session.
Reference Materials
- references/overview.md - Agntix webhook concepts, events, payload shapes, delivery
- references/setup.md - Add an endpoint, get the secret, choose events
- references/verification.md - Svix verification details, header fallback, manual verifier, timestamp window