Search and manage Recruit CRM candidates, companies, contacts and jobs, assign candidates to jobs and move them through hiring stages.
Recruit CRM ships in the w6w first-party pack. It declares 24 actions, 3 health checks, and the host runs its code in a sandbox that never sees the credential.
io.w6w.recruit-crmRecruit CRM is an applicant tracking system and CRM for recruitment agencies. This app connects a workflow to a Recruit CRM account so candidate, client and job data can flow between the ATS and the rest of the stack.
Search and read candidates, companies, contacts and jobs; create candidates, companies and contacts as leads arrive from forms or sourcing tools; assign a candidate to a job and move them through the hiring pipeline; and attach notes to any record.
Connect with the account’s API token from Admin Settings.
Three routes to the same 24 actions. The Workflow tab is generated from Recruit CRM's own manifest and carries its real ids, so it is copy-pasteable; the Code and CLI examples are the same call for any action on any app, so every app-specific value in them is a blank you fill in.
candidate-assign Assign a candidate to a job (`POST /v1/candidates/{id}/assign?job_slug=…`).
candidate-create Create a candidate (`POST /v1/candidates`, sent as multipart/form-data as the spec declares). Resume and avatar uploads are not supported.
candidate-delete Delete a candidate (`DELETE /v1/candidates/{id}`). Not reversible through the API.
candidate-hiring-stage-update Move a candidate to another hiring stage for a job (`POST /v1/candidates/{id}/hiring-stages/{job}`). Stage ids come from the Hiring Pipeline action.
candidate-search Find candidates by name, email or LinkedIn URL (`GET /v1/candidates/search`). Filters are combined; custom-field filters are not exposed.
candidate-unassign Remove a candidate from a job and return the candidate (`POST /v1/candidates/{id}/unassign?job_slug=…`).
candidate-update Edit a candidate (`POST /v1/candidates/{id}` — Recruit CRM edits with POST, as multipart/form-data). Only the fields you fill in are sent.
contact-search Find contacts by name, email or LinkedIn URL (`GET /v1/contacts/search`). Custom-field filters are not exposed.
hiring-pipeline-list The candidate hiring stages (`stage_id` + `label`) the Update Candidate Hiring Stage action needs (`GET /v1/hiring-pipeline`).
job-assigned-candidates List the candidates assigned to a job (`GET /v1/jobs/{id}/assigned-candidates`), one page at a time.
job-search Find jobs by name, status, location or company/contact details (`GET /v1/jobs/search`). Custom-field filters are not exposed.
note-create Add a note to a candidate, company, contact, job or deal (`POST /v1/notes`).
user-list The account's users (`GET /v1/users`) — the ids that go in the `Owner user id` fields.
A workflow step names the app and the action, and the editor fills in the
connection when you pick one. This is the Step shape from the
workflow spec, carrying Recruit CRM's real ids.
{
"manifestVersion": "2",
"name": "recruit-crm-example",
"steps": [
{
"id": "candidate-assign",
"uses": {
"app": "io.w6w.recruit-crm",
"action": "candidate-assign",
"connection": "conn_YOUR_CONNECTION_ID"
},
"with": {
"candidateId": "<candidateId>",
"jobId": "<jobId>"
}
}
]
}candidate-assign candidate-create candidate-get candidate-hiring-stage-update candidate-list +19 more actions available
Every app-specific value here is a blank you have to fill in. An
app action is reached through the connection that authenticates it, so the
address is a connection id, not the app id — and connections belong to your account,
so a public page cannot know yours. Create one for Recruit CRM, then fill in
the three blanks: conn_YOUR_CONNECTION_ID, the action key, and the
parameters that action declares. The call itself is real — the shape is transcribed
from the studio's own snippet builder, which prints the same kind of blanks — but
nothing in it is specific to Recruit CRM. The Workflow tab is where this app's
real ids are.
npm install @w6w/sdkyarn add @w6w/sdkpnpm add @w6w/sdkdeno add npm:@w6w/sdkimport { W6wClient, isActionRun } from "@w6w/sdk";
// Reads W6W_BASE_URL and W6W_TOKEN from the environment when omitted.
const client = new W6wClient();
const envelope = await client.run({
urn: "conn_YOUR_CONNECTION_ID",
action: "candidate-assign",
payload: {
candidateId: "<value>",
jobId: "<value>",
},
});
if (isActionRun(envelope)) console.log(envelope.value); npm install -g @w6w/cli w6w run conn_YOUR_CONNECTION_ID --action candidate-assign --payload '{"candidateId":"<value>","jobId":"<value>"}' Give an AI agent Recruit CRM — without giving it Recruit CRM's credentials. One MCP endpoint exposes every app, function and workflow the caller is entitled to, as tools it can discover and run. Access is granted per team while we onboard.
One tool call{
"name": "w6w_invoke",
"arguments": {
"ref": "app:io.w6w.recruit-crm#candidate-assign",
"input": {
"candidateId": "<candidateId>",
"jobId": "<jobId>"
}
}
}
Every tool names its target with a single ref. The
app: form above doesn't name a connection at all — the
host resolves which of the caller's Recruit CRM connections to sign
with, and refuses rather than guesses when the answer is ambiguous.
The token is attached host-side, at the moment of the call. It is never a tool argument, never in the model's context, and never in a transcript — so a prompt injection has nothing to exfiltrate.
Tools are derived per end user from what that person has actually connected and is entitled to — not one shared bot identity carrying the union of everyone's access.
Multi-step work runs on the workflow engine and returns a run handle the agent can poll — retries, branching and state survive the conversation that started them.
Recruit CRM's declared health checks are on the surface too, so an agent can tell "the vendor is down" from "your credential expired" before it burns a retry on either.
The MCP surface is part of the hosted platform. Recruit CRM itself is MIT, and the runtime that executes it is source-available (FSL).
Recruit CRM declares its own checks, so its health is a property of the app rather than something the host guesses at.
Page-level indicator from status.recruitcrm.io. INFORMATIONAL: the page's components are regions only and none names the API, so the whole-product indicator stands in for it.
Unauthenticated GET https://api.recruitcrm.io/v1/users. A 401 carrying Recruit CRM's JSON `{"error": …}` passes — it proves the API and its auth layer are answering. Credential validity is the derived `auth:*` check's job.