The same API the app uses, with your own key
Create a key in Settings and call Vayora from Zapier, Make, n8n or your own code, for the candidates, roles, clients, contacts, tasks and placements in your workspace. Or skip the code and connect Claude or ChatGPT through MCP.
Get a key
- 1. Create it in Settings. An admin opens Settings, then Connected apps, then API keys, and names the key after the tool it is for. The full key is shown once.
- 2. Send it in a header. Every request to https://api.vayora.co carries X-API-Key. Bodies and responses are JSON.
- 3. It acts as a recruiter. A key reads and writes in its own workspace with a recruiter's reach, never an admin's: it cannot change billing, invite people or create other keys. Revoke it in the same place and it stops at once.
- 4. Fair use. Requests share a per-workspace ceiling with the app. Past it you get a 429 with a Retry-After header, and nothing is lost.
Create a candidate
curl https://api.vayora.co/candidate \
-H "X-API-Key: vyr_your_key_here" \
-H "Content-Type: application/json" \
-d '{"name": "Priya Shah", "email": "priya@example.com",
"current_title": "Financial Controller", "location": "Manchester",
"skills": ["IFRS", "Month end"], "source": "api"}'Response
{"ok": true, "created": 1, "updated": 0, "skipped": 0,
"email": "priya@example.com", "candidate_id": "cand-9026bd0dd8a0"}Candidates are matched on email: a second create with the same email updates the record instead of adding a duplicate. It returns the same candidate_id when the email is written exactly as it was first saved.
Reference
This is the first version of the API. Fields may be added, and every call listed here is kept working: each one is checked under a real key before every release.
Candidates
/candidate/listThe workspace's candidates, newest first.
page, page_size (max 200), skill, mine=true. Send page or page_size to get items (each with id), total and has_more; without them the response is a plain list.
/candidateCreate a candidate, or update the one with that email.
name, email (both required), phone, location, current_title, current_company, linkedin_url, skills (list), source. Returns candidate_id, created, updated.
/candidate/by-id/{candidate_id}One candidate in full.
/candidate/{candidate_id}/activityLog a call, meeting, email or note on the timeline.
event_type (call, meeting, email, note), description, timestamp (ISO 8601, optional).
/candidate/{candidate_id}/activityThe candidate's timeline.
Roles
/rolesEvery role except archived ones; pass status=open for open roles only.
status (open, closed, paused, draft, archived), include_archived, search, client_id, mine=true.
/rolesCreate a role.
title (required), description, location, required_skills (list), client_id, engagement_type (perm, contract, fixed_term), number_of_openings. Returns requisition_id.
/roles/{requisition_id}One role in full.
Clients and contacts
/agency/clientsActive clients.
status, page, page_size (max 500). Returns clients, total, has_more.
/agency/clientsCreate a client company.
company_name (required), industry, website, city, country, linkedin_url. Returns client_id.
/agency/clients/{client_id}One client in full. Its contacts come from GET /agency/contacts?client_id= and its roles from GET /roles?client_id=.
/agency/clients/{client_id}/contactsAdd a contact to a client.
name (required), email, phone, title, role (hiring_manager, decision_maker, billing, interviewer, coordinator, executive_sponsor, talent_partner, champion, other). Returns contact_id.
/agency/contactsClient contacts across the workspace.
client_id, role, is_primary, page, page_size (max 200). Returns contacts, total, has_more.
/agency/contacts/{contact_id}One contact.
Tasks and placements
/tasksOpen tasks.
scope (all, mine, unassigned), status, task_type, due_range (today, overdue, week, no_date), priority, include_completed.
/tasksCreate a task, optionally tied to a record.
title (required), task_type (call, email, linkedin, meeting, follow_up, todo), priority (low, medium, high), due_date (ISO 8601), related_entity_type with related_entity_id. Returns task_id.
/agency/placementsPlacements, newest first.
client_id, limit (max 500). Returns placements, count.
IDs in paths are the ones the app shows in its URLs. A record from another workspace reads as not found, never as forbidden, so a key cannot tell what it is not allowed to see.
Webhooks, when Vayora should call you
Every activity event Vayora records, from a stage change to a note, can go to an endpoint you choose. Each one is signed, so your side can check it came from Vayora. That is how Zapier, Make and n8n listen without polling.
- 1. Register it as an admin. Send
POST /admin/webhookswith the endpoint's url and the events you want. It takes an account admin's own signed-in token asAuthorization: Bearer. An API key gets a 403, because a key acts as a recruiter. The endpoint has to be a public https address, and a workspace can have up to 25. - 2. Pick the events.
stage_changewhen a candidate moves stage on a role, Placed included. An accepted offer moves the card to Placed too.note_addedwhen someone saves a note on a record.notewhen a note is logged throughPOST /candidate/{candidate_id}/activity. Or"*"for every activity event. - 3. Check the signature. Each delivery is a POST whose JSON body holds event and data. Its
X-VYR-Signatureheader is the HMAC-SHA256 of the raw body in hex, keyed with the secret the create call returned. Work out the same over the bytes you received and compare the two. - 4. Keep an eye on it.
GET /admin/webhookslists them with each one's secret, last delivery and failure count.PUT /admin/webhooks/{webhook_id}changes the url, the events or the active flag, andDELETEon the same path removes it. Each event goes to each matching webhook once, and Vayora waits up to 5 seconds for an answer.
Rather set it up in the app? On Pro, an automation rule (Settings, then Automation) can call a webhook when a candidate applies, replies, moves stage or sits on a role with no activity, and when a pitch moves stage. Give the rule a signing secret and each call carries an X-Vayora-Signature header. Its value is sha256= followed by the HMAC-SHA256 of the body in hex.
Use them together: a webhook tells you something has happened, and a key lets you act on it.
Register a webhook
curl https://api.vayora.co/admin/webhooks \
-H "Authorization: Bearer your_admin_access_token" \
-H "Content-Type: application/json" \
-d '{"url": "https://hooks.example.com/vayora",
"events": ["stage_change", "note_added"]}'Response
{"webhook_id": "wh_3f2a9c1b7d4e",
"url": "https://hooks.example.com/vayora",
"events": ["stage_change", "note_added"],
"secret": "Vn4qXc1bT8kLp2RzE7sWd0YhGu5mJa9fKiOe3NtQwBy",
"active": true, "created_at": "2026-10-24T09:12:31.482913+00:00",
"last_triggered_at": null, "failure_count": 0}What arrives
POST https://hooks.example.com/vayora
Content-Type: application/json
X-VYR-Signature: 4f1cca5b582109cdffe81e9bd68a3ddcf4e75eb20bf2fd782265a6476a839297
{"event": "stage_change", "data": {
"event_id": "evt-5b0e2c7a91d3", "event_type": "stage_change",
"account_id": "acc_7d1f0a9c3e52",
"description": "Moved from offer to hired",
"occurred_at": "2026-10-24T09:15:02.118204+00:00",
"actor_id": "usr-4c8e1b7f2a90", "actor_name": null,
"entity": {"type": "candidate", "id": "cand-9026bd0dd8a0"},
"candidate_id": "cand-9026bd0dd8a0", "client_id": null,
"contact_id": null, "requisition_id": "req-2e6a9d4b1c08",
"submission_id": null, "placement_id": null,
"metadata": {"from_stage": "offer", "to_stage": "hired", "note": ""}}}Spread across lines here to read. It arrives on one line, and the signature covers those exact bytes. Check the raw body before you parse it. Signed with the example secret above, this body gives this signature.
Check the signature
import hashlib, hmac
def from_vayora(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)MCP, for the assistants your team already uses
Vayora is an MCP server. Claude and ChatGPT connect from Settings, Connected apps, with a Vayora sign-in rather than a pasted token, so each person's own permissions carry through. Cursor adds it in one click, and Claude Code connects with an API token from the same screen. The tools cover candidates, roles, clients, pipeline, tasks, outreach, the day's numbers and the meetings the note taker captured. Meeting transcripts are redacted. Everything is read-only unless the person allows changes.
Server address: https://api.vayora.co/mcp. It speaks OAuth with dynamic client registration, which is what the assistants expect. Every MCP tool, by group, is in the MCP reference.