# JobAdder Webhooks

JobAdder (recruitment CRM) sends an HTTP POST with a JSON body when an event occurs. Webhooks are
created through the Webhook API (`POST /webhooks`, partner app with `offline_access`). Payloads are
**not signed**. The only credential is the optional `authorization` value set on the webhook, which
JobAdder sends back as the `Authorization` header.

## When to Use This Skill

* How do I receive JobAdder webhooks?
* How do I verify the JobAdder `Authorization` header on a webhook?
* Does JobAdder sign webhook payloads? (No: shared `Authorization` value only)
* Why does my JobAdder webhook endpoint return 401?
* How do I handle `candidate_status_changed` or `placement_approved` events?

## Verification (core)

Set an `authorization` value when you create the webhook. Store it **exactly as set, including any
`Bearer ` or `Basic ` prefix**, as `JOBADDER_AUTHORIZATION`. Compare the **whole** `Authorization`
header to it. Never strip, parse or base64-decode it, even when it looks like Basic credentials:

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

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyJobAdderRequest(headers, expected = process.env.JOBADDER_AUTHORIZATION) {
  if (!expected) return false;                  // never accept when unconfigured
  const received = headers['authorization'];    // full value, e.g. "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
  return Boolean(received) && safeEqual(received, expected);
}

```

Return `401` when this fails, `400` for a body that isn't a JSON object, and `200` once handled.
Read the body raw (`express.raw({ type: '*/*' })`) so any content type works, and parse it only
after the check. Python and Next.js equivalents are in
[references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/jobadder-webhooks/references/verification.md).

**A static shared credential authenticates the sender, not the body, and gives no replay
protection** (no signature, no timestamp). Use HTTPS, dedupe on `eventId`, and rotate the
credential (`PUT /webhooks/{webhookId}`) if it leaks.

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

## Hookdeck Source Setup

Hookdeck's source type is `JOBADDER` (labelled "JobAdder"). Its setup note matches the above:
"Paste the authorization value exactly as set in the JobAdder webhook, including any "Bearer " or
"Basic " prefix."

## Event Types

The body names the event in `event`. Payload envelope:

```json
{ "apiVersion": 2, "event": "placement_approved", "eventId": "...", "eventDate": "2019-01-01T09:00:00Z", "eventUser": {}, "eventData": {} }

```

| Event | Triggered when |
| --- | --- |
| `candidate_status_changed` | A candidate's status changes |
| `job_status_changed` | A job's status changes |
| `jobapplication_status_changed` | A job application's status changes |
| `placement_approved` | A placement is approved |

JobAdder has about 40 more (`placement_status_changed`, `interview_scheduled`, `candidate_updated`,
`jobad_posted`, folder events and more); see [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/jobadder-webhooks/references/overview.md). The
docs say ordering is not guaranteed and duplicates may occur.

## Environment Variables

```bash
JOBADDER_AUTHORIZATION=Basic dXNlcm5hbWU6cGFzc3dvcmQ=   # full value as set on the webhook

```

## Local Development

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

```

No account required. Use the printed URL as the `url` of your JobAdder webhook.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/jobadder-webhooks/references/overview.md) - Events, payload envelope, event filters
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/jobadder-webhooks/references/setup.md) - Creating a webhook with the Webhook API and the authorization value
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/jobadder-webhooks/references/verification.md) - Header check in Node, Python and Next.js, gotchas
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/jobadder-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests