Skip to content

feat: add knock-webhooks skill#64

Merged
leggetter merged 2 commits into
mainfrom
feat/knock-webhooks
May 15, 2026
Merged

feat: add knock-webhooks skill#64
leggetter merged 2 commits into
mainfrom
feat/knock-webhooks

Conversation

@leggetter
Copy link
Copy Markdown
Collaborator

Summary

Adds a complete knock-webhooks provider skill for Knock (notification infrastructure). Manual HMAC-SHA256 verification with the unusual {timestamp_ms}.{body} signed content — the timestamps are milliseconds, not seconds (Knock's explicit deviation from Stripe's scheme).

What's included

  • skills/knock-webhooks/SKILL.md — entry point with frontmatter and verification core
  • skills/knock-webhooks/references/ — overview (full 23-event taxonomy across 6 categories), setup (dashboard config + per-endpoint secret), verification (algorithm, ⚠️ Critical milliseconds-vs-seconds callout, manual-only)
  • skills/knock-webhooks/examples/ — Express, Next.js App Router, FastAPI handlers + tests (all manual stdlib HMAC; no third-party verification library pulled in)
  • Integration: README.md, providers.yaml, .claude-plugin/marketplace.json (as a standalone plugin and added to the webhook-skills bundle, now 39 skills)

Notes

  • Header: x-knock-signature: t=<timestamp_ms>,s=<base64-hmac-sha256>
  • Signed content: {timestamp_ms}.{raw-body} (literal period separator). Pass the raw request body; don't JSON.parse and re-serialize.
  • Signing key: per-endpoint signing secret from the Knock dashboard (one secret per webhook). NOT the account API key.
  • Replay protection: docs recommend 5-minute tolerance (300_000 ms). The skill's verification reference recommends checking this on top of the signature.
  • ⚠️ The big gotcha: Knock explicitly states they follow the Stripe specification but deviate by using millisecond timestamps, not seconds. A Stripe-style verifier with seconds will silently fail — the timestamp gets multiplied by 1,000 inside the HMAC input string. The skill's references/verification.md calls this out with a dedicated section.
  • No SDK helper: confirmed by reading the source of @knocklabs/node v1.32.0 (npm) and knockapi v1.25.0 (PyPI) — neither exposes webhooks.unwrap(), constructEvent(), or any verification function for inbound webhooks. The official JS example in the docs uses crypto.createHmac directly. Manual stdlib HMAC is the canonical path. No sdks entry in providers.yaml because the verification path doesn't import the SDK.
  • Event taxonomy (23 events, 6 categories):
    • Message lifecycle (13): message.sent, message.delivered, message.delivery_attempted, message.undelivered, message.bounced, message.seen, message.unseen, message.read, message.unread, message.archived, message.unarchived, message.interacted, message.link_clicked
    • Workflow (2): workflow.updated, workflow.committed
    • Email layout (2): email_layout.updated, email_layout.committed
    • Translation (2): translation.updated, translation.committed
    • Source event action (2): source_event_action.updated, source_event_action.committed
    • Partial (2): partial.updated, partial.committed
  • Delivery semantics: at-least-once with up to 8 retries on non-2xx. Skill recommends idempotency keyed on the event id field.

Test plan

  • cd skills/knock-webhooks/examples/express && npm install && npm test
  • cd skills/knock-webhooks/examples/nextjs && npm install && npm test
  • cd skills/knock-webhooks/examples/fastapi && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt && pytest test_webhook.py -v
  • Spot-check that the verification core reproduces the docs' verbatim formula: "s=" + base64(HMAC-SHA256(secret, ts_ms + "." + raw_body)) and the timestamp is treated as milliseconds throughout (HMAC input string AND freshness comparison)
  • Confirm event names match https://docs.knock.app/developer-tools/outbound-webhooks/event-types
  • Confirm the webhook-skills marketplace bundle now lists 39 skill paths

Generation details

  • Generated via ./scripts/generate-skills.sh generate knock --config providers.yaml --model claude-opus-4-7
  • 1 iteration (initial generation passed review on first pass)
  • Locally: npx hookdeck-cli listen 3000 knock --path /webhooks/knock

https://claude.ai/code/session_01NNTgQRJss1V7gyzzJ9rjnB


Generated by Claude Code

claude added 2 commits May 14, 2026 10:07
Adds receive/verify skill for Knock outbound webhooks. Knock signs with
HMAC-SHA256 (base64) over `${timestamp_ms}.${raw_body}` and ships it in a
single `x-knock-signature: t=<ms>,s=<sig>` header — the timestamp is in
**milliseconds**, an explicit deviation from Stripe that silently breaks any
ported Stripe verifier. Includes Express, Next.js (App Router), and FastAPI
examples with regression tests that reject seconds-based signatures, plus
references covering the full 23-event taxonomy.

https://claude.ai/code/session_01NNTgQRJss1V7gyzzJ9rjnB
…ketplace.json

- README.md: add Knock row (alphabetically between Intercom and Linear), linkified to docs, flagging the millisecond-timestamp deviation in the description so reviewers don't miss it
- providers.yaml: add knock entry with HMAC-SHA256/base64 over `{timestamp_ms}.{body}` scheme, the full 23-event taxonomy across 6 categories, prominent ms-vs-seconds gotcha, and the explicit "no SDK helper" note (confirmed by reading @knocklabs/node v1.32.0 + knockapi v1.25.0 source — neither exposes a verify/unwrap/constructEvent function for inbound webhooks). No `sdks` field — the verification path is purely stdlib HMAC; the official SDKs aren't pulled into example dependencies.
- .claude-plugin/marketplace.json: add `knock-webhooks` plugin entry alphabetically and append `./skills/knock-webhooks` to the `webhook-skills` bundle (bundle now lists 39 skill paths).

Skill content (skills/knock-webhooks/) landed in the previous commit via the generator. Tests pass on Express, Next.js, and FastAPI. The verification reference prominently calls out the milliseconds-vs-seconds foot-gun for anyone porting from a Stripe verifier.

https://claude.ai/code/session_01NNTgQRJss1V7gyzzJ9rjnB
@leggetter leggetter merged commit 26550cb into main May 15, 2026
6 checks passed
@leggetter leggetter deleted the feat/knock-webhooks branch May 15, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants