HiringCenter Webhooks Overview
HiringCenter Outbound Webhooks
HiringCenter can notify your systems in real time when something happens in your account, for example when a prospect is created or a task is completed. HiringCenter sends a signed JSON POST to an HTTPS URL you choose. Your server receives the request; HiringCenter makes it.
You can also poll recent prospects with your API key (GET /webhooks/poll) if your integration cannot accept inbound HTTPS requests. See Polling instead of webhooks.
For the REST endpoints you call with an API key (prospects, notes, tasks, and so on), see the API Overview and the API Reference.
Add a Webhook
Webhooks are managed in the HiringCenter app by an account admin or owner, under Settings → API / Webhooks (see API Keys & Webhooks). API keys cannot create, change, or delete webhooks.
- Click Add Webhook.
- Enter your URL. It must be a public
https://address. Private, local, and non-HTTPS addresses are rejected when you save. - Choose one or more events (see Event types).
- Optionally add a description (up to 500 characters).
- Save, then copy the webhook secret (
whsec_...) and store it on your server, for example in an environment variable or a secrets manager. You need it to verify signatures.
From the webhook list you can also:
- Reveal the current secret again.
- Rotate the secret (see Rotate a secret).
- Test the webhook (see Send a test event).
- Edit the URL, events, or description.
- Disable or enable the webhook. Disabled webhooks receive no events.
- Delete the webhook.
The list also shows when each webhook was last used and its error rate over the last 7 days.
Each webhook must have a unique combination of URL and events. Adding a webhook with the same URL and the same events as an existing one re-enables the existing webhook and keeps its current secret (use Reveal to see it). Editing a webhook so that it matches another one is rejected.
The Request Your Endpoint Receives
| Item | Value |
|---|---|
| Method | POST |
Content-Type | application/json |
User-Agent | HiringCenter-Webhook/2.0 |
X-HiringCenter-Signature | t=<unix_seconds>,v1=<hex_hmac> |
| Body | JSON event envelope (see Event payload) |
Delivery rules
- An event is sent to every enabled webhook whose events include that event type.
- Your endpoint has 10 seconds to respond.
- Respond with any 2xx status to accept the event. Do slow work after you respond.
- Redirects are not followed. A
3xxresponse counts as a failed delivery, so use the final URL. - Failed deliveries (non-2xx, timeout, or connection error) are not retried. They count toward the webhook's 7-day error rate in Settings. If you cannot miss events, also reconcile periodically with the REST API (for example
GET /prospects). - Each delivery has a unique
id. Use it to ignore duplicates.
Event Types
| Event type | When it is sent |
|---|---|
prospect.created | A prospect is created in the app, through the API, by an import, or another way. Sent a few seconds after the prospect is created. |
prospect.updated | Any field on a prospect changes, including changes HiringCenter makes itself (for example when a message is sent or a meeting is booked). Check data.changes to react only to the fields you care about. |
task.created | A task is created in the app, through the API, or by HiringCenter AI. |
task.updated | A task's details (such as its title or due date) are changed in the app or through the API. |
task.completed | A task is marked complete in the app or through the API. Completing a task sends only task.completed, not task.updated. |
task.deleted | A task is deleted in the app or through the API. |
Prospects created with muteWebhook: true send no prospect events. See Suppress webhooks for a prospect.
Event Payload
Every delivery uses the same envelope:
Code
| Field | Description |
|---|---|
id | Unique id for this delivery (not the prospect or task id). |
type | Event type from the table above. |
apiVersion | Payload version, currently 2025-10-28. |
createdAt | ISO 8601 time the event was sent. |
accountId | Your HiringCenter account id. |
resource | prospect or task. |
resourceId | The prospect or task id. |
data | Event details. The shape depends on type (below). |
links | Present only on test events. A copy of data.links. |
Treat fields you do not recognize as optional; new fields may be added.
prospect.created
Code
data.prospect is the full prospect record. Use data.prospectId for the id. Timestamps inside data.prospect are Unix milliseconds. To get the prospect in the standard API format, call GET /prospects/{prospectId}.
prospect.updated
Code
changeshas one entry per changed field, withbeforeandaftervalues. A field that was added has nobefore; a field that was removed has noafter. Nested fields use dot paths such asaddress.city.prospectis the full prospect record after the update.
Task events
| Event | data contains |
|---|---|
task.created | taskId, task (the new task record, including task, prospectId, assignedTo, dueAt) |
task.updated | taskId, updates (the fields set by the update, plus updatedAt) |
task.completed | taskId, updates (including completedAt, completedBy, updatedAt) |
task.deleted | taskId |
Task timestamps (dueAt, completedAt, updatedAt) are Unix milliseconds. To get the full current task after an update, call GET /tasks/{taskId}.
Verify Signatures
Every delivery includes an X-HiringCenter-Signature header:
Code
tis the Unix time in seconds when the request was signed.- Each
v1is a lowercase hexHMAC_SHA256(webhookSecret, t + "." + rawBody). rawBodyis the exact request body you received. Do not parse and re-serialize the JSON before verifying.
Steps
- Read the raw body before any middleware changes it (for example
express.raw(...)in Express, orawait request.text()in fetch-style handlers). - Read the
X-HiringCenter-Signatureheader and split it on commas intokey=valueparts. - Take
tand everyv1. Reject the request if either is missing. - Reject the request if
tis more than 300 seconds from your current time (replay protection). - Compute
expected = HMAC_SHA256(webhookSecret, t + "." + rawBody)as lowercase hex. - Compare
expectedto eachv1in constant time. Accept if any match. - Only then parse the JSON and handle the event.
Common mistakes
- Verifying a parsed or re-stringified body instead of the raw body.
- Using a timestamp in milliseconds instead of the
tvalue from the header. - Checking only the first
v1(this breaks during secret rotation). - Comparing signatures with
===instead of a constant-time comparison.
Node.js helper
Code
Express example
Code
Python example
Code
Rotate a Secret
To replace a webhook's secret, click Rotate on the webhook in Settings → API / Webhooks and copy the new secret.
For 24 hours after a rotation, each delivery carries two signatures, one made with the new secret and one with the previous secret:
Code
A receiver that accepts any matching v1 keeps working with either secret during that window. To rotate without downtime:
- Rotate the secret and copy the new value.
- Deploy the new secret to your receiver within 24 hours.
- After 24 hours, deliveries are signed with the new secret only.
If a secret may have leaked, rotate it right away and deploy the new value as soon as possible. The previous secret still verifies for the 24-hour window.
Send a Test Event
Click Test on a webhook to send it a signed sample event right away. The test:
- Uses the webhook's first selected event type, with sample data shaped like a real event.
- Includes
"isTest": trueindata.metadata, plus samplelinks(so your handler can ignore test events). - Is signed exactly like a real delivery (including the previous secret during a rotation window), so it is a good check of your signature code.
- Is sent even if the webhook is disabled.
The result shows the payload and headers that were sent, the HTTP status your endpoint returned, and the start of your response body.
Suppress Webhooks for a Prospect
To create a prospect without sending prospect.created, set muteWebhook: true on POST /prospects:
Code
The setting stays on that prospect. A muted prospect sends no prospect.created or prospect.updated events, and the account's automations and sequences do not run for it. Task events for the prospect's tasks are still sent.
Polling Instead of Webhooks
If your integration cannot accept inbound HTTPS requests (for example a Zapier polling trigger), poll recent prospects with your API key:
Code
- Returns a JSON array of up to 100 of the account's most recently created prospects, newest first. There is no paging; poll often enough that you do not miss prospects between polls.
- Deduplicate on
id. The same prospects appear in consecutive polls until newer ones push them out. - If the account has no prospects, the array contains one sample record with
idsample_prospect_123. Ignore it. - Records use a flat snake_case shape (
first_name,last_name,stage_name,owner_email, and so on), not the webhook envelope.created_atandupdated_atare ISO 8601 strings. eventTypedefaults toprospect.created, which is the only supported value. Any other value returns400.- The account comes from your API key.
See GET /webhooks/poll in the API Reference for every field.
Related Documentation
- API Keys & Webhooks settings
- API Overview
- API Reference (Webhooks tag, including
GET /webhooks/poll)

