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.

13 toolsCreate and manage roles

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_roles

List 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 fields
FieldTypeDescription
organisationId*string
statusesarray of one of: imported, draft, open, paused, closed, archived
get_role

Get 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 fields
FieldTypeDescription
organisationId*string
roleId*string
generate_suggested_criteria

Generate 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 fields
FieldTypeDescription
organisationId*string
title*stringThe role title.
jobDescriptionstringThe role's job description. The richer this is, the better the criteria.
keywordsarray of stringSkills the hiring manager called out, when there is no job description yet.
roleIdstringAn 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_role

Create 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 fields
FieldTypeDescription
organisationId*string
name*stringThe role title
internalNamestring
shortDescriptionstring
descriptionstring
workplaceTypeone of: on-site, hybrid, remote, remote-first, flexible
employmentTypeone of: full-time, part-time, contractor, internship, volunteer
roleWebsiteUrlone of 2 shapes
compensationobject
locationNamesarray of string
categoryNamesarray of string
templateIdsarray of stringRole template ids to seed criteria and questions from
hiringGroupIdsarray of stringThe hiring groups to put the role in, any of the organisation's. Required, with at least one, when the organisation uses hiring groups
duplicate_role

Duplicate 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 fields
FieldTypeDescription
organisationId*string
roleId*stringThe role to copy
import_role

Adopt 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 fields
FieldTypeDescription
organisationId*string
roleId*string
update_role_details

Update 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 fields
FieldTypeDescription
organisationId*string
roleId*string
namestring
internalNamestring
shortDescriptionstring
descriptionstring
workplaceTypeone of: on-site, hybrid, remote, remote-first, flexible
employmentTypeone of: full-time, part-time, contractor, internship, volunteer
roleWebsiteUrlone of 2 shapes
compensationobject
locationNamesarray of string
categoryNamesarray of string
request_role_opening

Ask 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 fields
FieldTypeDescription
organisationId*string
roleId*string
approve_role_opening

Approve 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 fields
FieldTypeDescription
organisationId*string
roleId*string
publish_role

Publish 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 fields
FieldTypeDescription
organisationId*string
roleId*string
pause_role

Pause 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 fields
FieldTypeDescription
organisationId*string
roleId*string
close_roleCHANGES DATA

Close 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 fields
FieldTypeDescription
organisationId*string
roleId*string
archive_roleCHANGES DATA

Archive 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 fields
FieldTypeDescription
organisationId*string
roleId*string
7 toolsRole setup

Change a role's application process: its criteria, its questions and the topics each interview stage covers.

update_role_processCHANGES DATA

Update 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 fields
FieldTypeDescription
organisationId*string
roleId*string
changes*array of one of 19 shapesThe process changes to apply, in order
reorder_role_questions

Put 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 fields
FieldTypeDescription
organisationId*string
roleId*string
questionIds*array of stringOne section's question ids, in the new order
remove_role_process_componentCHANGES DATA

Remove 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 fields
FieldTypeDescription
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
questionIdstring
criterionIdstringRequired for stage criteria (type 'assess-stage'): without it, every stage criterion on the role would be removed
get_stage_topicsREAD ONLY

Get 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 fields
FieldTypeDescription
organisationId*string
roleId*string
pipelineStageIdstring
save_stage_topicsCHANGES DATA

Save 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 fields
FieldTypeDescription
organisationId*string
roleId*string
topics*array of object
reorder_stage_topics

Put 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 fields
FieldTypeDescription
organisationId*string
roleId*string
pipelineStageId*string
topicIds*array of string
remove_stage_topicsCHANGES DATA

Remove 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 fields
FieldTypeDescription
organisationId*string
roleId*string
topicIds*array of string
10 toolsSource candidates

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_market

Research 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 fields
FieldTypeDescription
organisationId*string
roleTitle*stringThe role to research, e.g. 'Software Engineer'.
location*stringWhere to research it, as the recruiter would say it — 'London', 'the UK', 'Berlin'. Not a coordinate or ISO code.
seniorityLevelstringThe seniority to narrow to, e.g. 'senior', 'lead'.
additionalContextstringAnything else that shapes the market, e.g. an industry or a required technology.
search_existing_candidates

Search 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 fields
FieldTypeDescription
organisationId*string
textstringWhat to look for: skills, a job title or a short description of the person, e.g. 'forklift operator' or 'React and TypeScript'.
likeCandidateIdstringFind people like this candidate instead of matching text.
titlesarray of stringJob 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.
niceToHavearray of stringSkills or words to rank by, e.g. ['dbt']: nobody is left out for missing one.
matchRoleIdstringRank 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.
criteriaOffarray of stringWith matchRoleId: the role's criteria, by the ids get_role returns, to leave out of this ranking. The role itself is unchanged.
criteriaMovedarray of stringWith 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.
disciplinestringOnly people in this discipline: an id from the `disciplines` the first page of list_candidates returns, or 'none' for people without one.
rightToWorkone 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.
companiesarray of stringOnly people who work at one of these companies now, as their CV says, e.g. ['Monzo', 'Starling Bank'].
mustHavearray of stringOnly people whose CV names every one of these, as whole words, e.g. ['Kubernetes'].
roleIdstringOnly people who applied to this role.
stageone of: new, uploading, applied, sourced, invited, in progress, complete, stalled, duplicate, archived, withdrawn, shortlisted, pipeline, hired, rejectedOnly people with an application at this stage, e.g. 'shortlisted' or 'hired'.
countrystringOnly people who applied from this country, as an ISO 3166-1 alpha-2 code (geocode_country turns a name into one).
latitudenumberWith longitude and radiusKm: only people who applied from near here.
longitudenumber
radiusKmnumberHow far from latitude/longitude, in km (geocode_place gives the point).
list_candidates

List 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 fields
FieldTypeDescription
organisationId*string
textstringPart of a name or email address. A whole address is looked up directly.
disciplinestringOnly people in this discipline: an id from the `disciplines` the first page of list_candidates returns, or 'none' for people without one.
rightToWorkone 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.
companiesarray of stringOnly people who work at one of these companies now, as their CV says, e.g. ['Monzo', 'Starling Bank'].
mustHavearray of stringOnly people whose CV names every one of these, as whole words, e.g. ['Kubernetes'].
roleIdstringOnly people who applied to this role.
stageone of: new, uploading, applied, sourced, invited, in progress, complete, stalled, duplicate, archived, withdrawn, shortlisted, pipeline, hired, rejectedOnly people with an application at this stage, e.g. 'shortlisted' or 'hired'.
countrystringOnly people who applied from this country, as an ISO 3166-1 alpha-2 code (geocode_country turns a name into one).
latitudenumberWith longitude and radiusKm: only people who applied from near here.
longitudenumber
radiusKmnumberHow far from latitude/longitude, in km (geocode_place gives the point).
appliedSincestringOnly people who last applied on or after this date, YYYY-MM-DD.
cursorstringThe nextCursor from the previous page, to continue from.
get_candidate

Get 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 fields
FieldTypeDescription
organisationId*string
candidateId*string
search_web_for_candidatesREAD ONLY

Search 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 fields
FieldTypeDescription
organisationId*string
query*stringA 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`.
numintegerHow many results to return, 1-10. Defaults to 3.
startintegerThe 1-based index of the first result, for paging. Defaults to 1; step by `num` to page.
dateRestrictstringGoogle only; ignored where the organisation's web search runs over public profile pages.
locationstringThe 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.
mustMentionarray of stringWords 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_candidates

Save 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 fields
FieldTypeDescription
organisationId*string
contacts*array of objectThe 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.
referencestringThe 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_candidates

List 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 fields
FieldTypeDescription
organisationId*string
reference*stringThe saved list to show.
clear_saved_candidatesCHANGES DATA

Empty 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 fields
FieldTypeDescription
organisationId*string
reference*stringThe saved list to empty.
add_candidate_to_role

Add 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 fields
FieldTypeDescription
organisationId*string
roleId*stringThe role to add them to.
candidateIdsarray of stringPeople 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.
urlsarray of stringPeople 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_role

Add 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 fields
FieldTypeDescription
organisationId*string
roleId*string
contactIds*array of stringThe ids of the saved candidates to add to the role as applications — the `id` values a save or a saved list returned.
referencestringThe 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.
23 toolsReview and move candidates

Search applications, move people through your stages, record how far they have got, reject or hire them, and undo those decisions.

search_applications

Search 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 fields
FieldTypeDescription
organisationId*string
querystringName or email to search for. Omit to list the whole of `list` instead — there is no term that returns every hire.
fieldone of: email, fullNameRestrict matching to one field; omit to match either
listone of: inbox, shortlisted, rejected, hired, all
sourcedbooleantrue for sourced candidates only, false for only those who applied themselves; omit for everyone
linkedInMessagedbooleantrue for only those somebody logged a LinkedIn message to, false for everyone else; omit for everyone
contactedbooleantrue 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
pageinteger
get_applicationREAD ONLY

Get 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
includeProfilebooleanSet false to leave out the candidate's CV history. Included by default.
progress_application

Progress 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
pipelineStageIdstringWhich pipeline stage to move to, from the stages get_stage_topics lists for the role. Needed to move an application already in the pipeline.
sendEmailbooleanEmail 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_application

Reject 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
sendEmailbooleanEmail 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_application

Hire 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
sendEmailbooleanEmail 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 DATA

Progress, 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 fields
FieldTypeDescription
organisationId*string
roleId*stringThe role every application is on, as list_roles returns.
action*one of: progress, reject, hireprogress shortlists, or moves applications already in the pipeline to pipelineStageId; reject; or hire.
applicationIds*array of stringThe applications to act on, all on the role, at most 100 at a time.
pipelineStageIdstringFor 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_role

Copy 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 fields
FieldTypeDescription
organisationId*string
roleId*stringThe role the applications are on now, as list_roles returns.
destinationRoleId*stringThe role to copy the applications to: another role that is a draft, open or paused.
applicationIds*array of stringThe applications to copy, all on the source role, at most 50 at a time.
move_candidate_back_to_pipeline

Move 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 fields
FieldTypeDescription
organisationId*string
applicationId*string
offerId*stringThe declined or withdrawn offer's id, as list_application_offers returns it.
to*one of: inbox, stageWhere the candidate goes: back to the Inbox, or to a pipeline stage.
pipelineStageIdstringWhich pipeline stage, from the stages get_stage_topics lists for the role. Needed with `to: stage`.
undo_shortlist

Move 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
undo_reject

Undo 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
undo_hire

Undo 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
reassess_application

Reassess 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
reassess_applications

Reassess 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 fields
FieldTypeDescription
organisationId*string
roleId*stringThe role's id, as returned by list_roles.
override_assessment

Change 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
changes*array of objectThe criteria to change, and what to change each to.
commentstringWhy, recorded on the timeline and as the reasoning of each new override.
score_stage

Score 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
pipelineStageIdstringThe 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 objectThe criteria to score. May be empty to post only a comment.
commentstringAbout 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_progress

Set 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
pipelineStageId*stringThe 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 shapesHow far they have got. null sets them back to Awaiting contact, or to the interview's result once the stage is scored.
commentstringGoes on the timeline with the change.
edit_stage_comment

Edit 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
activityId*stringThe timeline row the comment is on.
comment*stringThe comment as it should now read. Empty clears it.
change_rejection_reason

Change 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
rejectionReasonId*stringThe reason to record, one of the organisation's own that is not archived, as list_rejection_reasons returns them.
update_application_details

Update 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
fullNamestringThe candidate's full name. Cannot be cleared.
preferredNamestringWhat the candidate likes to be called.
emailstringThe candidate's email address. Cannot be cleared, and must not be one another application on the role already has.
phonestringThe candidate's mobile number.
phoneCountrystringThe ISO country code the mobile number is dialled from, such as GB. Only with phone; defaults to GB.
linkedInstringThe candidate's LinkedIn profile URL.
availabilitystringWhen the candidate can start: one of the role's availability options, or other.
availabilityOtherstringThe 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.
salaryExpectationsstringThe candidate's salary expectations, for a role that asks with options or free text: one of its options (or other), or the text.
salaryExpectationsOtherstringThe 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.
salaryExpectationsAmountstringThe 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.
salaryExpectationsCurrencystringThe ISO currency code of salaryExpectationsAmount, such as GBP. Only with salaryExpectationsAmount.
salaryExpectationsFrequencystringHow often salaryExpectationsAmount is paid: yearly, monthly, weekly, daily or hourly. Only with salaryExpectationsAmount.
summarystringThe summary of the candidate shown at the top of their application. Replacing First's generated summary records it as written by you.
locationobjectWhere the candidate is, as { city, country }. They are geocoded; give latitude and longitude as well to place the candidate exactly.
set_application_referrer

Set 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
referrer*one of 2 shapesWho referred the candidate: { "kind": "user", "userId": ... } for someone in the organisation on First, or { "kind": "person", "name": ..., "email": ... } for anyone else.
remove_application_referrerCHANGES DATA

Remove 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
archive_application

Archive 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
restore_application

Restore 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
9 toolsOffers

Draft, approve and extend offers, record the answer, and mark the contract signed.

list_offer_boardREAD ONLY

List 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 fields
FieldTypeDescription
organisationId*string
roleId*stringThe role's id, from list_roles.
list_application_offersREAD ONLY

List 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
draft_offer

Draft 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
salary*objectThe salary offered: an amount, its currency (ISO 4217, such as GBP) and how often it is paid.
startDate*stringThe start date offered, as YYYY-MM-DD.
notesstringAnything else about the offer, for the hiring team.
contractone of 2 shapesHow 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_offer

Approve 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 fields
FieldTypeDescription
organisationId*string
applicationId*string
offerId*stringThe offer's id, as list_application_offers returns it.
extend_offer

Extend 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 fields
FieldTypeDescription
organisationId*string
applicationId*string
offerId*stringThe offer's id, as list_application_offers returns it.
sendEmailbooleanEmail 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.
subjectstringThe email's subject. Needed with sendEmail.
bodystringThe 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 DATA

Withdraw 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 fields
FieldTypeDescription
organisationId*string
applicationId*string
offerId*stringThe offer's id, as list_application_offers returns it.
reason*stringWhy the offer is withdrawn, recorded on the offer and the application's timeline.
record_offer_accepted

Record 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 fields
FieldTypeDescription
organisationId*string
applicationId*string
offerId*stringThe offer's id, as list_application_offers returns it.
record_offer_declined

Record 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 fields
FieldTypeDescription
organisationId*string
applicationId*string
offerId*stringThe offer's id, as list_application_offers returns it.
reason*stringWhy the candidate declined, recorded on the offer and the application's timeline.
mark_contract_signed

Mark 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 fields
FieldTypeDescription
organisationId*string
applicationId*string
offerId*stringThe offer's id, as list_application_offers returns it.
10 toolsNotes, meetings and emails

Add notes and interview transcripts to an application, import meetings, and manage the emails waiting to go to a candidate.

add_note

Add 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
content*stringThe 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_note

Edit 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
noteId*stringThe note's id, as returned by add_note.
content*stringThe 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 DATA

Delete 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
noteId*stringThe note's id, as returned by add_note.
list_meeting_notesREAD ONLY

List 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
cursorstringThe nextCursor from the previous page, to look further back. Omit for the most recent meetings.
import_meeting_note

Import 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
noteId*stringThe meeting's id, as returned by the meeting notes list.
provider*one of: granola, googleWhich provider listed the meeting, as returned alongside its id. A meeting id only means something to the provider it came from.
add_transcript

Add 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
title*stringWhat the file is called on the candidate, e.g. the interview it records. Up to 200 characters.
type*one of: transcript, summary, notestranscript for the spoken record, summary for an AI note-taker's write-up, notes for notes somebody wrote. Defaults to transcript.
contents*stringThe text itself.
list_queued_emailsREAD ONLY

List 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
cancel_queued_emailCHANGES DATA

Cancel 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
emailId*stringThe email's id, as returned by list_queued_emails.
mark_candidate_contacted

Mark 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
channel"linkedin"How they were contacted: "linkedin" for a LinkedIn message. Unset for any other contact.
messagestringWith channel "linkedin" only: what was sent, for the hiring team to read. The candidate never sees it.
mark_rejection_sent_manually

Mark 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 fields
FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
3 toolsReporting and status

How hiring is going: applications, hires and time to hire, how candidates moved through your stages and where they dropped out.

get_reporting_summary

Get 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 fields
FieldTypeDescription
organisationId*stringThe organisation to report on.
fromstringFirst month in YYYY-MM format, inclusive.
tostringLast month in YYYY-MM format, inclusive.
get_monthly_reporting

Get 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 fields
FieldTypeDescription
organisationId*stringThe organisation to report on.
roleIdsarray of stringThe Roles to report on, by id. Omit for every Role the user can report on.
monthstringOne 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.
fromstringFirst month of a range in YYYY-MM format, inclusive; give with to instead of month.
tostringLast month of a range in YYYY-MM format, inclusive; at most 12 months after from.
get_stage_reporting

Get 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 fields
FieldTypeDescription
organisationId*stringThe organisation to report on.
roleIdsarray of stringThe Roles to report on, by id. Omit for every Role the user can report on.
monthstringOne 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.
fromstringFirst month of a range in YYYY-MM format, inclusive; give with to instead of month.
tostringLast month of a range in YYYY-MM format, inclusive; at most 12 months after from.
candidatesone of: everyone, applied, sourcedWhose time in stage to report: everyone (the default), or only the candidates who applied or who were sourced.
13 toolsOrganisation and team

The organisations you can act on, the hiring groups that decide who works on which roles, the referrals page and the rejection reasons.

list_organisations

List 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 ONLY

List 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 field
FieldTypeDescription
organisationId*string
save_hiring_groupCHANGES DATA

Create 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 fields
FieldTypeDescription
organisationId*string
hiringGroupIdstringThe group to update, from list_hiring_groups. Leave out to create one.
name*string
membersarray of objectEveryone 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 DATA

Update 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 fields
FieldTypeDescription
organisationId*string
useHiringGroupsbooleanTurn Hiring groups on or off for the organisation
anyoneCanOpenRolesbooleanWhether any role admin on a role can open it, rather than only the people a group lets approve opening
openRoleRuleone of: any, allWhile anyoneCanOpenRoles is off, whether any one or all of those people must approve
anyoneCanMakeOffersbooleanWhether anyone on a role's hiring team can make an offer, rather than only the people a group lets approve offers
offerRuleone of: any, allWhile anyoneCanMakeOffers is off, whether any one or all of those people must approve
delete_hiring_groupCHANGES DATA

Delete 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 fields
FieldTypeDescription
organisationId*string
hiringGroupId*stringThe group to delete, from list_hiring_groups.
set_role_hiring_groupsCHANGES DATA

Set 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 fields
FieldTypeDescription
organisationId*string
roleId*string
hiringGroupIds*array of stringThe hiring groups the role is in, replacing its current ones. At least one once the organisation has any
set_referrals_enabledCHANGES DATA

Turn 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 fields
FieldTypeDescription
organisationId*string
enabled*booleantrue to turn the organisation's referrals page on, false to turn it off.
list_rejection_reasonsREAD ONLY

List 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 field
FieldTypeDescription
organisationId*string
create_rejection_reason

Add 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 fields
FieldTypeDescription
organisationId*string
label*stringThe reason, in the organisation's words. At most 80 characters.
side*one of: rejected-by-us, rejected-by-candidate, otherWho ended it.
update_rejection_reason

Change 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 fields
FieldTypeDescription
organisationId*string
rejectionReasonId*stringThe reason's id, from list_rejection_reasons.
labelstringThe reason, in the organisation's words. At most 80 characters.
sideone of: rejected-by-us, rejected-by-candidate, otherWho ended it. Only the organisation's own reasons move between sides: First's defaults stay on theirs.
archive_rejection_reason

Archive 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 fields
FieldTypeDescription
organisationId*string
rejectionReasonId*stringThe reason's id, from list_rejection_reasons.
restore_rejection_reason

Restore 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 fields
FieldTypeDescription
organisationId*string
rejectionReasonId*stringThe reason's id, from list_rejection_reasons.
map_criteria_to_rejection_reason

Map 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 fields
FieldTypeDescription
organisationId*string
rejectionReasonId*stringThe reason's id, from list_rejection_reasons.
criterionIds*array of stringEvery 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.
2 toolsHelpers

Turn a place or country name into what a candidate search needs.

geocode_place

Resolve 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 fields
FieldTypeDescription
organisationId*string
name*stringThe place to resolve, e.g. 'London' or 'Leeds, UK'.
geocode_country

Resolve 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 fields
FieldTypeDescription
organisationId*string
name*stringThe 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.
First.cx logo
Copyright © OpenDigital Limited 2026

We use cookies to analyse site traffic and improve your experience. By clicking "Accept", you consent to our use of analytics cookies. See our Privacy Policy for details.