# Fountain Webhooks

Fountain (fountain.com) is hiring software for hourly workforces. Fountain Hire sends a JSON `POST`
webhook when an applicant moves through screening (stage transitions, Checkr/Onfido background-check
status) and when a Post-Hire worker is activated, deactivated, or has documents uploaded or approved.

Every request carries an `X-OBIQ-SIGNATURE-V2` header: the hex HMAC-SHA256 of the raw request body, keyed
with your Fountain Hire private API token.

## When to Use This Skill

* How do I receive Fountain webhooks?
* How do I verify the `X-OBIQ-SIGNATURE-V2` signature on a Fountain webhook?
* How do I handle Fountain applicant (screening) and worker (Post-Hire) webhook events?
* Why does my Fountain webhook signature verification fail (or return 401)?

## Verification (core)

```javascript
const crypto = require('crypto');

function verifyFountainSignature(rawBody, signatureHeader, secret = process.env.FOUNTAIN_WEBHOOK_SECRET) {
  if (!secret || !Buffer.isBuffer(rawBody)) return false;
  // Exactly 64 hex characters: Buffer.from(x, 'hex') silently drops bad characters.
  if (typeof signatureHeader !== 'string' || !/^[0-9a-fA-F]{64}$/.test(signatureHeader)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest(); // key = UTF-8 token
  return crypto.timingSafeEqual(Buffer.from(signatureHeader, 'hex'), expected);
}

```

* Header: `X-OBIQ-SIGNATURE-V2: <hex>`. The bare hex digest, no prefix; compare the whole value.
* Signed message: the **raw body bytes** only. No timestamp or id is signed, so there is no replay window to check.
* Key: your Fountain Hire private API token as UTF-8 bytes, used as-is (not hex- or base64-decoded).
  Rotating the API token rotates the webhook key.
* Python: `hmac.compare_digest(bytes.fromhex(sig), hmac.new(secret.encode(), raw, hashlib.sha256).digest())`
  on bytes after `re.fullmatch(r"[0-9a-fA-F]{64}", sig)`; never compare `str`.
* Missing, malformed or mismatched signature: return `401`.
* Read the body raw (`express.raw({ type: '*/*' })`, `await request.arrayBuffer()`, `await request.body()`)
  and parse JSON only after the check.

Paste your Fountain Hire private API token. Only the default body signing is supported: accounts switched to applicant ID signing fail verification.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/fountain-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/fountain-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/fountain-webhooks/examples/fastapi/).

## Event Types

Fountain has no event-name field or header. Dispatch on the shape of the payload:

| Event | Payload shape | Sent When |
| --- | --- | --- |
| `applicant` | `{ "applicant": { id, name, email, stage, funnel, background_checks, ... } }` | An applicant changes stage, or a background check (Checkr/Onfido) changes status |
| `worker` | Flat worker object `{ id, name, email, is_active, approved_at, data_collection_checks, ... }` | A Post-Hire worker is activated or deactivated, or documents are uploaded or approved |

Return `200` quickly and process slow work asynchronously: Fountain retries a failed request twice, and a
webhook that fails more than 10 times in 24 hours is disabled until you fix and re-enable it.

## Environment Variables

```bash
FOUNTAIN_WEBHOOK_SECRET=your_fountain_hire_private_api_token

```

## Local Development

Fountain can't reach `localhost`. Tunnel with the Hookdeck CLI (no account required):

```bash
npx hookdeck-cli listen 3000 fountain --path /webhooks/fountain

```

Use the printed URL as the webhook URL in Fountain Hire (Company Settings -> Webhooks).

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/fountain-webhooks/references/overview.md) - What Fountain webhooks are, events and payload shapes
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/fountain-webhooks/references/setup.md) - Configure the webhook and find your API token
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/fountain-webhooks/references/verification.md) - Signature details, gotchas, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/fountain-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests