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.

704

"Manage webhooks" ability

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

1278

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.

973

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.

FieldType/formatDescription
idintegerUniquely identifies the event. Subsequent delivery retries will have the same id.
attempt_countintegerThe count of attempts Recruitee has made to deliver the given event. It starts at 1. Subsequent delivery retries increment it by 1.
created_atISO 8601 UTCWhen the event originally happened. Does not change with subsequent delivery retries.
event_typestringIndicates which event this is. For information on supported events, see the detailed event documentation below.
event_subtypestringContains more information about the event. For information on possible values, see the detailed event documentation below.
payloadmapData 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"
      }
    ]
  }
}
FieldType/formatDescription
event_typestringAlways equals "new_candidate".
event_subtypestringCan be "email", "career_site", "manual", or "imported" to indicate the way the candidate was created.
payload.candidateCandidateNew candidate.
payload.companyCompany
payload.offersOffer[]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"
    }
  }
}
FieldType/formatDescription
event_typestringAlways equals "candidate_assigned".
event_subtypestringCan be "email", "career_site", "manual", or "imported" to indicate the way the candidate was assigned to an offer.
payload.candidateCandidateAssigned candidate.
payload.companyCompany
payload.offerOfferOffer 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",
        "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",
      "slug": "cruiser-captain",
      "tags": [],
      "title": "Cruiser Captain",
      "updated_at": "2020-12-11T23:50:55.835946Z"
    }
  }
}
FieldType/formatDescription
event_typestringAlways equals "candidate_moved".
event_subtypestringCan 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.candidateCandidateCandidate whose status was changed.
payload.companyCompany
payload.offerOfferOffer the status change was related to.
payload.detailsCandidateMovedDetailsAdditional 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"
      }
    ]
  }
}
FieldType/formatDescription
event_typestringAlways equals "candidate_deleted".
event_subtypestringAlways equals "manual".
payload.candidateCandidateDeleted candidate.
payload.companyCompany
payload.offersOffer[]Offers the deleted candidate was assigned to.

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",
      "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"
    }
  }
}
FieldType/formatDescription
event_typestringAlways equals "offer_published".
event_subtypestringAlways equals "manual".
payload.companyCompany
payload.offerOfferOffer that has been published.

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",
      "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"
    }
  }
}
FieldType/formatDescription
event_typestringAlways equals "offer_unpublished".
event_subtypestringAlways equals "manual".
payload.companyCompany
payload.offerOfferOffer that has been unpublished or archived.

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",
      "slug": "hr-manager",
      "status": "published",
      "tags": [
        {
          "id": 123,
          "name": "Important"
        }
      ],
      "title": "HR Manager",
      "updated_at": "2023-02-24T12:13:05.205048Z"
    }
  }
}
FieldType/formatDescription
event_typestringAlways equals "offer_updated".
event_subtypestringCan be "offer_changed", "status_changed", or "tags_changed".
payload.companyCompany
payload.offerOfferOffer that has been updated.

Payload structure


Candidate

FieldType/formatDescription
idinteger
namestring
emailsstring[]
phonesstring[]
photo_thumb_urlstring
sourcestringCan be one of "career_site", "email", "manual", "import", or "referral".
created_atISO 8601 UTC
updated_atISO 8601 UTC

Offer

FieldType/formatDescription
idinteger
titlestring
kindstringCan be one of "job" or "talent_pool".
slugstring
departmentDepartment
tagsTag[]
created_atISO 8601 UTC
updated_atISO 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":
FieldType/formatDescription
from_stageStageCandidate's previous stage in the pipeline.
to_stageStageCandidate's new stage in the pipeline.
  1. event_subtype is "disqualified":
FieldType/formatDescription
to_stageStageThe stage candidate was disqualified in.
disqualify_reasonDisqualifyReasonReason for candidate's disqualification.
  1. event_subtype is "requalified":
FieldType/formatDescription
to_stageStageThe stage candidate was brought back to.

SimpleEntity

Includes Company, Tag, Department, DisqualifyReason.

FieldType/formatDescription
idintegerEntity's ID.
namestringEntity's name.

Stage

FieldType/formatDescription
idintegerStage's ID.
namestringStage's name.
categorystringStage's category. Can be one of "none", "apply", "phone_screen",
"interview", "evaluation", "offer", "hire".

Email notifications


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

992