feat: add vapi-webhooks skill - #178
Merged
Merged
Conversation
Vapi voice-AI agent platform ("Server URL" webhooks). Authored from the
canonical docs (server-url, server-url/events, server-url/server-authentication)
because Vapi has no fixed signature scheme and needed careful, non-fabricating
treatment.
Key decisions:
- Auth is opt-in and per-endpoint. The skill's primary, fully-specified path is
the shared secret (Authorization: Bearer <token> or the legacy X-Vapi-Secret
header) — a literal, timing-safe compare, not an HMAC. OAuth 2.0 and a
fully-configurable HMAC are documented as secondary options WITHOUT asserting a
fixed header/algorithm/signed-string, since Vapi pins no defaults.
- The event type is nested at message.type (per /server-url/events), not the
flatter top-level shape shown in an informal CLI tutorial. The tutorial's
`verifyVapiSignature` is a placeholder, not a real export — not used.
- Handlers implement the request/response protocol: assistant-request,
tool-calls, transfer-destination-request, and knowledge-base-request return the
required JSON body; all other message types get a bare 200.
Tests: Express 19, Next.js 14, FastAPI 18 pass; validate-provider.sh passes.
Residual unknowns (no live account; HMAC construction undocumented) recorded in
skills/vapi-webhooks/TODO.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hookdeck/core added VAPI as a verified source (HMAC controller). Its config resolves the skill's biggest hedge — the HMAC signed-content construction: - Signed content = the RAW request body, no timestamp prefix (VAPI omits the generic HMAC alias's prefix_data_with_timestamp, so the controller signs body as-is). - Defaults: algorithm sha256, header x-signature, encoding hex; sha1/sha512 and base64/base64url also supported; MD5 deliberately excluded. Vapi's own docs still pin no defaults (re-confirmed 2026-08), so this is framed as a concrete DEFAULT from Hookdeck's verified integration — not a Vapi spec — and the customer's dashboard credential still wins. verification.md now ships a concrete verifyVapiHmac() with these defaults; SKILL.md and TODO.md updated to match. No handler/test changes; validate-provider.sh passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…struction
Verified the Vapi HMAC scheme by recomputing digests of real sandbox deliveries
(key "test", SHA-256, hex, secret verbatim, x-signature header):
- Payload Format {body} -> HMAC-SHA256(rawBody, secret). Matches hookdeck/core;
recommended (self-contained).
- Payload Format {timestamp}.{body} (Vapi default) -> HMAC-SHA256(
x-timestamp + "." + rawBody, secret), where x-timestamp is Vapi's send-time
epoch-ms header. Requires the timestamp header ON; with it off the signing
value is never delivered and the signature can't be verified. The signing
timestamp is the x-timestamp header, NOT message.timestamp (differ ~40ms).
verification.md now documents both formats + a format-aware verifier + self-
computed known-answer vectors; SKILL.md HMAC bullet updated; TODO marks HMAC
verified. Added a known-answer test to the Express suite locking both digests.
Express 21 pass; validate-provider.sh passes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a webhook skill for Vapi (voice-AI agent platform — the "Server URL"). Collision check clean: no prior
vapi-webhooksskill orproviders.yamlentry.Authored from Vapi's canonical docs (server-url, events, server-authentication). This one needed care rather than a stock HMAC template — Vapi has no fixed signature scheme, so the skill is written to avoid fabricating a construction.
Why this skill is unusual
X-Vapi-Secret, OAuth 2.0, and a fully-configurable HMAC.Authorization: Bearer <token>or legacyX-Vapi-Secret) — a literal, timing-safe compare, not an HMAC. The examples implement this and read whichever header is present.references/verification.mdrefuses to assert one. OAuth 2.0 is covered as the token-validation path.verifyVapiSignaturename in a CLI tutorial is an unimplemented placeholder — the skill explicitly does not use it. No official SDK verify helper exists; no documented source-IP allowlist.message.type, not top-level (a CLI tutorial shows a flatter shape with names likecall-started— that's informal example code, not the wire format; the skill follows/server-url/events).assistant-request(hard ~7.5s timeout),tool-calls,transfer-destination-request,knowledge-base-request. All other types get a bare200.Tests
validate-provider.sh vapi-webhooks: passedAuth tests cover both the Bearer and legacy
X-Vapi-Secretheaders, wrong/missing secret → 401, and the required response bodies for all four request/response types.Notes for the reviewer
skills/vapi-webhooks/TODO.mdfor a future live pass.Left as a draft pending sign-off.
🤖 Generated with Claude Code