Skip to content

Developers

The public API and webhooks: what they cover, how they behave and what we promise about change.

OpenAPI document (JSON) · v0.537.0-beta

Authentication

Every request carries an API key as a bearer token: Authorization: Bearer <key>. An administrator of your organisation creates keys under Integrations, each with an explicit list of scopes and an optional expiry. A key only ever sees the data of the organisation it belongs to; an expired or revoked key answers 401, a key without the operation’s scope answers 403.

Limits

Each key has its own window of 120 requests a minute, and writes a tighter one of 30 a minute. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; a 429 adds Retry-After.

Sandbox key

Try the API against synthetic data: a sandbox key reads every resource of the public demo, holds every read scope and nothing that writes, expires after 24 hours and allows 1000 requests a day.

Sandbox keys are issued on the public demo, which holds only synthetic data. Open the demo’s developer page

Conventions

  • Every list answers { data, page: { nextCursor, hasMore } } and takes limit (default 50, at most 200), cursor and updatedSince. The walk is ascending by last change, so a client deduplicates on id.
  • Writes accept an Idempotency-Key header: the same key with the same body replays the first answer, with a different body it is a 409. An invalid body answers 422 with one entry per field.
  • Every failure is an RFC 9457 problem document (application/problem+json) with a stable type. Switch on type, never on the title or the detail text.

Endpoints

MethodPathScopeWhat it does
GET/api/v1/candidatescandidates:readList candidates (mentees)
POST/api/v1/candidatescandidates:writeCreate a candidate
GET/api/v1/candidates/{id}candidates:readGet a candidate
PATCH/api/v1/candidates/{id}candidates:writeUpdate a candidate
PATCH/api/v1/relationships/{id}/stagerelationships:writeMove a relationship’s pipeline stage
GET/api/v1/me—Who am I (connector auth test)
GET/api/v1/hookshooks:writeList this key’s subscriptions
POST/api/v1/hookshooks:writeSubscribe (REST hook)
DELETE/api/v1/hooks/{id}hooks:writeUnsubscribe
POST/api/v1/exports—Export a whole resource (asynchronous)
GET/api/v1/exports/{id}—Get an export
POST/api/v1/imports/candidatescandidates:writeImport candidates in bulk (queued)
GET/api/v1/imports/{id}candidates:writeGet an import and its report
GET/api/v1/relationshipsrelationships:readList relationships
GET/api/v1/relationships/{id}relationships:readGet a relationship
GET/api/v1/interactionsinteractions:readList interactions
POST/api/v1/interactionsinteractions:writeLog an interaction
GET/api/v1/interactions/{id}interactions:readGet an interaction
GET/api/v1/meetingsmeetings:readList meetings
GET/api/v1/meetings/{id}meetings:readGet a meeting
GET/api/v1/companiescompanies:readList companies
GET/api/v1/companies/{id}companies:readGet a company
GET/api/v1/requisitionshiring:readList requisitions
GET/api/v1/requisitions/{id}hiring:readGet a requisition
GET/api/v1/offershiring:readList offers
GET/api/v1/offers/{id}hiring:readGet an offer
GET/api/v1/interview-requestshiring:readList interview requests
GET/api/v1/interview-requests/{id}hiring:readGet an interview request

Error types

typeHTTP status
invalid_api_key401
insufficient_scope403
org_unbound403
feature_locked403
invalid_link403
company_key_not_allowed403
not_found404
validation_failed400 / 422
idempotency_key_reused409
idempotency_in_progress409
limit_reached409
duplicate_candidate409
invalid_transition409
stage_conflict409
rate_limited429
quota_exceeded429

Webhooks

A webhook is a POST of { event, data, sentAt } with an X-Signature header — the hex HMAC-SHA256 of the exact request body under the hook’s secret — plus X-Event and X-Delivery-Id. Verify the signature against the raw body before parsing it, and deduplicate on X-Delivery-Id: delivery is at least once.

Verifying a delivery (Node.js)

const { createHmac, timingSafeEqual } = require('node:crypto');

// rawBody: the request body exactly as received (string or Buffer), before JSON.parse.
function verifyWebhook(secret, rawBody, signatureHeader) {
  const expected = Buffer.from(createHmac('sha256', secret).update(rawBody).digest('hex'), 'hex');
  const given = Buffer.from(String(signatureHeader || ''), 'hex');
  return given.length === expected.length && timingSafeEqual(given, expected);
}

Events

application.created@1

Payload schema of data

{
  "version": 1,
  "description": "Someone applied to a mentor through the public application form.",
  "schema": {
    "type": "object",
    "properties": {
      "mentorId": {
        "type": "string",
        "minLength": 1
      },
      "menteeName": {
        "type": "string"
      },
      "email": {
        "type": "string"
      }
    },
    "required": [
      "mentorId",
      "menteeName",
      "email"
    ],
    "additionalProperties": false
  }
}
pipeline.stage_change@1

Payload schema of data

{
  "version": 1,
  "description": "A mentorship moved to another pipeline stage (`from` and `to` are stage keys).",
  "schema": {
    "type": "object",
    "properties": {
      "relationId": {
        "type": "string",
        "minLength": 1
      },
      "from": {
        "type": "string"
      },
      "to": {
        "type": "string"
      }
    },
    "required": [
      "relationId",
      "from",
      "to"
    ],
    "additionalProperties": false
  }
}
mentorship.created@1

Payload schema of data

{
  "version": 1,
  "description": "A mentor was assigned to a mentee.",
  "schema": {
    "type": "object",
    "properties": {
      "relationId": {
        "type": "string",
        "minLength": 1
      },
      "mentorId": {
        "type": "string",
        "minLength": 1
      },
      "menteeId": {
        "type": "string",
        "minLength": 1
      },
      "companyId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      }
    },
    "required": [
      "relationId",
      "mentorId",
      "menteeId",
      "companyId"
    ],
    "additionalProperties": false
  }
}
interaction.logged@1

Payload schema of data

{
  "version": 1,
  "description": "An interaction (meeting, feedback, e-mail …) was logged on a mentorship.",
  "schema": {
    "type": "object",
    "properties": {
      "relationId": {
        "type": "string",
        "minLength": 1
      },
      "type": {
        "type": "string"
      },
      "date": {
        "type": "string",
        "format": "date-time"
      },
      "autoLogged": {
        "const": true
      }
    },
    "required": [
      "relationId",
      "type",
      "date"
    ],
    "additionalProperties": false
  }
}
evaluation.added@1

Payload schema of data

{
  "version": 1,
  "description": "An evaluation was submitted for a mentorship.",
  "schema": {
    "type": "object",
    "properties": {
      "relationId": {
        "type": "string",
        "minLength": 1
      },
      "type": {
        "type": "string"
      },
      "authorId": {
        "type": "string",
        "minLength": 1
      }
    },
    "required": [
      "relationId",
      "type",
      "authorId"
    ],
    "additionalProperties": false
  }
}
meeting.scheduled@1

Payload schema of data

{
  "version": 1,
  "description": "One or more meetings were scheduled (a batch, an instant meeting or a series occurrence).",
  "schema": {
    "type": "object",
    "properties": {
      "title": {
        "type": "string"
      },
      "scheduledAt": {
        "type": [
          "string",
          "null"
        ],
        "format": "date-time"
      },
      "count": {
        "type": "integer",
        "minimum": 0
      },
      "instant": {
        "const": true
      },
      "seriesId": {
        "type": "string",
        "minLength": 1
      }
    },
    "required": [
      "title",
      "scheduledAt",
      "count"
    ],
    "additionalProperties": false
  }
}
meeting.rescheduled@1

Payload schema of data

{
  "version": 1,
  "description": "A meeting moved to another time.",
  "schema": {
    "type": "object",
    "properties": {
      "meetingId": {
        "type": "string",
        "minLength": 1
      },
      "scheduledAt": {
        "type": "string",
        "format": "date-time"
      },
      "previousScheduledAt": {
        "type": [
          "string",
          "null"
        ],
        "format": "date-time"
      }
    },
    "required": [
      "meetingId",
      "scheduledAt",
      "previousScheduledAt"
    ],
    "additionalProperties": false
  }
}
meeting.cancelled@1

Payload schema of data

{
  "version": 1,
  "description": "A meeting (or `count` meetings of one batch) was called off.",
  "schema": {
    "type": "object",
    "properties": {
      "meetingId": {
        "type": "string",
        "minLength": 1
      },
      "scheduledAt": {
        "type": [
          "string",
          "null"
        ],
        "format": "date-time"
      },
      "count": {
        "type": "integer",
        "exclusiveMinimum": 0
      }
    },
    "required": [
      "meetingId",
      "scheduledAt",
      "count"
    ],
    "additionalProperties": false
  }
}
interview_panel.completed@1

Payload schema of data

{
  "version": 1,
  "description": "Every interviewer on a panel has scored, or an admin closed it.",
  "schema": {
    "type": "object",
    "properties": {
      "panelId": {
        "type": "string",
        "minLength": 1
      },
      "subjectId": {
        "type": "string",
        "minLength": 1
      }
    },
    "required": [
      "panelId",
      "subjectId"
    ],
    "additionalProperties": false
  }
}
interview_panel.outcome@1

Payload schema of data

{
  "version": 1,
  "description": "A closed panel's decision was recorded.",
  "schema": {
    "type": "object",
    "properties": {
      "panelId": {
        "type": "string",
        "minLength": 1
      },
      "applicationId": {
        "type": "string",
        "minLength": 1
      },
      "outcome": {
        "type": "string",
        "enum": [
          "ADVANCE",
          "REJECT",
          "OFFER"
        ]
      }
    },
    "required": [
      "panelId",
      "applicationId",
      "outcome"
    ],
    "additionalProperties": false
  }
}
interview_request.scheduled@1

Payload schema of data

{
  "version": 1,
  "description": "An approved interview request got a meeting.",
  "schema": {
    "type": "object",
    "properties": {
      "requestId": {
        "type": "string",
        "minLength": 1
      },
      "meetingId": {
        "type": "string",
        "minLength": 1
      },
      "scheduledAt": {
        "type": "string",
        "format": "date-time"
      },
      "panelId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      }
    },
    "required": [
      "requestId",
      "meetingId",
      "scheduledAt",
      "panelId"
    ],
    "additionalProperties": false
  }
}
interview_request.cancelled@1

Payload schema of data

{
  "version": 1,
  "description": "An interview request was called off.",
  "schema": {
    "type": "object",
    "properties": {
      "requestId": {
        "type": "string",
        "minLength": 1
      },
      "meetingId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      }
    },
    "required": [
      "requestId",
      "meetingId"
    ],
    "additionalProperties": false
  }
}
offer.accepted@1

Payload schema of data

{
  "version": 1,
  "description": "The candidate accepted an offer.",
  "schema": {
    "type": "object",
    "properties": {
      "offerId": {
        "type": "string",
        "minLength": 1
      },
      "applicationId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      },
      "requisitionId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      }
    },
    "required": [
      "offerId",
      "applicationId",
      "requisitionId"
    ],
    "additionalProperties": false
  }
}
offer.declined@1

Payload schema of data

{
  "version": 1,
  "description": "The candidate declined an offer.",
  "schema": {
    "type": "object",
    "properties": {
      "offerId": {
        "type": "string",
        "minLength": 1
      },
      "applicationId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      },
      "requisitionId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      }
    },
    "required": [
      "offerId",
      "applicationId",
      "requisitionId"
    ],
    "additionalProperties": false
  }
}
requisition.filled@1

Payload schema of data

{
  "version": 1,
  "description": "An acceptance took the last opening of a requisition.",
  "schema": {
    "type": "object",
    "properties": {
      "requisitionId": {
        "type": "string",
        "minLength": 1
      },
      "offerId": {
        "type": "string",
        "minLength": 1
      }
    },
    "required": [
      "requisitionId",
      "offerId"
    ],
    "additionalProperties": false
  }
}
mentorship.lifecycle_changed@1

Payload schema of data

{
  "version": 1,
  "description": "A mentorship was paused, resumed, ended or reopened (`from` and `to` are lifecycle states).",
  "schema": {
    "type": "object",
    "properties": {
      "relationId": {
        "type": "string",
        "minLength": 1
      },
      "from": {
        "type": "string"
      },
      "to": {
        "type": "string"
      },
      "reasonCode": {
        "type": [
          "string",
          "null"
        ]
      }
    },
    "required": [
      "relationId",
      "from",
      "to",
      "reasonCode"
    ],
    "additionalProperties": false
  }
}
application.stage_changed@1

Payload schema of data

{
  "version": 1,
  "description": "A candidate's application for a requisition moved to another stage (`from` and `to` are stage keys, e.g. SHORTLISTED → INTERVIEW).",
  "schema": {
    "type": "object",
    "properties": {
      "applicationId": {
        "type": "string",
        "minLength": 1
      },
      "requisitionId": {
        "type": "string",
        "minLength": 1
      },
      "from": {
        "type": "string"
      },
      "to": {
        "type": "string"
      }
    },
    "required": [
      "applicationId",
      "requisitionId",
      "from",
      "to"
    ],
    "additionalProperties": false
  }
}
mentorship.transferred@1

Payload schema of data

{
  "version": 1,
  "description": "A mentee's mentor changed — corrected in place, or a new pairing that carries the stage over.",
  "schema": {
    "type": "object",
    "properties": {
      "relationId": {
        "type": "string",
        "minLength": 1
      },
      "previousRelationId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      },
      "menteeId": {
        "type": "string",
        "minLength": 1
      },
      "fromMentorId": {
        "type": "string",
        "minLength": 1
      },
      "toMentorId": {
        "type": "string",
        "minLength": 1
      },
      "mode": {
        "type": "string",
        "enum": [
          "corrected",
          "transferred"
        ]
      },
      "reasonCode": {
        "type": "string"
      }
    },
    "required": [
      "relationId",
      "previousRelationId",
      "menteeId",
      "fromMentorId",
      "toMentorId",
      "mode",
      "reasonCode"
    ],
    "additionalProperties": false
  }
}
offer.sent@1

Payload schema of data

{
  "version": 1,
  "description": "An offer was sent to the candidate.",
  "schema": {
    "type": "object",
    "properties": {
      "offerId": {
        "type": "string",
        "minLength": 1
      },
      "applicationId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      },
      "requisitionId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      }
    },
    "required": [
      "offerId",
      "applicationId",
      "requisitionId"
    ],
    "additionalProperties": false
  }
}
offer.withdrawn@1

Payload schema of data

{
  "version": 1,
  "description": "A sent offer was withdrawn before the candidate decided.",
  "schema": {
    "type": "object",
    "properties": {
      "offerId": {
        "type": "string",
        "minLength": 1
      },
      "applicationId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      },
      "requisitionId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      }
    },
    "required": [
      "offerId",
      "applicationId",
      "requisitionId"
    ],
    "additionalProperties": false
  }
}
offer.expired@1

Payload schema of data

{
  "version": 1,
  "description": "A sent offer passed its expiry date without a decision.",
  "schema": {
    "type": "object",
    "properties": {
      "offerId": {
        "type": "string",
        "minLength": 1
      },
      "applicationId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      },
      "requisitionId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      }
    },
    "required": [
      "offerId",
      "applicationId",
      "requisitionId"
    ],
    "additionalProperties": false
  }
}
interview_request.created@1

Payload schema of data

{
  "version": 1,
  "description": "A company asked to interview a shortlisted candidate for a requisition.",
  "schema": {
    "type": "object",
    "properties": {
      "requestId": {
        "type": "string",
        "minLength": 1
      },
      "requisitionId": {
        "type": "string",
        "minLength": 1
      },
      "menteeId": {
        "type": "string",
        "minLength": 1
      },
      "companyId": {
        "type": "string",
        "minLength": 1
      }
    },
    "required": [
      "requestId",
      "requisitionId",
      "menteeId",
      "companyId"
    ],
    "additionalProperties": false
  }
}
interview_request.approved@1

Payload schema of data

{
  "version": 1,
  "description": "The programme approved an interview request; scheduling comes next.",
  "schema": {
    "type": "object",
    "properties": {
      "requestId": {
        "type": "string",
        "minLength": 1
      },
      "requisitionId": {
        "type": "string",
        "minLength": 1
      },
      "menteeId": {
        "type": "string",
        "minLength": 1
      }
    },
    "required": [
      "requestId",
      "requisitionId",
      "menteeId"
    ],
    "additionalProperties": false
  }
}
interview_request.declined@1

Payload schema of data

{
  "version": 1,
  "description": "The programme declined an interview request (`reasonCode` is a decline code, never the note).",
  "schema": {
    "type": "object",
    "properties": {
      "requestId": {
        "type": "string",
        "minLength": 1
      },
      "requisitionId": {
        "type": "string",
        "minLength": 1
      },
      "menteeId": {
        "type": "string",
        "minLength": 1
      },
      "reasonCode": {
        "type": "string"
      }
    },
    "required": [
      "requestId",
      "requisitionId",
      "menteeId",
      "reasonCode"
    ],
    "additionalProperties": false
  }
}
company_interest.changed@1

Payload schema of data

{
  "version": 1,
  "description": "A company flagged a candidate as interesting, shortlisted or passed (`from` and `to` are interest states).",
  "schema": {
    "type": "object",
    "properties": {
      "interestId": {
        "type": "string",
        "minLength": 1
      },
      "companyId": {
        "type": "string",
        "minLength": 1
      },
      "menteeId": {
        "type": "string",
        "minLength": 1
      },
      "requisitionId": {
        "type": [
          "string",
          "null"
        ],
        "minLength": 1
      },
      "from": {
        "type": [
          "string",
          "null"
        ]
      },
      "to": {
        "type": "string"
      }
    },
    "required": [
      "interestId",
      "companyId",
      "menteeId",
      "requisitionId",
      "from",
      "to"
    ],
    "additionalProperties": false
  }
}

Versioning and deprecation

Within v1 a change is additive only: new endpoints, new optional parameters, new fields and new error types may appear at any time, so ignore what you do not know. Removing or renaming a field, parameter or endpoint, changing a type or tightening validation is breaking: it is announced at least 6 months ahead and listed below, and a deprecated operation answers with Deprecation and Sunset headers. A change that cannot keep that promise ships as an opt-in parameter within v1, or as v2.

Announced deprecations

WhatSinceRemoved on
`candidates` key on `GET /api/v1/candidates` (alias of `data`)#19882027-01-31

Every shipped change, the API included, is dated in the release notes. Release notes