Agent skill
Recruitee Webhooks Skill
Receive and verify Recruitee webhooks. Use when setting up Recruitee webhook handlers, debugging X-Recruitee-Signature verification, or handling recruiting events like new_candidate, candidate_moved, candidate_assigned, and candidate_deleted.
Install this skill
npx skills add hookdeck/webhook-skills --skill recruitee-webhooks
When to Use This Skill
- How do I receive Recruitee webhooks?
- How do I verify the X-Recruitee-Signature header?
- How do I handle new_candidate, candidate_moved, or candidate_assigned events?
- How do I detect a disqualified candidate in a Recruitee webhook?
- Why is my Recruitee webhook signature verification failing?
Verification (core)
Recruitee signs the raw request body with HMAC-SHA256 keyed on the webhook secret and sends the hex digest in X-Recruitee-Signature (no prefix, no timestamp). There is no official SDK, so verify manually: compute the HMAC over the raw body (before JSON parsing) and compare timing-safe.
The event name is in the body, not a header. Every payload is { id, attempt_.
Node:
const crypto = require('crypto');
function verifyRecruiteeWebhook(rawBody, signatureHeader, secret) {
if (!signatureHeader || !secret) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
// Compare hex strings as UTF-8 bytes; Buffer.from(x, 'hex') stops at the first non-hex char
const received = Buffer.from(signatureHeader.trim().toLowerCase(), 'utf8');
const wanted = Buffer.from(expected, 'utf8');
return received.length === wanted.length && crypto.timingSafeEqual(received, wanted);
}
Python:
import hmac, hashlib
def verify_recruitee_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool:
if not signature_header or not secret:
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature_header.strip().lower().encode(), expected.encode())
Recruitee publishes a test vector: secret DuP4ej5yyJB5TIrIEI/dCtJN7sHj with a candidate_moved body gives 3b64e3049cb9e108fbb18a453e909cb4a32e3ae140ea01d84c2b5d316c19162f (see the tests in the examples).
Read the body as raw bytes regardless of content type (Express: express.raw({ type: '*/*' })), because any re-serialisation changes the digest.
For complete handlers with route wiring, event dispatch, and tests, see:
Test Request
When you click Test or Verify and create, Recruitee POSTs {"test":true} and the endpoint must answer 200 for the webhook to be created. Handle this before event dispatch.
Common Event Types
event_type carries the event; event_subtype refines it.
event_type | event_subtype | Triggered When |
|---|---|---|
new_candidate | email, career_site, manual, imported | A new candidate applies or is created |
candidate_assigned | email, career_site, manual, imported | A candidate is assigned to a job or talent pool |
candidate_moved | stage_changed, disqualified, requalified | A candidate changes pipeline stage, is disqualified, or is requalified |
candidate_deleted | manual | A candidate is deleted |
offer_published | manual | A job is published |
offer_unpublished | manual | A job is unpublished or archived |
offer_updated | offer_changed, status_changed, tags_changed | A job is updated |
There is no
candidate_disqualifiedevent type. A disqualification arrives ascandidate_movedwithevent_andsubtype: "disqualified" payload.details.disqualify_.reason
For the full event reference, see Recruitee webhook docs.
Important Headers
| Header | Description |
|---|---|
X-Recruitee-Signature | Hex HMAC-SHA256 of the raw body, keyed with the webhook secret |
User-Agent: Recruitee-Webhooks | Identifies Recruitee requests. Do not use for auth |
Delivery Behaviour
- Respond with exactly 200 within 10 seconds. Any other status is a failure.
- Failures retry up to nine times (1 min, 3 min, 10 min, 45 min, 2 h, 5 h, 10 h, 24 h, 48 h).
- Retries keep the same
idand incrementattempt_count: dedupe onid.
Environment Variables
RECRUITEE_WEBHOOK_SECRET=your_webhook_secret # Settings > Apps and Plugins > Webhooks
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 recruitee --path /webhooks/recruitee
Reference Materials
- references/overview.md - Recruitee webhook concepts and events
- references/setup.md - Dashboard configuration guide
- references/verification.md - Signature verification details