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.

"Manage webhooks" ability"Manage webhooks" ability

"Manage webhooks" ability

Configure webhooks under Settings > Apps and Plugins > Webhooks by clicking + New webhook.

Webhook configuration dialogWebhook configuration dialog

Webhook configuration dialog

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.

Webhook secretWebhook secret

Webhook secret

The value of X-Recruitee-Signature header is a base64-encoded 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",
        "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",
      "slug": "cruiser-captain",
      "tags": [],
      "title": "Cruiser Captain",
      "updated_at": "2020-12-11T23:50:55.835946Z"
    }
  }
}

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.

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"
      },
      "to_stage": {
        "id": 4110,
        "name": "Phone interview"
      }
    },
    "offer": {
      "created_at": "2018-07-10T10:29:15.000000Z",
      "department": {
        "id": 9,
        "name": "Maritime"
      },
      "id": 617,
      "kind": "job",
      "slug": "cruiser-captain",
      "tags": [],
      "title": "Cruiser Captain",
      "updated_at": "2020-12-11T23:50:55.835946Z"
    }
  }
}

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.

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",
        "slug": "cruiser-captain",
        "tags": [],
        "title": "Cruiser Captain",
        "updated_at": "2020-12-11T23:50:55.835946Z"
      }
    ]
  }
}

Field

Type/format

Description

event_type

string

Always equals "candidate_deleted".

event_subtype

string

Always equals "manual".

payload.candidate

Candidate

Deleted candidate.

payload.company

Company

payload.offers

Offer[]

Offers the deleted candidate was assigned to.

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".

slug

string

department

Department

tags

Tag[]

created_at

ISO 8601 UTC

updated_at

ISO 8601 UTC

CandidateMovedDetails

The contents of payload.details in the "Pipeline change" event depend on event_subtype. There are three options.

  1. event_subtype is "stage_changed":

Field

Type/format

Description

from_stage

Stage

Candidate's previous stage in the pipeline.

to_stage

Stage

Candidate's new stage in the pipeline.

  1. event_subtype is "disqualified":

Field

Type/format

Description

from_stage

Stage

The stage candidate was disqualified from.

disqualify_reason

DisqualifyReason

Reason for candidate's disqualification.

  1. event_subtype is "requalified":

Field

Type/format

Description

to_stage

Stage

The stage candidate was brought back to.

SimpleEntity

Includes Company, Tag, Department, Stage, DisqualifyReason.

Field

Type/format

Description

id

integer

Entity's ID.

name

string

Entity's name.

Email notifications


Under Settings > Apps and Plugins > Webhooks you can choose team members that should be notified about failed webhook request deliveries.