
Lever has announced an official MCP connector that is read-only and not yet shipped, community MCP servers run on a single API key, and the Lever API v1 is the first-party surface you can build on today. Behind the API sits a partner-gated OAuth flow whose scopes are all account-wide. This guide compares what each option lets a recruiting copilot do, where per-user boundaries have to come from, and how to run the API path across many customer companies in production.
You are building a recruiting copilot inside a B2B HR product, and 80 customer companies run their hiring in Lever. The copilot needs to tell a hiring manager which of their candidates have stalled, nudge interviewers who never submitted feedback, log a note on a candidate after a phone screen, and move someone to Onsite when the recruiter approves. You go looking for a Lever MCP server. Lever has announced one, read-only and not yet shipped. Registries list a handful of community servers that each wrap one API key. And there is the Lever API v1, with OAuth gated behind a partner program and scopes that all end in :admin. Those three options differ on writes, on whose permissions apply, and on who runs the server. Here's how to pick.
Lever's own entry is the Lever MCP Connector. Its page on lever.co describes "a secure server" that "securely connects your ATS to Claude, Microsoft Copilot, ChatGPT, and Slack," and works with "any MCP-compatible AI client." The use cases are self-serve: hiring managers checking pipeline status without pinging a recruiter, and interviewers reading candidate background before they walk in. Three facts matter for builders. Access is read-only. It is scoped to each person's existing Lever permissions, with "no separate login." And as of November 2026 the page carries a "Coming Soon" label, with setup routed through "your Customer Account Manager." Lever publishes no endpoint URL, tool list, or auth documentation for it yet.
What runs today is community and vendor work:
Every community server we reviewed authenticates the same way: a Lever API key in LEVER_API_KEY. None runs OAuth.
The Lever API is REST at https://api.lever.co/v1, HTTPS only. Its model is relational. A contact is one person; an opportunity is one candidacy of that contact, and each opportunity carries at most one application. The older /candidates routes are deprecated in favor of /opportunities. Around that core sit stages, archive reasons, tags, sources, notes, feedback forms and templates, profile forms, interviews and panels, offers, referrals, resumes, files, postings, requisitions and requisition fields, users, roles, audit events, and webhooks. Lists return data, next, and hasNext; you pass next back as the opaque offset and cap limit at 100.
Auth takes two forms:
A sandbox mirrors production at https://api.sandbox.lever.co/v1, with its own OAuth app and authorize URL at https://sandbox-lever.auth0.com/authorize, per Lever's OAuth guide. There is also a public Postings API for job sites that needs no auth and has nothing to do with candidate data.
Lever's official connector is built for a different job than the one most agent builders have. It answers questions for the person asking, within what they can already see, and it never writes. That is exactly right for a hiring manager in Copilot. It cannot chase a scorecard, move a candidate, or react when a stage changes, and it is not shipped.
The community servers cover more writes than the official one, but on a single key. A recruiter's archive and a hiring manager's question run with the same reach, whatever that key was granted.
"Which of my candidates for the Senior Backend role have been sitting in Onsite for more than a week, and who still owes feedback" on the raw API is a walk across five resources. You resolve the hiring manager's user ID with GET /users, find postings where they are hiringManager, map the stage name to a stage UID, list opportunities filtered by posting_id and stage_id with an advanced_at_end cutoff, then call interviews and feedback per opportunity and compare interviewers against submitted forms. Timestamps are Unix milliseconds throughout. Every page lands in the model's context.
An MCP server folds that into a conversation. With the official connector, the hiring manager connects with no extra login, and Lever decides what they can see. With a community server, the model picks tools by name and the server handles the paging. For a single recruiting team, that is a working assistant in an afternoon. Writing the schema is the hard part. Not the API call.
The tradeoff is ownership. Lever's connector will change on Lever's schedule, with no published changelog yet. A community server changes when its maintainer pushes, and one of the two we looked at is flagged inactive. An agent tuned against either inherits that.
On the MCP path, the official connector promises per-user access with "no separate login," which suggests it rides each person's existing Lever session, but Lever has not documented the mechanism. The community servers use an API key that an admin creates in the customer's Lever account settings. The key has no documented expiry, carries whatever endpoint permissions and confidential access were chosen when it was made, and sits in plaintext in claude_desktop_config.json or a .env file on someone's laptop. When that person leaves, the key keeps working until someone revokes it.
On the direct API path, you pick between that API key and an OAuth app. The key is right for an internal script against your own company's Lever. For a product serving customer companies, Lever expects OAuth, and OAuth means the partner process. Lever creates your OAuth app and client credentials, you build against a sandbox account, Lever runs a QA session where you walk through the full customer experience on a shared screen, and only then does Lever issue production credentials. Sandbox and production are separate apps, and callback URLs are registered per app, not per customer.
The token lifecycle is standard but strict. You request scopes space-separated, with offline_access to receive a refresh token. Access tokens return expires_in: 3600. Refresh tokens expire "after one year or after 90 days of inactivity," and each refresh returns a new refresh token that replaces the old one. A customer account your agent touches quarterly can lapse between runs.
Then the scopes. Lever's scope list runs from opportunities:read:admin to webhooks:write:admin, and every data scope ends in :admin; opportunities:write:admin reads "View and manage all opportunities." Lever's OAuth guide describes a Super Admin authorizing your app for their organization. The grant your agent holds is account-wide. There is also confidential:access:admin, and without it list endpoints default to non-confidential records, which is a quiet way for an agent to report "no candidates" for an executive search.
What the user can't do, the agent can't do. On Lever's official MCP Connector, Lever says it enforces that. On the API path, the token can do what a Super Admin can do, and the hiring manager asking the question is a row in your database, not a principal Lever knows about.
In a multi-tenant B2B agent, MCP gives you a credential per user and the direct API gives you a credential per user. In neither case does the path itself solve storage, rotation, or revocation; those are infrastructure problems regardless of which path you choose.
Lever puts the isolation boundary in two places at once. Across customers, each connected account is one company's whole Lever account, so binding a grant to exactly one customer is the first line, and a mix-up exposes every candidate's resume, contact details, and interview feedback to the wrong company. Inside a customer, the grant sees every posting, so a hiring manager's copilot session must be narrowed to the postings that person may see. lever_posting_users_list returns the users and roles with access to a posting's candidates, which gives you Lever's own answer to check against. Candidate data is personal data, and EEO and diversity survey responses sit behind their own scopes for a reason.
Recommended reading: access control for multi-tenant AI agents
On the MCP path, with a community server you own the code: you pin the version, read the diff, and handle failures, because nobody else will. Rate limits apply per API key, so one recruiter's chat that pages through 30,000 archived opportunities eats the budget every other integration on that key shares. With the official connector, Lever owns the server and the permission checks, and you own nothing but the rollout. Neither MCP option learns anything on its own; tools run when someone asks.
On the API path, you own pagination, retries, and the token lifecycle. Lever rate-limits with a token bucket: 10 requests per second steady state per key, bursts to 20 when capacity allows, no endpoint-specific limits documented, and the defaults "are not guaranteed." Back off exponentially on 429. Ten requests per second sounds generous until a feedback chaser makes four calls per opportunity across 400 active candidates in one customer account.
Writes carry Lever's attribution model. Many write endpoints take perform_as, a Lever user UID that Lever records as the actor. Scalekit's docs mark it required for lever_opportunity_create and optional on tools like lever_note_create and lever_opportunity_stage_update, and Knit's API guide notes it is required for posting creation and file uploads. It is attribution, not authorization: the token's reach stays the same whichever user you name.
Two more Lever constraints shape agent writes. Notes created in the Lever app cannot be updated through the API, so lever_note_update only works on notes your integration made. And interviews can only be scheduled through the API on externally managed panels, which means panels your integration created with lever_panel_create, not ones a coordinator built in Lever's scheduler.
Webhooks are how a headless agent learns that something happened. A Super Admin configures them on the webhooks tab of the account's integration settings, and the API exposes webhook management under webhooks:write:admin. Events include applicationCreated, candidateHired, candidateStageChange, candidateArchiveChange, candidateDeleted, contactCreated, and contactUpdated, plus interview events in beta. Three properties shape the handler:
Use Lever MCP when:
Use the Lever API when:
Both paths hand you a Lever credential per account or per user. Neither hands you a vault. A recruiting copilot serving 80 customer companies on the API path holds 80 OAuth grants, each authorized by a Super Admin, each with an access token that dies hourly and a refresh token that rotates on every use and lapses after 90 quiet days. Behind them sits the client secret Lever issued to your partner app. Every refresh token must be persisted atomically when it rotates, because two workers refreshing the same grant at once leave one of them holding a dead token. Every grant must be bound to one customer, kept out of logs and LLM context, and discarded when the customer disconnects.
The MCP path doesn't make the count smaller. Lever's official connector moves identity to each person's session, which matches Lever's permission model, but it is read-only and not yet available. The community servers move API keys onto laptops, where nobody can list or revoke them centrally. The tempting shortcut on the API path is asking each customer's admin to paste an API key into your settings page. Fewer partner hoops, larger blast radius: 80 keys with no documented expiry and account-wide reach, in your database.
Scalekit's lever connector runs the OAuth flow for each customer account, stores the tokens in the token vault, and refreshes them ahead of expiry, so the MCP vs API decision doesn't change your auth infrastructure. Your agent passes an identifier; credentials never touch the agent runtime or the LLM context.
Recommended reading: how to handle token refresh for AI agents
Scalekit's lever connector is an API-path connector built by Scalekit with 77 tools: 46 read-only, 25 write, and 6 destructive (lever_feedback_delete, lever_file_delete, lever_interview_delete, lever_note_delete, lever_panel_delete, lever_requisition_delete). It covers opportunities and contacts, stages and archive reasons, tags, sources, links, notes, feedback and feedback templates, profile forms, interviews and panels, offers, referrals, resumes and files, postings and application questions, requisitions and requisition fields, diversity surveys, users, and deletion feeds. The core set for a recruiting copilot:
The connector does not cover file uploads or webhook registration, and offers are read-only through lever_offers_list.
In the Scalekit dashboard, go to AgentKit > Connections, create a Lever connection, and copy its redirect URI. The name you give the connection is the connection_name your code passes, and the string must match the dashboard exactly. This is the most common integration error.
Lever connections use your own OAuth app, and for Lever that means the partner program: Lever registers the app, with your callback URIs and required scopes, and issues the Client ID and Client Secret. Register the Scalekit redirect URI as a callback, request the scopes your tools need plus offline_access, and keep the list tight; Lever caps an app at 20 scopes and says most integrations need 5 to 8. Build against a sandbox app first, then repeat with the production app Lever issues after QA. Enter the Client ID and Client Secret on the Lever connection. Copy SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, and SCALEKIT_CLIENT_SECRET from Developers > API Credentials into .env. See the connector docs.
get_or_create_connected_account returns the connected account for an identifier. If it is not ACTIVE, get_authorization_link returns the Lever consent link. Because the grant covers the customer's whole Lever account, the identifier is your customer's ID and the person who clicks should be one of their Lever Super Admins. After consent, the code resolves the Lever user your integration writes as. Many customers create a dedicated user such as recruiting-copilot@acme.com for this, so notes and stage moves show up under a name everyone recognizes. Store that user's UID; your code, not the model, passes it as perform_as.
This is not a catalog load. The agent asks for the tools this connected account is authorized to call, then narrows them to the six a hiring manager's pipeline assistant needs. Because every Lever scope is account-wide, this list is where the agent's reach first gets smaller than a Super Admin's. lever_opportunity_archive, lever_opportunity_stage_update, and lever_requisition_delete never reach the model, so it cannot reject a candidate or delete a requisition while answering a status question.
The tool list limits what the agent can do. It does not limit which candidates it can see, because lever_opportunities_list answers for the whole account. So before the loop, the code computes the hiring manager's postings and active opportunities with execute_tool, and the loop enforces them: list calls are pinned to those postings, and any call that names an opportunity outside the set is refused. The boundary lives in code, not in the prompt.
For pipelines, skip the model. execute_tool runs one tool with a fixed input and returns data plus an execution_id you can log. This handler runs when a customer's Lever account sends candidateStageChange. It verifies the body signature, then treats the payload as a hint, because the signature covers token and triggeredAt but not data. It re-reads the opportunity and acts only if Lever confirms the candidate is now in toStageId. Then it finds non-canceled interviews from the stage the candidate left, compares their interviewers with submitted feedback, and logs one note that @mentions whoever still owes it. The note is attributed to the integration user resolved at setup, set by code.
The signing token is per Lever account, so it belongs in your own store keyed by customer, next to the integration user's UID. Webhook setup is not a connector tool: a Super Admin enables candidateStageChange on the webhooks tab and points it at your endpoint. Lever retries up to five times, so the dedupe set must survive restarts.
Stage moves follow the same pattern with one difference: they should wait for a person. lever_opportunity_stage_update takes the opportunity and a stage UID, and is idempotent. Run it after a recruiter clicks approve in your product, with perform_as set to that recruiter's Lever UID, so Lever's activity feed shows the human who made the decision.
Some work belongs in a plain script against your own company's Lever, such as seeding a sandbox with sourced candidates before a demo. Here it is against the API directly with an API key and Basic auth. POST /opportunities requires perform_as as a query parameter, and Lever dedupes by email, so the script checks first. For customer accounts, the same calls go through Scalekit with each account's OAuth grant, and custom connectors cover authenticated REST calls the prebuilt tools don't, such as file uploads.
Pass confidential=all on list calls only if the key was created with confidential access; otherwise Lever returns non-confidential records and says nothing about the rest.
A recruiting copilot rarely stops at Lever. It chases feedback, then posts a digest to the hiring channel in Slack. Load all 77 lever tools and the model holds lever_opportunity_archive, lever_requisition_delete, and lever_contact_update while answering "who owes feedback on Onsite." At roughly 200 tokens each, 77 tools is about 15,400 tokens before the agent does any work, and with every Lever scope account-wide, the tool list is the main thing standing between a misread request and an archived candidate. The fix is not better prompting. It is surface reduction. A Scalekit virtual MCP server declares exactly which tools an agent role can see across connections, and session tokens keep each run bound to one customer's connected accounts. Docs: set up a virtual MCP server.
This role reads and reports. It leaves out lever_note_create, because its perform_as input would let the model choose which Lever user a note appears to come from. Writes with attribution stay in the execute_tool pipeline above, where code sets that field.
One server definition serves every customer. Before each run, check that the customer's accounts are active and mint a short-lived session token scoped to them. With Lever's 90-day inactivity limit on refresh tokens, this check is where a lapsed grant surfaces as an authorization link for the customer's Super Admin, not as a failed run. Any MCP client that speaks Streamable HTTP can connect; this example uses langchain-mcp-adapters (pip install langchain-mcp-adapters) and passes the token as a Bearer header.
The agent sees five tools instead of 77 plus everything Slack exposes. The endpoint is static; the identity is per customer. Compared with Lever's announced connector, this endpoint is available now, can include write tools for roles that should hold them, and works for agents that run on a schedule rather than inside one person's chat.
Every execute_tool call returns an execution_id, and every execution is attributed to a connected account, which maps to one customer's Lever account. When a recruiting lead asks which agent run posted 40 feedback reminders overnight or moved a candidate to Offer on a Sunday, the answer is a lookup, not an investigation.
Lever records the other half. Writes land in Lever under the perform_as user, and Lever exposes audit events through the API under audit_events:read:admin. That makes perform_as the most consequential field an agent touches: whoever controls it decides whose name appears in a customer's Lever history. Keep it out of the model's hands, set it from your own mapping of signed-in user to Lever UID, and log it beside the execution_id. On a community MCP server the picture is flatter: every action runs on one API key, and nothing records which teammate asked for it. Shared credentials are a single-user solution. They do not survive a second user.
Recommended reading: audit trails for agent auth
If your agent is a hiring manager's read-only assistant inside Copilot or Claude, Lever's official MCP Connector is the right target once it ships: Lever enforces each person's permissions and you run nothing. Until then, a community server with one API key works for a single team that accepts what that key can reach. If you are building a recruiting copilot into a product for many customer companies, build on the Lever API with OAuth, plan for the partner program's sandbox and QA steps, and enforce posting-level visibility yourself, because the grant is account-wide. If your agent reacts to stage changes, writes notes and stage moves, or schedules interviews, the API is the only surface that does it. Either way, you hold an :admin-scoped Lever grant per customer with rotating refresh tokens, and that's what needs production-grade infrastructure.
Lever has announced the Lever MCP Connector for Claude, Copilot, ChatGPT and Slack, but its product page still says coming soon and publishes no endpoint or tool list. Lever states access will be read-only and scoped to each person's existing Lever permissions, with setup through your Customer Account Manager.
They work for one team that accepts their limits. Every community server reviewed reads a single Lever API key from the environment, so every request runs with that key's reach, the key has no documented expiry and it sits in a config file on someone's laptop. One of the two examined is listed as inactive.
OAuth is part of Lever's partner program. Lever registers your app, you build against a sandbox, Lever runs a QA session and then issues production credentials. Access tokens last one hour, refresh tokens rotate on every use and expire after one year or 90 days of inactivity, and every data scope ends in admin.
An OAuth grant reaches the whole Lever account, not one recruiter's postings, so the hiring manager asking a question is a row in your database rather than a principal Lever knows about. The agent layer has to narrow visibility itself, for example by pinning list calls to the postings that manager owns.
The API moves candidates between stages, archives them with hire reasons, writes notes with mentions, schedules interviews on externally managed panels, lists deletions for sync and delivers stage-change webhooks. The official connector is read-only and community servers run only inside a chat, so headless agents and reliable writes belong on the API.
Scalekit's Lever connector runs the OAuth flow for each customer account through your own Lever partner app, stores the tokens in a vault and refreshes them ahead of expiry. It exposes 77 tools, 46 read, 25 write and 6 destructive, which you narrow per agent role and serve through a virtual MCP server.
Browse the Scalekit Lever connector and the Lever connector docs with the full tool list. See Scalekit pricing, or talk to us.