MCP tool reference
What an AI assistant connected to First can actually do — every tool it can call, and what each one changes.
Once your agent is connected, these are the tools it can use. To connect one, follow Connect your agent with MCP. Developers connecting with a personal access token, see the developer documentation.
Every tool acts as you. It can only reach the organisations your account belongs to and the roles you can see, and changing a role needs the same admin rights it would in First. Tools marked CHANGES DATA alter or withdraw something people can see, and a good assistant will ask you before running one.
- Create and manage roles13 toolsDraft a role from a brief, with suggested criteria, ask for it to be approved to open, then publish, pause, close or archive it.
- Role setup7 toolsChange a role's application process: its criteria, its questions and the topics each interview stage covers.
- Source candidates10 toolsResearch the market for a role, search people who have already applied, find new people on the web, and add them to a role.
- Review and move candidates23 toolsSearch applications, move people through your stages, record how far they have got, reject or hire them, and undo those decisions.
- Offers9 toolsDraft, approve and extend offers, record the answer, and mark the contract signed.
- Notes, meetings and emails10 toolsAdd notes and interview transcripts to an application, import meetings, and manage the emails waiting to go to a candidate.
- Reporting and status3 toolsHow hiring is going: applications, hires and time to hire, how candidates moved through your stages and where they dropped out.
- Organisation and team13 toolsThe organisations you can act on, the hiring groups that decide who works on which roles, the referrals page and the rejection reasons.
- Helpers2 toolsTurn a place or country name into what a candidate search needs.
Draft a role from a brief, with suggested criteria, ask for it to be approved to open, then publish, pause, close or archive it.
list_rolesList roles
List an organisation's roles, optionally filtered by status. Only returns roles the token owner is allowed to see. Use status 'imported' to find ATS-synced roles awaiting adoption.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| statuses | array of one of: imported, draft, open, paused, closed, archived |
get_roleGet a role
A role's current state: its details, criteria and questions, and readiness — which required details are still missing before it can be published. role.applyUrl is the candidate-facing link to share when announcing the role on Slack, LinkedIn or by email; roleWebsiteUrl is the company's own page, not where candidates apply. applyUrl is null while candidates cannot apply: the role is not open or paused, is private, or the organisation has no candidate site.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string |
generate_suggested_criteriaGenerate suggested role criteria
Generate a role's assessment criteria from its title and job description. Returns process changes ready to apply to a role (update_role_process) — the criteria every candidate added to it is assessed against. Nothing is created or changed: show the criteria to the user, adjust if they want, then apply them. `requirementsPreview` summarises what they ask for, which also tells you who to look for when sourcing. ALWAYS pass `roleId` when the role already exists — when you are working on a role rather than drafting one. That is what pulls in its intake call transcripts and note-taker summaries, which say what the hiring manager actually asked for; a job description is often boilerplate. Omitting it silently generates from the description alone. Omit it only while the role is still a draft.
Show 5 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| title* | string | The role title. |
| jobDescription | string | The role's job description. The richer this is, the better the criteria. |
| keywords | array of string | Skills the hiring manager called out, when there is no job description yet. |
| roleId | string | An existing role to draw intake material from. Its saved intake call transcripts and note-taker summaries are read in alongside the job description. Omit when the role does not exist yet. |
create_roleCreate a role
Create a new draft role. The token owner becomes the hiring manager. Location and category names that don't exist in the organisation are created automatically. When the organisation uses hiring groups, hiringGroupIds is required: without it the error lists the groups to choose from.
Show 13 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| name* | string | The role title |
| internalName | string | |
| shortDescription | string | |
| description | string | |
| workplaceType | one of: on-site, hybrid, remote, remote-first, flexible | |
| employmentType | one of: full-time, part-time, contractor, internship, volunteer | |
| roleWebsiteUrl | one of 2 shapes | |
| compensation | object | |
| locationNames | array of string | |
| categoryNames | array of string | |
| templateIds | array of string | Role template ids to seed criteria and questions from |
| hiringGroupIds | array of string | The hiring groups to put the role in, any of the organisation's. Required, with at least one, when the organisation uses hiring groups |
duplicate_roleDuplicate a role
Duplicate an existing role into a new draft, copying its details, settings, hiring groups, criteria and questions. Returns the new role's id.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | The role to copy |
import_roleAdopt an imported role
Adopt a role synced from the ATS: moves it from 'imported' to a draft the team can configure and publish. Use list_roles with status 'imported' to find adoptable roles.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string |
update_role_detailsUpdate a role's details
Update a role's details. Provide only the fields to change; everything else keeps its current value. Location and category names replace the existing sets. The response lists any fields locked to a connected ATS which kept their synced values.
Show 12 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | |
| name | string | |
| internalName | string | |
| shortDescription | string | |
| description | string | |
| workplaceType | one of: on-site, hybrid, remote, remote-first, flexible | |
| employmentType | one of: full-time, part-time, contractor, internship, volunteer | |
| roleWebsiteUrl | one of 2 shapes | |
| compensation | object | |
| locationNames | array of string | |
| categoryNames | array of string |
request_role_openingAsk for a role to be approved to open
Ask for a draft or closed role to be approved to open, when publish_role refuses you because opening it needs approval. The people who must approve it are emailed: with hiring groups, those on the role who can open it; otherwise any one admin. Approval unlocks opening; it does not open the role, so call publish_role once the request is approved. Asking again while one is waiting answers with that one and emails nobody. For whoever manages the role, where the organisation uses offers and approvals. A role missing details it needs to open is refused, with missingFields.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string |
approve_role_openingApprove opening a role
Approve the waiting request to open a role, as one of the people who must approve it or as an organisation admin, who can always approve alone. Once there are enough approvals, whoever asked is emailed and can open it. Approving does not open the role. Confirm with the person first: it is their sign-off. Approving again answers with where the request stands. Only where the organisation uses offers and approvals.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string |
publish_rolePublish a role
Publish a role: opens it for applications, putting it live on the job board and apply page and approving any criteria still awaiting review. Also resumes a paused role and reopens a closed one, restarting progress towards any pause goal. Fails with the missing fields when the role's details are incomplete. On success returns applyUrl, the candidate-facing link to share when announcing the role on Slack, LinkedIn or by email; it is null for a private role, one past its closing date, or when the organisation has no candidate site.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string |
pause_rolePause a role
Pause an open role: candidates can still apply, but new applications wait to be assessed until the role is resumed with publish_role. Fails when the role is not open.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string |
close_roleCHANGES DATAClose a role
Close an open or paused role: candidates can no longer apply. Reopen it with publish_role. Fails when the role is not open or paused.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string |
archive_roleCHANGES DATAArchive a role
Archive a role: hides it from active lists and stops new applications. Archived roles can be restored in First. Fails when the role is already archived.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string |
Change a role's application process: its criteria, its questions and the topics each interview stage covers.
update_role_processCHANGES DATAUpdate a role's application process
Apply changes to a role's application process: its criteria and questions. Each change is one item of the shared process-change contract, applied in order. Criteria changed on a live role may await review and are approved when the role is next published. Use get_role first to see the current criteria and questions.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | |
| changes* | array of one of 19 shapes | The process changes to apply, in order |
reorder_role_questionsPut a section of a role's form in order
Put the questions of one section of a role's form in the order the candidate answers them: its video questions, one kind of knowledge question (technical-knowledge or behaviours-and-culture), its custom requirements, or its custom basic details, on the initial application or on one stage's follow-up form. Read the role with get_role first: each question's processComponent (type and itemType) and pipelineStageId say which section it is in, and order says where it sits. Pass every questionId of that section in the new order; a question left out keeps its place after the ones given. Sections themselves always keep their place on the form, and the questions a section always asks first can't be moved, so leave them out: the standard basic details (name, email and the other personal details, even one added as custom), the standard requirements and code review questions. Returns changed: false when the questions were already in that order.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | |
| questionIds* | array of string | One section's question ids, in the new order |
remove_role_process_componentCHANGES DATARemove a criterion or question from a role
Remove a criterion or question from a role's application process by process component type and subtype. For stage criteria (type 'assess-stage'), pass the specific criterionId — otherwise every stage criterion on the role would be removed.
Show 6 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | |
| type* | one of: collect-basic-details, check-requirements, check-skills-and-experience, test-knowledge-and-behaviour, ask-video-question, ask-for-document, survey, assess-stage, legacy-criterion, legacy-question | |
| itemType* | one of 8 shapes | |
| questionId | string | |
| criterionId | string | Required for stage criteria (type 'assess-stage'): without it, every stage criterion on the role would be removed |
get_stage_topicsREAD ONLYGet a stage's interview topics
A role's pipeline stages (id, name, kind) and, for the stage given, its interview topics in order: each with its guidance for the interviewer, its questions and follow-ups, and the criteria scored from the answers (id, title, what good looks like, priority, scale). Call it without pipelineStageId to find the stage ids; topics go on interview stages. Read a stage's topics before changing them, so existing topics are changed rather than duplicated.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | |
| pipelineStageId | string |
save_stage_topicsCHANGES DATASave interview topics on a stage
Add or change interview topics on a role's interview stage, each saved with its questions and criteria in one go - typically set up from a client's scorecard or interview template. A topic with a topicId from get_stage_topics is changed; one without is added at the end of its stage (use reorder_stage_topics to move it). Criteria left out of a changed topic are kept, not removed. A section with questions but nothing scored, such as an introduction, is a topic with no criteria. Confirm the topics with the user first.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | |
| topics* | array of object |
reorder_stage_topicsPut a stage's interview topics in order
Put a stage's interview topics in the order the interviewer covers them - for example an introduction first, or the order a scorecard lists them in. Pass every topicId from get_stage_topics in the new order; a topic left out keeps its place after the ones given.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | |
| pipelineStageId* | string | |
| topicIds* | array of string |
remove_stage_topicsCHANGES DATARemove interview topics from a stage
Remove interview topics from a role, by topicId from get_stage_topics. Each topic goes with its questions and the criteria scored under it, and any scores already given for them - the same as removing the topic in First. Always confirm with the user, naming the topics, before calling this.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | |
| topicIds* | array of string |
Research the market for a role, search people who have already applied, find new people on the web, and add them to a role.
research_talent_marketResearch a talent market
Research the talent market for a role — how deep the pool is, what it pays, and who else is hiring for it. Use it before committing to a hire, to sanity-check a salary, or to tell the user whether a location is realistic. Searches the web, not the organisation's candidate database (search_existing_candidates does that). Nothing is created or changed. `location` is a place as the recruiter would say it — 'London', 'the UK' — not coordinates or an ISO code. A market it finds nothing for returns found: false; say so rather than retrying.
Show 5 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleTitle* | string | The role to research, e.g. 'Software Engineer'. |
| location* | string | Where to research it, as the recruiter would say it — 'London', 'the UK', 'Berlin'. Not a coordinate or ISO code. |
| seniorityLevel | string | The seniority to narrow to, e.g. 'senior', 'lead'. |
| additionalContext | string | Anything else that shapes the market, e.g. an industry or a required technology. |
search_existing_candidatesSearch existing candidates
Find the people in the organisation's own candidate database most like a description, a job title or another candidate, best match first, up to 50. Search by `text` (e.g. 'forklift operator', 'React and TypeScript'), `titles` and `niceToHave`, or by `likeCandidateId`. `titles` puts people whose CV headline holds one first; `companies` keeps only people who work at one now; `mustHave` keeps only CVs naming every word; `niceToHave` only ranks. These are the Search candidates tab's own filters; the market map isn't available here. Sourcing for a role: pass `matchRoleId` to rank everyone against that role's criteria, as the tab's Match to does. It leaves out people already on the role; any `text` and `niceToHave` rank with the role. `criteriaOff` leaves some of the role's criteria out of this ranking and `criteriaMoved` swaps them between Must have and Nice to have, by the criterion ids get_role returns; the response's `matchRole` says which took effect. `roleId` is different: it keeps only people who applied to that role. Similarity only: judge the list yourself, then add the people worth it with add_candidate_to_role, where the role's assessment scores them.
Show 18 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| text | string | What to look for: skills, a job title or a short description of the person, e.g. 'forklift operator' or 'React and TypeScript'. |
| likeCandidateId | string | Find people like this candidate instead of matching text. |
| titles | array of string | Job titles, e.g. ['Data Engineer']: people whose CV headline holds one come first, then the closest others. Ranks by them too, so they can stand in for text. |
| niceToHave | array of string | Skills or words to rank by, e.g. ['dbt']: nobody is left out for missing one. |
| matchRoleId | string | Rank everyone against this role's criteria, as the Search candidates tab's Match to does, leaving out people already on the role. Not a filter: roleId is 'applied to'. Any text ranks with the role; niceToHave then puts CVs naming more of them first, without changing who is found; mustHave still keeps only CVs naming every word. |
| criteriaOff | array of string | With matchRoleId: the role's criteria, by the ids get_role returns, to leave out of this ranking. The role itself is unchanged. |
| criteriaMoved | array of string | With matchRoleId: the role's criteria, by id, to rank at the other weight for this search, a Must have as a Nice to have or the other way round. |
| discipline | string | Only people in this discipline: an id from the `disciplines` the first page of list_candidates returns, or 'none' for people without one. |
| rightToWork | one of 2 shapes | 'yes': only people who said yes to the right-to-work question on an application. The question names no country; a country code such as 'GB' is accepted and means the same. |
| companies | array of string | Only people who work at one of these companies now, as their CV says, e.g. ['Monzo', 'Starling Bank']. |
| mustHave | array of string | Only people whose CV names every one of these, as whole words, e.g. ['Kubernetes']. |
| roleId | string | Only people who applied to this role. |
| stage | one of: new, uploading, applied, sourced, invited, in progress, complete, stalled, duplicate, archived, withdrawn, shortlisted, pipeline, hired, rejected | Only people with an application at this stage, e.g. 'shortlisted' or 'hired'. |
| country | string | Only people who applied from this country, as an ISO 3166-1 alpha-2 code (geocode_country turns a name into one). |
| latitude | number | With longitude and radiusKm: only people who applied from near here. |
| longitude | number | |
| radiusKm | number | How far from latitude/longitude, in km (geocode_place gives the point). |
list_candidatesList the candidate pool
List the organisation's candidate pool, most recently applied first: one row per person, however many times they applied. Narrow it by part of a name or email (`text`; a whole address is looked up directly), `discipline`, `rightToWork`, `companies` (where they work now), `mustHave` (words their CV must name) or `appliedSince`. The first page says how many match out of the whole pool; pass `nextCursor` back as `cursor` for the next page. For people who fit a role, use search_existing_candidates instead.
Show 14 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| text | string | Part of a name or email address. A whole address is looked up directly. |
| discipline | string | Only people in this discipline: an id from the `disciplines` the first page of list_candidates returns, or 'none' for people without one. |
| rightToWork | one of 2 shapes | 'yes': only people who said yes to the right-to-work question on an application. The question names no country; a country code such as 'GB' is accepted and means the same. |
| companies | array of string | Only people who work at one of these companies now, as their CV says, e.g. ['Monzo', 'Starling Bank']. |
| mustHave | array of string | Only people whose CV names every one of these, as whole words, e.g. ['Kubernetes']. |
| roleId | string | Only people who applied to this role. |
| stage | one of: new, uploading, applied, sourced, invited, in progress, complete, stalled, duplicate, archived, withdrawn, shortlisted, pipeline, hired, rejected | Only people with an application at this stage, e.g. 'shortlisted' or 'hired'. |
| country | string | Only people who applied from this country, as an ISO 3166-1 alpha-2 code (geocode_country turns a name into one). |
| latitude | number | With longitude and radiusKm: only people who applied from near here. |
| longitude | number | |
| radiusKm | number | How far from latitude/longitude, in km (geocode_place gives the point). |
| appliedSince | string | Only people who last applied on or after this date, YYYY-MM-DD. |
| cursor | string | The nextCursor from the previous page, to continue from. |
get_candidateGet a candidate and their applications
One person in the candidate pool and what they have applied to, newest first, by the candidateId list_candidates or search_existing_candidates returned. Applications on roles you can't see are left out. Use get_application for an application's full detail.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| candidateId* | string |
search_web_for_candidatesREAD ONLYSearch the web for candidates
Search the public web for new candidates — people not in the organisation's database — with a Google X-Ray query (e.g. site:linkedin.com/in with OR-grouped job titles, target employers and must-have skills). It runs on Google, or on Exa for organisations on the newer web search; the query and the results are the same shape either way: a name, a title, a link and a snippet. It saves nothing: pass the ones worth keeping to save_web_candidates. Put the place into the query text; on the newer web search, also pass it as `location` to leave out people whose profile says they are somewhere else, and pass `mustMention` to keep only profiles naming every one of those words. Cast a wide net: OR together every equivalent job title, and run a search per target employer.
Show 7 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| query* | string | A Google X-Ray query, e.g. site:linkedin.com/in with OR-grouped job titles and must-have skills. Put the place into the query text too, as well as in `location`. |
| num | integer | How many results to return, 1-10. Defaults to 3. |
| start | integer | The 1-based index of the first result, for paging. Defaults to 1; step by `num` to page. |
| dateRestrict | string | Google only; ignored where the organisation's web search runs over public profile pages. |
| location | string | The place to keep people in, with its country, e.g. 'London, UK': the country is what rules out someone who only states one, such as 'Remote, United States'. Anyone whose profile says they are somewhere else is left out; anyone whose profile names no place is kept. Only where the web search runs over public profile pages; ignored on Google. |
| mustMention | array of string | Words a profile must name, every one, e.g. ['MHRA', 'PharmD']. Anyone whose profile doesn't name them all is left out. Only where the web search runs over public profile pages; ignored on Google. |
save_web_candidatesSave web search results
Save up to 10 search_web_for_candidates results to a saved list, ready for add_saved_candidates_to_role. Each is looked up as it is saved: through a paid enrichment where the search ran on Google, from each person's own page where it ran on Exa. Pass the `reference` from an earlier save to keep everything in one list, or omit it to start one and reuse the reference that comes back.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| contacts* | array of object | The profiles worth keeping, built from a prior web search. Send only the ones the caller has decided on: where the search was Google's each is enriched and billed on the way in; where it ran over public profile pages each is built from its page and nothing is billed. |
| reference | string | The saved list to save into. Pass the `reference` from an earlier save to keep everything together; omit to start a new one, whose reference comes back in the response. |
get_saved_candidatesList a saved list
List every candidate saved under a `reference` — before deciding who to add to a role, or after losing track of what was saved. A role's id lists the role's own list, which First's app sources into. An empty or unknown reference returns an empty list.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| reference* | string | The saved list to show. |
clear_saved_candidatesCHANGES DATAEmpty a saved list
Remove every candidate saved under a `reference`. Starting a fresh reference achieves the same without discarding anything, so prefer that unless the user asks to start this one over.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| reference* | string | The saved list to empty. |
add_candidate_to_roleAdd candidates to a role
Add people to a role as applications, wherever they were found. Pass existing candidates as `candidateIds`, from search_existing_candidates, list_candidates or get_candidate: each is added as a copy of their newest application. Pass people on the web as `urls`, their LinkedIn profile URLs — from search_web_for_candidates, or the address of a profile page you are looking at — on organisations on the newer web search; elsewhere, save them with save_web_candidates and add them with add_saved_candidates_to_role. First looks each one up itself, builds a CV from their page and looks up their email and phone in the background (paid lookups, about a minute; get_application shows them after) so they can be contacted; nothing else about them is taken from you. Each becomes an application like any other: it is assessed against the role's criteria once the role is published, and synced to the organisation's ATS where that is on. Adding someone already on the role finds their application rather than making another. The response lists who was added, with their application ids, and who could not be.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | The role to add them to. |
| candidateIds | array of string | People already in the organisation's candidate database, by the `candidateId` search_existing_candidates, list_candidates or get_candidate returned. Each is added as a copy of their newest application. |
| urls | array of string | People on the public web, by their LinkedIn profile URL: a `url` the web search returned, or the address of a profile page you are looking at. Only on organisations on the newer web search; elsewhere, save them to a saved list and add them from there. First looks each person up itself; nothing else about them is taken from the caller. |
add_saved_candidates_to_roleAdd saved candidates to a role
Add people from a saved list to a role as applications, where they are assessed against its criteria. The contactIds are the ids save_web_candidates or get_saved_candidates returned; pass the list's `reference` too, or omit it for the role's own list. The response lists the ids that were added; ids that no longer resolve to a saved candidate are simply absent.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | |
| contactIds* | array of string | The ids of the saved candidates to add to the role as applications — the `id` values a save or a saved list returned. |
| reference | string | The saved list the candidates are on. Pass the `reference` a save returned; omit for the role's own list, which First's app sources into. |
Search applications, move people through your stages, record how far they have got, reject or hire them, and undo those decisions.
search_applicationsSearch applications
Search the organisation's applications by name or email across every role. Only returns applications for a role the token owner is allowed to see. Use this to find a specific person by name or email address. Omit `query` to list rather than search: `list: "hired"` with no query lists hires, which is how you answer "who have we hired recently" — there is no search term that returns them all. Each row carries `hiredAt`, `shortlistedAt` and `rejectedAt`, so filter to a date window on those rather than on `createdAt`, which is when the application arrived rather than when the outcome happened. A row in the pipeline carries `pipelineStageId` and `pipelineStageName`, the stage it is at; the id is the one progress_application takes. `sourced: true` keeps only sourced candidates (`false` only those who applied themselves), as the candidate list's filter does. Each row carries `sourceMethod`, and `firstContactedAt` once anyone has contacted the candidate; a sourced row without it has not been contacted yet. `contacted: true` keeps only those somebody has contacted, by an email from First, a LinkedIn message logged or marked as contacted, and `false` only those nobody has, so `sourced: true, contacted: false` lists the sourced candidates still to approach. `linkedInMessaged: true` keeps only those somebody logged a LinkedIn message to, and such a row carries `lastLinkedInMessagedAt`. **Read `truncated` before quoting any total.** When it is true this is a partial answer — `found` is a lower bound and paging to the end will not reach the rest, so say the count is at least that rather than stating it as the number. Only when `truncated` is false is `found` the whole answer.
Show 8 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| query | string | Name or email to search for. Omit to list the whole of `list` instead — there is no term that returns every hire. |
| field | one of: email, fullName | Restrict matching to one field; omit to match either |
| list | one of: inbox, shortlisted, rejected, hired, all | |
| sourced | boolean | true for sourced candidates only, false for only those who applied themselves; omit for everyone |
| linkedInMessaged | boolean | true for only those somebody logged a LinkedIn message to, false for everyone else; omit for everyone |
| contacted | boolean | true for only those somebody has contacted (an email from First, a LinkedIn message logged, or marked as contacted), false for only those nobody has; omit for everyone |
| page | integer |
get_applicationREAD ONLYGet an application
One application's record: which stage it has reached (and, while it is in the pipeline, pipelineStageId and pipelineStageName: the id progress_application takes), when it was hired, shortlisted or rejected and why (rejection), where the candidate came from (sourceMethod, and referrer for a direct applicant), and its assessment score with the criteria version behind it. Search finds the person; this says what happened to them. Returns not found for a role you are not allowed to see. Their work and education history comes back too, taken from the CV already held against the application — use this rather than the web-sourcing tools for anyone who has applied before: it costs nothing and it is their own CV, not a third-party guess at it. Pass includeProfile: false to leave it out.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| includeProfile | boolean | Set false to leave out the candidate's CV history. Included by default. |
progress_applicationProgress an application
Move an application forward. One that has not reached the pipeline yet is shortlisted; one already in it moves to the pipeline stage you name, and without one nothing changes. A candidate who has not finished applying is fast-tracked past the steps they have left, as First does. Recorded against you. The candidate is only emailed when you set sendEmail, and the email waits at least 30 minutes so a person can cancel it; confirm with the person before doing either. Where the role sends no email for the move, sendEmail is refused rather than ignored, as it is wherever First has not switched on candidate emails through the API. Offered from the stages First offers it from (stalled, in progress, submitted, in the pipeline); anything else, such as a rejected or hired application, is refused, as is a candidate with an open offer: withdraw it in First before moving them on. Moving it to the stage it is at already does nothing. The answer's changed is false when there was nothing to do: it already stood so, from an earlier attempt or somebody else's action, and nothing was sent.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| pipelineStageId | string | Which pipeline stage to move to, from the stages get_stage_topics lists for the role. Needed to move an application already in the pipeline. |
| sendEmail | boolean | Email the candidate the invitation to the stage they move to from the organisation's templates. Nothing is sent unless this is true. The email waits at least 30 minutes, during which it can be cancelled from the application in First. Only where First has switched on candidate emails through the API for the organisation; elsewhere asking for one is refused and nothing changes. |
reject_applicationReject an application
Reject an application, recorded against you as a manual rejection. The candidate is only emailed the organisation's rejection when you set sendEmail, and it waits at least 30 minutes (longer if the organisation delays rejections) so a person can cancel it. Ending somebody's application is not yours to decide: confirm who, that they mean it, and whether to email them, before calling this. Only a stalled, submitted or pipeline application can be rejected, as in First; one already rejected is left as it is. A candidate who has an offer can only be rejected by someone who manages the role, and not at all while the offer is open: withdraw it in First before rejecting them. Where the role sends no rejection email, sendEmail is refused rather than ignored, as it is wherever First has not switched on candidate emails through the API. The answer's changed is false when there was nothing to do: it already stood so, from an earlier attempt or somebody else's action, and nothing was sent.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| sendEmail | boolean | Email the candidate the rejection from the organisation's templates. Nothing is sent unless this is true. The email waits at least 30 minutes, during which it can be cancelled from the application in First. Only where First has switched on candidate emails through the API for the organisation; elsewhere asking for one is refused and nothing changes. |
hire_applicationHire an applicant
Mark an application as hired, closing out their process. Recorded against you. The candidate is only emailed the organisation's hired email when you set sendEmail, and it waits at least 30 minutes so a person can cancel it. Confirm with the person first — this is the end of somebody's hiring process. Only a submitted or pipeline application can be hired, as in First; one already hired is left as it is. Where the organisation hires through offers, this is refused: the candidate is hired by accepting an offer made in First. Where the role sends no email on a hire, sendEmail is refused rather than ignored, as it is wherever First has not switched on candidate emails through the API. The answer's changed is false when there was nothing to do: it already stood so, from an earlier attempt or somebody else's action, and nothing was sent.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| sendEmail | boolean | Email the candidate the hired email from the organisation's templates. Nothing is sent unless this is true. The email waits at least 30 minutes, during which it can be cancelled from the application in First. Only where First has switched on candidate emails through the API for the organisation; elsewhere asking for one is refused and nothing changes. |
bulk_application_actionCHANGES DATAProgress, reject or hire many applications
Progress, reject or hire up to 100 applications on one role in a single call, as First's application list does. Recorded against you. Never emails a candidate: use the single actions with sendEmail for that. Every application must be on the role, or nothing is changed. Each one then meets the same rules as the single action — offered only from the stages First offers it from, left as it is when already there, and refused for a candidate with an open offer — and its outcome comes back one by one: done when it moved, unchanged when it was already there so nothing was done, or refused with the reason. Rejecting or hiring many people is not yours to decide: confirm exactly who, and that the person means it, before calling this.
Show 5 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | The role every application is on, as list_roles returns. |
| action* | one of: progress, reject, hire | progress shortlists, or moves applications already in the pipeline to pipelineStageId; reject; or hire. |
| applicationIds* | array of string | The applications to act on, all on the role, at most 100 at a time. |
| pipelineStageId | string | For progress: the pipeline stage to move to. Applications not yet in the pipeline are shortlisted straight into it, or into the first stage when it is left out. Those already in the pipeline move to it, and stay where they are when it is left out. |
bulk_copy_applications_to_roleCopy applications to another role
Copy up to 50 applications from one role onto another, as First's bulk copy does: each person gets a new application on the destination role carrying their answers, CV and assessment, and the original stays where it is. Emails nobody. The destination must be another role you can see that is a draft, open or paused, and every application must be on the source role, or nothing is copied. An application already copied to that role is not copied again; its existing copy's id comes back instead. Each copy appears to the destination role's hiring team, so confirm the people and the role with the person first.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | The role the applications are on now, as list_roles returns. |
| destinationRoleId* | string | The role to copy the applications to: another role that is a draft, open or paused. |
| applicationIds* | array of string | The applications to copy, all on the source role, at most 50 at a time. |
move_candidate_back_to_pipelineMove a candidate back to the pipeline
Move a candidate whose offer was declined or withdrawn back to the Inbox or a pipeline stage, off the Offers & hires board. The offer stays in their history. Nobody is emailed. Only for someone who manages the role.
Show 5 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | |
| offerId* | string | The declined or withdrawn offer's id, as list_application_offers returns it. |
| to* | one of: inbox, stage | Where the candidate goes: back to the Inbox, or to a pipeline stage. |
| pipelineStageId | string | Which pipeline stage, from the stages get_stage_topics lists for the role. Needed with `to: stage`. |
undo_shortlistMove an application back out of the pipeline
Take an application in the pipeline back out of it, undoing its shortlisting: a submitted one goes back to the inbox, and one that was fast-tracked goes back to the candidate finishing their application. Its pipeline stage is cleared. To move back to an earlier pipeline stage instead, use progress_application with that stage. Recorded against you on the timeline. Nobody is emailed; an invitation to the stage that is still queued is cancelled, but one already sent cannot be taken back. Offered only for an application in the pipeline, as in First; one not in it is left as it is, unless it is rejected, hired or withdrawn, which is refused. The answer's changed is false when there was nothing to do: it already stood so, from an earlier attempt or somebody else's action, and nothing was sent.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
undo_rejectUndo a rejection
Take back a rejection, putting the application where it was before: back in the inbox, back among the stalled, or back at its pipeline stage (the first stage, if the role no longer has the one it was at). Recorded against you on the timeline. Nobody is emailed; a rejection email that is still queued is cancelled, but one already sent cannot be taken back, so check with the person whether the candidate needs telling. Offered only for a rejected application, as in First; one that is not rejected is left as it is, unless it was hired or withdrawn instead, which is refused. The answer's changed is false when there was nothing to do: it already stood so, from an earlier attempt or somebody else's action, and nothing was sent.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
undo_hireUndo a hire
Take back a hire, putting the application where it was before: back at its pipeline stage (the first stage, if the role no longer has the one it was at), or in the inbox if it was hired from there. Recorded against you on the timeline. Nobody is emailed; an offer email that is still queued is cancelled, but one already sent cannot be taken back, so check with the person whether the candidate needs telling. Offered only for a hired application, as in First, and not for a hire the organisation's ATS recorded, which has to be undone there. One that is not hired is left as it is, unless it was rejected or withdrawn instead, which is refused. The answer's changed is false when there was nothing to do: it already stood so, from an earlier attempt or somebody else's action, and nothing was sent.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
reassess_applicationReassess an application
Reassess one application against its role's criteria as they now stand — what First offers once the criteria have changed since it was assessed. Overrides already made are kept. Only for people who manage the role. The assessment is queued and takes a few minutes, so the answer says queued: true and get_application shows the new assessment once it has finished. queued: false means there was nothing to do: the assessment is already up to date or already on its way. Refused for an application never assessed, or while the role is closed, paused at its shortlist target, or has criteria waiting for approval. Nothing is sent to the candidate. To reassess everybody on a role, use reassess_applications.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
reassess_applicationsReassess a role's applications
Reassess every application on a role whose assessment the role's criteria have moved on from — what First's Reassess button does after the criteria change. Overrides already made are kept. Only for people who manage the role. The reassessments are queued and finish over the next few minutes; the answer says how many were queued, and 0 means none needed it. Each one is a paid assessment, so on a large role tell the person how many will run first. Refused while the role is closed, paused at its shortlist target, or has criteria waiting for approval. Nothing is sent to candidates.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | The role's id, as returned by list_roles. |
override_assessmentChange an application's assessment
Change an application's assessment by hand, criterion by criterion, as First's Change form does — when the person disagrees with how a criterion was judged. A scored criterion takes a whole-number score on its scale, which decides whether it is met; any other takes met or not met. Criterion ids come from get_role. Only criteria an assessment decides can be changed: one scored at an interview is refused. Earlier overrides are kept unless you name them, and setting a criterion back to what the assessment said clears its override. Recorded against you on the timeline, with the comment; say why in it. changed: false means the assessment already said this. Confirm the change with the person first. Nothing is sent to the candidate.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| changes* | array of object | The criteria to change, and what to change each to. |
| comment | string | Why, recorded on the timeline and as the reasoning of each new override. |
score_stageScore an interview stage
Score an application's interview stage, as First's stage scoring panel does, from what the interviewer reported: each of the stage's interviewer-scored criteria takes a whole-number score on its scale (which decides whether it is met), or met or not met, with notes. get_stage_topics lists the stages. The stage's interviewer-scored criteria are not among its topics: get_role lists them in its criteria, as those with checker 'human-assessor' whose checkerConfiguration.pipelineStageId is the stage, with their scales. Leave out pipelineStageId to score the video and document answers on the initial application that the hiring team scores: those 'human-assessor' criteria with no pipelineStageId. Notes you leave out keep those already written. The comment covers what no criterion does, such as notice period or salary, and goes on the timeline. Recorded against you, so only score what the interviewer actually said, and confirm the scores with the person before saving. Open to anyone on the role's hiring team. saved: false means it already stood as given. Nothing is sent to the candidate.
Show 5 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| pipelineStageId | string | The interview stage being scored, from the role's pipeline (get_stage_topics lists its stages). Leave out to score the video and document answers on the initial application that the hiring team scores. |
| scores* | array of object | The criteria to score. May be empty to post only a comment. |
| comment | string | About the interview as a whole, for what no criterion covers, such as notice period or salary. Posted on the timeline with the save. Up to 1,000 characters. |
set_stage_progressSet a candidate's Stage Progress
Record where a candidate has got to within one of the role's interview stages, as a screener does in First: unreachable, interview_booked, did_not_attend, spoken_to or awaiting_feedback, or null to set them back to Awaiting contact. Any interview stage of the role, so an earlier one can be put right. An optional comment goes on the timeline with it. Every call is recorded, as every change is in First: setting unreachable twice records two tries, so do not call it again to retry without checking. The candidate is not told. Anyone on the role's hiring team can. Answers with the activityId of the timeline row, which edit_stage_comment takes.
Show 5 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| pipelineStageId* | string | The interview stage to record it against. Any of the role's interview stages, not only the one the candidate is in, so an earlier stage's can be put right. |
| progress* | one of 2 shapes | How far they have got. null sets them back to Awaiting contact, or to the interview's result once the stage is scored. |
| comment | string | Goes on the timeline with the change. |
edit_stage_commentEdit a stage comment
Rewrite the comment you left with an interview's scores, a Stage Progress (set_stage_progress answers with its activityId), or a moved or cancelled interview. Only whoever left a comment can edit it, as in First. Which kind it is is read off the timeline row: an interview's scores also need you to manage the role, as scoring it did. An empty comment clears it; the change itself stays as it was.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| activityId* | string | The timeline row the comment is on. |
| comment* | string | The comment as it should now read. Empty clears it. |
change_rejection_reasonChange an application's rejection reason
Say why a rejected or withdrawn application closed, choosing another of the organisation's rejection reasons from list_rejection_reasons. Recorded on the timeline against you; the candidate is not told. Only someone who manages the role can, as in First, and only where the organisation uses rejection reasons. An application still open is refused, as is an archived reason. Choosing the reason it already has changes nothing.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| rejectionReasonId* | string | The reason to record, one of the organisation's own that is not archived, as list_rejection_reasons returns them. |
update_application_detailsUpdate an application's details
Correct what a candidate gave on their application: name, email, mobile, LinkedIn, availability, salary expectations, location, or the summary First wrote of them. Only for somebody who manages the role. Give only the details to change; an empty string clears one where the role allows it, though a name and email can never be cleared. Each detail is checked against the question the role asks for it — one the role does not ask for is refused — and if any is refused, none is saved and the error says which and why. A companion detail (phoneCountry, availabilityOther, salaryExpectationsOther, salaryExpectationsCurrency or salaryExpectationsFrequency) is only taken with the detail it goes with; left out, the words beside the same option and an amount's currency and frequency keep what is stored. The candidate is not told. Recorded against you on the timeline; sending the same details again changes nothing, and answers with changed false.
Show 17 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| fullName | string | The candidate's full name. Cannot be cleared. |
| preferredName | string | What the candidate likes to be called. |
| string | The candidate's email address. Cannot be cleared, and must not be one another application on the role already has. | |
| phone | string | The candidate's mobile number. |
| phoneCountry | string | The ISO country code the mobile number is dialled from, such as GB. Only with phone; defaults to GB. |
| string | The candidate's LinkedIn profile URL. | |
| availability | string | When the candidate can start: one of the role's availability options, or other. |
| availabilityOther | string | The candidate's own words beside their availability option, such as when it is other. Only with availability; left out, the words stored beside that same option are kept. |
| salaryExpectations | string | The candidate's salary expectations, for a role that asks with options or free text: one of its options (or other), or the text. |
| salaryExpectationsOther | string | The candidate's own words beside their salaryExpectations option, such as when it is other. Only with salaryExpectations; left out, the words stored beside that same option are kept. |
| salaryExpectationsAmount | string | The candidate's expected salary, for a role that asks for an amount. Its currency and frequency are kept from the stored amount unless given; with none stored, give salaryExpectationsCurrency and salaryExpectationsFrequency with it. |
| salaryExpectationsCurrency | string | The ISO currency code of salaryExpectationsAmount, such as GBP. Only with salaryExpectationsAmount. |
| salaryExpectationsFrequency | string | How often salaryExpectationsAmount is paid: yearly, monthly, weekly, daily or hourly. Only with salaryExpectationsAmount. |
| summary | string | The summary of the candidate shown at the top of their application. Replacing First's generated summary records it as written by you. |
| location | object | Where the candidate is, as { city, country }. They are geocoded; give latitude and longitude as well to place the candidate exactly. |
set_application_referrerSet who referred a candidate
Set or change who referred the candidate on an application, recorded on its timeline against you. Nobody is emailed. The referrer is someone in the organisation on First, by user id, or anyone else by name and email. Setting the referrer it already has changes nothing. Only someone who manages the role (an account owner or admin, or one of the role's role admins) can change it, and only where the organisation has referrals.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| referrer* | one of 2 shapes | Who referred the candidate: { "kind": "user", "userId": ... } for someone in the organisation on First, or { "kind": "person", "name": ..., "email": ... } for anyone else. |
remove_application_referrerCHANGES DATARemove who referred a candidate
Remove the referrer from an application, recorded on its timeline against you. Nobody is emailed. An application without one is left as it is. Only someone who manages the role can change it, and only where the organisation has referrals.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
archive_applicationArchive an application
Archive an application, taking it out of the role's inbox, pipeline and decision lists without rejecting or otherwise deciding anything about the candidate. Nobody is emailed. restore_application puts it back. Recorded against you on the timeline. Offered from any stage except withdrawn, as in First; one already archived is left as it is. The answer's changed is false when there was nothing to do: it already stood so, from an earlier attempt or somebody else's action, and nothing was sent.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
restore_applicationRestore an archived application
Restore an archived application, putting it back in the role's lists at the stage it had reached before it was archived. Nobody is emailed. Recorded against you on the timeline. Offered only for an archived application, as in First; one that is not archived is left as it is. The answer's changed is false when there was nothing to do: it already stood so, from an earlier attempt or somebody else's action, and nothing was sent.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
Draft, approve and extend offers, record the answer, and mark the contract signed.
list_offer_boardREAD ONLYList a role's Offers & hires board
The role's candidates with offers, by the board's columns: awaiting approval (with who it waits on), offered, hired, and not accepted (declined or withdrawn). Each entry has the applicationId the other offer tools take. Offer terms (salary, start date, contract, notes) are only shown to people who manage the role or approve its offers; for anyone else withTerms is false. Only for organisations with offers and approvals.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | The role's id, from list_roles. |
list_application_offersREAD ONLYList an application's offers
Every offer made to a candidate on the application's role, newest first, with its id, status and who made, extended or closed it. One waiting for approval says who must approve it, who has, and whether you can (youCanApprove). Terms are only shown to people who manage the role or approve its offers. Only for organisations with offers and approvals.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
draft_offerDraft offer
Draft an offer for a candidate, recorded against you: the salary, start date, optional notes and how the contract reaches them. Confirm the terms with the person first. Where the organisation's Hiring groups need offers approved, it waits for its approvers, who are emailed; otherwise it is ready to extend. The candidate is not told until it is extended (extend_offer). Only for someone who manages the role, on a submitted or pipeline application with no open offer, in an organisation with offers and approvals.
Show 6 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| salary* | object | The salary offered: an amount, its currency (ISO 4217, such as GBP) and how often it is paid. |
| startDate* | string | The start date offered, as YYYY-MM-DD. |
| notes | string | Anything else about the offer, for the hiring team. |
| contract | one of 2 shapes | How the contract reaches the candidate: a link to it, or sent for e-signature (marked signed by hand). Leave it out to send it separately. Refused when the organisation doesn't send contracts that way, or doesn't send contracts with offers. |
approve_offerApprove offer
Approve an offer waiting for approval, as yourself. Only someone who must approve it now (list_application_offers says youCanApprove) or an organisation admin can; managing the role is not enough. Once it has every approval it needs, it is ready to extend. Approve only when the person has said they approve it.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | |
| offerId* | string | The offer's id, as list_application_offers returns it. |
extend_offerExtend offer
Extend an approved offer, so it is the candidate's to answer. Recorded against you. The candidate is only emailed when you set sendEmail, with the subject and body you write; show the person the email and get their go-ahead before sending it. It waits at least 30 minutes, during which a person can cancel it from the application in First, and withdrawing the offer cancels it. Where First has not switched on candidate emails through the API, sendEmail is refused and the offer is not extended. A contract given as a link becomes the email's button, so put {{{button}}} in the body where it goes. emailQueued in the answer says whether an email is on its way. Only for someone who manages the role.
Show 6 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | |
| offerId* | string | The offer's id, as list_application_offers returns it. |
| sendEmail | boolean | Email the candidate the offer, with the subject and body given. Nothing is sent unless this is true. The email waits at least 30 minutes, during which it can be cancelled from the application in First, and withdrawing the offer cancels it too. Only where First has switched on candidate emails through the API for the organisation; elsewhere asking for one is refused and the offer is not extended. |
| subject | string | The email's subject. Needed with sendEmail. |
| body | string | The email's body, which may use the organisation's template variables, and the offer's own: {{offerSalary}}, {{offerStartDate}} and {{contractLink}}, filled in from the offer. {{contractLink}} is refused unless the contract is a link. Needed with sendEmail. Where the contract is a link, put {{{button}}} where its button goes. |
withdraw_offerCHANGES DATAWithdraw an offer
Take back an open offer, with the reason why, recorded against you. An offer email still waiting to go is cancelled; one already sent is not. The candidate waits under Not accepted until they are made a new offer, moved back to the pipeline or rejected. Confirm with the person first. Only for someone who manages the role.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | |
| offerId* | string | The offer's id, as list_application_offers returns it. |
| reason* | string | Why the offer is withdrawn, recorded on the offer and the application's timeline. |
record_offer_acceptedRecord that an offer was accepted
Record that the candidate accepted an extended offer, which hires them, recorded against you. This is the only way to hire where the organisation hires through offers. Only record what the candidate actually said, and confirm with the person first. Nobody is emailed, and an offer email still waiting to go is cancelled. Only for someone who manages the role.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | |
| offerId* | string | The offer's id, as list_application_offers returns it. |
record_offer_declinedRecord that an offer was declined
Record that the candidate turned down an offer they have, with their reason, recorded against you. They are not rejected: they wait under Not accepted. Only record what the candidate actually said. Nobody is emailed, and an offer email still waiting to go is cancelled. Only for someone who manages the role.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | |
| offerId* | string | The offer's id, as list_application_offers returns it. |
| reason* | string | Why the candidate declined, recorded on the offer and the application's timeline. |
mark_contract_signedMark an offer's contract signed
Record that the candidate signed a contract sent for e-signature, once the offer is extended. There is no e-signature integration yet, so it is marked by hand: only do so when the person says it is signed. Only for someone who manages the role.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | |
| offerId* | string | The offer's id, as list_application_offers returns it. |
Add notes and interview transcripts to an application, import meetings, and manage the emails waiting to go to a candidate.
add_noteAdd a note to an application
Write a note on an application's timeline, as you, for the hiring team to read. The candidate never sees it. Tagging a colleague on the role's hiring team with @ and their full name emails them a link to the note; tagging anyone else refuses the whole note, with the reason. Returns the note's id, which edit_note and delete_note take. Posting the same words as your newest note on the application returns that note instead of writing it twice, with changed false, and nobody is emailed again.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| content* | string | The note, in markdown. Tag a colleague on the role's hiring team with `@` and their full name to email them a link to it; tagging anyone else refuses the note. |
edit_noteEdit a note on an application
Rewrite a note you wrote on an application's timeline, replacing its words entirely. Only your own notes can be edited, as in First; a colleague's is refused. Colleagues the new words tag for the first time are emailed a link to it; those already tagged are not emailed again, and an @name removed from the words removes the tag. The answer's changed is false when the note already had these words, and then nobody is emailed.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| noteId* | string | The note's id, as returned by add_note. |
| content* | string | The note, in markdown. Tag a colleague on the role's hiring team with `@` and their full name to email them a link to it; tagging anyone else refuses the note. |
delete_noteCHANGES DATADelete a note on an application
Delete a note you wrote on an application's timeline. It cannot be brought back, so confirm with the person which note, and that they mean it, first. Only your own notes can be deleted, as in First; a colleague's is refused. Deleting one already deleted does nothing, and answers with changed false.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| noteId* | string | The note's id, as returned by add_note. |
list_meeting_notesREAD ONLYList your meetings to import
Your own recent meetings, newest first, from every meetings provider you have connected to First (your Google calendar and Meet, and the organisation's Granola), so you can pick the interview with this candidate and import it with import_meeting_note. Each has an id, its provider, a title, when it happened and, where the provider gives them, its attendees. Only ever your meetings, never a colleague's. Pass nextCursor back to look further back. Empty when you have no provider connected: say so, and that one can be connected in First's settings. Fails with what to do when your connection has lapsed.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| cursor | string | The nextCursor from the previous page, to look further back. Omit for the most recent meetings. |
import_meeting_noteImport a meeting onto an application
Import one of your meetings onto a candidate's application: the note-taker's summary and, where there is one, the transcript, stored as files on the candidate that the hiring team can read and that stage notes can be suggested from. Fetched through your own connection, and recorded against you. Take the noteId and provider from list_meeting_notes, and check with the person that it is the right meeting for this candidate — it will be visible to everyone on the role. Importing a meeting already imported adds nothing.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| noteId* | string | The meeting's id, as returned by the meeting notes list. |
| provider* | one of: granola, google | Which provider listed the meeting, as returned alongside its id. A meeting id only means something to the provider it came from. |
add_transcriptAdd a transcript or interview notes
Add a meeting transcript, an AI note-taker's summary or interview notes to a candidate's application, as text. It is stored as a file on the candidate, recorded against you, read by everyone on the role's hiring team, and used when suggesting stage notes. Use import_meeting_note instead for a meeting First can fetch from your connected provider. Adding the same text under the same title again adds nothing. Make sure it is about this candidate before adding it.
Show 5 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| title* | string | What the file is called on the candidate, e.g. the interview it records. Up to 200 characters. |
| type* | one of: transcript, summary, notes | transcript for the spoken record, summary for an AI note-taker's write-up, notes for notes somebody wrote. Defaults to transcript. |
| contents* | string | The text itself. |
list_queued_emailsREAD ONLYList an application's scheduled emails
The emails scheduled to go to an application's candidate that have not been sent yet — an invitation to the next stage, a rejection or an offer chosen with a delay — soonest first, each with its emailId, kind, subject, recipient and when it is due (sendAt, epoch milliseconds). The email extending an offer has no subject or recipient unless you may see the offer's terms, and only someone who may see them can cancel it. Pass the emailId to cancel_queued_email. An email scheduled a second or two ago may not be listed yet: the list can lag just behind. A scheduled email cannot be sent early from here: the wait is what lets a person stop it. Returns not found for a role you are not allowed to see.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
cancel_queued_emailCHANGES DATACancel a scheduled email
Cancel a scheduled email so the candidate never receives it. The decision behind it stands — a rejection stays a rejection, just without its email — and a cancelled email cannot be scheduled again, so confirm with the person first. As in First, any other scheduled email of the same kind to this candidate is cancelled too. One that has already gone or is going is left alone; the state returned says where it got to. Find the emailId with list_queued_emails.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| emailId* | string | The email's id, as returned by list_queued_emails. |
mark_candidate_contactedMark a candidate as contacted
Record that you contacted a candidate outside First, on their timeline for the hiring team. First sends nothing. Set channel to "linkedin" to log a LinkedIn message, optionally with the words you sent; log each one once it has gone, on any candidate, and every call logs another. Leave channel unset to mark a sourced candidate as contacted some other way, such as by phone; that is refused for a candidate who applied, and marking one already contacted does nothing. For a sourced candidate not yet contacted, this is their first contact: it records that they were told First holds their details and where they came from, and keeps them under the organisation's applicant retention policy instead of deleting them a month after sourcing. Unlike an email from First, a call or a LinkedIn message says none of that on its own, so check with the person that they did. Needs manage access to the role. Do not use it for email: emails are sent from First and recorded on their own.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
| channel | "linkedin" | How they were contacted: "linkedin" for a LinkedIn message. Unset for any other contact. |
| message | string | With channel "linkedin" only: what was sent, for the hiring team to read. The candidate never sees it. |
mark_rejection_sent_manuallyMark a rejection as sent
Record that a rejected candidate has already been told — by phone, or an email sent outside First — so First stops offering to send them the rejection email. Sends the candidate nothing, and is recorded against you on their timeline. Only offered, as in First, for a rejected application on a role that sends rejection emails, while no rejection email has gone or is scheduled; marking one already sent or marked does nothing. Check with the person that the candidate really has been told.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| applicationId* | string | The application's id, as returned by search_applications. |
How hiring is going: applications, hires and time to hire, how candidates moved through your stages and where they dropped out.
get_reporting_summaryGet organisation reporting summary
Organisation-level trend reporting: one row per month with totals across every role — application volumes, hires and time to hire (average/median days), completion funnel percentages, aged applications and the share of roles using AI assessment. Use this first for organisation-wide topline questions and month-on-month trends (organisation owners only). from/to are inclusive YYYY-MM bounds (at most 24 months); omit both for the trailing 12 months. Months without a report are omitted. Periods: establish today's date first, then make one call. Months are YYYY-MM and inclusive; a range is at most 12 months. This month or month to date: month = this month (inProgress: say "so far"). Last month: month = the month before (in January, December of the year before). Last three months, including this one: from = two months ago, to = this month. Last three full months: from = three months ago, to = last month. This quarter (to date): from = the quarter's first month, to = this month (quarters: Jan–Mar, Apr–Jun, Jul–Sep, Oct–Dec). Last quarter: its first to its last month. Year to date: from = January this year, to = this month. Last 12 months: from = 11 months ago, to = this month. For example, on 6 October 2026 last three full months is from 2026-07 to 2026-09, and year to date from 2026-01 to 2026-10. A month-by-month trend: one call per month, or get_reporting_summary for organisation totals. The last 30 days or a date range (1–19 March): not possible yet; say so, and offer this month to date or each row's applicationsLast30Days. Every answer has period (the months covered), asOf (when the latest snapshot was built, nightly) and inProgress: name the period, and say how fresh it is.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | The organisation to report on. |
| from | string | First month in YYYY-MM format, inclusive. |
| to | string | Last month in YYYY-MM format, inclusive. |
get_monthly_reportingGet monthly recruitment reporting
Monthly reporting on Roles: one row per Role with application volumes (in period, total, last 7/30 days, rolling daily averages), hires and time to hire (average/median days), completion funnel percentages (started, complete, shortlisted), stalled and aged application counts, role status and whether the role uses AI assessment, time to fill, offers (made, extended, accepted, declined, withdrawn, acceptance rate, time to offer and to accept), applications and hires by source, sources: how candidates arrived, each with its name and group (applied or sourced) and applications, shortlisted and hires, and rejectionReasons: why Applications closed in the period, by the organisation's own reasons with who ended it (side), most first, for each Role and in the totals row, and referrers: where direct applicants came from (LinkedIn, Careers site, Indeed, Unknown when their link said nothing…), with applications and hires; sourced, copied and ATS Applications aren't counted. Defaults to the latest available month. Pass roleIds for specific Roles (list_roles gives the ids); omit it for every Role the user can report on. Organisation owners report on every Role, role admins on the Roles they're a role admin of. Requested Roles the user can't report on come back in notVisible: tell the user plainly you can't report on them. Requested Roles with no activity in the month are in noActivity. Periods: establish today's date first, then make one call. Months are YYYY-MM and inclusive; a range is at most 12 months. This month or month to date: month = this month (inProgress: say "so far"). Last month: month = the month before (in January, December of the year before). Last three months, including this one: from = two months ago, to = this month. Last three full months: from = three months ago, to = last month. This quarter (to date): from = the quarter's first month, to = this month (quarters: Jan–Mar, Apr–Jun, Jul–Sep, Oct–Dec). Last quarter: its first to its last month. Year to date: from = January this year, to = this month. Last 12 months: from = 11 months ago, to = this month. For example, on 6 October 2026 last three full months is from 2026-07 to 2026-09, and year to date from 2026-01 to 2026-10. A month-by-month trend: one call per month, or get_reporting_summary for organisation totals. The last 30 days or a date range (1–19 March): not possible yet; say so, and offer this month to date or each row's applicationsLast30Days. Every answer has period (the months covered), asOf (when the latest snapshot was built, nightly) and inProgress: name the period, and say how fresh it is. A from/to range gives ONE row per Role for the whole period, not one per month. Each figure is labelled in `figures`: `period` figures count what happened in the period and are added up over a range; `now` figures are the state at the latest month's snapshot, so never add them up across months. With more than one Role, `totals` adds them up. Medians and averages are worked out again from the underlying day counts, and rates from the combined counts. Figures that can't be combined exactly (months from before day counts were kept, percentages with nothing to rework them from) are null in folded and totals rows and named in `notCombined`: say so rather than estimating them. `missingMonths` lists months in the range without a report. Use this to compare Roles; use get_reporting_summary for organisation-wide trends (organisation owners only) and get_stage_reporting for pipeline bottlenecks.
Show 5 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | The organisation to report on. |
| roleIds | array of string | The Roles to report on, by id. Omit for every Role the user can report on. |
| month | string | One month, YYYY-MM, worked out from today's date as the description says. Omitted: the latest month with a report, usually this month so far. |
| from | string | First month of a range in YYYY-MM format, inclusive; give with to instead of month. |
| to | string | Last month of a range in YYYY-MM format, inclusive; at most 12 months after from. |
get_stage_reportingGet pipeline stage reporting
Pipeline funnel reporting on Roles: one row per Role per pipeline stage, in pipeline order, with how many applications reached each stage, how many of those reached the next (conversionToNext, a percentage) and the average/median days spent in it. Use this to find where candidates drop off or wait the longest (pass-through rates, bottlenecks). Defaults to the latest available month. Months before stage metrics existed return no rows. Pass roleIds for specific Roles; omit it for every Role the user can report on (organisation owners: every Role; role admins: the Roles they're a role admin of). Requested Roles the user can't report on are in notVisible, and ones with no activity in noActivity. Every stage figure is the state `now`, over the Role's history up to the snapshot, so a range gives the latest month's figures. Sourced candidates are often moved through stages at once, pulling the median days down: pass candidates: applied to leave them out, or sourced to see them alone. Months whose report was built before that split give no rows and are listed in unsplitMonths: say so rather than falling back to everyone. Periods: establish today's date first, then make one call. Months are YYYY-MM and inclusive; a range is at most 12 months. This month or month to date: month = this month (inProgress: say "so far"). Last month: month = the month before (in January, December of the year before). Last three months, including this one: from = two months ago, to = this month. Last three full months: from = three months ago, to = last month. This quarter (to date): from = the quarter's first month, to = this month (quarters: Jan–Mar, Apr–Jun, Jul–Sep, Oct–Dec). Last quarter: its first to its last month. Year to date: from = January this year, to = this month. Last 12 months: from = 11 months ago, to = this month. For example, on 6 October 2026 last three full months is from 2026-07 to 2026-09, and year to date from 2026-01 to 2026-10. A month-by-month trend: one call per month, or get_reporting_summary for organisation totals. The last 30 days or a date range (1–19 March): not possible yet; say so, and offer this month to date or each row's applicationsLast30Days. Every answer has period (the months covered), asOf (when the latest snapshot was built, nightly) and inProgress: name the period, and say how fresh it is. A from/to range gives ONE row per Role for the whole period, not one per month. Each figure is labelled in `figures`: `period` figures count what happened in the period and are added up over a range; `now` figures are the state at the latest month's snapshot, so never add them up across months. With more than one Role, `totals` adds them up. Medians and averages are worked out again from the underlying day counts, and rates from the combined counts. Figures that can't be combined exactly (months from before day counts were kept, percentages with nothing to rework them from) are null in folded and totals rows and named in `notCombined`: say so rather than estimating them. `missingMonths` lists months in the range without a report.
Show 6 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | The organisation to report on. |
| roleIds | array of string | The Roles to report on, by id. Omit for every Role the user can report on. |
| month | string | One month, YYYY-MM, worked out from today's date as the description says. Omitted: the latest month with a report, usually this month so far. |
| from | string | First month of a range in YYYY-MM format, inclusive; give with to instead of month. |
| to | string | Last month of a range in YYYY-MM format, inclusive; at most 12 months after from. |
| candidates | one of: everyone, applied, sourced | Whose time in stage to report: everyone (the default), or only the candidates who applied or who were sourced. |
The organisations you can act on, the hiring groups that decide who works on which roles, the referrals page and the rejection reasons.
list_organisationsList organisations
The organisations you can act on, with their ids. Every other tool needs an organisationId — call this first to find it rather than asking the user for one. Returns exactly one organisation for most people: use it without asking.
This tool takes no arguments.
list_hiring_groupsREAD ONLYList hiring groups
List the organisation's hiring group settings (whether it uses hiring groups, and who may open roles and make offers), its hiring groups with everyone in them, and the organisation members who can be put in one. A hiring group is a set of people who join the hiring team of every role in it. Owners and admins are in every group. Read-only. For the organisation's owners and admins, where the organisation has offers and approvals.
Show 1 fieldHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string |
save_hiring_groupCHANGES DATACreate or update a hiring group
Create a hiring group, or update one by its hiringGroupId, giving its name and everyone in it, which replaces who was in it; leave members out to keep who is in it, when renaming say. Each member joins every role in the group as a role-admin or a role-member. Members must be organisation members, from list_hiring_groups' people; owners and admins are in every group already, so they are left out. Two groups cannot share a name. People who leave a group leave the hiring team of its roles at once, so confirm the change with the person first. Needs hiring groups turned on (update_hiring_group_settings). For the organisation's owners and admins.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| hiringGroupId | string | The group to update, from list_hiring_groups. Leave out to create one. |
| name* | string | |
| members | array of object | Everyone in the group, replacing who was in it. Leave out to keep who is in it when updating, or to create an empty group. Organisation owners and admins are in every group already, so they are left out |
update_hiring_group_settingsCHANGES DATAUpdate hiring group settings
Turn hiring groups on or off for the organisation (useHiringGroups), and set who may open roles and make offers while they are on. Only the settings given change. Once the organisation uses hiring groups and has at least one, every new role needs a group, and a role in none must be put in one before its team changes. This changes how the whole organisation hires, so confirm with the person first. For the organisation's owners and admins, where the organisation has offers and approvals.
Show 6 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| useHiringGroups | boolean | Turn Hiring groups on or off for the organisation |
| anyoneCanOpenRoles | boolean | Whether any role admin on a role can open it, rather than only the people a group lets approve opening |
| openRoleRule | one of: any, all | While anyoneCanOpenRoles is off, whether any one or all of those people must approve |
| anyoneCanMakeOffers | boolean | Whether anyone on a role's hiring team can make an offer, rather than only the people a group lets approve offers |
| offerRule | one of: any, all | While anyoneCanMakeOffers is off, whether any one or all of those people must approve |
delete_hiring_groupCHANGES DATADelete a hiring group
Delete a hiring group, even while roles are in it, as First does: its people leave the hiring team of every role it was on, unless they are on it in their own right or through another group. A role left in no group must be put in one (set_role_hiring_groups) before its team changes again. Confirm with the person first. Needs hiring groups turned on. For the organisation's owners and admins.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| hiringGroupId* | string | The group to delete, from list_hiring_groups. |
set_role_hiring_groupsCHANGES DATASet a role's hiring groups
Put a role in the hiring groups given, replacing the ones it was in. Everyone in them joins its hiring team at the level their group gives them, and whoever was there only through a group it leaves goes. Any of the organisation's groups can be picked, and a refusal lists them. Once the organisation has any group, a role must be in at least one. For whoever manages the role, while the organisation uses hiring groups.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| roleId* | string | |
| hiringGroupIds* | array of string | The hiring groups the role is in, replacing its current ones. At least one once the organisation has any |
set_referrals_enabledCHANGES DATATurn the referrals page on or off
Turn the organisation's referrals page on or off: the page where people in the organisation refer candidates to its roles. Account owners only, and only where the organisation has referrals. It can only be turned on once the organisation has a work email domain. Turning it off takes the page down for everyone, so confirm with the person first.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| enabled* | boolean | true to turn the organisation's referrals page on, false to turn it off. |
list_rejection_reasonsREAD ONLYList rejection reasons
The organisation's own reasons for why an application closed, with who ended it (rejected-by-us, rejected-by-candidate, other), whether each is one of First's defaults or archived, and the criteria mapped to it. Read-only, and open to every member where the organisation uses rejection reasons.
Show 1 fieldHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string |
create_rejection_reasonAdd a rejection reason
Add one of the organisation's own rejection reasons, at the end of its side. Account owners only. A label the organisation already has, archived or not, is refused: restore_rejection_reason brings back an archived one.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| label* | string | The reason, in the organisation's words. At most 80 characters. |
| side* | one of: rejected-by-us, rejected-by-candidate, other | Who ended it. |
update_rejection_reasonChange a rejection reason
Rename a rejection reason, or move one of the organisation's own to another side. First's defaults stay on their side. Applications closed with it read the new name. Account owners only.
Show 4 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| rejectionReasonId* | string | The reason's id, from list_rejection_reasons. |
| label | string | The reason, in the organisation's words. At most 80 characters. |
| side | one of: rejected-by-us, rejected-by-candidate, other | Who ended it. Only the organisation's own reasons move between sides: First's defaults stay on theirs. |
archive_rejection_reasonArchive a rejection reason
Take a rejection reason out of the reject select. Applications closed with it keep naming it, so reasons are archived rather than deleted, and restore_rejection_reason puts one back. Account owners only. Archiving one already archived changes nothing.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| rejectionReasonId* | string | The reason's id, from list_rejection_reasons. |
restore_rejection_reasonRestore a rejection reason
Put an archived rejection reason back in the reject select, where it was. Account owners only. Restoring one in use changes nothing.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| rejectionReasonId* | string | The reason's id, from list_rejection_reasons. |
map_criteria_to_rejection_reasonMap criteria to a rejection reason
Set the criteria a rejection reason is suggested for, replacing the ones it had: a candidate whose highest-priority unmet criterion is one of them is suggested this reason when rejected. A criterion is on one reason at most, so any other reason one was on loses it (listed in movedFrom). Ids that are not the organisation's criteria in use are left out and listed in ignoredCriterionIds. Account owners only.
Show 3 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| rejectionReasonId* | string | The reason's id, from list_rejection_reasons. |
| criterionIds* | array of string | Every criterion this reason should be suggested for, replacing the ones it has. A candidate whose highest-priority unmet criterion is one of these is suggested this reason. Each criterion is on one reason at most, so any other reason one was on loses it. Empty takes them all off. |
Turn a place or country name into what a candidate search needs.
geocode_placeResolve a place to coordinates
Turn a city or place name into the coordinates a 'radius' search needs. Call this before sourcing near somewhere, then pass the coordinates as `location`. Returns found: false when the name is not an actual city (a whole country, or unrecognisable) — use geocode_country for a country, or fall back to searchType 'everywhere'.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| name* | string | The place to resolve, e.g. 'London' or 'Leeds, UK'. |
geocode_countryResolve a country to its ISO code
Turn a country a recruiter named — 'United Kingdom', 'UK' or 'GB' — into the ISO 3166-1 alpha-2 code a 'country' search needs. Call this before sourcing country-wide, then pass the code as `country`. Returns found: false when the value is not a recognisable country.
Show 2 fieldsHide fields
| Field | Type | Description |
|---|---|---|
| organisationId* | string | |
| name* | string | The country to resolve, e.g. 'United Kingdom'. |
For developers
The same operations are available over HTTP in the REST API reference. The full catalogue is published as JSON at /v1/mcp.json.
How your assistant is told to use these tools▼
The server hands every assistant these instructions when it connects. They are what makes an assistant source against agreed criteria rather than guessing.
First is a recruitment platform. These tools research the talent market, manage roles, find candidates in the organisation's own database and on the public web, add them to roles, and report on hiring. Every tool but one takes an organisationId. Never ask the user for it — call list_organisations and use what comes back. Most people belong to exactly one organisation, so use it without checking; ask which they mean only when there are several. The hiring workflow, when someone is hiring rather than asking a one-off question. Offer it, do not force it — and never walk someone through it who only wanted a number or a database search: 1. RESEARCH the role and market — research_talent_market for demand, pay and who else is hiring. Then suggest a role, with their go-ahead before you create anything. 2. CAPTURE the intake — ask for the hiring manager's brief in whatever form it exists: notes, an interview transcript, a job description. There is no form to fill in, so take it as it comes. 3. CREATE the role — create_role, then generate_suggested_criteria from the brief and update_role_process with them. These criteria are what every candidate added is assessed against. 4. SOURCE a long list — see below — and show it to the user before adding anyone. 5. ADD and PUBLISH — add the people the user chose, then get_role to check readiness and publish_role. Added candidates are assessed against the role's criteria once it is published. Sourcing tools: - search_existing_candidates — people already in the organisation's database: they applied before, so First holds their CV and history. Fast and free. For a role that already has criteria, pass matchRoleId to rank them against it. - search_web_for_candidates — people on the public web who have not applied, with a Google X-Ray query. Thin results, and nothing is saved. - save_web_candidates — saves the web results worth keeping to a saved list, looking each person up (paid). Then add_saved_candidates_to_role adds them; get_saved_candidates reads a list and clear_saved_candidates empties it. - add_candidate_to_role — adds existing candidates by candidateId. On organisations on the newer web search, it also takes people on the web by their LinkedIn URL, skipping the save. You do the judging; there is no separate matching step. Read what comes back, keep the people who fit the brief, and let the role's assessment score them once they are added. To source well, first time: - Check existing candidates first, and say so: "I'll check your existing candidates first, then look on the web." Re-engaging someone who applied before beats cold-sourcing a stranger. Go to the web when the database is thin, or straight away when the user asks for new people, the web or LinkedIn. Never make the user pick before you start. - Before searching the web, work out the equivalent job titles (a principal engineer is also a staff, lead or senior staff engineer) and the companies that employ people like this — competitors, companies the user names, companies in the same sector and place. OR the titles together in one query, and run a search per company: a wider net brings back a longer, better list. - Put the place, and any skill that must appear on a profile, into the query text. - Show the user a short reason for each person you suggest, then add the ones they choose. Adding someone already on the role finds their application rather than making another. Searching the database near a place: latitude, longitude and radiusKm need coordinates, and country needs an ISO code, so resolve the name first with geocode_place or geocode_country. If either returns found: false, search without a place rather than guessing coordinates. Before creating a role, gather what it needs: a title, a job description and at least one location are required to publish. Employment type, workplace type and compensation are not required but make the listing usable. Ask the user for anything missing rather than guessing.