Outbound webhooks
Configure HTTPS webhooks for partner platforms to receive signed InsightHire events.
Outbound webhooks let partner systems (GlazierHire, PivotalMetrics, custom integrations) receive real-time notifications when candidates apply, complete journeys, or scores update. Org admins manage endpoints from Settings → Integrations in the Partner webhooks section.
Settings path
| Action | Location |
|---|---|
| Create / edit webhooks | /dashboard/settings/integrations (Partner webhooks) |
| Dedicated webhooks page | /dashboard/integrations/webhooks |
| Delivery logs | Same UI — recent deliveries panel |
Requires Org Admin role (orgAdminOrgProcedure).
Creating a webhook
Use the Settings UI or webhooks.create:
| Field | Rules |
|---|---|
| Name | 1–120 characters |
| URL | HTTPS required (http://localhost allowed for development) |
| Secret | 8–256 characters; used for HMAC signing |
| Events | One or more from event catalog |
| Enabled | Default true |
| Description | Optional, max 500 chars |
InsightHire generates a signing secret (recommended: 64-char hex from 32 random bytes). Store the secret at creation — only a masked version is shown afterward.
Event catalog
Available events (webhooks.eventCatalog):
| Event | Label |
|---|---|
application.created | Application created (candidate applied) |
application.status_updated | Application status changed |
journey.started | Journey / assessment session started |
journey.step.completed | Journey step completed |
journey.completed | Journey completed |
journey.failed | Journey failed / rejected by score gate |
score.updated | Scores / results available |
Subscribe only to events your partner endpoint handles — reduces noise and delivery cost.
Payload envelope
Every delivery uses a stable JSON envelope:
{
"id": "evt_abc123",
"event": "application.created",
"timestamp": "2026-08-03T20:00:00.000Z",
"organizationId": "org_uuid",
"data": {
"...": "event-specific fields"
}
}Partners should adapt to this shape rather than expecting per-partner custom schemas.
Signature verification (Stripe-style)
Headers on each POST:
| Header | Value |
|---|---|
X-InsightHire-Timestamp | Unix seconds |
X-InsightHire-Signature | t=<ts>,v1=<hex> |
X-InsightHire-Event | Event type string |
X-InsightHire-Delivery-Id | Delivery row ID |
Verification algorithm:
signed_payload = `${timestamp}.${rawBody}`
expected_sig = hex(HMAC-SHA256(secret, signed_payload))Compare v1 from the signature header to expected_sig using constant-time comparison. Reject timestamps older than 300 seconds (configurable tolerance).
Reference implementation: outbound-webhook-signing.ts in insighthire-api.
Delivery lifecycle
- InsightHire emits event via
emitOutboundWebhookEvent - Matching webhooks (subscribed + enabled) get
organization_webhook_deliveriesrows deliverOutboundWebhookPOSTs signed payload to partner URL- Status:
PENDING→SUCCESS/FAILED/RETRYING
Monitoring
webhooks.list— all org webhooks with delivery countswebhooks.deliveries— recent attempts with HTTP status, error, attempts countwebhooks.retryDelivery— manual retry for failed delivery
Test ping
webhooks.sendTestEvent sends a signed test payload:
{
"test": true,
"message": "InsightHire webhook test ping",
"webhookName": "My Partner Hook"
}Use this to verify URL reachability and signature validation before going live.
Partner implementation checklist
- Expose HTTPS endpoint (TLS required in production).
- Persist
organizationId+ eventidfor idempotency. - Verify signature before processing body.
- Respond
2xxwithin timeout — non-2xx triggers retry. - Handle at-least-once delivery (duplicates possible on retry).
Example verification (Node.js)
const crypto = require('crypto');
function verifyInsightHireWebhook(rawBody, signatureHeader, secret, toleranceSec = 300) {
const parts = Object.fromEntries(
signatureHeader.split(',').map(p => {
const [k, ...rest] = p.trim().split('=');
return [k, rest.join('=')];
})
);
const ts = Number(parts.t);
const v1 = parts.v1;
if (!Number.isFinite(ts) || !v1) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - ts) > toleranceSec) return false;
const signed = `${ts}.${rawBody}`;
const expected = crypto.createHmac('sha256', secret).update(signed, 'utf8').digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(v1, 'hex'));
}CRUD operations
| Procedure | Purpose |
|---|---|
webhooks.eventCatalog | List subscribable events |
webhooks.list | List org webhooks |
webhooks.create | Register endpoint |
webhooks.update | Edit URL, events, rotate secret |
webhooks.delete | Remove webhook |
webhooks.deliveries | Delivery log |
webhooks.retryDelivery | Retry failed delivery |
webhooks.sendTestEvent | Test ping |
Inbound vs outbound
| Direction | Purpose | Settings |
|---|---|---|
| Outbound (this doc) | InsightHire → partner URL | Partner webhooks in Integrations |
| Inbound | Vendor → InsightHire (LinkedIn apply, Accurate status, Greenhouse assessment) | Vendor portal + API webhook routes |
Inbound webhook stats: ats.webhooks.stats and ats.webhooks.recent.
Related
- Integrations overview
- LinkedIn apply webhook — inbound example
- Background checks — Accurate inbound webhook

