# 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](https://github.com/hookdeck/webhook-skills/blob/main/skills/propelauth-webhooks/references/verification.md) and drop or widen its timestamp window.

```javascript
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

```

```python
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/](https://github.com/hookdeck/webhook-skills/tree/main/skills/propelauth-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/propelauth-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/propelauth-webhooks/examples/fastapi/).

## Common Event Types

| Event | Triggered When |
| --- | --- |
| `user.created` | A new user signed up |
| `user.updated` | Some information about the user changed (only `user_id` is recorded; fetch the latest from the API) |
| `user.deleted` | A user was deleted |
| `org.created` | An organization was created |
| `user.added_to_org` | A user was added to an organization |

See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/propelauth-webhooks/references/overview.md) for the full event list, including `org.*`, other `user.*`, and `email.*` events.

## Environment Variables

```bash
# 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

```bash
# 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

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/propelauth-webhooks/references/overview.md) - PropelAuth webhook concepts, full event list, payloads
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/propelauth-webhooks/references/setup.md) - Register an endpoint and get the signing secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/propelauth-webhooks/references/verification.md) - Signature verification details and gotchas