MCP server

Connect rinkats to your own agent over the Model Context Protocol. Each side has its own server — JSON-RPC 2.0 over HTTP POST — exposing task-level tools: one call gets a decision-ready result.

Surface v2.3.6 - last updated 29 Oct 2026
Prerequisite — you need a rinkats account before an agent can connect. Tokens and API keys are scoped to your roles, so there's nothing to try until you're signed up. Get started here, then connect via OAuth or generate an API key.

Endpoints

The path decides whose tools you get and which credential is accepted.

  • /api/mcp/candidate — a candidate in their own rinkats space. A candidate belongs to no organisation: a candidate key (rkc_, from account settings (/account)) or an OAuth token granted the candidate scope. An organisation's key is refused here.
  • /api/mcp/recruiter — an organisation's desk: requirements, pipeline, candidates, clients, placements, invoices and admin. An rk_ key or an OAuth token; recruiter, lead, admin or billing.
  • /api/mcp/interviewer — an interviewer's interviews, scorecards, comments and availability. An rk_ key scoped to interviewer, or an OAuth token granted the interviewer scope.

Connect

Two ways to authenticate, both sending a Bearer token to your endpoint:

  • OAuth (recommended for interactive agents) — the user signs in on rinkats and approves access; rinkats issues the token. No secret to copy. Clients that speak MCP authorization discover everything from /.well-known/oauth-authorization-server and register themselves — nothing to configure.
  • API key (for non-interactive services) — an organisation member pastes an rk_ key from their account page; a candidate pastes an rkc_ key from their account settings (/account).
Claude quick-start — in Claude (web, desktop or mobile) go to Settings → Connectors → Add custom connector and paste https://www.rinkats.com/api/mcp/recruiter (or …/candidate, …/interviewer). Claude opens the rinkats sign-in, you approve the scopes on the consent screen, and the endpoint's tools appear in your chats. Use the www URL exactly — the bare apex domain redirects, and MCP clients don't follow redirects.

Static config with a key:

{ "mcpServers": { "rinkats": { "url": "https://www.rinkats.com/api/mcp/recruiter", "headers": { "Authorization": "Bearer rk_p_…" } }, "rinkats-candidate": { "url": "https://www.rinkats.com/api/mcp/candidate", "headers": { "Authorization": "Bearer rkc_p_…" } } } }

OAuth flow

rinkats is the authorization server. On an unauthenticated call, an endpoint answers 401 with a WWW-Authenticate pointer to /.well-known/oauth-protected-resource. The client then runs the standard OAuth 2.1 code flow (with PKCE) against our endpoints: it registers itself, sends the user to our sign-in — password or their linked Google / LinkedIn / Microsoft SSO — then to a consent screen where they approve the scopes. rinkats mints its own access token and returns it; the user's SSO credentials never reach the client. Request the scope for your endpoint (candidate, recruiter, interviewer, lead) — a token carries exactly its granted scopes intersected with the user's roles (an admin account satisfies any organisation role scope).

List tools

curl -X POST https://www.rinkats.com/api/mcp/recruiter \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Call a tool

curl -X POST https://www.rinkats.com/api/mcp/recruiter \ -H "Authorization: Bearer rk_p_…" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call", "params":{"name":"list_my_requirements","arguments":{}}}'

Candidate tools

13 tools at /api/mcp/candidate, grouped by what they act on.

Jobs & Applications

find_matching_jobsThe roles on the rinkats job board closest to the signed-in candidate's current resume — any organisation's, never naming the employer or the agency. Use this to answer 'what should I apply to?': each match carries the jobId apply_to_job takes, the title, summary, location, industry line, skills, a quiet closeMatch flag (never a percentage) and the application id when they already applied. Salary only where the role's jurisdiction shows it. Requires a candidate credential.
list_my_applicationsList the caller's applications as candidate-visible statuses (e.g. 'Under review', 'Interview stage') — never internal stage names, never the employer or the agency. Use for 'where do my applications stand?'; each id is what withdraw_application and the message tools take. Requires a candidate credential.
apply_to_jobApply as the caller to a role on the rinkats job board (find_matching_jobs gives the jobId). Needs a verified email address and an uploaded resume; the candidate never learns which organisation posted the role. The application lands in the role's first stage; a role is applied to ONCE — a second application is refused with already_applied, and a recent rejection or withdrawal answers cooling_period with the date it opens again. Requires a candidate credential.

Profile & Resume

get_my_profileRead the caller's own rinkats candidate profile — headline, summary, location, years, notice, expected pay, skills, roles, education, domains — and whether an AI draft from their latest upload is waiting for review in the portal. Read it before update_my_profile. Requires a candidate credential.
update_my_profileSave one section of the caller's own rinkats candidate profile — section is one of headline, summary, location, totalYears, noticeDays, expectedPayInr, skills, experience, education, domains. The section replaces what is there; every field is bounded and scrubbed of contact details server-side. Send only that section's data. The saved profile is the candidate's confirmed profile; a pending AI draft stays pending for them to review in the portal. Requires a candidate credential.
upload_resumeUpload a new current resume to the caller's own rinkats space. File content travels as base64 (fileName + contentBase64) — there's no multipart upload over MCP. A byte-identical file changes nothing (unchanged: true); at most 4 uploads in any rolling 2 hours. The agencies they already applied to are offered the newer file; no other organisation sees it. Requires a candidate credential.

Application Messages

withdraw_applicationWithdraw one of the caller's own applications, with a required message explaining why. Rejects if it's already withdrawn or closed. Requires a candidate credential.
list_application_messagesList the message thread on one of the caller's own applications — the conversation with the recruiter and the caller's own private notes. Requires a candidate credential.
send_application_messagePost a message on one of the caller's own applications — public (visible to the recruiter) or a private note to self. Requires a candidate credential.
edit_application_messageEdit one of the caller's own application messages. Only a message the caller wrote, on one of their own applications — anything else reads as not found. Requires a candidate credential.
delete_application_messageDelete one of the caller's own application messages. Only a message the caller wrote, on one of their own applications. Requires a candidate credential.

Interviews & Availability

list_my_interviewsList the caller's own interviews in candidate-visible words — the role, industry, time, mode and status, never the employer or the agency — with whether a reschedule or cancel can be requested and any request waiting. Requires a candidate credential.
request_interview_changeAsk to reschedule (with a proposed alternate time) or cancel one of the caller's own upcoming interviews, with a mandatory reason. Re-requesting replaces a request the recruiter has not acted on yet; proposedStart must be in the future for a reschedule. Requires a candidate credential.

Recruiter tools

119 tools at /api/mcp/recruiter, grouped by what they act on.

Organizations

list_my_organizationsThe organizations this account belongs to, with the roles held in each and which one this credential is acting in. Data NEVER crosses organizations: an API key is bound to ONE org at mint time; OAuth/session credentials act in the active org. Use this first when the user might belong to several organizations, and ASK THE USER which org to work in rather than guessing — an `org` argument (id or slug) on any tool is only a cross-check against the credential, never a way to switch.

Jobs & Applications

find_jobsList published open roles IN THE CALLER'S OWN ORGANIZATION, optionally filtered by skills (comma-separated) or a location substring. A credential is bound to one org at mint, so this never returns another agency's roles — the cross-agency board on rinkats.com/jobs is a candidate service on the public site, not an agent catalogue. Salary is included only for recruiter-tier callers. Use when you need the raw catalog rather than personalized matches.
get_jobFetch one published role by id, with description, benefits and openings.
apply_to_jobApply somebody to one of the caller's own organization's roles, by name + email. Whether this is somebody the org already holds is decided by its own identity rule (the NAME and a contact channel, never an email alone), and never onto a privately held record the caller may not see. The application lands in the role's first pipeline stage, Applications, with no owner (the lead assigns it); a candidate who already has a live card there gets a note on it instead of a second card. Returns the candidate-visible status. Requires a recruiter, lead or admin credential.

Interviews & Availability

list_interviewsList the organization's interviews on the requirements the caller can see — candidate, role, client, round, kind, mode, time, duration and status. Requires a recruiter, lead or admin credential.

Candidates

search_candidatesFast name/email/skills TYPEAHEAD (min 2 characters) over the candidate database, for picking an existing candidate to attach to a requirement — up to 8 matches. For a combinable multi-filter search (skill AND name AND region AND experience range AND last-working-date range), use find_candidates instead. Privately held candidates never surface. Requires a recruiter-tier key.
search_by_skillsSKILLS named ('who knows Kubernetes', 'java and react candidates') → everything at once: this desk's candidates with ALL the skills, the requirements asking for them, and the rinkats TALENT POOL when the caller may use it — or `pool.ok=false` with WHY (not verified, the plan, a review) and `pool.waiting=true` when there are candidates with these skills behind that door. Skills are matched against the resume catalog (typos corrected); `skills` lists what was understood. Never a list-all.
find_similar_candidatesThe candidates on this desk most like ONE candidate — deterministic, no model: their skills, role, location and experience become a brief and every candidate is scored against it (skills 55, experience 20, location 15, pay 5, notice 5), best first with the reasons. Use for 'find someone like X'. Privately held candidates never surface. Requires a recruiter-tier key.
find_candidatesSearch the candidate database with any combination of filters — name, skill, location, experience-years range, last-working-date range — all combinable (AND). Use this over search_candidates when you need more than a name/email/skills typeahead. Privately held candidates never surface. Requires a recruiter-tier key.
search_talent_poolSearch the rinkats TALENT POOL — candidates outside this desk who chose to let recruiters find them. Needs `q` or `requirementId`: there is no list-all. `q` is free form ("graphic designer in pune with indesign") or Boolean like Naukri — quotes for phrases, AND / OR / NOT (any case once the query has quotes, brackets or a capital operator), brackets, `-term` to exclude, bare words = AND; a malformed Boolean is refused with a hint. `requirementId` ranks the pool against one of this organisation's open roles (we recommend the best by our criteria); with `q` as well, the role ranks and `q` narrows (every result must also match it). Returns 10 results per call (`limit` up to 50) and a `cursor` (the last searched id): pass it back with the SAME q / requirementId / filters for the next page. The list ENDS at the relevance floor or the depth cap (`ended`: floor | cap | end) and never reports how many people the pool holds. Each result: poolId, first name + last initial, headline, location, years, notice, skills with years, the last two roles, education, `match` (boolean | keyword | semantic band + Match score | ai score + reason), `matched` (the evidence), `alreadyInOrg`. Never an email, phone, link or original resume — rinkats holds the contact; add someone from the web Talent pool page. `ai: true` reads free-form text with the organisation's AI (editable `understood` chips; pass them back edited to re-run without a second read) and scores the top results — it bills the organisation's AI; when AI cannot run the results come back without it and `notes` says why. Searches are rate-limited, budgeted per day and logged; sweeping the pool raises an automatic review. Refused unless the plan includes the talent pool (forbidden), the caller has verified their identity (verify_identity) and is not under review; left out of tools/list when the workspace does not show the pool. Requires a recruiter-tier key.
upload_resumesBulk resume intake — N resumes in one call (base64, like upload_resume). Any resume we can READ is accepted. Each file is read TWICE: a free deterministic read runs inside this call, so the candidates come back with phone, location, education, role, company, experience and skills already filled wherever the file gave them up; the AI read is then queued on a priority lane and normally lands seconds later, ahead of scheduled work (it obeys the workspace's AI budgets and kill switches, and waits for the next scheduled run when one of them stops it). Identity comes from the read, not from the upload. When the name and email can't be picked out of the file deterministically the candidate is created with a placeholder label ("Unnamed resume, reading…") and is renamed, de-duplicated and completed once the read lands — so do NOT re-upload a file because the returned candidate has no proper name yet. Only a file with nothing to read (no text layer and not a PDF), an unsupported type, or one the virus scanner flags is rejected. With requirementId, each lands as a submittal on that stream; without it, each lands in the general candidate pool (same anti-poach hold rules as adding a candidate one at a time). Partial failure is normal and reported per file, not thrown. Requires a recruiter-tier key.
attach_candidate_resumeAttach a resume file (base64) to an EXISTING candidate — the new file becomes their current resume version. A free deterministic read runs inside this call and fills the candidate's still-empty fields immediately; the AI read is then queued on a priority lane and normally lands seconds later, unless the candidate opted out of AI parsing or one of the workspace's AI budgets/kill switches stops it, in which case it joins the next scheduled run. The profile draft lands with that read; the deep summary is on demand. Use this to push an updated resume for someone already in the database; use upload_resumes when you don't know who the resume belongs to. If the email inside the file matches a different candidate the attach still happens but the result's `note` flags the mismatch — check it. Requires a recruiter-tier key.
create_candidateCreate a candidate. With requirementId they are attached to it at the 'sourced' stage (the pipeline board's 'quick add'); without one they go straight into the candidate database (a private hold of the caller until put on a requirement, when holds are enabled). Give an email, or the profileUrl the candidate was sourced from. Rejects if an identical candidate (same name + a matching contact) or the same profile page already exists — use search_candidates first. Requires a recruiter-tier key.
update_candidatePartial update to a candidate's fields — only the fields present are changed. Rejects if the change would make this record identical to another candidate's (same name + a matching contact). Requires a recruiter-tier key.
flag_candidateBlacklist/flag a candidate with a required reason (logged to the candidate's flag history). Requires a recruiter-tier key.
unflag_candidateClear a candidate's blacklist/flag status with a required reason. Requires a recruiter-tier key.
compose_candidate_messageDRAFT a message to a candidate (owner 2026-10-12): a short mail from you, plus the same text formatted for WhatsApp and the candidate's number when you may see it (open https://wa.me/<number>?text=… to send it there). Let the person edit it, then message_candidate for the email.
preview_mailRENDER a composed mail as it will arrive (owner 2026-10-12): the Markdown inside rinkats' real mail template, as the portal's Preview tab shows it. Nothing is sent or stored. Use it to show the person the final mail before `schedule_meeting`, `submit_to_client` or `message_candidate`.
message_candidateSEND an email to a candidate from you at the sending domain (replies go to no-reply until the inbox exists); it is in the email log. ALWAYS show the person the mail before sending.
send_candidate_profileEMAIL a candidate's STANDARD formatted resume (the desk's own PDF render — never AI-written at send time) to the client or agency contacts of the job card's requirement, with an optional short covering message. Pass the job card (`submittalId`, or the card's reference like PLAT-3) and the recipients' emails; without `recipients` it SENDS NOTHING and returns the contacts it may send to (`contacts`), the candidate, the role and the last send — call it again with the picked emails. A real email, logged on the card. Requires a recruiter-tier key.

Requirements & Pipeline

create_requirementCreate a requirement (lead/admin only). Publishing (isPublished: true) triggers async SEO-description generation and a Google Jobs indexing ping. `code` auto-generates from client/agency/title when omitted. Pass `sourceUrl` (an http/https page) when the requirement came from a job page: it is shown as Imported from {host} and lets find_requirement_by_source / apply_requirement_source keep it in step with the page. See /docs/reference (Recruiting → POST /recruiter/requirements) for the full field list (fee mode, salary, skills, etc).
compose_requirement_shareDRAFT a requirement's job description as a mail (owner 2026-10-12: Share on the requirement page): the subject and Markdown — a greeting, the role's facts table, the description, the apply link when the role is published — plus `whatsappText` and a `whatsappUrl` that opens WhatsApp Web with it typed in. The client is named only when the role publishes it.
share_requirementSEND the job description by email to ANY addresses (a candidate, a contact, a colleague) from the person at the sending domain — a REAL email, in the email log. ONLY A PUBLISHED, OPEN REQUIREMENT can be shared (the mail links to its public job page and the candidate portal); an unpublished one is refused by name — publish it first. ALWAYS show the person the mail (compose_requirement_share, edited as they like) and the addresses before sending.
update_requirementFull update of a requirement's editable fields (not assignment-scoped). The code is immutable and ignored if sent. Requires a recruiter-tier key.
find_requirement_by_sourceIs there already a requirement imported from this job page? The address is cleaned (fragment and utm_*/ref/src parameters dropped, host lower-cased) and matched case-insensitively. Returns the requirement's id, code, title, client, status and when its page was last checked (sourceCheckedAt), or null. Pass fields=true to include the current values a re-read of the page is compared with. Requires a recruiter-tier key.
apply_requirement_sourceUpdate a requirement from its source page: change ONLY the fields given (title, description, location, workMode, openings, minExperienceYears, maxExperienceYears, salaryMin, salaryMax, currency, skills — every field left out keeps its value, unlike update_requirement) and stamp sourceCheckedAt. An empty fields object only marks the page as checked. The status is never changed. A human decision: show the person what changes first. Requires a recruiter-tier key.
set_requirement_statusChange a requirement's status (draft/open/on_hold/closed/canceled). Not assignment-scoped — any recruiter-tier org member may change any requirement's status. Reopening a closed/canceled requirement whose linked contract was since terminated is refused (409) unless detachTerminatedContract is set, which reopens it with no contract linked. Requires a recruiter-tier key.
research_requirementsForce the grounded public-web research pass on up to 10 requirements at once — it fills in the likely skills, seniority and market context for a role from the client company's public footprint, which sharpens matching and screening. Thin requirements (no description, no skills) get this automatically once; call it to re-run after the market moved, or for a role that has a description. It is one of the heavier AI calls, so prefer filling in the description by hand when you can. Results land on the requirement in the background. Requires a recruiter-tier key.
add_requirement_commentPost a comment to a requirement card. Staff may only post to a section their role can read (internal vs interviewer). Requires a recruiter-tier key.
add_assigneeAssign a recruiter or lead to a requirement — scopes who sees/works this requirement everywhere else in the API. Requires a lead or admin key.
remove_assigneeRemove an assignment row (undoes add_assignee). Requires a lead or admin key.
get_pipelineGet the full board for one requirement: ordered stages plus every card (candidate, current stage, owner, days-in-stage). Use after list_my_requirements to review a role's pipeline in one call. Requires assignment to the requirement.
advance_candidateMove a submittal to a new stage, recording your note. Free any→any move; when moving to a drop-off stage ('rejected', 'withdrawn', 'offer_rejected', 'not_interested') a note is REQUIRED (a 422-style error is returned otherwise). Use to progress a candidate after a decision — reaching 'placed' creates the placement record automatically. 'removed' takes the card off the board (no note needed, the candidate is never emailed); for one candidate on one requirement only ONE card may be outside 'removed', so moving a removed card back is refused (409) while a newer card for that candidate is on the requirement. An APPLICATION (a card waiting on the Applications page, or a dismissed one) is never moved — it is decided with accept_applications / dismiss_applications. Requires assignment to the requirement.
reorder_pipeline_cardDrag-reorder a submittal within or across columns to an explicit index — distinct from advance_candidate, which only appends to the bottom of the destination column. Requires assignment to the requirement.
update_engagement_offerThe offer on a card: set or clear (empty string) the offered CTC in rupees a year (recording one makes the offer; locked once placed), set or clear the joining date (YYYY-MM-DD — the placement's start date once joined), and/or change the offer's state (offered / accepted / rejected / withdrawn), which MOVES the card the way the offer row does — rejected and withdrawn need a reason. Send any of the three. Requires assignment to the requirement.
add_offerAdd an offer for a candidate on an OPEN requirement: their live card moves to Offer released (which makes the offer); with no live card — never added, or only removed or dismissed — a card is created there. If that requirement already has an offer on this candidate the call fails with the existing card's id. Optional offered CTC and joining date go on the card.
set_candidate_skill_scoreSet (or clear, with score: null) a manually-entered 0–100 skill score on a candidate — alongside, never overwriting, a verified test score. Requires a recruiter-tier key.
search_and_attach_candidateCollapse the pipeline board's 2-step 'quick add' into one call: pass candidateId to attach an existing candidate directly, or a resume file (base64, like upload_resume) to intake. Any readable resume is accepted — matched to an existing candidate by the email inside it when one can be read, otherwise a candidate is created with a placeholder label and named by the AI read a moment later. Requires assignment to the requirement.
add_submittal_commentPost a comment to a submittal (pipeline card). Staff may only post to a section their role can read. Requires assignment to the requirement.
reassign_submittalChange a submittal's owner. Only a lead, an admin, or the current owner may reassign; the new owner must be recruiting staff. Requires assignment to the requirement.
compose_client_submissionDRAFT the client submission for a job card (owner 2026-10-12): the client's contacts to pick from (`info.contacts`), the mail with the candidate's facts table and the summary for the role, the resume to attach. Let the person edit it, then submit_to_client.
submit_to_clientSEND the composed submission: a REAL email to each picked contact from you, the formatted resume attached (redacted unless includeContact). moveTo: "submitted" moves the card to Submitted first — what a board move does. ALWAYS show the person the mail before sending.

Applications

list_applicationsList the applications waiting to be accepted or dismissed — candidates who applied to a role and are NOT on the pipeline board yet — newest first, on the requirements you can see. state='dismissed' lists the dismissed ones instead. Each comes with its card reference (e.g. BE-12), the candidate, the role, when they applied, the job remark and the AI screening score when there is one. Use before accept_applications / dismiss_applications.
accept_applicationsAccept applications: each card goes onto the pipeline board in Sourced (no owner yet — a lead assigns one). Name them by card reference (BE-12) or id, up to 200. Each is decided on its own; one outcome per card comes back, a refused one with its reason. The candidate is not emailed. Optional remark is written to each card's job remark. Can be undone for five minutes with undo_application_decision. Requires assignment to the requirement.
dismiss_applicationsDismiss applications: they leave the waiting list and rest under Dismissed (not Removed — still readable with list_applications state='dismissed'). Name them by card reference (BE-12) or id, up to 200; one outcome per card. The candidate is not emailed; their portal shows the application as not moving forward. They may apply to the role again once the cooling period is over. Can be undone for five minutes with undo_application_decision. Requires assignment to the requirement.
undo_application_decisionTake back YOUR OWN Accept or Dismiss on these applications — within five minutes of making it, and only while the card has not moved since; they go back to waiting. This is the only way to reverse a decision, so use it straight after a mistake.
clear_dismissed_applicationsClear DISMISSED applications from the Dismissed list — named by card reference (BE-12) or id, or all=true for every dismissed application the person can see (200 per call; `remaining` says how many are left). A soft delete: the record stays and still counts for the cooling period, but nothing in rinkats lists, searches or opens it again. It can be undone for five minutes with undo_clear_applications — after that only a developer can restore it, so confirm with the person before clearing. Only dismissed applications can be cleared.
undo_clear_applicationsBring back dismissed applications YOU cleared, within five minutes of clearing them — the only way back. They return to the Dismissed list.

Clients

list_clientsEvery client in the org — clients have no per-user assignment scoping (unlike requirements). Requires a recruiter-tier key.
get_clientOne client with its contacts and contracts. Requires a recruiter-tier key.
create_clientCreate a new client, status 'prospect'. Requires a recruiter-tier key.
add_client_contactAdd a contact to a client. A new primary contact demotes the client's existing primary. Requires a recruiter-tier key.
add_client_contractAdd a fee contract (percent-of-salary, percent + bonus, or flat) to a client, status 'active'. Requires a recruiter-tier key.
set_client_statusInline status change (prospect/active/dormant only) — blacklisting/un-blacklisting is a separate governed lead/admin flow, not available here. Requires a recruiter-tier key.

Agencies

list_agenciesEvery sourcing-agency account in the org. Requires a recruiter-tier key.
get_agencyOne agency with its contacts. Requires a recruiter-tier key.
create_agencyCreate a new sourcing-agency account, status 'prospect'. Requires a recruiter-tier key.
update_agencyFull update of an agency's name/website. Requires a recruiter-tier key.
set_agency_statusInline status change (prospect/active/dormant only) — blacklisting/un-blacklisting is a separate governed lead/admin flow, not available here. Requires a recruiter-tier key.
add_agency_contactAdd a contact to an agency — a new primary contact demotes the agency's existing primary. Requires a recruiter-tier key.

Interviewer Directory

list_interviewersEvery interviewer profile in the org, with linked clients/agencies. A profile-only interviewer (no login yet) is never emailed. Requires a recruiter-tier key.
get_interviewerOne interviewer profile with linked clients/agencies and their assignment history (scheduled meetings). Requires a recruiter-tier key.
search_interviewers_directoryTypeahead over interviewer name/email, up to 8 matches — for picking an interviewer when scheduling a meeting. Requires a recruiter-tier key.
create_interviewerCreate or update an interviewer profile — upsert by email (an existing email updates name/skills instead of duplicating). Optionally links the interviewer to clients/agencies in the same call. Never sends an email. Requires a recruiter-tier key.
update_interviewer_skillsReplace an interviewer's skill list (max 30, deduplicated case-insensitively). Requires a recruiter-tier key.
set_interviewer_statusActivate or deactivate an interviewer profile. Requires a recruiter-tier key.

Calendar & Meetings

list_meetingsEvery meeting in the org, optionally filtered to one candidate. Requires a recruiter-tier key.
get_meetingOne meeting's full detail. Requires a recruiter-tier key.
list_candidate_cardsA candidate's job cards (submittals), for the optional 'link to a card' field when scheduling a meeting. Requires a recruiter-tier key.
schedule_meetingSchedule an interview/meeting with a candidate. sendInvite: true sends a REAL EMAIL with an ICS invite to the candidate, interviewer, and organizer — a live external side effect, not just a DB write. Naming an interviewer's email creates/updates their directory profile (never emails a profile-only interviewer). When the meeting is linked to a job card (submittalId), EVERY copy of the invite and its calendar event also describe the role — location, experience, skills and the requirement's description; never the salary band or fee. The organiser and the interviewer read the client's name; the candidate's copy names the client only when the requirement publishes its company (otherwise '<industry> company'). If the organisation's plan caps emails and the invite does not fit in what is left, the mails are not sent and the email log says so. Requires a recruiter-tier key.
update_meetingEdit a meeting — everything except the candidate and linked job card, which are fixed once created. Same live-email behavior as schedule_meeting when sendInvite: true. Requires a recruiter-tier key.
cancel_meetingMark a meeting cancelled. sendCancel: true sends a REAL cancellation email (ICS CANCEL) to the candidate, interviewer, and organizer. Requires a recruiter-tier key.
resend_meeting_inviteResend the ICS invite to ONE recipient already on the meeting's roster (candidate, interviewer, organizer, or a listed participant). ALWAYS sends a real email — there's no dry-run toggle. Requires a recruiter-tier key.

Requirements

get_job_posting_qrA QR CODE the recruiter can copy, download or print (the app draws it in the chat): for ONE role — its public job posting; for SEVERAL — the job board kept to those roles; for a rinkats page — `path` (e.g. the page in <ctx>). Name roles by title or code, comma-separated. Says which roles are not published (the code still works once they are). Shown by the app itself.

Placements

list_placementsActive placements. Admin/billing see every placement; recruiter/lead see only their assigned requirements' placements. Requires a recruiter, lead, admin or billing key.
get_placementOne placement's full detail. Requires a recruiter, lead, admin or billing key.
update_placementPartial update of a placement's feeAmount/offeredCtc/startDate/probationEndDate. Blocked while the placement is in a locked fee state (invoiced/paid/fell_off/refund_issued/written_off) — set_placement_state first. Requires a recruiter-tier key.
set_placement_stateMove a placement to any fee state — including reopening a locked one (the unlock path), which requires `reason`. Requires a recruiter-tier key.
repoint_placement_contractFix a placement whose snapshotted contract was later terminated (which blocks invoicing) by re-pointing it to a different active client or agency contract — re-snapshots the fee. Blocked while locked. Requires a recruiter-tier or billing key.

Invoices & Payments

list_invoicesInvoices and credit notes with paid/outstanding roll-ups. Requires a billing or admin API key or session — never reachable via OAuth (billing/admin are not OAuth-grantable scopes).
get_invoiceOne invoice with its lines, payments, and paid/outstanding roll-up. Requires a billing or admin API key or session.
create_invoiceCreate an invoice. Pass placementIds to create a draft from one or more placements (they must share a billing party, and none may have a terminated contract) — OR pass kind ('client'|'agency'|'custom') + the target to create a blank draft to add lines to afterwards. Requires a billing or admin API key or session.
save_invoice_linesReplace a DRAFT invoice's whole line set in one call. Lines with an id update the matching line; lines without one are inserted as adjustment lines; lines omitted from the list are removed. Requires a billing or admin API key or session.
issue_invoiceIssue a draft invoice: sets issued/due dates and LOCKS its placements (state → invoiced). Requires at least one line. Requires a billing or admin API key or session.
void_invoiceVoid an invoice and REOPEN its placements (back to fee_due) so they can be re-billed. Requires a billing or admin API key or session.
create_credit_noteIssue a credit note mirroring an invoice's lines negated. Can't credit a credit note, or an invoice still in draft. Requires a billing or admin API key or session.
email_invoiceSend a self-contained HTML copy of an issued invoice to a recipient and log the send. The invoice must be issued (not a draft). This sends a REAL email to the address given. Requires a billing or admin API key or session.
record_paymentRecord a (possibly partial) payment against an issued invoice and advance its status (part_paid/paid). Requires a billing or admin API key or session.
edit_paymentEdit a recorded payment. Changing the amount requires `reason` (audited); editing only the date/method/reference/note needs none. Requires a billing or admin API key or session.
delete_paymentRemove a mistakenly recorded payment. `reason` is mandatory and audited. Requires a billing or admin API key or session.

Admin

create_userCreate or invite a staff user. Someone who is ALREADY a member of your organization is granted the requested portal role(s). Any other address is INVITED — an address nobody holds gets a new account and a 48h set-password link; an address that already has a rinkats account gets an invitation it must accept itself, and you are told nothing about that account (not its id, not its name, not that it exists). Candidate access is self-serve and can't be granted here. STAFF SEATS: an organisation on an INDIVIDUAL plan has no lead or recruiter seats — only admin, billing and interviewer can be granted there, and asking for the other two is refused with a reason (422). Call `admin_settings_notice` if the user needs that changed; nobody inside the organisation can change it. Requires an admin API key or session.
set_user_roleGrant (value: true) or revoke (value: false) one portal role for an existing org member. An admin can't remove their own admin role. STAFF SEATS: an organisation on an INDIVIDUAL plan has no lead or recruiter seats — only admin, billing and interviewer can be granted there, and asking for the other two is refused with a reason (422). Call `admin_settings_notice` if the user needs that changed; nobody inside the organisation can change it. Revoking a role is never blocked by it. Requires an admin API key or session.
get_brandingRead the organisation's branding — the logo (public URL and the colours read from it), the two colour roles in force (primary: headings, links, buttons, resume section titles; accent: the rule under the header), the heading and body fonts with the list of fonts available, and the organisation's email signature template with its placeholders ({name} {email} {phone} {title} {org} {website}). Admin only. Refused with the plan sentence when Custom branding is not in the plan — then every mail and resume carries rinkats' own look.
set_brandingChange the organisation's branding: `primary` / `accent` (a hex colour like #0A5A63, or null to follow the logo's suggestion), `headingFont` / `bodyFont` (a key from get_branding's fonts.available — email-safe fonts only), `signature` (the organisation's signature template in Markdown with placeholders; null clears it), `logoAssetId` (the id returned by a logo upload through POST /api/brand/assets, or null to remove the logo — a logo cannot be sent through this tool). Only the fields sent change. Admin only; applies to the next mail and resume. Returns the branding as get_branding does.
get_my_signatureRead the caller's own email signature in this organisation (Markdown), and whether they use the organisation's template instead.
set_my_signatureSet the caller's own email signature (Markdown; images only from the organisation's brand uploads). Send null to go back to the organisation's template. It ends every mail the caller composes from then on.
list_ai_runsRead the AI run log — every AI run (resume parses, requirement rephrases, re-embeds) with job type, trigger (scheduled/manual), who ran it, per-item stage trails, and CLASSIFIED failure causes with fix hints (rate limit, invalid key, out of credits, model unavailable, provider down, safety block). Use this to diagnose why AI output is missing or failing before touching keys or settings. Defaults to the last 3 hours; pass startIso/endIso for a window, beforeIso (the oldest run's createdAtIso) to page back, runId for exactly one run, or q to filter. Privately-held candidates come back masked. Requires an admin API key or session. ERROR DETAIL: every run and item carries `ref` — a short, stable reference id (RK-7F3Q-9K2D-8BHC) for that log row. On an organisation whose error detail is `generic` (the default), the error text is one fixed sentence for the failure's category plus that reference — never the provider's message, a stack, a model id or a key name; quote the reference to rinkats support and they read the full message. On `full` the raw system error comes back as before. The reference is present in both modes, and pasting one into the search/`q` filter finds exactly its run. Each run also carries `label` (what it is in a person's words, e.g. "Parse again with AI"), `instruction` (the note the person typed when they ran it) and `share` (the import charge share that applied, when its usage was share-billed).
set_user_activeActivate or deactivate a user. Deactivating blocks sign-in immediately and kills their live sessions. An admin can't deactivate their own account. Requires an admin API key or session.

Other

search_helpHow rinkats itself works: keyboard shortcuts / hotkeys, where a page or setting is, what a feature does, how to do something in the app, what the API or the tools can do. Use it for ANY question about rinkats rather than about the recruiter's own records — and before ever saying a feature, page or shortcut does not exist.
get_ai_usageHow much of this cycle's AI usage the organisation has used, how much assistant chat, how the chat is charged, and the date both reset. Use it for any question about AI usage, tokens, AI spend, limits or 'how much is left'.
admin_settings_noticeOrganization-level admin settings (the private candidate-hold policy and every AI setting at /r/admin/settings, plus per-record AI overrides and personal AI keys) cannot be changed through MCP tools. Some of them are MANAGED BY RINKATS for this organization, which means nobody inside the org can change them either — the result lists exactly which, under `managedByRinkats`. Call this tool INSTEAD OF attempting an unsupported action whenever a request is an org-configuration change rather than a business record (candidate/requirement/invoice/etc). Always succeeds; its result is the instruction to relay to the user, not an error.

Sourcing

list_sourcing_requirementsOpen requirements the caller may source for, each as the BRIEF a portal search is built from: title, client, location, work mode, skills, experience range, pay range and currency, a 1,200-character description excerpt, and the counts of profiles already on its review list (new / shortlisted / added). Filter by pay (payMin/payMax), experience, location or text; convert LPA/lakhs to absolute INR before calling (30 LPA = 3000000). Requires the browser_assistant plan feature.
list_sourced_profilesThe review list for one requirement — profiles the browser assistant captured from a portal, best deterministic fit first. Each carries the portal, its stable external id and URL, the visible facts (headline, company, role, location, experience, pay, notice, education, skills), the fit score with its reasons, the status (new / shortlisted / dismissed / added), whether contact has been revealed by a recruiter, the candidate id once promoted, and the other requirements the same profile sits on. Default status filter: new,shortlisted,added.
shortlist_sourced_profileMark a captured profile as shortlisted on its requirement's review list. Changes nothing on the pipeline — the recruiter promotes separately.
dismiss_sourced_profileDismiss a found or captured profile from its requirement's review list. It stays on record, so a re-capture does not resurface it as new and no later web search on the organisation finds the person again. An optional `reason` (240 characters, e.g. 'Too junior', 'Wrong location') is kept on the person and steers the next web search on the requirement - the planner reads it without the person's name. Changes nothing on the pipeline.
capture_sourced_profilesSave up to 100 profiles read off a portal page onto a requirement's review list (upsert by portal + external id; a re-capture refreshes facts and fit but never moves a status a recruiter set). The body has NO contact fields by design — contact enters only through record_sourced_contact after a recruiter revealed it on the portal. Each profile: externalId, name, and optionally profileUrl, headline, currentCompany, currentRole, location, preferredLocation, totalExperienceYears, currentCtc (+ currentCtcCurrency), expectedCtc, noticePeriodDays, education, skills[], summary, lastActiveLabel, extra{}.
record_sourced_contactRecord contact details a RECRUITER revealed on the portal (or typed) for a captured profile — email and/or phone. Writes an audit line (who, when, which requirement). The assistant never reveals contact itself; this records a human's action.
promote_sourced_profileCreate the candidate from a captured profile and attach them to its requirement at the 'sourced' stage — the pipeline quick-add, from a captured profile. Needs an email (recorded via record_sourced_contact or passed here). The org's identity rule applies: the same NAME and contact already on file (and visible to the caller) is attached, not duplicated — and refreshed from the captured profile only when the portal copy is newer than the rinkats record (the result's `existing.freshest` / `existing.updatedFields` say which; the email is never changed; fields a person edited by hand are kept, listed in `existing.keptManual`); first-touch provenance is 'sourced' by the caller for this requirement. A HUMAN decision: agents must confirm with the recruiter before calling.
start_sourcing_runFind talent for one open requirement on the public web. `web` (the default channel, and the only one the Sourcing tab runs) is a background run over public professional profiles with a hard budget of searches, page reads, model calls and minutes. For API callers two more channels exist: `database` (the organisation's own candidates and the talent pool, answered in this call; in the app that answer is Recommendations) and `portals` (the job-portal start links, for the recruiter to open in the browser assistant). The web run is queued and this call returns its run id at once - poll get_sourcing_run, then read the people with list_sourcing_people. `instructions` is an optional plain-words steer, 600 characters at most - what to change since the last search; a re-run without it is 'Find more' (same target, new queries and surfaces), and every earlier run's words and results and every dismissal with its reason travel to the planner as the search's conversation, so a re-run only adds people (anyone already found, dismissed included, is never found again). A second start while a run is planned, running or paused is refused with 409 `sourcing_running`. `scope` says where the web run may look (`searchIn`, the public surfaces, default all: the developer and research sites, `hackernews` - the monthly 'Who wants to be hired?' posts -, `bluesky` and `mastodon` - #opentowork posts -, `linkedin` - X-ray web queries only, a LinkedIn page is never fetched - and `web`) and what it never searches, reads or follows (`excludeHosts`); a start without it searches everywhere - the recruiter's Find talent dialog re-opens on the previous run's scope, an API caller passes it on every start. Every person found gets a likely-to-move score (`interest`) before the AI judges them; a reachable person scored `low` is kept as tier `unlikely` and never judged, so the run spends on people who will move. Pause, resume or cancel the run with pause_sourcing_run, resume_sourcing_run and cancel_sourcing_run. Requires a recruiter-tier key and the talent_sourcing plan feature. Nothing it finds reaches the pipeline: a person is added as a candidate only by a recruiter's decision (promote_sourced_profile).
get_sourcing_runOne web sourcing run on a requirement: status (planned, running, paused, done, failed, cancelled), the caps it started with, what it has spent, how many people it found (by tier: presented, borderline), where it was allowed to look (scope), the persona and surfaces it tried, the latest plain-words progress lines and, while it is planned, running or paused, `live`: the people found so far in the order found, each with the step it is in (found, reading, read, judging, done). Requires a recruiter-tier key and the talent_sourcing plan feature.
get_sourcing_run_logWhat one web sourcing run did, step by step: every plan, search (with its hits or refusal), page read or skipped (read, walled, robots, excluded, not a person, error), and decision about a person (found, judged with the fit, not judged, dropped with the reason), in time order, each with its detail (the full query, the address, the reason, the judge's strengths and concern). 300 rows at most (`capped`, `total`). A run still going returns what it has logged so far. Never a contact value. Requires a recruiter-tier key and the talent_sourcing plan feature.
pause_sourcing_runPause a planned or running web sourcing run: it stops within one search, page read or judgement, keeps everything it has done and reads `paused` (409 when it has already finished or is already paused). Requires a recruiter-tier key and the talent_sourcing plan feature.
resume_sourcing_runResume a paused web sourcing run: a new background job carries on from the same step without repeating a search, a page or a judgement; refused with 409 while the paused job is still stopping (try again in a moment) or when the run is not paused. Requires a recruiter-tier key and the talent_sourcing plan feature.
cancel_sourcing_runCancel a planned, running or paused web sourcing run for good: nothing more is searched, read or written (409 when it has already finished). Requires a recruiter-tier key and the talent_sourcing plan feature.
list_sourcing_peopleThe people a requirement's web sourcing found, best first, each with the dossier: why they fit, the evidence quotes with their links, the AI fit score and confidence, `interest` (likely to move: high, medium or low, with every signal behind it and its weight - availability posts, tenure, job changes, location, seniority gap, fame figures), and `contact`: the channels the person publishes on their own public pages (email, phone, LinkedIn, X, messaging handles, website, contact page), each with the page it was read on; `contactable` says whether one can reach them for a requirement (LinkedIn, an email for contact, a phone, a messaging handle or a contact page - a bare website is not; an email published on GitHub is flagged in `contact.emailFlags`, not for cold outreach). Contact values are never in a dossier. tier no_contact lists the people read but with no way to reach them, tier unlikely the reachable people unlikely to move (neither is judged). Promoting a person with a found email needs no email typed (a GitHub email is never used). Requires a recruiter-tier key and the talent_sourcing or browser_assistant plan feature.
list_site_profilesHow the browser assistant reads each portal, as versioned data (URL patterns, selectors with fallbacks, pacing caps, the controls it must never click). The extension refreshes these on start; a fixed selector reaches every install without a store release.

Interviewer tools

7 tools at /api/mcp/interviewer, grouped by what they act on.

Organizations

list_my_organizationsThe organizations this account belongs to, with the roles held in each and which one this credential is acting in. Data NEVER crosses organizations: an API key is bound to ONE org at mint time; OAuth/session credentials act in the active org. Use this first when the user might belong to several organizations, and ASK THE USER which org to work in rather than guessing — an `org` argument (id or slug) on any tool is only a cross-check against the credential, never a way to switch.

Interviews & Availability

list_my_interviewsList the interviews the caller conducts — candidate, role, client, round, kind, mode, time, duration and status. Requires an interviewer credential.
request_interview_changeAsk to reschedule (with a proposed alternate time) or cancel an interview the caller conducts, with a mandatory reason. Refused while another request on it is still pending. Requires an interviewer credential.
submit_scorecardSubmit (or resubmit) the caller's scorecard for one interview they conducted — an array of competencies (name, rating: strong/good/mixed/weak, evidence) plus an overall recommendation (strong_no/no/yes/strong_yes). Scoped to the caller's own interview. Requires an interviewer credential.
add_interview_commentPost a comment on the shared submittal card for one of the caller's interviews — always lands in the interviewer-only section (interviewers can never post or read the internal recruiter section). Requires an interviewer credential.
get_my_availabilityList every persisted non-busy availability slot for the caller — a slot with no row is busy by default. Requires an interviewer credential.
set_my_availabilitySet, change, or clear one weekday × time-segment availability slot for the caller. state is 'available', 'preferred', or omitted/null to clear back to busy. Requires an interviewer credential.