Webhooks
Webhooks allow you to receive notifications when events happen in Recruitee. They may be used standalone or as a complement to the Recruitee JSON API. Once a webhook is configured, Recruitee will send HTTP POST requests to the given URL whenever one of the chosen events occurs.
Configuring webhooks
To manage webhooks, a team member's role needs to have the "Manage webhooks" ability assigned in their hiring role under Add-ons.
Configure webhooks under Settings > Apps and Plugins > Webhooks by clicking + New webhook.
Name
can be filled in with anything meaningful to you.
Fill in the endpoint that Recruitee should send notifications to under POST URL
; you need to use HTTPS.
Select the events you are interested in. It is fine not to select any events yet - you can adjust that later on.
The Test
button prompts Recruitee to send a test request to your endpoint. A special test
parameter is included in the request so that you know it's not a real event. When developing your service, make sure that you handle this parameter. Example test request:
POST /your-path HTTP/1.1
Content-Type: application/json
User-Agent: Recruitee-Webhooks
{"test":true}
You can also use the Test payload
buttons. They work just like the Test
button, but the requests carry example event payloads in addition to the test
parameter.
To successfully Verify and create
a webhook configuration, your service needs to respond with 200
HTTP status to the test request.
Request log
Every request is logged for 30 days. You can find the request log under Settings > Apps and Plugins > Webhooks in the Logs tab. Upon revealing request details, you'll see the request body, response, next attempt at (in case of a failure), as well as options to cancel the automatic retry and retry the request immediately.
Delivery guarantees
Any response status other than 200
is considered a webhook delivery failure. Recruitee will then keep trying to deliver the webhook. Each successive retry will be delayed more and more, for up to nine attempts:
- the 1st retry will happen after one minute
- the 2nd retry will happen 3 minutes after the 1st one has failed
- the 3rd retry - 10 minutes after
- the 4th retry - 45 minutes after
- 5th - 2 hours
- 6th - 5 hours
- 7th - 10 hours
- 8th - 24 hours
- 9th retry - 48 hours
The next scheduled attempt date for each request can be found on the Logs tab.
Requests will timeout after 10 seconds. Make sure your service responds within this time frame.
Rate limiting
Trial companies are limited to 5 requests per minute. All others have the quota of 100 requests per minute. When the company reaches its limit, any subsequent webhook requests get delayed by 10 +/- 5 minutes.
Webhook verification
We strongly recommend that you verify the webhooks we send. You can do that thanks to the X-Recruitee-Signature
header we include in all HTTP requests and the webhook secret you can find under Settings > Apps and Plugins > Webhooks.
The value of X-Recruitee-Signature
header is a base16-encoded (hexadecimal) HMAC-SHA256 digest of the webhook secret and body of the HTTP request.
For example, let us assume that your secret is DuP4ej5yyJB5TIrIEI/dCtJN7sHj
, and the HTTP request looks as follows:
POST /your-path HTTP/1.1
Content-Type: application/json
User-Agent: Recruitee-Webhooks
X-Recruitee-Signature: 3b64e3049cb9e108fbb18a453e909cb4a32e3ae140ea01d84c2b5d316c19162f
{"attempt_count":1,"created_at":"2020-12-14T04:47:33.905241Z","event_subtype":"stage_changed","event_type":"candidate_moved","id":30,"payload":{"candidate":{"created_at":"2020-12-09T14:36:56.578000Z","emails":[],"id":975539,"name":"test","phones":[],"photo_thumb_url":"https://recruitee-staging.s3.eu-central-1.amazonaws.com/candidates/975539/thumb_photo_anydaa715ph6.png","referrer":null,"source":"manual","updated_at":"2020-12-09T14:37:01.119831Z"},"company":{"id":5943,"name":"MP company 2"},"details":{"from_stage":{"id":911876,"name":"Phone interview"},"to_stage":{"id":911877,"name":"On-site interview"}},"offer":{"created_at":"2020-12-09T14:37:59.875608Z","department":null,"id":269122,"kind":"job","slug":"test-job","tags":[],"title":"test job","updated_at":"2020-12-09T14:38:17.539041Z"}}}
To verify the webhook request, you need to recreate those steps in your service:
$ SECRET='DuP4ej5yyJB5TIrIEI/dCtJN7sHj'
$ BODY='{"attempt_count":1,"created_at":"2020-12-14T04:47:33.905241Z","event_subtype":"stage_changed","event_type":"candidate_moved","id":30,"payload":{"candidate":{"created_at":"2020-12-09T14:36:56.578000Z","emails":[],"id":975539,"name":"test","phones":[],"photo_thumb_url":"https://recruitee-staging.s3.eu-central-1.amazonaws.com/candidates/975539/thumb_photo_anydaa715ph6.png","referrer":null,"source":"manual","updated_at":"2020-12-09T14:37:01.119831Z"},"company":{"id":5943,"name":"MP company 2"},"details":{"from_stage":{"id":911876,"name":"Phone interview"},"to_stage":{"id":911877,"name":"On-site interview"}},"offer":{"created_at":"2020-12-09T14:37:59.875608Z","department":null,"id":269122,"kind":"job","slug":"test-job","tags":[],"title":"test job","updated_at":"2020-12-09T14:38:17.539041Z"}}}'
$ echo -n $BODY | openssl dgst -sha256 -hmac $SECRET
3b64e3049cb9e108fbb18a453e909cb4a32e3ae140ea01d84c2b5d316c19162f
As you can see, we have arrived at the same result as the contents of X-Recruitee-Signature
header. This confirms that Recruitee sent the webhook and its body is intact.
Event structure
The body of each HTTP request sent by Recruitee contains an event. All events share a common structure.
Field | Type/format | Description |
---|---|---|
id | integer | Uniquely identifies the event. Subsequent delivery retries will have the same id . |
attempt_count | integer | The count of attempts Recruitee has made to deliver the given event. It starts at 1. Subsequent delivery retries increment it by 1. |
created_at | ISO 8601 UTC | When the event originally happened. Does not change with subsequent delivery retries. |
event_type | string | Indicates which event this is. For information on supported events, see the detailed event documentation below. |
event_subtype | string | Contains more information about the event. For information on possible values, see the detailed event documentation below. |
payload | map | Data related to the event, different for each event type. For information on possible values, see the detailed event documentation below. |
Candidate created
Triggered when a new candidate applies or is created.
Example request body:
{
"attempt_count": 1,
"created_at": "2020-12-11T23:50:56.168592Z",
"event_subtype": "manual",
"event_type": "new_candidate",
"id": 73,
"payload": {
"candidate": {
"created_at": "2020-12-11T23:50:54.110368Z",
"emails": [
"[email protected]"
],
"id": 21056,
"name": "John Doe",
"phones": [
"123123123"
],
"photo_thumb_url": "https://recruitee-dev.s3.eu-central-1.amazonaws.com/candidates/21056/thumb_photo_vweaseucrbtp.png",
"source": "manual",
"updated_at": "2020-12-11T23:50:54.110368Z"
},
"company": {
"id": 1,
"name": "Devtest Company"
},
"offers": [
{
"created_at": "2018-07-10T10:29:15.000000Z",
"department": {
"id": 9,
"name": "Maritime"
},
"id": 617,
"kind": "job",
"locations": [
{
"country_code": "DE",
"full_address": "Germany, Saarland, Düsseldorf, 90910, Saarlander Street",
"id": 8,
"state_code": "SL"
},
{
"country_code": "PL",
"full_address": "Poland, Wielkopolskie, Poznań, 61-248, Polish Street",
"id": 63,
"state_code": "WP"
}
],
"slug": "cruiser-captain",
"tags": [],
"title": "Cruiser Captain",
"updated_at": "2020-12-11T23:50:55.835946Z"
}
]
}
}
Field | Type/format | Description |
---|---|---|
event_type | string | Always equals "new_candidate". |
event_subtype | string | Can be "email", "career_site", "manual", or "imported" to indicate the way the candidate was created. |
payload.candidate | Candidate | New candidate. |
payload.company | Company | |
payload.offers | Offer[] | Offers the new candidate was assigned to on creation. |
Candidate assigned
Triggered when a new or existing candidate is assigned to a job or talent pool.
Example request body:
{
"attempt_count": 1,
"created_at": "2020-12-11T23:50:56.233856Z",
"event_subtype": "manual",
"event_type": "candidate_assigned",
"id": 74,
"payload": {
"candidate": {
"created_at": "2020-12-11T23:50:54.110368Z",
"emails": [
"[email protected]"
],
"id": 21056,
"name": "John Doe",
"phones": [
"123123123"
],
"photo_thumb_url": "https://recruitee-dev.s3.eu-central-1.amazonaws.com/candidates/21056/thumb_photo_vweaseucrbtp.png",
"source": "manual",
"updated_at": "2020-12-11T23:50:54.110368Z"
},
"company": {
"id": 1,
"name": "Devtest Company"
},
"offer": {
"created_at": "2018-07-10T10:29:15.000000Z",
"department": {
"id": 9,
"name": "Maritime"
},
"id": 617,
"kind": "job",
"locations": [
{
"country_code": "DE",
"full_address": "Germany, Saarland, Düsseldorf, 90910, Saarlander Street",
"id": 8,
"state_code": "SL"
},
{
"country_code": "PL",
"full_address": "Poland, Wielkopolskie, Poznań, 61-248, Polish Street",
"id": 63,
"state_code": "WP"
}
],
"slug": "cruiser-captain",
"tags": [],
"title": "Cruiser Captain",
"updated_at": "2020-12-11T23:50:55.835946Z"
},
"placement_locations": [
{
"country_code": "DE",
"full_address": "Germany, Saarland, Düsseldorf, 90910, Saarlander Street",
"id": 8,
"state_code": "SL"
}
]
}
}
Field | Type/format | Description |
---|---|---|
event_type | string | Always equals "candidate_assigned". |
event_subtype | string | Can be "email", "career_site", "manual", or "imported" to indicate the way the candidate was assigned to an offer. |
payload.candidate | Candidate | Assigned candidate. |
payload.company | Company | |
payload.offer | Offer | Offer the candidate was assigned to. |
payload.placement_locations | PlacementLocations | Offer locations the candidate has been assigned to. |
Move on pipeline
Triggered when a candidate is moved to a different pipeline change, disqualified or requalified.
{
"attempt_count": 1,
"created_at": "2020-12-12T00:30:01.860998Z",
"event_subtype": "stage_changed",
"event_type": "candidate_moved",
"id": 75,
"payload": {
"candidate": {
"created_at": "2020-12-11T23:50:54.110368Z",
"emails": [
"[email protected]"
],
"id": 21056,
"name": "John Doe",
"phones": [
"123123123"
],
"photo_thumb_url": "https://recruitee-dev.s3.eu-central-1.amazonaws.com/candidates/21056/thumb_photo_vweaseucrbtp.png",
"source": "manual",
"updated_at": "2020-12-11T23:50:54.110368Z"
},
"company": {
"id": 1,
"name": "Devtest Company"
},
"details": {
"from_stage": {
"id": 5716,
"name": "Applied",
"category": "apply"
},
"to_stage": {
"id": 4110,
"name": "Phone interview",
"category": "phone_screen"
}
},
"offer": {
"created_at": "2018-07-10T10:29:15.000000Z",
"department": {
"id": 9,
"name": "Maritime"
},
"id": 617,
"kind": "job",
"locations": [
{
"country_code": "DE",
"full_address": "Germany, Saarland, Düsseldorf, 90910, Saarlander Street",
"id": 8,
"state_code": "SL"
},
{
"country_code": "PL",
"full_address": "Poland, Wielkopolskie, Poznań, 61-248, Polish Street",
"id": 63,
"state_code": "WP"
}
],
"slug": "cruiser-captain",
"tags": [],
"title": "Cruiser Captain",
"updated_at": "2020-12-11T23:50:55.835946Z"
},
"placement_locations": [
{
"country_code": "DE",
"full_address": "Germany, Saarland, Düsseldorf, 90910, Saarlander Street",
"id": 8,
"state_code": "SL"
}
]
}
}
Field | Type/format | Description |
---|---|---|
event_type | string | Always equals "candidate_moved". |
event_subtype | string | Can be "stage_changed", "disqualified", or "requalified" to indicate the way the candidate's state was changed. event_subtype affect the contents of the payload.details field. |
payload.candidate | Candidate | Candidate whose status was changed. |
payload.company | Company | |
payload.offer | Offer | Offer the status change was related to. |
payload.details | CandidateMovedDetails | Additional details about the change, format depends on event_subtype . |
payload.placement_locations | PlacementLocations | Offer locations the candidate was assigned to |
Candidate deleted
Triggered when a candidate is deleted.
Example request body:
{
"attempt_count": 1,
"created_at": "2020-12-11T23:50:56.168592Z",
"event_subtype": "manual",
"event_type": "candidate_deleted",
"id": 73,
"payload": {
"candidate": {
"created_at": "2020-12-11T23:50:54.110368Z",
"emails": [
"[email protected]"
],
"id": 21056,
"name": "John Doe",
"phones": [
"123123123"
],
"photo_thumb_url": "https://recruitee-dev.s3.eu-central-1.amazonaws.com/candidates/21056/thumb_photo_vweaseucrbtp.png",
"source": "manual",
"updated_at": "2020-12-11T23:50:54.110368Z"
},
"company": {
"id": 1,
"name": "Devtest Company"
},
"offers": [
{
"created_at": "2018-07-10T10:29:15.000000Z",
"department": {
"id": 9,
"name": "Maritime"
},
"id": 617,
"kind": "job",
"locations": [
{
"country_code": "DE",
"full_address": "Germany, Saarland, Düsseldorf, 90910, Saarlander Street",
"id": 8,
"state_code": "SL"
},
{
"country_code": "PL",
"full_address": "Poland, Wielkopolskie, Poznań, 61-248, Polish Street",
"id": 63,
"state_code": "WP"
}
],
"slug": "cruiser-captain",
"tags": [],
"title": "Cruiser Captain",
"updated_at": "2020-12-11T23:50:55.835946Z"
}
]
}
}
Job published
Triggered when a new job is published.
Example request body:
{
"attempt_count": 1,
"created_at": "2022-03-03T20:25:33.995135Z",
"event_subtype": "manual",
"event_type": "offer_published",
"id": 75,
"payload": {
"company": {
"id": 1,
"name": "Devtest Company"
},
"offer": {
"created_at": "2020-10-22T17:45:00Z",
"department": {
"id": 123,
"name": "Marketing"
},
"id": 123,
"kind": "job",
"locations": [
{
"country_code": "DE",
"full_address": "Germany, Saarland, Düsseldorf, 90910, Saarlander Street",
"id": 8,
"state_code": "SL"
},
{
"country_code": "PL",
"full_address": "Poland, Wielkopolskie, Poznań, 61-248, Polish Street",
"id": 63,
"state_code": "WP"
}
],
"slug": "front-end-web-developer",
"tags": [
{
"id": 123,
"name": "Important"
},
{
"id": 123,
"name": "Hiring Forecast Q3"
}
],
"title": "Front-end Web Developer",
"updated_at": "2020-10-22T17:45:00Z"
}
}
}
Job unpublished
Triggered when a job is unpublished or archived.
Example request body:
{
"attempt_count": 1,
"created_at": "2022-03-03T20:25:33.995135Z",
"event_subtype": "manual",
"event_type": "offer_unpublished",
"id": 76,
"payload": {
"company": {
"id": 1,
"name": "Devtest Company"
},
"offer": {
"created_at": "2020-10-22T17:45:00Z",
"department": {
"id": 123,
"name": "Marketing"
},
"id": 123,
"kind": "job",
"locations": [
{
"country_code": "DE",
"full_address": "Germany, Saarland, Düsseldorf, 90910, Saarlander Street",
"id": 8,
"state_code": "SL"
},
{
"country_code": "PL",
"full_address": "Poland, Wielkopolskie, Poznań, 61-248, Polish Street",
"id": 63,
"state_code": "WP"
}
],
"slug": "front-end-web-developer",
"tags": [
{
"id": 123,
"name": "Important"
},
{
"id": 123,
"name": "Hiring Forecast Q3"
}
],
"title": "Front-end Web Developer",
"updated_at": "2020-10-22T17:45:00Z"
}
}
}
Job updated
Triggered when a job is updated.
Example request body:
{
"attempt_count": 1,
"created_at": "2023-02-24T12:13:05.293345Z",
"event_subtype": "offer_changed",
"event_type": "offer_updated",
"id": 77,
"payload": {
"company": {
"id": 1,
"name": "Devtest Company"
},
"offer": {
"created_at": "2022-04-19T10:28:01.492893Z",
"department": {
"id": 123,
"name": "Marketing"
},
"id": 123,
"kind": "job",
"locations": [
{
"country_code": "DE",
"full_address": "Germany, Saarland, Düsseldorf, 90910, Saarlander Street",
"id": 8,
"state_code": "SL"
},
{
"country_code": "PL",
"full_address": "Poland, Wielkopolskie, Poznań, 61-248, Polish Street",
"id": 63,
"state_code": "WP"
}
],
"slug": "hr-manager",
"status": "published",
"tags": [
{
"id": 123,
"name": "Important"
}
],
"title": "HR Manager",
"updated_at": "2023-02-24T12:13:05.205048Z"
}
}
}
Payload structure
Candidate
Field | Type/format | Description |
---|---|---|
id | integer | |
name | string | |
emails | string[] | |
phones | string[] | |
photo_thumb_url | string | |
source | string | Can be one of "career_site", "email", "manual", "import", or "referral". |
created_at | ISO 8601 UTC | |
updated_at | ISO 8601 UTC |
Offer
Field | Type/format | Description |
---|---|---|
id | integer | |
title | string | |
kind | string | Can be one of "job" or "talent_pool". |
locations | Location[] | |
slug | string | |
department | Department | |
tags | Tag[] | |
created_at | ISO 8601 UTC | |
updated_at | ISO 8601 UTC |
PlacementLocations
Locations attached to candidate's assignment to offer. A list of Location
.
CandidateMovedDetails
The contents of payload.details
in the "Pipeline change" event depend on event_subtype
. There are three options.
event_subtype
is "stage_changed":
event_subtype
is "disqualified":
Field | Type/format | Description |
---|---|---|
to_stage | Stage | The stage candidate was disqualified in. |
disqualify_reason | DisqualifyReason | Reason for candidate's disqualification. |
event_subtype
is "requalified":
Field | Type/format | Description |
---|---|---|
to_stage | Stage | The stage candidate was brought back to. |
SimpleEntity
Includes Company
, Tag
, Department
, DisqualifyReason
.
Field | Type/format | Description |
---|---|---|
id | integer | Entity's ID. |
name | string | Entity's name. |
Stage
Field | Type/format | Description |
---|---|---|
id | integer | Stage's ID. |
name | string | Stage's name. |
category | string | Stage's category. Can be one of "none", "apply", "phone_screen", "interview", "evaluation", "offer", "hire". |
Location
Field | Type/format | Description |
---|---|---|
id | integer | Location's ID. |
country_code | string | Locations' ISO 3166 country code |
state_code | string | Locations' ISO 3166 state code |
full_address | string | Concatenated values of location's country name, state, city, postal code and street. |
Email notifications
Under Settings > Apps and Plugins > Webhooks you can choose team members that should be notified about failed webhook request deliveries.
Updated 3 months ago