
List applications. Filter by jobid, candidateid, stageid or exact stagename (e.g. 'Offer'), status (active, rejected, hired, converted), or created/last-activity date range. Returns candidate ID, job, current stage and status. Use this for pipeline questions such as applications per stage for a job. Paginate with page_info.cursor.
Get one application by ID: candidate ID, job ID, current stage, status, source, recruiter and coordinator IDs and dates. Set include.rejectiondetails for the rejection reason and record of a rejected application. Does not include the candidate profile; use getcandidate. Use list_applications to find application IDs.
Create an application for an existing candidate: on a job (jobid), or a prospect (prospect true, optional jobids and pool). Source, referrer, prospect pool and pool stage accept a name or an ID. Use listcandidates for candidateid, listjobs for jobid, listsources for sources, getjobstages for initialstage_id. Returns the application with its ID.
Advance an application to the next interview stage, or to tostageid / tostagename on the same job. fromstageid is read automatically when omitted. Use listapplications for application IDs and getjobstages for stage IDs. To move backwards or to another job use moveapplication or transfer_application.
Reject an active application with a reason, optional internal note, custom fields and a templated rejection email (template by ID or name, now or scheduled). Use listrejectreasons for rejectionreasonid and listusers for emailfromuserid. Reverse with unrejectapplication; edit the reason later with updateapplication_rejection.
Restore rejected application to active status. Application must currently be 'rejected'. Returns to previous stage and clears rejection details. Use get_application to verify status first.
Move an application to any stage, forward or back (tostageid or tostagename), or onto another job (tojobid). fromstageid is read automatically when omitted. Use listapplications for application IDs, getjobstages for stage IDs, listjobs for job IDs. Undo by moving it back.
Transfer an application to a different job, optionally into a stage on that job (targetstageid or targetstagename; defaults to the first stage). Keeps candidate info and history; prospects cannot be transferred. Use listjobs for targetjobid and getjob_stages on the target job for stage IDs.
Validate application meets all hiring prerequisites. Checks status, offer completeness, approvals, and job openings. Returns readiness report with blocking issues and resolution steps. Run it before hire_application.
Add a note anchored to one application: a NOTE, an ACTIVITY entry, or a logged EMAIL (needs subject and emailfrom/to/cc). visibility is public, private or adminonly. Use list_applications for application IDs. Notes cannot be edited or deleted afterwards. Returns the note ID.
Mark an application hired and fill an opening on its job. Run checkhirereadiness first. Use listapplications for applicationid, listjobopenings for openingid (required when the job has several open openings) and listclosereasons for closereason_id. Cannot be undone: the API has no unhire.
Update an application's source, referrer, recruiter, coordinator, prospect pool or stage, dates or custom fields. Not for stage moves (use moveapplication). Source, referrer, pool and pool stage accept a name or ID. Use listapplications for applicationid, listsources and list_users for IDs. Only fields you pass change.
Convert a prospect application into a candidate application on a job. The prospect must already be considered for that job or its pool. Use listapplications (prospect true) for applicationid, listjobs for jobid and getjobstages for tojobinterviewstageid. Returns the new application. Cannot be undone.
Change the rejection reason or rejection-question answers of an already-rejected application without changing its rejection date. Use listapplications (status rejected) for applicationid and listrejectreasons for rejectionreasonid. To reject an active application use reject_application.
Permanently delete one application with its stages, scorecards and rejection and prospect details. The candidate and their other applications stay. Cannot be undone; to record an outcome use rejectapplication instead. Use listapplications to find application IDs. Confirm with the user first.
Attach a file (resume, cover letter, take-home test, offer document, other) to an application, as base64 content or a public url (exactly one). Use listapplications for applicationid. A new resume or cover letter reclassifies the previous one as 'other'. Returns the attachment ID; remove with delete_attachment.
Permanently delete an attachment from its application; deleting a resume also clears its resume score. Cannot be undone. Use get_candidate with include attachments to find attachment IDs. Confirm with the user first.
List candidates, newest first. Filter by email, tag, candidateids, or created/updated date range. Returns profile and contact details but not applications or jobs; to find candidates for a job use listapplications with jobid. Paginate with pageinfo.cursor.
Get a candidate profile by ID: name, contacts, tags, custom fields, timestamps. Set include flags to add attachments (download URLs), notes, employments, educations, applied tags and prospect details, with the IDs the delete tools take; each costs extra calls. For applications use listapplications. Use listcandidates to find IDs.
Create a candidate (firstname and lastname required) with contacts, tags and custom fields, and optionally their first application in the same call: application.jobid for a job, or application.prospect true for a prospect. Use listjobs, listsources, listusers and discovercustomandrequiredfields('candidate'). Returns the candidate ID and application.
Update a candidate's name, title, company, contacts, time zone, privacy, tags, linked users or custom fields. Only fields you pass change, but each list you pass (phones, emails, tags...) replaces the whole list: read it with getcandidate first. Use listcandidates for IDs. Work history and education have their own add/delete tools.
Merge a duplicate candidate into a primary one. The duplicate's applications, notes, tags and history move to the primary and the duplicate is deleted. Irreversible: confirm both IDs with the user first. Use list_candidates to find IDs.
Add tags to a candidate by tag name. Tags that do not exist yet are created. Use listtags to see existing tags and listcandidates to find candidate IDs.
Remove tags from a candidate by tag name. Tags not on the candidate are skipped. Does not delete the tag itself. Use listtags to see tag names and listcandidates to find candidate IDs.
Add a candidate-level note: a NOTE, an ACTIVITY entry, or a logged EMAIL (needs subject and emailfrom/to/cc). visibility is public, private or adminonly. For a note tied to one application use addapplicationnote. Use list_candidates for IDs. Notes cannot be edited or deleted afterwards. Returns the note ID.
Permanently delete a candidate with all their applications, attachments, scorecards and notes. Cannot be undone; for privacy requests prefer anonymizecandidate, which keeps hiring history. Use listcandidates to find IDs. Confirm with the user first.
Erase selected personal data groups from a candidate (e.g. fullname, emailaddresses, phonenumbers, attachments, notes) for privacy requests, keeping hiring activity for reporting. Irreversible. Large scopes run asynchronously (status 'accepted'). Use listcandidates to find IDs. Confirm with the user first.
Add a work-history entry (company, title, start date, optional end date; no end date means current job) to a candidate. Use listcandidates for candidateid. Entries cannot be edited: delete with deletecandidateemployment and add again. Returns the entry ID.
Add an education entry to a candidate: school, degree and discipline by name (matched to the org's option lists) or option ID, plus dates. Use listcandidates for candidateid. Entries cannot be edited: delete with deletecandidateeducation and add again. Returns the entry ID.
Delete one work-history entry from a candidate. Cannot be undone (re-adding creates a new ID). Use get_candidate with include employments to find entry IDs.
Delete one education entry from a candidate. Cannot be undone (re-adding creates a new ID). Use get_candidate with include educations to find entry IDs.
List scheduled interviews. Filter by applicationids, jobids, jobinterviewids (slots from getjobstages), organizerids, status, externaleventid, or start/end, all-day, created or updated date ranges. Returns each interview's ID for updateinterview and deleteinterview, but not the panel. Use listapplications or listjobs for filter IDs. Paginate with pageinfo.cursor.
Record an already-scheduled interview on an application. Requires an externaleventid from the caller's calendar; creates no calendar event. Use listapplications for applicationid, getjobstages for jobinterviewid, listusers for interviewers. Returns the new interview ID; deleteinterview undoes it.
Change an interview's start/end time, location, video link, externaleventid or panel. Only the fields you pass change, but interviewers replaces the whole panel, so pass everyone who should stay. Updates Greenhouse only, not the calendar event. Use listinterviews to find interview IDs and listusers for interviewers.
Delete an interview, with its panel, in-progress scorecards and reminders. Only 'scheduled' or 'awaitingfeedback' interviews can be deleted; complete ones fail. Does not remove the external calendar event, and cannot be undone. Use listinterviews to find interview IDs. Ask the user before deleting.
List the panel of scheduled interviews: one row per interviewer per interview, with userid, email, RSVP responsestatus and the submitted scorecardid (null means feedback outstanding). Filter by interviewids (from listinterviews), userids, scorecardids, ids, responsestatus or created/updated ranges. For interviews not yet booked use listdefaultinterviewers.
List interview kits, the scorecard template and prep content for each interview-plan slot. Filter by jobinterviewids (slot IDs from getjobstages), jobids or ids; use fields to skip the long exercises text. Returns the interviewkitid that createscorecard, listscorecardquestions and listdefaultinterviewers take. Paginate with page_info.cursor.
List the default interviewers set on interview kits, to see who will run an interview that is not booked yet. Filter by interviewkitids (getjobstages, then listinterviewkits), user_ids or ids. Only named users are returned: defaults set as an interviewer group or hiring-team role (e.g. Hiring Manager) are not, so an empty result may mean role-based defaults. Ask the recruiter then.
List jobs, newest first. Filter by status, departmentid, officeid, requisitionid, jobids (up to 50, to look up several jobs at once), or created/updated date range; there is no name search. Use listdepartments or listoffices for filter IDs. Each job is large, so prefer filters or perpage 100 or less over paging every job. Paginate with pageinfo.cursor.
Get one job by ID: name, status, requisition ID, department ID, office IDs, custom fields and dates. Does not include the hiring team, stages, openings or approvals; use getjobstages, listjobopenings and getjobapprovalflows for those. Use listjobs to find job IDs; to look up several jobs at once, call listjobs with jobids instead.
Create a job by copying a template job (templatejobid), which supplies stages, departments and offices. Optionally override the name, requisition ID, department, offices and custom fields. Use listjobs to find a job to copy and discovercustomandrequired_fields('job') for org-specific fields. Returns the new job ID.
Update a job's name, requisition ID, notes, team description, selling points, department, offices or custom fields. Only the fields you pass change; officeids replaces the job's offices. Custom fields you leave out were observed to keep their values, but Greenhouse documents customfields as replacing the set, so pass every custom field you need kept. Use getjob for current values and discovercustomandrequired_fields('job') for custom fields. Ask the user before changing a job.
List the interview stages of one job (jobid) or several (jobids) in pipeline order, each with its interview slots and their jobinterviewid, which createinterview needs. Stage IDs feed moveapplication and listapplications' stageid. Filter by active or slot schedulingtype; includeinterviews=false returns stages only. Use listjobs for job IDs. Paginate with pageinfo.cursor.
List a job's openings (headcount slots): opening ID, external openingid, open or closed, opened and closed dates, close reason and the application that filled it. Use listjobs to find job IDs.
Add openings (headcount slots) to a job. Optionally set an external openingid. Use listjobs to find job IDs and updatejobopening to close an opening later. Returns the created opening.
Update a job opening: close it (status 'closed' plus an optional closereasonid from listclosereasons), reopen it (status 'open'), or change its external openingid or target start date. Use listjob_openings to find opening IDs. Only the fields you pass change.
Get a job's approval flows: approval type (e.g. offercandidate), status (pending or approved), whether approval is sequential, and dates. Does not include who the approvers are. Use listjobs to find job IDs.
List all departments in the organization including department names, IDs, parent-child relationships, external IDs, and child department hierarchies. Departments organize jobs and users by functional area (e.g., Engineering, Sales, Marketing). Use this to discover valid department_id values for job creation and filtering. Returns complete department hierarchy with parent and child relationships.
Retrieve detailed information about a specific department by ID including department name, parent department, child departments, external ID, and complete hierarchical relationships. Use this to understand department structure before assigning jobs or filtering candidates by department.
Create a department. Optionally nest it under parentid and set an externalid. Names must be unique. Use list_departments to find parent IDs. Returns the new department ID.
Update an existing department's information including name, parent department relationship, and external ID. All fields are optional - only provide fields you want to update. Changes are immediately reflected for all associated jobs and users. Use get_department first to see current values. Returns updated department with new values.
List offices with ID, name, location text, parent and child offices, primary contact user ID and external ID. Use these IDs for the officeid filter on listjobs and when creating jobs.
Retrieve detailed information about a specific office by ID including office name, physical location, primary contact user, parent office, child offices, external ID, and complete hierarchical relationships. Use this to understand office structure before assigning jobs or users to locations.
Create an office. Optionally set location as plain text (e.g. 'San Francisco, CA'), parentid, primaryinhousecontactuserid and externalid. Names must be unique. Use listoffices to find parent IDs. Returns the new office ID.
Update an existing office's information including name, location, primary contact user, parent office relationship, and external ID. All fields are optional - only provide fields you want to update. Changes are immediately reflected for all associated jobs and users. Use get_office first to see current values. Returns updated office with new values.
List candidate sources (e.g. LinkedIn, Referral, Career Site) with IDs and types. Use these IDs for source_id when creating candidates or applications.
List all reasons available for closing job openings to track why positions are no longer active (e.g., 'Hire - New Headcount', 'Position Eliminated', 'Filled Internally', 'Budget Frozen'). Close reasons help analyze hiring outcomes and workforce planning. Use this to discover valid closereasonid values for updatejobopening. Returns complete list of all configured close reasons with IDs.
List rejection reasons with IDs, names and type (We rejected them, They rejected us, ...). Use these IDs for rejectionreasonid on rejectapplication and updateapplicationrejection. Set includedefaults to also get Greenhouse's built-in reasons. Paginate with page_info.cursor.
List custom field definitions for every object type: name, type, whether required, and options for dropdowns. Large response; use discovercustomandrequiredfields for one object type.
List every candidate tag with ID and name. Use these names with addcandidatetags, removecandidatetags and the tag filter on list_candidates.
Show the required, recommended and optional fields, plus org custom fields, for creating a candidate, job, application, department, office or user. Call this before a create when unsure which fields an org requires.
List interview scorecards across all applications: overall rating, status, interviewer, application and dates. Filter by created or updated date range. For one application use getapplicationscorecards; for answers and attribute ratings use get_scorecard.
Get one interview scorecard with the overall rating, notes, each interview question with the interviewer's answer, and each attribute rating (for example a skill) with its note. Use getapplicationscorecards or listscorecards to find scorecard IDs. detailwarnings lists any part that could not be loaded.
List the scorecards for one application: overall rating (strongyes to definitelynot), status, interviewer and submitted date. Call getscorecard on a scorecard ID for its question answers and attribute ratings. Use listapplications to find application IDs.
Count one application's scorecards: total, submitted, and how many of each overall recommendation (strongyes, yes, no, definitelynot, nodecision). Use getscorecard for the written feedback. Use list_applications to find application IDs.
Write a scorecard: rating, notes, answers, attribute ratings. Saves a draft; set status='complete' only when the user asks to submit. IDs: listinterviewkits (interviewkitid), listscorecardquestions (questions), listscorecardquestionoptions (select options), listjobcandidateattributes (attributes), listusers (interviewer, submitter). Returns the ID for updatescorecard; no delete.
Revise or submit a scorecard. interviewerid, interviewedat, submitterid required every call (copy from getscorecard). Notes replaced whole; only answers/attributes you pass change; value null clears an answer. Select answers take IDs from listscorecardquestionoptions. status='complete' submits (irreversible); only when the user asks. Scorecard IDs: getapplication_scorecards.
List the questions on interview kits, with each question's ID, text, answertype (text, yesno, singleselect, multiselect, instruction) and whether it is required. Filter by interviewkitids (from listinterviewkits), ids or active. Use before createscorecard or updatescorecard to build questionanswers. Paginate with pageinfo.cursor.
List the choices of singleselect and multiselect scorecard questions, with each option's ID, label and whether it is still selectable. Filter by scorecardquestionids (from listscorecardquestions), ids or active; fields narrows the response. Pass option IDs as questionanswers[].value on createscorecard/updatescorecard. Paginate with pageinfo.cursor.
List the attributes (skills, traits) a job's scorecards rate, with each attribute's ID and name. Filter by jobids (use listjobs or the application's jobid), ids, candidateattributetypeids or active. Use before createscorecard or updatescorecard to build candidateattributes. Greenhouse limits this to site admins. Paginate with pageinfo.cursor.
List Greenhouse users (recruiters, hiring managers, admins) with ID, name, email, site admin and deactivated flags. Filter by email, employeeid or date range. Do not pick an onbehalf_of ID from this list; ask the user which Greenhouse user they are.
Get one Greenhouse user by ID: name, primary email, employee ID, site admin and deactivated flags, linked candidate IDs and dates. Use list_users to find user IDs.
Create a Greenhouse user with firstname, lastname and email. Optionally set employeeid. To create a user and send an invitation email in one step, use inviteuser. Ask the user before creating an account. Returns the new user.
Update an existing Greenhouse user's information including firstname, lastname, email, employeeid, and linkedcandidateids. All fields are optional - only provide fields you want to update. Cannot change siteadmin or deactivated status through this endpoint (use enableuser/disableuser instead). Requires onbehalfof user ID with admin privileges. Returns updated user with new values.
Deactivate a Greenhouse user so they cannot sign in; their history stays. Reverse it with enableuser. Use listusers to find user IDs. Confirm with the user before disabling an account.
Reactivate a deactivated Greenhouse user so they can sign in again. Use list_users to find user IDs.
Get whether a Greenhouse user is a site admin and whether they are deactivated. Does not return roles or per-job permissions. Use list_users to find user IDs.
Create a Greenhouse user and send them an invitation email. Requires email, firstname and lastname. Sends a real email: confirm the address with the user first. Returns the new user ID.
Validate Greenhouse credentials. Verifies credentials during setup.

In an mcp.json file, add the configuration below, and restart Cursor.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "url": "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
5 "headers": {
6 "Authorization": "Bearer yMt*****"
7 }
8 }
9 }
10}
11Open your Claude Desktop configuration file and add the server configuration below. You'll also need to restart the application for the changes to take effect.
Make sure Claude is using the Node v20+.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "command": "npx",
5 "args": [
6 "-y",
7 "mcp-remote@latest",
8 "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
9 "--header",
10 "Authorization: Bearer ${AUTH_TOKEN}"
11 ],
12 "env": {
13 "AUTH_TOKEN": "yMt*****"
14 }
15 }
16 }
17}Open your Windsurf MCP configuration file and add the server configuration below.
Click on the refresh button in the top right of the Manage MCP server page or in the top right of the chat box in the box icon.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "command": "npx",
5 "args": [
6 "-y",
7 "mcp-remote@latest",
8 "https://ah-api.merge.dev/api/v1/tool-packs/<tool-pack-id>/registered-users/<registered-user-id>/mcp",
9 "--header",
10 "Authorization: Bearer ${AUTH_TOKEN}"
11 ],
12 "env": {
13 "AUTH_TOKEN": "<ah-production-access-key>"
14 }
15 }
16 }
17 }In Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows), run "MCP: Open User Configuration".
You can then add the configuration below and press "start" right under servers. Enter the auth token when prompted.
Learn more in the official documentation ↗
1{
2 "inputs": [
3 {
4 "type": "promptString",
5 "id": "agent-handler-auth",
6 "description": "Agent Handler AUTH_TOKEN", // "yMt*****" when prompt
7 "password": true
8 }
9 ],
10 "servers": {
11 "agent-handler": {
12 "type": "stdio",
13 "command": "npx",
14 "args": [
15 "-y",
16 "mcp-remote@latest",
17 "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
18 "--header",
19 "Authorization: Bearer ${input:agent-handler-auth}"
20 ]
21 }
22 }
23}Here are just a few use cases:
Here are just a few popular tools across data types:
Applications
Candidates
Jobs
Merge Agent Handler provides platform-level capabilities that aren’t inherent to MCP alone:
Yes, you can set a wide range of security rules for Greenhouse tool calls in Merge Agent Handler. Here are just a few examples:
Here are the steps to get started:
1. Create your Agent Handler account. Sign up for a free Merge Agent Handler account here.
2. Set up a Tool Pack. Navigate to "Tool Packs" in the dashboard sidebar and click "Create New Tool Pack." Add the Greenhouse connector from the list of available MCP connectors and give your Tool Pack a descriptive name (e.g., "Recruiting Automation Pack").
3. Configure authentication. Choose between individual authentication, where each user provides their own Greenhouse credentials, and shared authentication, which uses organization-level credentials for all users.
4. Register a user identity. Navigate to "Registered Users" and create an entry. This represents the identity (either human or system) that’ll perform actions through your agent.
5. Associate the Tool Pack with your agent. Save your Tool Pack and connect it to your agent using the MCP entry URL provided in the Tool Pack settings.
Yes, Agent Handler for Employees lets your employees connect Claude, ChatGPT, Microsoft Copilot, Cursor, and other MCP-compatible AI tools to Greenhouse without bypassing IT governance.
Instead of setting up direct connections with personal credentials that IT can't monitor or revoke, each employee authenticates through Agent Handler and gets individual credentials tied to their identity.
IT also provisions access by role or group via SCIM. A recruiter, for example, gets Greenhouse access to manage job openings and candidate pipelines, Slack to coordinate interview scheduling with hiring teams, and Gmail to manage candidate outreach; while an HR manager gets Greenhouse access to track offer approvals and new hire data, HiBob to sync onboarding records, and Google Sheets to report on pipeline health and time-to-hire metrics.
Every tool call an employee's AI makes to Greenhouse is also inspected against your DLP rules and logged to a searchable audit trail, giving security teams full visibility into what data was accessed and by whom.
Whether you're an engineer experimenting with agents or a product manager looking to add tools, you can get started for free now