# folk Webhooks

[folk](https://www.folk.app) is a CRM. Its webhooks report changes to people, companies, custom objects (such as deals), notes, reminders and person enrichments. They follow the Standard Webhooks spec, so every delivery is signed with `webhook-id`, `webhook-timestamp` and `webhook-signature` headers, and folk recommends the official Standard Webhooks libraries for verification.

## When to Use This Skill

* How do I receive folk webhooks?
* How do I verify a folk webhook signature?
* Where do I find my folk webhook signing secret? (It is shown only once.)
* How do I handle `person.created`, `person.updated`, `company.updated` or `object.created` events?
* Why is my folk `webhook-signature` verification failing?
* Why are Hookdeck retries or replays of folk webhooks rejected after 5 minutes?

## Verification (core)

| Header | Value |
| --- | --- |
| `webhook-id` | Unique event ID (same as the payload's `id`), stable across redeliveries |
| `webhook-timestamp` | Unix seconds of the delivery |
| `webhook-signature` | `v1,<base64 HMAC-SHA256>`, possibly several space-separated entries |

Signed content is `{webhook-id}.{webhook-timestamp}.{raw body}`. The key is the base64-**decoded** part of the secret after `whsec_` (for example `whsec_QWFSUzl1QUVoQW1kdWtpTnJRTUFpbXNlZmxLTg==`). folk shows the secret only once, when the webhook is created.

```javascript
const { Webhook } = require('standardwebhooks');

// rawBody: exact request body string, NOT re-serialised JSON
function verifyFolkWebhook(rawBody, headers, secret = process.env.FOLK_WEBHOOK_SECRET) {
  return new Webhook(secret).verify(rawBody, {
    'webhook-id': headers['webhook-id'],
    'webhook-timestamp': headers['webhook-timestamp'],
    'webhook-signature': headers['webhook-signature'],
  }); // returns the parsed event; throws → respond 400
}

```

Python: `from standardwebhooks.webhooks import Webhook`, then `Webhook(secret).verify(await request.body(), dict(headers))`.

> **5-minute window.** The `standardwebhooks` and `svix` libraries always enforce a fixed 5-minute timestamp window, and it can't be changed. **Hookdeck retries and replays resend the original timestamp**, so the library rejects any retry or replay that arrives more than 5 minutes after folk signed it. If you receive folk webhooks through Hookdeck and need late retries or replays to verify, use the dependency-free verifier in [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/folk-webhooks/references/verification.md): it checks every space-separated `v1,<base64>` entry in constant time, and lets you drop or widen the window.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/folk-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/folk-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/folk-webhooks/examples/fastapi/). The Express example reads the raw body with `express.raw({ type: '*/*' })` so any content type verifies.

## Event Envelope

```json
{ "id": "evt_6004f2ef-dcc3-4c56-a704-9326730ad100", "type": "person.updated",
  "createdAt": "2025-09-30T09:55:52.715Z",
  "source": "https://api.folk.app/v1/webhooks/wbk_2f15ec4f-0f70-4884-a43d-1f88ef7a6665",
  "data": { "id": "per_05837e2d-9bf7-4f17-8729-e95f1be4de67",
            "url": "https://api.folk.app/v1/people/per_05837e2d-9bf7-4f17-8729-e95f1be4de67",
            "changes": [{ "path": ["firstName"], "type": "set", "value": "John" }] } }

```

Payloads are thin: `data.id` and `data.url` identify the resource (GET `data.url` for the full record). Updates add `data.changes`, deletions add `data.details`. Dispatch on the body's `type`.

## Common Event Types

| Event | Fired when |
| --- | --- |
| `person.created` / `person.updated` / `person.deleted` | A person is created, updated (native attributes or custom fields) or deleted |
| `person.groups_updated` | A person's groups change |
| `person.workspace_interaction_metadata_updated` | A person's workspace interaction metadata changes |
| `company.created` / `company.updated` / `company.deleted` | A company is created, updated or deleted |
| `company.groups_updated` | A company's groups change |
| `object.created` / `object.updated` / `object.deleted` | A custom object (for example a deal) is created, updated or deleted |
| `note.created` / `note.updated` / `note.deleted` | A note is created, updated or deleted |
| `reminder.created` / `reminder.updated` / `reminder.deleted` / `reminder.triggered` | A reminder changes or fires |
| `person_enrichment.finished` | A person enrichment completes or fails |

> **Full payloads:** [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/folk-webhooks/references/overview.md) and [folk's events documentation](https://developer.folk.app/webhooks/events-and-payloads).

## Delivery Semantics

* Return a `2xx` quickly and process asynchronously. Any other status is a failure.
* folk retries for up to one day (immediately, 5s, 5m, 30m, 2h, 5h, 10h, 14h, 20h, 24h).
* After all retries fail, or on a high error rate, folk deactivates the webhook and emails you. A `410 Gone` response deactivates it immediately.
* Events can arrive out of order and more than once. Dedupe on `webhook-id`.

## Environment Variables

```bash
FOLK_WEBHOOK_SECRET=whsec_xxxxx   # the webhook's signing secret; handlers fail closed (500) without it

```

## Local Development

```bash
# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 folk --path /webhooks/folk

```

Create a webhook in your workspace API settings (or with `POST https://api.folk.app/v1/webhooks`) that targets the printed URL, and subscribe to `person.created` to start.

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/folk-webhooks/references/overview.md) - Event catalog, payload fields, retries
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/folk-webhooks/references/setup.md) - Creating webhooks (dashboard and API), saving the secret, deactivation
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/folk-webhooks/references/verification.md) - Library and manual verification, dropping or widening the 5-minute window, debugging