Agent skill

PropelAuth Webhooks Skill

Receive and verify PropelAuth webhooks (delivered via Svix). Use when setting up PropelAuth webhook handlers, debugging svix-signature verification, or handling authentication events like user.created, user.updated, user.deleted, org.created, and user.added_to_org.

Install this skill

npx skills add hookdeck/webhook-skills --skill propelauth-webhooks


PropelAuth (authentication and user management) sends webhooks through Svix. PropelAuth's page says "We use Svix for our Webhooks" and links to Svix's verification guide, so the signature headers and secret format come from Svix: every delivery is an HTTP POST with a JSON body and three headers, svix-id, svix-timestamp, and svix-signature.

When to Use This Skill

  • How do I receive PropelAuth webhooks?
  • How do I verify PropelAuth webhook signatures (svix-signature)?
  • How do I handle user.created, user.deleted, or org.created events?
  • How do I sync PropelAuth users and organizations into my own database?
  • Why is my PropelAuth webhook signature verification failing?

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-delimited v1,<signature> entries. Use the official svix library, which also enforces a 5-minute timestamp window and compares signatures in constant time. Hookdeck retries and replays resend the original svix-timestamp, so the library rejects them once they are more than 5 minutes old. If you receive through Hookdeck and need late retries to pass, use the manual check in references/verification.md and drop or widen its timestamp window.

import { Webhook } from 'svix'; // svix 2.x is ESM-only

const wh = new Webhook(process.env.PROPELAUTH_WEBHOOK_SECRET); // "whsec_..."
// rawBody: the raw Buffer/string, never re-serialized JSON
wh.verify(rawBody, {
  'svix-id': req.headers['svix-id'],
  'svix-timestamp': req.headers['svix-timestamp'],
  'svix-signature': req.headers['svix-signature'],
}); // throws on a bad signature or stale timestamp
const event = JSON.parse(rawBody.toString('utf8')); // parse only after verify passes
from svix.webhooks import Webhook, WebhookVerificationError

wh = Webhook(os.environ["PROPELAUTH_WEBHOOK_SECRET"])
wh.verify(raw_body, {"svix-id": msg_id, "svix-timestamp": ts, "svix-signature": sig})

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Common Event Types

EventTriggered When
user.createdA new user signed up
user.updatedSome information about the user changed (only user_id is recorded; fetch the latest from the API)
user.deletedA user was deleted
org.createdAn organization was created
user.added_to_orgA user was added to an organization

See references/overview.md for the full event list, including org.*, other user.*, and email.* events.

Environment Variables

# Dashboard > Integrations > Webhooks > your endpoint > signing secret (starts with whsec_)
# Test and production endpoints each have their own secret.
PROPELAUTH_WEBHOOK_SECRET=whsec_xxxxx

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 propelauth --path /webhooks/propelauth

Register the tunnel URL as a test endpoint in the PropelAuth Dashboard under Integrations > Webhooks, then trigger an event (for example sign up a test user).

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 5, 2026

View on GitHub →