JobMojito MCP
An official MCP server provided by JobMojito that enables AI agents to autonomously create AI interviews, invite candidates, and retrieve scored results. It supports voice-only or avatar-based interviews in 32 languages, integrates with 70+ ATS platforms, and provides explainable, science-backed candidate-job fit scores while maintaining GDPR and EU AI Act compliance.
Endpoint and access
No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.
https://mcp.jobmojito.com/mcpWhat its tools do
44 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.
Read-only (24)
search_documentationget_documentationlist_my_merchantsget_interview_definitionlist_interviewsget_catalogue_directorylist_catalogue_directoriesget_interview_result_detailslist_interview_resultsget_merchant_analyticsget_merchant_credit_usagelist_report_fieldslist_reportsget_reportrun_reportpreview_reportlist_candidateslist_knowledge_base_documentslist_sub_merchantslist_avatarsget_merchant_statuslist_languagesget_portal_configurationjobmojito_configuration
Can change data (20)
invite_candidatescreate_interviewcreate_interview_from_questionscreate_personaupdate_interviewset_interview_stategenerate_interview_urlregister_users_for_interviewcreate_catalogue_directoryupdate_catalogue_directorygenerate_interview_reportrequest_another_interview_attemptset_assessment_resultcreate_reportupdate_reportupload_knowledge_base_documentset_knowledge_base_document_interviewsupdate_portal_configurationsubmit_feature_requestreport_technical_issue
Checks and signals
Each line is separate evidence, not a trust score.
- Passed Endpoint answered the live check ·
- Passed Official MCP Registry namespace is tied to jobmojito.com ·
- Passed Endpoint is on the registry namespace domain ·
- Availability 1 of 1 checks passed in 30 days
What you can do with it
- Create AI interviews for candidates
- Invite candidates to take interviews
- Fetch scored results and fit analysis
- Integrate hiring workflows with AI agents
Source and maintenance
- Maintenance checked
Protocol and capabilities
- Server
- JobMojito 1.5.0
- MCP protocol negotiated in check
- 2025-11-25
- Tools
- 44 advertised
- Resources
- 1 advertised
- Resource templates
- 0 advertised
- Prompts
- 0 advertised
{
"tools": {
"listChanged": true
},
"logging": {},
"prompts": {
"listChanged": true
},
"resources": {
"subscribe": false,
"listChanged": true
},
"extensions": {
"io.modelcontextprotocol/ui": {}
},
"experimental": {}
}Server-provided instructions
Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.
Tools
44 advertised
search_documentation — Search JobMojito documentation
Search ALL JobMojito documentation. This is the single entry point. One call searches both documentation sources in parallel and returns a merged, source-labeled list — you do not need to choose a source or call a separate tool: • "developer" — developer.jobmojito.com: API reference, request/response schemas, tables, webhooks, code examples, integration guides. • "help" — help.jobmojito.com: recruiter, candidate, and administrator product guides (how the platform behaves for end users). Use this whenever you need to understand how a feature, endpoint, field, or workflow works — including before calling an action tool you're unsure about. Then call `get_documentation(url)` with a returned URL to read the full page.
{
"type": "object",
"required": [
"query"
],
"properties": {
"limit": {
"type": "integer",
"default": 8,
"description": "Max results per source (1-25)."
},
"query": {
"type": "string",
"description": "Natural-language search query or keywords."
},
"source": {
"type": "string",
"default": "all",
"description": "\"all\" (default), \"developer\", or \"help\" to restrict the search."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
},
"additionalProperties": false
}{
"type": "object",
"properties": {
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"additionalProperties": true
}{
"title": "Search JobMojito documentation",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}get_documentation — Read a JobMojito documentation page
Fetch the full content of a single JobMojito documentation page. Accepts a URL returned by `search_documentation`. For developer docs the clean Markdown (.md) variant is fetched automatically. Only developer.jobmojito.com and help.jobmojito.com URLs are allowed.
{
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"description": "The documentation page URL."
},
"max_chars": {
"type": "integer",
"default": 20000,
"description": "Truncate content to this many characters (default 20000)."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
},
"additionalProperties": false
}{
"type": "object",
"properties": {
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"additionalProperties": true
}{
"title": "Read a JobMojito documentation page",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}list_my_merchants — List merchants you can act as
FALLBACK merchant list for clients WITHOUT UI support. Do NOT use this to choose or switch merchants when a UI is available — call `jobmojito_configuration` instead (it renders an interactive picker), and do not hand-format a merchant list as text. Use this tool only when the client cannot render MCP App UI. Returns the user's own account plus any sub-merchants; after a pick, pass `merchant_id=<chosen id>` on subsequent calls (OMIT for the own account).
{
"type": "object",
"properties": {
"search": {
"type": "string",
"default": "",
"description": "Optional case-insensitive filter on sub-merchant name."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
},
"additionalProperties": false
}{
"type": "object",
"properties": {
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"additionalProperties": true
}{
"title": "List merchants you can act as",
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}invite_candidates — Invite candidates to an interview or position
[Interviews] Invite candidates to an interview or position: e-mails each one their invitation. The same invitation the admin console's Add candidates sends: creates or finds the candidate, uses the company's invitation budget, and queues the invitation e-mail (with the optional recruiter message). Each row comes back with its outcome; only 'Invited' means an e-mail was queued. To get a personal interview link WITHOUT sending an e-mail (to share it yourself, or for a test), use register_users_for_interview instead.
{
"type": "object",
"required": [
"interview_id",
"candidates"
],
"properties": {
"language": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Language code for new candidates (e.g. 'en', 'sk'); defaults to English."
},
"candidates": {
"type": "array",
"items": {
"type": "object",
"required": [
"email"
],
"properties": {
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Display name, e.g. 'Jane Doe'."
},
"email": {
"type": "string",
"description": "The candidate's e-mail address; the invitation goes here."
},
"phone": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional mobile number in international format (+421903123456) for SMS / WhatsApp notifications."
}
}
},
"maxItems": 50,
"minItems": 1,
"description": "The people to invite."
},
"merchant_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Only for an account acting for a sub-merchant; omit for your own company."
},
"interview_id": {
"type": "string",
"description": "The interview (interview_def_set) or position (position_def_set) id, from list_interviews."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"message_from_recruiter": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional personal note shown above the invitation in the e-mail."
}
},
"additionalProperties": false
}{
"type": "object",
"properties": {
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"additionalProperties": true
}{
"title": "Invite candidates to an interview or position",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}create_interview — Create interview
[Interviews] Create a new interview and auto-generate its question sequence from position data. The `interview_template_id` you pass also sets the modality (voice-only vs realtime/pre-recorded avatar) — see `list_avatars`. Creates a new interview / coaching / assessment definition, generates its description, questions and candidate expectations via AI, and provisions default steps. Optionally provisions an embed key.
{
"type": "object",
"required": [
"name",
"location",
"interview_template_id",
"mojito_language_code",
"status",
"type",
"visibility"
],
"properties": {
"code": {
"type": [
"string",
"null"
],
"example": "my code",
"description": "Optional external code/reference for the interview."
},
"name": {
"type": "string",
"example": "Project manager",
"minLength": 1,
"description": "Interview / position name."
},
"tags": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"interview-practice",
"sales"
],
"description": "Free-form tags stored on the interview. Tags are also the coaching-catalogue mapping key: a catalogue directory (see the catalogue-tag-create / catalogue-tag-update endpoints) lists a coaching or persona session when the session's tags contain EVERY tag in that directory's `tags_interview_set_filter`. Only `active` sessions with visibility `public` or `merchant_public` are listed."
},
"type": {
"enum": [
"interview",
"coaching",
"assessment"
],
"type": "string",
"example": "interview",
"description": "Product type of the interview. Options — `interview`: Standard candidate interview for a role — answers are AI-scored and produce a hiring recommendation. | `coaching`: Practice/coaching session — candidate-facing feedback to help them improve; not a hiring evaluation. Only available on the coaching portal, NOT the interview portal. | `assessment`: Skills/knowledge assessment — evaluates competencies and is scored like an interview.."
},
"status": {
"enum": [
"draft",
"active"
],
"type": "string",
"example": "active",
"description": "Lifecycle status of the interview. Options — `draft`: Created but not published — not visible to candidates and cannot be run yet. Use to stage an interview before going live. | `active`: Published and live — candidates can run it.."
},
"location": {
"type": "string",
"example": "remote",
"minLength": 1,
"description": "Job location — a city/country, or `remote`. Required and must not be empty: when the job description gives no location, pass `Not specified`."
},
"recording": {
"enum": [
"audio_first_5_answers",
"audio_all",
"video_all",
"video_first_5_answers",
null
],
"type": [
"string",
"null"
],
"example": "video_all",
"description": "Cheating/proctoring detection mode for candidate answers — this is NOT a full session recording. Video options also record the candidate. Omit/null to disable. Options — `audio_first_5_answers`: Audio-only cheating detection, first 5 answers only. | `audio_all`: Audio-only cheating detection on every answer. | `video_all`: Audio + video cheating detection on every answer (candidate is recorded for all answers). | `video_first_5_answers`: Audio + video cheating detection, first 5 answers only.."
},
"visibility": {
"enum": [
"merchant_public",
"merchant_invite",
"merchant_unlisted"
],
"type": "string",
"example": "merchant_public",
"description": "Who can discover and access the interview. Options — `merchant_public`: Listed on the merchant's public interview list — anyone with the merchant link can find and start it. | `merchant_invite`: Invite-only — only candidates explicitly invited (by email/link) can access it; not listed anywhere. | `merchant_unlisted`: Reachable only via a direct link — not listed anywhere; share the link manually.."
},
"description": {
"type": [
"string",
"null"
],
"example": "Backend role focused on Python, async APIs, and AWS.",
"description": "Short, two-sentence job description shown to the candidate. Provide it to use it as-is; leave it null/blank and it is AI-generated from the position name and any other context."
},
"environment": {
"enum": [
"production",
"uat",
"development",
"demo"
],
"type": "string",
"example": "production",
"description": "Which of your webhook environments results from this interview are delivered to. Defaults to production. Options — `production`: Live hiring. Results reach the webhooks configured as production. This is the default when the field is omitted. | `uat`: User-acceptance testing - an isolated environment for pre-release verification. | `development`: Development/testing. Use for interviews created by a test or preview app so their results never reach the production webhook. | `demo`: Demonstrations and sales trials.."
},
"is_embedded": {
"type": [
"boolean",
"null"
],
"example": false,
"description": "Set true when the interview will be embedded as an iframe on an external page. Provisions an embed key and returns embed_id / embed_signing_key, which are used to authenticate/sign the iframe embed. Where it runs depends on the type: interviews, assessments and interview role-plays embed on the interview portal (`https://<interview portal domain>/embed?embed_id=...`); coaching sessions and coaching personas embed on the consumer (coaching) portal (`https://<coaching portal domain>/embed?embed_id=...`, a link built on the interview portal is forwarded there). A signed coaching embed (name, email, signature) signs the visitor in as a learner of that portal, and the session is charged to the merchant's interview credits on completion, not to the learner's coaching credits; it cannot start while the merchant has none."
},
"merchant_id": {
"type": [
"string",
"null"
],
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Merchant id. Admin / sub-merchant callers only; otherwise taken from your token."
},
"result_view": {
"enum": [
"none",
"minimal",
"minimal_with_score",
"advanced",
"full",
"full_expand_scores",
null
],
"type": [
"string",
"null"
],
"example": "full",
"description": "Result screen shown to the candidate after finishing. With any value other than `none`, the candidate sees a results screen where they can provide feedback, record an intro video and edit the transcript, and must then submit the result; the value sets how much score/result detail is shown. Options — `none`: No results screen at all — the interview is submitted immediately when the candidate finishes (no feedback, intro video, transcript edit or manual submit step). | `minimal`: Minimal results layout, no score shown. | `minimal_with_score`: Minimal results layout including the overall score. | `advanced`: Advanced results layout with more detail. | `full`: Full results layout with all sections. | `full_expand_scores`: Full results with every score breakdown expanded.."
},
"max_duration": {
"type": [
"number",
"null"
],
"example": 1200,
"description": "Maximum interview duration in seconds. Scopes how many questions are generated (see interview_length) and is stored on the interview as the live session limit and the basis for the credit multiplier. Defaults to 1200 (20 minutes) when omitted."
},
"max_followups": {
"type": [
"integer",
"null"
],
"example": 2,
"maximum": 999,
"minimum": 0,
"description": "Maximum number of AI follow-up questions. 0 disables follow-ups; presets are 0-3 (none/low/normal/high) and custom values start at 4; null uses the template default (Normal)."
},
"custom_scoring": {
"type": [
"object",
"null"
],
"description": "Custom result-scoring overrides merged with defaults and template overrides.",
"additionalProperties": {}
},
"interview_tone": {
"enum": [
"relaxed",
"simple",
"professional",
"persuasive",
"exact",
null
],
"type": [
"string",
"null"
],
"example": "professional",
"description": "Interview tone — configures the AI avatar's speaking style and the tone of the AI-generated questions and follow-ups. Case-insensitive; omit to default to relaxed. Options — `relaxed`: Friendly and conversational tone that helps candidates feel at ease. | `simple`: Plain language at CEFR A2 level — short sentences and simple words. | `professional`: Formal and business-like approach suitable for senior roles. | `persuasive`: Engaging style that encourages candidates to elaborate. | `exact`: Asks the questions exactly as provided, without rephrasing — for interviews built from your own questions (create_interview_from_questions) where the wording is a script.."
},
"interview_type": {
"enum": [
"pre-screening",
"pre-screening-with-test-questions",
"second-interview",
"remote-freelancer-verification",
"strength-based-interview",
"potential-based-interview",
"process-verification-from-knowledge-base",
"knockout-screening"
],
"type": "string",
"example": "pre-screening-with-test-questions",
"description": "Interview style — configures the AI avatar and shapes both the AI-generated questions and the follow-up questions asked during the interview. Defaults to pre-screening when omitted. Options — `pre-screening`: Pre-screening — quick qualification check focusing on basic requirements and availability. | `pre-screening-with-test-questions`: Pre-screening with test questions — pre-screening plus practical questions to test relevant skills. | `second-interview`: Second round interview — deeper dive for candidates who passed initial screening. | `remote-freelancer-verification`: Remote worker verification — verify remote work capabilities and communication skills. | `strength-based-interview`: Strength-based interview — focus on what candidates enjoy and excel at to predict job satisfaction. | `potential-based-interview`: Potential-based interview — assess learning ability and growth potential rather than past experience. | `process-verification-from-knowledge-base`: Knowledge Base interview — generate questions from your knowledge base documents. | `knockout-screening`: Knock-out screening — a short (about 5 minute) check of the role's must-haves with closed questions; ends early, politely, when a must-have is clearly not met. On creation it defaults custom_scoring.early_stop to true (where the avatar supports early stop) and max_followups to 1 unless you send them.."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"example": "https://example.com/cover.png",
"description": "Cover image URL."
},
"seniority_level": {
"enum": [
"entry-level",
"intermediate",
"senior",
"managerial",
"director",
"executive",
null
],
"type": [
"string",
"null"
],
"example": "senior",
"description": "Target seniority level for the role; auto-detected from the job description when omitted. Options — `entry-level`: Early-career or graduate roles. | `intermediate`: Some experience required. | `senior`: Experienced professional. | `managerial`: Team or department lead. | `director`: Director-level responsibility. | `executive`: C-suite or executive role.."
},
"welcome_message": {
"type": [
"string",
"null"
],
"description": "Custom welcome message shown to the candidate."
},
"description_long": {
"type": [
"string",
"null"
],
"description": "Full job description in Markdown (Job Purpose, Responsibilities, Required & Preferred Qualifications). Provide it to use it as-is; leave it null/blank and it is AI-generated (interview and assessment types only). Rendered as Markdown on the candidate-facing position page, including chips, callouts, cards, columns and buttons — formatting guide: https://developer.jobmojito.com/cookbooks/format-content-with-markdown"
},
"interview_length": {
"anyOf": [
{
"type": "number"
},
{
"type": "string"
},
{}
],
"example": 8,
"description": "Number of questions to generate (1-40). Also capped by max_duration, which allows one question per 2 minutes: 20 min -> 10 questions, 30 min -> 15, 45 min -> 22, 60 min -> 30, 80 min -> 40. Asking for more than the cap is not an error — you get the cap, and the response reports the real count in questions_generated. Omit this field to let the AI pick 5-8."
},
"interview_salary": {
"type": [
"string",
"null"
],
"example": "$80k - $100k",
"description": "Salary range shown for the position."
},
"thank_you_message": {
"type": [
"string",
"null"
],
"description": "Custom thank-you message shown after the interview."
},
"additional_context": {
"type": [
"object",
"null"
],
"description": "Arbitrary additional context object merged into AI generation.",
"additionalProperties": {}
},
"hiring_for_company": {
"type": [
"object",
"null"
],
"example": {
"name": "undisclosed"
},
"properties": {
"name": {
"type": [
"string",
"null"
],
"example": "Unimo Enterprises",
"description": "End-employer name. Omit/null when hiring for yourself, 'undisclosed' for an unnamed external client, or the client's company name."
},
"sector": {
"type": [
"string",
"null"
],
"example": "Logistics and Supply Chain",
"description": "Industry / sector of the end employer."
},
"location": {
"type": [
"string",
"null"
],
"example": "Sri Lanka",
"description": "Primary location of the end employer."
},
"description": {
"type": [
"string",
"null"
],
"example": "Unimo Enterprises is a leading logistics and supply chain solutions provider.",
"description": "Short description of the end employer, used as background context by the agent."
},
"company_size": {
"type": [
"string",
"null"
],
"example": "100-200",
"description": "Approximate headcount of the end employer."
}
},
"description": "Who the position is really for. Omit/null (or an object with name null/blank) when hiring for yourself; { name: 'undisclosed' } for an unnamed external client; or { name: '<company>' } plus optional description/location/sector/company_size for a named client. Stored in creation_parameters.hiring_for_company."
},
"interview_attempts": {
"type": [
"number",
"null"
],
"example": 1,
"maximum": 20,
"minimum": 1,
"description": "Allowed candidate attempts (1-20)."
},
"instructional_video": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "Show an instructional video before approval. Defaults to false."
},
"interview_department": {
"type": [
"string",
"null"
],
"example": "Engineering",
"description": "Department the position belongs to."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi"
],
"type": "string",
"example": "en",
"description": "Platform language code used for the interview. Must be one of the platform-languages.json codes."
},
"recruiter_profile_id": {
"type": [
"string",
"null"
],
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Profile id of the recruiter owning this interview. Must be a merchant/merchant_owner/admin profile of the same merchant."
},
"interview_template_id": {
"type": "string",
"format": "uuid",
"example": "baa3bf7c-6926-46fd-9005-16738a31f72d",
"minLength": 1,
"description": "Id of the interview template to base this interview on."
},
"candidate_expectations": {
"type": [
"string",
"null"
],
"description": "Free-text candidate expectations folded into AI generation."
},
"include_closing_prompt": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "Include a closing prompt. Defaults to true."
},
"pdf_export_auto_config": {
"type": [
"object",
"null"
],
"properties": {
"files": {
"type": "boolean",
"description": "Include uploaded candidate files."
},
"template": {
"enum": [
"classic",
"modern",
"one_pager"
],
"type": "string",
"description": "Report layout: classic, modern, or one_pager. Omit for the merchant default (modern when unset)."
},
"analytics": {
"type": "boolean",
"description": "Include analytics."
},
"transcript": {
"type": "boolean",
"description": "Include the interview transcript."
},
"contact_details": {
"type": "boolean",
"description": "Include candidate contact details."
},
"answer_recording": {
"type": "boolean",
"description": "Include per-answer recordings."
},
"ai_scoring_rubric": {
"type": "boolean",
"description": "Include the AI scoring rubric."
},
"group_by_question": {
"type": "boolean",
"description": "Group transcript answers by expected question."
},
"session_recording": {
"type": "boolean",
"description": "Include the full-session recording."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi"
],
"type": "string",
"example": "en",
"description": "Report language code (a platform-languages.json code)."
},
"ai_recruiter_assessment": {
"type": "boolean",
"description": "Include the AI recruiter assessment."
}
},
"description": "Auto-generate a candidate PDF report with these options once the interview completes. null disables auto-export."
},
"recording_full_session": {
"enum": [
"audio_all",
"video_all",
null
],
"type": [
"string",
"null"
],
"example": "video_all",
"description": "Full interview-session recording (includes the avatar and voice) produced as a single file. Independent of `recording`. Omit/null to disable. Options — `audio_all`: Record the whole session audio (avatar + candidate voice) into a single file. Adds +0.2 credits. | `video_all`: Record the whole session video + audio (avatar + candidate) into a single file. Adds +0.4 credits.."
},
"required_pronunciation": {
"type": [
"boolean",
"null"
],
"example": false,
"description": "Require pronunciation assessment (restricts to pronunciation-capable languages). Defaults to false."
},
"knowledge_base_store_id": {
"type": [
"string",
"null"
],
"description": "Knowledge base store id to source additional context from. Documents limited to chosen interviews are left out, because a new interview is on no list yet."
},
"microphone_test_message": {
"type": [
"string",
"null"
],
"example": "Before we start, let's check your microphone. Say \"Hello, I'm ready.\"",
"description": "Custom microphone-test instruction for a recorded (offline avatar) interview, spoken by the avatar and shown on screen before the first question. Omitted uses the default prompt in the interview language."
},
"questions_random_subset": {
"type": [
"number",
"null"
],
"example": 0.5,
"maximum": 0.9,
"minimum": 0.01,
"description": "Ask only a random subset of the questions, expressed as a fraction between 0.01 and 0.9 (e.g. 0.5 = 50%). null asks all questions."
},
"include_rapport_question": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "Include an opening rapport question. Defaults to false."
},
"interview_available_till": {
"type": [
"string",
"null"
],
"example": "2026-12-31",
"description": "ISO date/time after which the interview is no longer available to candidates. null keeps it always available."
},
"use_enhanced_expectations": {
"type": [
"boolean",
"null"
],
"description": "Reserved flag passed through to generation."
},
"candidate_video_introduction": {
"enum": [
"optional",
"required",
null
],
"type": [
"string",
"null"
],
"example": "optional",
"description": "Whether a candidate video introduction is optional or required."
},
"interview_conversation_speed": {
"enum": [
"slower",
"normal",
"faster",
"fluent",
null
],
"type": [
"string",
"null"
],
"example": "normal",
"description": "Conversation pace of the AI avatar. Omit/null keeps the template default pace. Options — `slower`: The avatar speaks more slowly — easier to follow for non-native speakers. | `normal`: Default speaking pace. | `faster`: The avatar speaks more quickly for a snappier conversation. | `fluent`: Talking pace: the avatar answers 300 ms after the candidate stops speaking. A pause mid-sentence also ends their turn.."
},
"result_enable_edit_transcript": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "Allow editing the transcript on the result view. Defaults to true."
},
"instructional_video_custom_text": {
"type": [
"string",
"null"
],
"description": "Custom narration text for the instructional video."
},
"microphone_test_no_speech_message": {
"type": [
"string",
"null"
],
"example": "We can't hear you. Check your microphone and try again.",
"description": "Custom text shown when the microphone test hears nothing. Omitted uses the default text in the candidate's language."
}
}
}{
"type": "object",
"required": [
"interview_def_set_id",
"questions_generated",
"max_duration"
],
"properties": {
"embed_id": {
"type": [
"string",
"null"
],
"description": "Embed id, present only when is_embedded=true."
},
"max_duration": {
"type": [
"number",
"null"
],
"example": 1200,
"description": "Live session limit in seconds stored on the interview — the value sent, or the 1200 default when omitted."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
},
"embed_signing_key": {
"type": [
"string",
"null"
],
"description": "Embed signing key, present only when is_embedded=true."
},
"questions_generated": {
"type": [
"number",
"null"
],
"example": 8,
"description": "Number of questions actually generated. May be lower than the requested interview_length, which is capped by max_duration."
},
"interview_def_set_id": {
"type": [
"string",
"null"
],
"example": "00000000-0000-0000-0000-000000000000",
"description": "Id of the newly created interview definition set."
}
},
"description": "Id of the created interview, the number of questions generated and the stored max_duration. Includes embed_id/embed_signing_key when is_embedded=true.",
"x-fastmcp-top-level-schema": "JobInterviewCreateResponse"
}{
"title": "Create interview",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}create_interview_from_questions — Create interview from questions
[Interviews] Create a new interview from an explicit array of questions. The AI rephrases your wording by default; pass `interview_tone="exact"` to have each question asked verbatim, which is what you want when the wording is a script (compliance, translated, or legally reviewed text). Creates a new interview definition set from a caller-provided array of questions, builds its default and generated steps, optionally activates it, and optionally creates an embed key.
{
"type": "object",
"required": [
"name",
"location",
"interview_template_id",
"mojito_language_code",
"description",
"status",
"type",
"visibility",
"questions"
],
"properties": {
"code": {
"type": [
"string",
"null"
],
"description": "Optional external code/reference."
},
"name": {
"type": "string",
"example": "Project manager",
"minLength": 1,
"description": "Interview/position name."
},
"tags": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"interview-practice",
"sales"
],
"description": "Free-form tags stored on the interview. Tags are also the coaching-catalogue mapping key: a catalogue directory (see the catalogue-tag-create / catalogue-tag-update endpoints) lists a coaching or persona session when the session's tags contain EVERY tag in that directory's `tags_interview_set_filter`. Only `active` sessions with visibility `public` or `merchant_public` are listed."
},
"type": {
"enum": [
"interview",
"coaching",
"assessment"
],
"type": "string",
"example": "interview",
"description": "Product type of the interview. Options — `interview`: Standard candidate interview for a role — answers are AI-scored and produce a hiring recommendation. | `coaching`: Practice/coaching session — candidate-facing feedback to help them improve; not a hiring evaluation. Only available on the coaching portal, NOT the interview portal. | `assessment`: Skills/knowledge assessment — evaluates competencies and is scored like an interview.."
},
"status": {
"enum": [
"draft",
"active"
],
"type": "string",
"example": "active",
"description": "Lifecycle status of the interview. Options — `draft`: Created but not published — not visible to candidates and cannot be run yet. Use to stage an interview before going live. | `active`: Published and live — candidates can run it.."
},
"location": {
"type": "string",
"example": "remote",
"minLength": 1,
"description": "Job location — a city/country, or `remote`. Required and must not be empty: when the job description gives no location, pass `Not specified`."
},
"questions": {
"type": "array",
"items": {
"type": "object",
"required": [
"question"
],
"properties": {
"id": {
"type": "string",
"description": "Identifier for this question. job-interview-get returns the question's real id here; send it back to job-interview-update so an unchanged question keeps its existing record (and with it its answer rules and any rendered avatar video). Also the handle another question references via conditional_question_main_id. On job-interview-create-from-array it is a caller-local value, only needed for those references."
},
"label": {
"type": [
"string",
"null"
],
"description": "Optional label/tag stored on the question."
},
"duration": {
"type": [
"number",
"null"
],
"description": "Answer duration in seconds for this question."
},
"question": {
"type": "string",
"description": "The question text shown to the candidate."
},
"is_expert": {
"type": [
"boolean",
"null"
],
"description": "Expert listening question (view 'with listening expert')."
},
"external_id": {
"type": [
"string",
"null"
],
"description": "External identifier stored on the question. job-interview-update matches on this first, so an ATS that owns stable ids can send its own array and have the diff line up without round-tripping our ids."
},
"is_skippable": {
"type": [
"boolean",
"null"
],
"example": false,
"description": "The candidate may skip this question: the recorded (offline avatar) interview shows a Skip button on it, and a skipped question is saved without an answer. Defaults to false."
},
"external_data": {
"type": [
"object",
"null"
],
"description": "Arbitrary JSON metadata stored on the question.",
"additionalProperties": {}
},
"is_conditional": {
"type": [
"boolean",
"null"
],
"description": "A yes/no question that decides which follow-ups are asked (view 'with listening conditional'). Its follow-ups point at it with conditional_question_main_id."
},
"knowledge_base_id": {
"type": [
"string",
"null"
],
"description": "Knowledge-base store id (uuid) the question draws context from."
},
"is_multiple_choice": {
"type": [
"boolean",
"null"
],
"description": "Multiple-choice question (view 'multiple choice')."
},
"is_without_scoring": {
"type": [
"boolean",
"null"
],
"description": "Question is asked but not scored (view 'without scoring')."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi",
null
],
"type": [
"string",
"null"
],
"example": "en",
"description": "Per-question language override (one of the platform-languages.json codes). Inherits the interview language when omitted."
},
"question_alternatives": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Alternative phrasings for the question."
},
"candidate_expectations_json": {
"type": [
"object",
"null"
],
"properties": {
"weak": {
"type": "array",
"items": {
"type": "string"
},
"description": "Baseline requirements every viable candidate should meet (table stakes)."
},
"strong": {
"type": "array",
"items": {
"type": "string"
},
"description": "High-bar requirements only standout candidates clear."
},
"moderate": {
"type": "array",
"items": {
"type": "string"
},
"description": "Requirements expected of a solid, competent candidate."
}
},
"description": "Per-question candidate expectations, bucketed by requirement level (weak/moderate/strong). Extra keys are preserved."
},
"conditional_question_answer": {
"type": [
"boolean",
"null"
],
"example": false,
"description": "With conditional_question_main_id: the answer to the yes/no question that shows this follow-up. `true` (the default) asks it after a yes, `false` after a no, so each answer can lead to its own follow-ups. Ignored on a question without conditional_question_main_id."
},
"conditional_question_main_id": {
"type": [
"string",
"null"
],
"description": "For a follow-up question, the id (the \"id\" field above) of the yes/no question (is_conditional) in this same array that decides whether it is asked. The yes/no question must appear earlier in the array. Use conditional_question_answer to choose the answer that shows it."
},
"is_candidate_asking_recruiter": {
"type": [
"boolean",
"null"
],
"description": "Candidate-asks-recruiter prompt (view 'candidate asking recruiter')."
}
},
"additionalProperties": {}
},
"minItems": 1,
"description": "Ordered list of interview questions to create as steps."
},
"recording": {
"enum": [
"audio_first_5_answers",
"audio_all",
"video_all",
"video_first_5_answers",
null
],
"type": [
"string",
"null"
],
"example": "video_all",
"description": "Cheating/proctoring detection mode for candidate answers — this is NOT a full session recording. Video options also record the candidate. Omit/null to disable. Options — `audio_first_5_answers`: Audio-only cheating detection, first 5 answers only. | `audio_all`: Audio-only cheating detection on every answer. | `video_all`: Audio + video cheating detection on every answer (candidate is recorded for all answers). | `video_first_5_answers`: Audio + video cheating detection, first 5 answers only.."
},
"visibility": {
"enum": [
"merchant_public",
"merchant_invite",
"merchant_unlisted"
],
"type": "string",
"example": "merchant_public",
"description": "Who can discover and access the interview. Options — `merchant_public`: Listed on the merchant's public interview list — anyone with the merchant link can find and start it. | `merchant_invite`: Invite-only — only candidates explicitly invited (by email/link) can access it; not listed anywhere. | `merchant_unlisted`: Reachable only via a direct link — not listed anywhere; share the link manually.."
},
"description": {
"type": "string",
"example": "Project manager role",
"minLength": 1,
"description": "Short interview description."
},
"environment": {
"enum": [
"production",
"uat",
"development",
"demo"
],
"type": "string",
"example": "production",
"description": "Which of your webhook environments results from this interview are delivered to. Defaults to production. Options — `production`: Live hiring. Results reach the webhooks configured as production. This is the default when the field is omitted. | `uat`: User-acceptance testing - an isolated environment for pre-release verification. | `development`: Development/testing. Use for interviews created by a test or preview app so their results never reach the production webhook. | `demo`: Demonstrations and sales trials.."
},
"is_embedded": {
"type": [
"boolean",
"null"
],
"description": "Set true when the interview will be embedded as an iframe on an external page. Creates an embed key and returns embed_id/embed_signing_key, used to authenticate/sign the iframe embed."
},
"merchant_id": {
"type": [
"string",
"null"
],
"description": "Target merchant id (admins / sub-merchant only)."
},
"result_view": {
"enum": [
"none",
"minimal",
"minimal_with_score",
"advanced",
"full",
"full_expand_scores",
null
],
"type": [
"string",
"null"
],
"example": "full",
"description": "Result screen shown to the candidate after finishing. With any value other than `none`, the candidate sees a results screen where they can provide feedback, record an intro video and edit the transcript, and must then submit the result; the value sets how much score/result detail is shown. Options — `none`: No results screen at all — the interview is submitted immediately when the candidate finishes (no feedback, intro video, transcript edit or manual submit step). | `minimal`: Minimal results layout, no score shown. | `minimal_with_score`: Minimal results layout including the overall score. | `advanced`: Advanced results layout with more detail. | `full`: Full results layout with all sections. | `full_expand_scores`: Full results with every score breakdown expanded.."
},
"max_followups": {
"type": [
"integer",
"null"
],
"example": 2,
"maximum": 999,
"minimum": 0,
"description": "Maximum number of AI follow-up questions. 0 disables follow-ups; presets are 0-3 (none/low/normal/high) and custom values start at 4; null uses the template default (Normal)."
},
"custom_scoring": {
"type": [
"object",
"null"
],
"description": "Custom scoring overrides merged with defaults.",
"additionalProperties": {}
},
"interview_tone": {
"enum": [
"relaxed",
"simple",
"professional",
"persuasive",
"exact",
null
],
"type": [
"string",
"null"
],
"example": "professional",
"description": "Tone — configures the AI avatar's speaking style and the follow-up questions it generates; the base `questions` you supply are not affected. Case-insensitive; omit to default to relaxed. Options — `relaxed`: Friendly and conversational tone that helps candidates feel at ease. | `simple`: Plain language at CEFR A2 level — short sentences and simple words. | `professional`: Formal and business-like approach suitable for senior roles. | `persuasive`: Engaging style that encourages candidates to elaborate. | `exact`: Asks the questions exactly as provided, without rephrasing — for interviews built from your own questions (create_interview_from_questions) where the wording is a script.."
},
"interview_type": {
"enum": [
"pre-screening",
"pre-screening-with-test-questions",
"second-interview",
"remote-freelancer-verification",
"strength-based-interview",
"potential-based-interview",
"process-verification-from-knowledge-base",
"knockout-screening",
null
],
"type": [
"string",
"null"
],
"example": "pre-screening-with-test-questions",
"description": "Interview style — configures the AI avatar and the follow-up questions it generates during the interview. The base `questions` you supply are used as-is and are NOT affected by this setting. Options — `pre-screening`: Pre-screening — quick qualification check focusing on basic requirements and availability. | `pre-screening-with-test-questions`: Pre-screening with test questions — pre-screening plus practical questions to test relevant skills. | `second-interview`: Second round interview — deeper dive for candidates who passed initial screening. | `remote-freelancer-verification`: Remote worker verification — verify remote work capabilities and communication skills. | `strength-based-interview`: Strength-based interview — focus on what candidates enjoy and excel at to predict job satisfaction. | `potential-based-interview`: Potential-based interview — assess learning ability and growth potential rather than past experience. | `process-verification-from-knowledge-base`: Knowledge Base interview — generate questions from your knowledge base documents. | `knockout-screening`: Knock-out screening — a short (about 5 minute) check of the role's must-haves with closed questions; ends early, politely, when a must-have is clearly not met. On creation it defaults custom_scoring.early_stop to true (where the avatar supports early stop) and max_followups to 1 unless you send them.."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"description": "Cover image URL."
},
"seniority_level": {
"enum": [
"entry-level",
"intermediate",
"senior",
"managerial",
"director",
"executive",
null
],
"type": [
"string",
"null"
],
"example": "senior",
"description": "Target seniority level for the role; auto-detected from the job description when omitted. Options — `entry-level`: Early-career or graduate roles. | `intermediate`: Some experience required. | `senior`: Experienced professional. | `managerial`: Team or department lead. | `director`: Director-level responsibility. | `executive`: C-suite or executive role.."
},
"welcome_message": {
"type": [
"string",
"null"
],
"description": "Custom welcome message."
},
"description_long": {
"type": [
"string",
"null"
],
"description": "Long-form interview description. Rendered as Markdown on the candidate-facing position page, including chips, callouts, cards, columns and buttons — formatting guide: https://developer.jobmojito.com/cookbooks/format-content-with-markdown"
},
"interview_salary": {
"type": [
"string",
"null"
],
"example": "$80k - $100k",
"description": "Salary range shown for the position."
},
"thank_you_message": {
"type": [
"string",
"null"
],
"description": "Custom thank-you message."
},
"additional_context": {
"type": [
"object",
"null"
],
"description": "Extra context forwarded to expectation generation.",
"additionalProperties": {}
},
"hiring_for_company": {
"type": [
"object",
"null"
],
"example": {
"name": "undisclosed"
},
"properties": {
"name": {
"type": [
"string",
"null"
],
"example": "Unimo Enterprises",
"description": "End-employer name. Omit/null when hiring for yourself, 'undisclosed' for an unnamed external client, or the client's company name."
},
"sector": {
"type": [
"string",
"null"
],
"example": "Logistics and Supply Chain",
"description": "Industry / sector of the end employer."
},
"location": {
"type": [
"string",
"null"
],
"example": "Sri Lanka",
"description": "Primary location of the end employer."
},
"description": {
"type": [
"string",
"null"
],
"example": "Unimo Enterprises is a leading logistics and supply chain solutions provider.",
"description": "Short description of the end employer, used as background context by the agent."
},
"company_size": {
"type": [
"string",
"null"
],
"example": "100-200",
"description": "Approximate headcount of the end employer."
}
},
"description": "Who the position is really for. Omit/null (or an object with name null/blank) when hiring for yourself; { name: 'undisclosed' } for an unnamed external client; or { name: '<company>' } plus optional description/location/sector/company_size for a named client. Stored in creation_parameters.hiring_for_company."
},
"interview_attempts": {
"type": [
"number",
"null"
],
"example": 1,
"description": "Allowed attempts, 1-20."
},
"instructional_video": {
"type": [
"boolean",
"null"
],
"description": "Enable an instructional video before approval."
},
"interview_department": {
"type": [
"string",
"null"
],
"example": "Engineering",
"description": "Department the position belongs to."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi"
],
"type": "string",
"example": "en",
"description": "Platform language code (one of the platform-languages.json codes); must also resolve to a supported language with an Azure speech mapping."
},
"recruiter_profile_id": {
"type": [
"string",
"null"
],
"description": "Profile id of the recruiter owning this interview. Must be a merchant/merchant_owner/admin profile of the same merchant."
},
"disable_deduplication": {
"type": [
"boolean",
"null"
],
"description": "When true, skip step deduplication on insert."
},
"interview_template_id": {
"type": "string",
"example": "46b98d37-1557-4391-beca-03037ead19f2",
"minLength": 1,
"description": "Id of the interview template to use. Must reference an existing interview_templates row."
},
"candidate_expectations": {
"type": [
"string",
"null"
],
"description": "Free-text candidate expectations."
},
"pdf_export_auto_config": {
"type": [
"object",
"null"
],
"properties": {
"files": {
"type": "boolean",
"description": "Include uploaded candidate files."
},
"template": {
"enum": [
"classic",
"modern",
"one_pager"
],
"type": "string",
"description": "Report layout: classic, modern, or one_pager. Omit for the merchant default (modern when unset)."
},
"analytics": {
"type": "boolean",
"description": "Include analytics."
},
"transcript": {
"type": "boolean",
"description": "Include the interview transcript."
},
"contact_details": {
"type": "boolean",
"description": "Include candidate contact details."
},
"answer_recording": {
"type": "boolean",
"description": "Include per-answer recordings."
},
"ai_scoring_rubric": {
"type": "boolean",
"description": "Include the AI scoring rubric."
},
"group_by_question": {
"type": "boolean",
"description": "Group transcript answers by expected question."
},
"session_recording": {
"type": "boolean",
"description": "Include the full-session recording."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi"
],
"type": "string",
"example": "en",
"description": "Report language code (a platform-languages.json code)."
},
"ai_recruiter_assessment": {
"type": "boolean",
"description": "Include the AI recruiter assessment."
}
},
"description": "Auto-generate a candidate PDF report with these options once the interview completes. null disables auto-export."
},
"recording_full_session": {
"enum": [
"audio_all",
"video_all",
null
],
"type": [
"string",
"null"
],
"example": "video_all",
"description": "Full interview-session recording (includes the avatar and voice) produced as a single file. Independent of `recording`. Omit/null to disable. Options — `audio_all`: Record the whole session audio (avatar + candidate voice) into a single file. Adds +0.2 credits. | `video_all`: Record the whole session video + audio (avatar + candidate) into a single file. Adds +0.4 credits.."
},
"required_pronunciation": {
"type": [
"boolean",
"null"
],
"example": false,
"description": "Require pronunciation assessment (restricts to pronunciation-capable languages). Defaults to false."
},
"knowledge_base_store_id": {
"type": [
"string",
"null"
],
"description": "Optional knowledge base store id; validated for existence."
},
"microphone_test_message": {
"type": [
"string",
"null"
],
"example": "Before we start, let's check your microphone. Say \"Hello, I'm ready.\"",
"description": "Custom microphone-test instruction for a recorded (offline avatar) interview, spoken by the avatar and shown on screen before the first question. Omitted uses the default prompt in the interview language."
},
"questions_random_subset": {
"type": [
"number",
"null"
],
"description": "Fraction of questions to randomly ask, between 0.01 and 0.9."
},
"interview_available_till": {
"type": [
"string",
"null"
],
"example": "2026-12-31",
"description": "ISO date/time after which the interview is no longer available to candidates. null keeps it always available."
},
"candidate_expectations_json": {
"type": [
"object",
"null"
],
"properties": {
"weak": {
"type": "array",
"items": {
"type": "string"
},
"description": "Baseline requirements every viable candidate should meet (table stakes)."
},
"strong": {
"type": "array",
"items": {
"type": "string"
},
"description": "High-bar requirements only standout candidates clear."
},
"moderate": {
"type": "array",
"items": {
"type": "string"
},
"description": "Requirements expected of a solid, competent candidate."
}
},
"description": "Pre-generated candidate expectations, bucketed by requirement level (weak/moderate/strong); auto-generated when omitted for type=interview. Extra keys are preserved."
},
"candidate_video_introduction": {
"enum": [
"optional",
"required",
null
],
"type": [
"string",
"null"
],
"description": "Whether a candidate video introduction is optional or required."
},
"interview_conversation_speed": {
"enum": [
"slower",
"normal",
"faster",
"fluent",
null
],
"type": [
"string",
"null"
],
"example": "normal",
"description": "Conversation pace of the AI avatar. Omit/null keeps the template default pace. Options — `slower`: The avatar speaks more slowly — easier to follow for non-native speakers. | `normal`: Default speaking pace. | `faster`: The avatar speaks more quickly for a snappier conversation. | `fluent`: Talking pace: the avatar answers 300 ms after the candidate stops speaking. A pause mid-sentence also ends their turn.."
},
"result_enable_edit_transcript": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "Allow editing the transcript on the result view. Defaults to true."
},
"instructional_video_custom_text": {
"type": [
"string",
"null"
],
"description": "Custom text for the instructional video."
},
"microphone_test_no_speech_message": {
"type": [
"string",
"null"
],
"example": "We can't hear you. Check your microphone and try again.",
"description": "Custom text shown when the microphone test hears nothing. Omitted uses the default text in the candidate's language."
}
}
}{
"type": "object",
"required": [
"interview_def_set_id"
],
"properties": {
"embed_id": {
"type": [
"string",
"null"
],
"description": "Embed id, present only when is_embedded=true."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
},
"embed_signing_key": {
"type": [
"string",
"null"
],
"description": "Embed signing key, present only when is_embedded=true."
},
"interview_def_set_id": {
"type": [
"string",
"null"
],
"description": "Id of the newly created interview definition set."
}
},
"description": "The created interview definition set id, plus embed credentials when is_embedded=true.",
"x-fastmcp-top-level-schema": "JobInterviewCreateFromArrayResponse"
}{
"title": "Create interview from questions",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}create_persona — Create role-play persona
[Interviews] Create a role-play persona: an avatar that plays a defined role in a free-form conversation instead of a scored Q&A interview. FIRST CHOOSE `portal`, because it selects between two different products: `portal="interview"` builds a SIMULATED PERSONA a recruiter invites candidates to — scored, billed to merchant credits, and listed with the recruiter's other results; `portal="coaching"` (THE DEFAULT) builds a coaching persona learners start themselves from the catalogue, paid per second from their own coaching minutes and never visible to recruiters. Pass `portal="interview"` explicitly for any hiring, screening or assessment use. Then set `persona_role_avatar`/`persona_role_user` for the roles and `opening_line` for the avatar's first spoken line (defaults to a generic 'Hello'). FIRST DECIDE `portal`. This endpoint creates two different products and the default is NOT the recruiter one: - `portal: "interview"` — an INTERVIEW ROLE-PLAY. Use this whenever the goal is to ASSESS or SCREEN candidates: hiring, assessments, sales role-plays for job applicants, anything a recruiter runs. Candidates are invited through the normal invitation flow, results appear in the recruiter's result list, it is billed against merchant credits, and attempts are capped via `interview_attempts` — exactly like an interview. - `portal: "coaching"` (THE DEFAULT) — a coaching persona for practice/training on the coaching portal. Consumed against the mentee's own coaching credits, self-started from the catalogue, and its results are NOT visible to recruiters. Omitting `portal` gives you this one, so pass `portal: "interview"` explicitly for any recruiting/assessment use case. The conversation itself behaves identically in both. The avatar plays a defined role in a free-form conversation instead of running a scored Q&A interview. No AI question/description generation runs; the persona role fields ARE the configuration. The session runs as: a welcome message → the avatar's OPENING LINE (the first thing it says, set via `opening_line`) → the candidate replies and the free-form role-play begins → a closing message. Because there is no question list, `opening_line` is how the avatar starts the scene — set it to a concrete in-character line; if omitted it defaults to a generic "Hello". Set `welcome_message` and `thank_you_message` too — omitting them leaves the generic platform defaults. Also set `candidate_expectations`: it is the yardstick the session is scored against. The four avatar prompts divide up as: `persona_avatar_who_is` (identity and what drives it), `persona_avatar_knowledge` (the private facts it may use), `persona_avatar_progress` (how the conversation is allowed to move forward, and what gates the later personal details), and `persona_avatar_end_conditions` (when to stop). Without `persona_avatar_progress` the avatar has no defined arc and tends to either concede immediately or never concede at all. Provisions the default conversational steps and optionally an embed key.
{
"type": "object",
"required": [
"name",
"interview_template_id",
"mojito_language_code",
"status",
"visibility",
"persona_role_avatar",
"persona_role_user"
],
"properties": {
"code": {
"type": [
"string",
"null"
],
"example": "my code",
"description": "Optional external code/reference for the persona."
},
"name": {
"type": "string",
"example": "Difficult customer role-play",
"minLength": 1,
"description": "Persona / session name."
},
"tags": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"interview-practice",
"sales"
],
"description": "Free-form tags stored on the persona. Tags are also the coaching-catalogue mapping key: a catalogue directory (see the catalogue-tag-create / catalogue-tag-update endpoints) lists a coaching or persona session when the session's tags contain EVERY tag in that directory's `tags_interview_set_filter`. Only `active` sessions with visibility `public` or `merchant_public` are listed."
},
"portal": {
"enum": [
"coaching",
"interview",
null
],
"type": [
"string",
"null"
],
"example": "interview",
"description": "REQUIRED IN PRACTICE — pick deliberately; the default is the coaching product, not the recruiting one. `interview`: an interview role-play. Choose this for ANY recruiting or assessment use case (screening candidates, hiring, sales role-plays for applicants). Candidates are invited through the normal invitation flow, results are visible to the recruiter alongside ordinary interview results, it is billed against your merchant credits on the same basis as an interview, and attempts are capped (see `interview_attempts`). | `coaching` (DEFAULT when omitted): the classic coaching persona for practice/training. Runs on the coaching portal, is consumed against the mentee's own coaching credits, is started by the mentee from the catalogue or a shared link, and its results are NOT visible to recruiters. The conversation itself behaves identically in both cases — only the portal, billing, visibility and attempt limits differ."
},
"status": {
"enum": [
"draft",
"active"
],
"type": "string",
"example": "active",
"description": "Lifecycle status of the interview. Options — `draft`: Created but not published — not visible to candidates and cannot be run yet. Use to stage an interview before going live. | `active`: Published and live — candidates can run it.."
},
"recording": {
"enum": [
"audio_first_5_answers",
"audio_all",
"video_all",
"video_first_5_answers",
null
],
"type": [
"string",
"null"
],
"example": "video_all",
"description": "Cheating/proctoring detection mode for candidate answers — this is NOT a full session recording. Video options also record the candidate. Omit/null to disable. Options — `audio_first_5_answers`: Audio-only cheating detection, first 5 answers only. | `audio_all`: Audio-only cheating detection on every answer. | `video_all`: Audio + video cheating detection on every answer (candidate is recorded for all answers). | `video_first_5_answers`: Audio + video cheating detection, first 5 answers only.."
},
"visibility": {
"enum": [
"merchant_public",
"merchant_invite",
"merchant_unlisted"
],
"type": "string",
"example": "merchant_public",
"description": "Who can discover and access the interview. Options — `merchant_public`: Listed on the merchant's public interview list — anyone with the merchant link can find and start it. | `merchant_invite`: Invite-only — only candidates explicitly invited (by email/link) can access it; not listed anywhere. | `merchant_unlisted`: Reachable only via a direct link — not listed anywhere; share the link manually.."
},
"description": {
"type": [
"string",
"null"
],
"example": "A quick sales role-play with an unhappy customer. Your goal is to resolve their complaint and keep the account.",
"description": "Short persona description shown to the candidate on the pre-session poster. Candidate-visible — keep it to max 2 sentences. Unlike job-interview-create, personas run no AI generation, so this is never auto-generated: leave it null/blank and the poster simply shows no description; set it to frame the scene."
},
"environment": {
"enum": [
"production",
"uat",
"development",
"demo"
],
"type": "string",
"example": "production",
"description": "Which of your webhook environments results from this interview are delivered to. Defaults to production. Options — `production`: Live hiring. Results reach the webhooks configured as production. This is the default when the field is omitted. | `uat`: User-acceptance testing - an isolated environment for pre-release verification. | `development`: Development/testing. Use for interviews created by a test or preview app so their results never reach the production webhook. | `demo`: Demonstrations and sales trials.."
},
"is_embedded": {
"type": [
"boolean",
"null"
],
"example": false,
"description": "Set true when the persona will be embedded as an iframe on an external page. Provisions an embed key and returns embed_id / embed_signing_key, used to sign the iframe link. Where it runs depends on the type: interviews, assessments and interview role-plays embed on the interview portal (`https://<interview portal domain>/embed?embed_id=...`); coaching sessions and coaching personas embed on the consumer (coaching) portal (`https://<coaching portal domain>/embed?embed_id=...`, a link built on the interview portal is forwarded there). A signed coaching embed (name, email, signature) signs the visitor in as a learner of that portal, and the session is charged to the merchant's interview credits on completion, not to the learner's coaching credits; it cannot start while the merchant has none."
},
"merchant_id": {
"type": [
"string",
"null"
],
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Merchant id. Admin / sub-merchant callers only; otherwise taken from your token."
},
"result_view": {
"enum": [
"none",
"minimal",
"minimal_with_score",
"advanced",
"full",
"full_expand_scores",
null
],
"type": [
"string",
"null"
],
"example": "full",
"description": "Result screen shown to the candidate after finishing. With any value other than `none`, the candidate sees a results screen where they can provide feedback, record an intro video and edit the transcript, and must then submit the result; the value sets how much score/result detail is shown. Options — `none`: No results screen at all — the interview is submitted immediately when the candidate finishes (no feedback, intro video, transcript edit or manual submit step). | `minimal`: Minimal results layout, no score shown. | `minimal_with_score`: Minimal results layout including the overall score. | `advanced`: Advanced results layout with more detail. | `full`: Full results layout with all sections. | `full_expand_scores`: Full results with every score breakdown expanded.."
},
"max_duration": {
"type": [
"number",
"null"
],
"example": 1200,
"description": "Maximum conversation duration in seconds. Defaults to 1200 (20 min) when omitted."
},
"opening_line": {
"type": [
"string",
"null"
],
"example": "Oh, finally — someone actually comes over! I've been waiting for ages. Are you going to help me or not?",
"description": "The avatar's opening line — the FIRST thing it says out loud when the session starts, before the candidate has said anything. This is a literal spoken line, NOT a description: write the exact words the avatar should say, in character and consistent with persona_role_avatar. For an escalated scenario it should already convey that state (e.g. an angry customer opens angrily). If omitted, the platform inserts a generic default (\"Hello\"), which is usually a weak opener — set this for anything other than a neutral greeting."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"example": "https://example.com/cover.png",
"description": "Cover image URL."
},
"welcome_message": {
"type": [
"string",
"null"
],
"example": "Welcome! You are about to speak with a customer who is unhappy about a recent price increase.",
"description": "Custom welcome message spoken to the candidate before the role-play starts — set it; omitting it leaves a generic platform default. This is the scene-setting message; the avatar's first in-character line is `opening_line`, which comes after it."
},
"persona_role_user": {
"type": "string",
"example": "is to be a sales person trying to sell an additional product to the customer",
"minLength": 1,
"description": "The role the candidate (mentee) plays. Candidate-visible — shown on the pre-session poster as 'Your role', so write it as candidate-facing setup and keep it to max 2 sentences. Example: 'is to be a sales person trying to sell an additional product to the customer'."
},
"thank_you_message": {
"type": [
"string",
"null"
],
"example": "Thanks — your role-play has been recorded and will be reviewed by the hiring team.",
"description": "Custom thank-you message shown after the session — set it; omitting it leaves a generic platform default."
},
"interview_attempts": {
"type": [
"number",
"null"
],
"example": 3,
"maximum": 20,
"minimum": 1,
"description": "Allowed candidate attempts (1-20), defaulting to 3. Only meaningful when `portal` is `interview`; coaching personas are unlimited. A recruiter can still grant an extra attempt afterwards."
},
"interview_location": {
"type": [
"string",
"null"
],
"example": "remote",
"description": "Optional location label shown for the session."
},
"persona_role_avatar": {
"type": "string",
"example": "is to act as a happy customer responding to questions",
"minLength": 1,
"description": "The role the AI avatar plays. Candidate-visible — shown on the pre-session poster as 'Role of the agent', so write it as candidate-facing setup and keep it to max 2 sentences. Example: 'is to act as a happy customer responding to questions'."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi"
],
"type": "string",
"example": "en",
"description": "Platform language code used for the conversation. Must be one of the platform-languages.json codes."
},
"recruiter_profile_id": {
"type": [
"string",
"null"
],
"description": "Profile id of the recruiter owning this persona. Must be a merchant/merchant_owner/admin profile of the same merchant."
},
"interview_template_id": {
"type": "string",
"format": "uuid",
"example": "baa3bf7c-6926-46fd-9005-16738a31f72d",
"minLength": 1,
"description": "Id of the interview template (avatar) the persona uses."
},
"persona_avatar_who_is": {
"type": [
"string",
"null"
],
"description": "Who the avatar represents: name, role, context, personality, woven with what drives them underneath (motive, fear, what they refuse until heard, what they do not know until told). One continuous description; no labelled subsections."
},
"candidate_expectations": {
"type": [
"string",
"null"
],
"maxLength": 2100,
"description": "Mentee assessment goals — free-text describing what the candidate is expected to achieve (max 2100 chars)."
},
"recording_full_session": {
"enum": [
"audio_all",
"video_all",
null
],
"type": [
"string",
"null"
],
"example": "video_all",
"description": "Full interview-session recording (includes the avatar and voice) produced as a single file. Independent of `recording`. Omit/null to disable. Options — `audio_all`: Record the whole session audio (avatar + candidate voice) into a single file. Adds +0.2 credits. | `video_all`: Record the whole session video + audio (avatar + candidate) into a single file. Adds +0.4 credits.."
},
"persona_avatar_progress": {
"type": [
"string",
"null"
],
"example": "Mode: turning point\nInitial hold: ...\nUnlocks when: ...\nAfter unlock: ...\nHard limits: ...\nNo goalposts: ...\nGates: ...",
"description": "How the conversation moves forward — one plain-text value starting with `Mode: turning point` (resistance) or `Mode: steps` (difficult conversation protocol), then the labelled lines for that mode (Initial hold / Unlocks when / … or Framework / Steps / …), including a Gates line: do not share later personal details from persona_avatar_knowledge until progress is earned."
},
"persona_avatar_knowledge": {
"type": [
"string",
"null"
],
"description": "Private facts the avatar can use (numbers, dates, names, objections), plus any personal details shareable only after progress and only if natural — timing written inline, not as labelled subsections. Those personal details are never required for the goal."
},
"candidate_video_introduction": {
"enum": [
"optional",
"required",
null
],
"type": [
"string",
"null"
],
"example": "optional",
"description": "Whether a candidate video introduction is optional or required."
},
"interview_conversation_speed": {
"enum": [
"slower",
"normal",
"faster",
"fluent",
null
],
"type": [
"string",
"null"
],
"example": "normal",
"description": "Conversation pace of the AI avatar. Omit/null keeps the template default pace. Options — `slower`: The avatar speaks more slowly — easier to follow for non-native speakers. | `normal`: Default speaking pace. | `faster`: The avatar speaks more quickly for a snappier conversation. | `fluent`: Talking pace: the avatar answers 300 ms after the candidate stops speaking. A pause mid-sentence also ends their turn.."
},
"persona_avatar_end_conditions": {
"type": [
"string",
"null"
],
"description": "When the avatar should end the session. Prefer referring to persona_avatar_progress: wrap up when progress is complete (turning point unlocked and a plan accepted, or steps \"Done when\" reached), or when the conversation has clearly broken down."
}
}
}{
"type": "object",
"required": [
"interview_def_set_id"
],
"properties": {
"embed_id": {
"type": [
"string",
"null"
],
"description": "Embed id, present only when is_embedded=true."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
},
"embed_signing_key": {
"type": [
"string",
"null"
],
"description": "Embed signing key, present only when is_embedded=true."
},
"interview_def_set_id": {
"type": [
"string",
"null"
],
"example": "00000000-0000-0000-0000-000000000000",
"description": "Id of the newly created persona definition set."
}
},
"description": "Id of the created persona. Includes embed_id/embed_signing_key when is_embedded=true.",
"x-fastmcp-top-level-schema": "PersonaCreateResponse"
}{
"title": "Create role-play persona",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}get_interview_definition — Get interview definition
[Interviews] Get the definition/configuration of an interview (position), including its ordered `questions` array. The questions come back in the same format create_interview_from_questions accepts, so you can read an interview here, change the array, and send it to update_interview. Each question's `id` identifies it — keep the ids you did not mean to change. Retrieves the interview definition for a given interview-definition id or position id. Returns the compiled `calc_definition_json`, the ordered `questions` array (in the same format job-interview-create-from-array accepts, so it round-trips into job-interview-update) plus basic metadata. Access is subject to the caller's row-level security.
{
"type": "object",
"required": [
"position_id"
],
"properties": {
"position_id": {
"type": "string",
"format": "uuid",
"example": "00000000-0000-0000-0000-000000000000",
"minLength": 1,
"description": "Identifier of either an interview definition (single-stage) or a position definition (multi-stage). The function resolves whichever matches."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"name",
"created_at",
"updated_at",
"questions",
"is_multistage",
"status",
"visibility",
"stage",
"type",
"environment",
"code",
"interview_location",
"cover_image_url",
"merchant_id",
"interview_template_id",
"interview_template_type",
"is_voice_only",
"knowledge_base_store_id",
"mojito_language_code",
"speech_language_code",
"speech_language_name",
"recording",
"recording_full_session",
"type_credit",
"result_view",
"candidate_video_introduction",
"description",
"interview_description_long",
"candidate_expectations",
"interview_department",
"interview_salary",
"interview_available_till",
"candidate_notification_channel",
"recruiter_profile_id",
"slug",
"tags",
"coach_plan",
"interview_conversation_speed",
"max_followups",
"max_duration",
"questions_random_subset",
"required_pronunciation",
"result_enable_edit_transcript"
],
"properties": {
"code": {
"type": [
"string",
"null"
],
"description": "Caller-supplied external code/reference."
},
"name": {
"type": [
"string",
"null"
],
"description": "Interview or position name."
},
"slug": {
"type": [
"string",
"null"
],
"description": "URL slug of the public listing, when one was generated."
},
"tags": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"interview-practice",
"sales"
],
"description": "Free-form tags. Also the coaching-catalogue mapping key: a catalogue directory lists this session when its `tags_interview_set_filter` is a subset of these tags. Null for multi-stage positions."
},
"type": {
"enum": [
"coaching",
"interview",
"persona",
"persona_interview",
"public_avatar",
"assessment",
null
],
"type": [
"string",
"null"
],
"description": "Interview type. Null for multi-stage positions."
},
"stage": {
"enum": [
"Interest",
"Application submitted",
"Interview 1st planned",
"Interview 1st completed",
"Interview 2nd planned",
"Interview 2nd completed",
"Interview 3rd planned",
"Interview 3rd completed",
"Offer negotiations",
"Hired",
"Rejected",
"Offer declined",
"Other",
null
],
"type": [
"string",
"null"
],
"description": "Hiring-pipeline stage. Null for multi-stage positions."
},
"status": {
"enum": [
"draft",
"active",
"archived",
"deleted",
"preparing",
"completed",
null
],
"type": [
"string",
"null"
],
"description": "Lifecycle status of the interview/position."
},
"questions": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"question"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "Identifier for this question. job-interview-get returns the question's real id here; send it back to job-interview-update so an unchanged question keeps its existing record (and with it its answer rules and any rendered avatar video). Also the handle another question references via conditional_question_main_id. On job-interview-create-from-array it is a caller-local value, only needed for those references."
},
"label": {
"type": [
"string",
"null"
],
"description": "Optional label/tag stored on the question."
},
"duration": {
"type": [
"number",
"null"
],
"description": "Answer duration in seconds for this question."
},
"question": {
"type": [
"string",
"null"
],
"description": "The question text shown to the candidate."
},
"is_expert": {
"type": [
"boolean",
"null"
],
"description": "Expert listening question (view 'with listening expert')."
},
"external_id": {
"type": [
"string",
"null"
],
"description": "External identifier stored on the question. job-interview-update matches on this first, so an ATS that owns stable ids can send its own array and have the diff line up without round-tripping our ids."
},
"is_skippable": {
"type": [
"boolean",
"null"
],
"example": false,
"description": "The candidate may skip this question: the recorded (offline avatar) interview shows a Skip button on it, and a skipped question is saved without an answer. Defaults to false."
},
"external_data": {
"type": [
"object",
"null"
],
"description": "Arbitrary JSON metadata stored on the question.",
"additionalProperties": {}
},
"is_conditional": {
"type": [
"boolean",
"null"
],
"description": "A yes/no question that decides which follow-ups are asked (view 'with listening conditional'). Its follow-ups point at it with conditional_question_main_id."
},
"knowledge_base_id": {
"type": [
"string",
"null"
],
"description": "Knowledge-base store id (uuid) the question draws context from."
},
"is_multiple_choice": {
"type": [
"boolean",
"null"
],
"description": "Multiple-choice question (view 'multiple choice')."
},
"is_without_scoring": {
"type": [
"boolean",
"null"
],
"description": "Question is asked but not scored (view 'without scoring')."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi",
null
],
"type": [
"string",
"null"
],
"example": "en",
"description": "Per-question language override (one of the platform-languages.json codes). Inherits the interview language when omitted."
},
"question_alternatives": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Alternative phrasings for the question."
},
"candidate_expectations_json": {
"type": [
"object",
"null"
],
"properties": {
"weak": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Baseline requirements every viable candidate should meet (table stakes)."
},
"strong": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "High-bar requirements only standout candidates clear."
},
"moderate": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Requirements expected of a solid, competent candidate."
}
},
"description": "Per-question candidate expectations, bucketed by requirement level (weak/moderate/strong). Extra keys are preserved."
},
"conditional_question_answer": {
"type": [
"boolean",
"null"
],
"example": false,
"description": "With conditional_question_main_id: the answer to the yes/no question that shows this follow-up. `true` (the default) asks it after a yes, `false` after a no, so each answer can lead to its own follow-ups. Ignored on a question without conditional_question_main_id."
},
"conditional_question_main_id": {
"type": [
"string",
"null"
],
"description": "For a follow-up question, the id (the \"id\" field above) of the yes/no question (is_conditional) in this same array that decides whether it is asked. The yes/no question must appear earlier in the array. Use conditional_question_answer to choose the answer that shows it."
},
"is_candidate_asking_recruiter": {
"type": [
"boolean",
"null"
],
"description": "Candidate-asks-recruiter prompt (view 'candidate asking recruiter')."
}
},
"additionalProperties": {}
},
"description": "The interview questions, in the order they are asked, in the same format job-interview-create-from-array accepts — so this array can be edited and sent straight back to job-interview-update, or used to create a copy of this interview. Each `id` is the question's real identifier: send it back unchanged and the question keeps its existing record (and with it its answer rules and any rendered avatar video). The welcome, thank-you and instructional-video steps are NOT included — they are not questions in this format, and job-interview-update leaves them untouched. Null for multi-stage positions, whose questions live on the individual interview stages."
},
"recording": {
"enum": [
"audio_first_5_answers",
"audio_all",
"video_all",
"video_first_5_answers",
null
],
"type": [
"string",
"null"
],
"description": "Per-answer recording mode. Null for multi-stage positions."
},
"coach_plan": {
"enum": [
"demo",
"screening",
"2nd",
"3rd",
"closing",
"job-specific",
"other",
null
],
"type": [
"string",
"null"
],
"description": "Coaching-plan stage this session belongs to. Null for multi-stage positions and for sessions outside any plan."
},
"created_at": {
"type": [
"string",
"null"
],
"example": "2026-01-15T09:30:00.000Z"
},
"updated_at": {
"type": [
"string",
"null"
],
"example": "2026-01-20T14:05:00.000Z"
},
"visibility": {
"enum": [
"public",
"merchant_public",
"merchant_invite",
"merchant_unlisted",
"sub_item",
null
],
"type": [
"string",
"null"
],
"description": "Who can see and access the interview/position."
},
"description": {
"type": [
"string",
"null"
],
"description": "Short description."
},
"environment": {
"enum": [
"production",
"uat",
"development",
"demo",
null
],
"type": [
"string",
"null"
],
"example": "production",
"description": "Which webhook environment this interview's results are delivered to. Null for multi-stage positions, whose stages each carry their own."
},
"merchant_id": {
"type": [
"string",
"null"
],
"description": "Owning merchant id."
},
"result_view": {
"enum": [
"minimal",
"advanced",
"full",
"full_expand_scores",
"minimal_with_score",
"none",
null
],
"type": [
"string",
"null"
],
"description": "Result view level. Null for multi-stage positions."
},
"type_credit": {
"enum": [
"resume_check",
"interview_coach_starter",
"interview_coach_contributor",
"interview_coach_manager",
"cover_letter",
null
],
"type": [
"string",
"null"
],
"description": "Credit bucket the interview draws from. Null for multi-stage positions."
},
"max_duration": {
"type": [
"number",
"null"
],
"description": "Live session limit in seconds. Null for multi-stage positions."
},
"custom_fields": {
"type": [
"object",
"null"
],
"example": {
"cost_centre": "CC-100",
"contract_type": "permanent"
},
"description": "Company-defined fields (key: value) of a multi-stage position, when custom fields are enabled for the company (multi-stage positions only: absent for an interview). Keys are the field keys of the company's custom field definitions; values are text, numbers, YYYY-MM-DD dates, option values (a list for multiple choice), {min, max, currency} ranges or user profile ids.",
"additionalProperties": {}
},
"is_multistage": {
"type": [
"boolean",
"null"
],
"description": "True when the id resolved to a multi-stage position rather than a single interview."
},
"is_voice_only": {
"type": [
"boolean",
"null"
],
"description": "Convenience flag derived from interview_template_type: true when voice-only (`interactive_elevenlabs`), false when avatar-based, null when the template type could not be resolved."
},
"max_followups": {
"type": [
"number",
"null"
],
"description": "Maximum number of AI follow-up questions; null uses the template default. Null for multi-stage positions."
},
"result_scoring": {
"description": "Resolved result-scoring config (update field `custom_scoring`; its `max_retries` is the update field `interview_attempts`). Null means the platform defaults apply. Null for multi-stage positions."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"description": "Cover image URL."
},
"interview_salary": {
"type": [
"string",
"null"
],
"example": "$80k - $100k",
"description": "Salary range shown for the position."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
},
"interview_location": {
"type": [
"string",
"null"
],
"description": "Location (create field `location`)."
},
"creation_parameters": {
"description": "The creation parameters recorded at build time (interview_type, interview_tone, interview_length, additional_context, include_rapport_question, include_closing_prompt, knowledge_base_store_id, seniority_level, hiring_for_company)."
},
"calc_definition_json": {
"description": "The compiled interview definition JSON (structure varies by interview type)."
},
"country_availability": {
"description": "Portal availability by the candidate's country: {countries_allowed?: string[], countries_allowed_eu?: boolean, countries_blocked?: string[], countries_blocked_eu?: boolean} (ISO 3166-1 alpha-2, EU = EU + EEA + Switzerland; blocked wins), judged by the candidate's IP on the portal; invited candidates keep access. Null = not used, every country. Set it with job-interview-update."
},
"interview_department": {
"type": [
"string",
"null"
],
"example": "Engineering",
"description": "Department the position belongs to."
},
"mojito_language_code": {
"type": [
"string",
"null"
],
"example": "en",
"description": "Platform (mojito) language code."
},
"recruiter_profile_id": {
"type": [
"string",
"null"
],
"description": "Profile id of the recruiter owning this interview/position."
},
"speech_language_code": {
"type": [
"string",
"null"
],
"description": "Azure speech language code. Null for multi-stage positions."
},
"speech_language_name": {
"type": [
"string",
"null"
],
"description": "Azure speech language display name. Null for multi-stage positions."
},
"interview_template_id": {
"type": [
"string",
"null"
],
"description": "Interview template id. For multi-stage positions this is the first interview step's template."
},
"candidate_expectations": {
"type": [
"string",
"null"
],
"description": "Free-text candidate expectations. Null for multi-stage positions."
},
"pdf_export_auto_config": {
"description": "Auto-PDF-report options applied when the interview completes; null when auto-export is off. Null for multi-stage positions."
},
"recording_full_session": {
"enum": [
"audio_first_5_answers",
"audio_all",
"video_all",
"video_first_5_answers",
null
],
"type": [
"string",
"null"
],
"description": "Full-session recording mode. Null for multi-stage positions."
},
"required_pronunciation": {
"type": [
"boolean",
"null"
],
"description": "Whether a pronunciation assessment is required. Null for multi-stage positions."
},
"interview_template_type": {
"enum": [
"offline_elai",
"offline_synthesia",
"interactive_heygen",
"offline_heygen",
"interactive_elevenlabs",
"interactive_spatius",
null
],
"type": [
"string",
"null"
],
"description": "Type of the linked interview template. `interactive_elevenlabs` is voice-only; the others (`interactive_spatius`, `interactive_heygen`, `offline_heygen`, `offline_elai`, `offline_synthesia`) are avatar-based. Null when the template could not be resolved."
},
"knowledge_base_store_id": {
"type": [
"string",
"null"
],
"description": "Linked knowledge base store id. Null for multi-stage positions."
},
"questions_random_subset": {
"type": [
"number",
"null"
],
"description": "Fraction of the questions actually asked (0.01-0.9); null asks all of them. Null for multi-stage positions."
},
"interview_available_till": {
"type": [
"string",
"null"
],
"description": "ISO date/time after which the interview is no longer available to candidates. Null means always available."
},
"interview_description_long": {
"type": [
"string",
"null"
],
"description": "Long description (create/update field `description_long`). Rendered as Markdown on the candidate-facing position page — formatting guide: https://developer.jobmojito.com/cookbooks/format-content-with-markdown"
},
"candidate_expectations_json": {
"description": "Structured candidate expectations JSON — the scoring rubric. Null for multi-stage positions."
},
"candidate_video_introduction": {
"enum": [
"hidden",
"optional",
"required",
null
],
"type": [
"string",
"null"
],
"description": "Whether a candidate video introduction is hidden/optional/required. Null for multi-stage positions."
},
"interview_conversation_speed": {
"type": [
"string",
"null"
],
"description": "Conversation pace of the AI avatar (slower/normal/faster/fluent). Null keeps the template default. Null for multi-stage positions."
},
"result_enable_edit_transcript": {
"type": [
"boolean",
"null"
],
"description": "Whether the candidate may edit the transcript on the result view. Null for multi-stage positions."
},
"candidate_notification_channel": {
"enum": [
"reminders",
"last_reminder",
"all",
null
],
"type": [
"string",
"null"
],
"description": "SMS / WhatsApp candidate notifications: null = off, reminders = with both e-mail reminders, last_reminder = only with the final reminder, all = invitation + reminders + pre-screening accepted."
}
},
"x-fastmcp-top-level-schema": "JobInterviewGetResponse"
}{
"title": "Get interview definition",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}update_interview — Update interview or position
[Interviews] Update the configuration of an existing interview/position: name, description, avatar template, recording, scoring, tags and the rest of the create-time settings. Only the fields you send are changed. To change the questions, send `questions` — the WHOLE list you want the interview to end up with, in order, in the format get_interview_definition returns. OMIT `questions` and the existing questions are left completely alone; there is no way to change one question on its own, so read the interview first, edit that array, and send it back. Resending an unchanged array does nothing. Not updated by this tool at all: the welcome and thank-you messages and the instructional-video screen (stored as steps, not questions), and the language, which the existing questions are already written in. Use `tags` to place a coaching session into a catalogue directory. MULTI-STAGE POSITIONS: some per-interview settings do not exist at position level and are rejected with a 422 naming the field — `candidate_expectations_json` is the one seen in practice. Read the position with get_interview_definition, then update the individual stage you mean instead of sending that field to the position. A null argument counts as not sent and changes nothing, except on three fields where null is the only way to switch something off: `candidate_notification_channel` (no SMS / WhatsApp), `questions_random_subset` (ask all questions) and `pdf_export_auto_config` (no automatic report). Leave those three out unless that is what you mean. Updates the configuration of an existing interview (single-stage) or position (multi-stage). Only the fields present in the request body are written — everything else keeps its current value. A null argument counts as not sent, except on candidate_notification_channel, questions_random_subset and pdf_export_auto_config. The question list is only touched when you send `questions`; omit that field and the questions are left exactly as they are. When sent, it must be the complete list and is applied as a diff against what is stored (matched on external_id, then id, then identical content), so unchanged questions keep their existing records, edited ones are unlinked and re-created, and dropped ones are unlinked — and re-sending the array job-interview-get returned changes nothing. See the field description, and `questions_diff` in the response for what was decided. Questions of an interview that is already `active` can only be changed on interactive avatar templates, where re-publishing is instant; on the offline (pre-rendered video) templates set the interview back to `draft` first. The welcome / thank-you messages and the instructional-video screen are stored as steps rather than questions and are never changed here. `mojito_language_code` cannot be changed — the existing questions and rendered videos are in the original language — so create a new interview to change language. A multi-stage position only carries the shared identity fields (name, code, location, description, description_long, cover_image_url, department, salary, available_till, recruiter, status, visibility, hiring_for_company); sending an interview-only field for a position is a 422.
{
"type": "object",
"required": [
"position_id"
],
"properties": {
"code": {
"type": [
"string",
"null"
],
"description": "External code/reference. Blank is stored as null."
},
"name": {
"type": [
"string",
"null"
],
"example": "Project manager",
"description": "Interview / position name."
},
"tags": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"interview-practice",
"sales"
],
"description": "Free-form tags stored on the interview. Tags are also the coaching-catalogue mapping key: a catalogue directory (see the catalogue-tag-create / catalogue-tag-update endpoints) lists a coaching or persona session when the session's tags contain EVERY tag in that directory's `tags_interview_set_filter`. Only `active` sessions with visibility `public` or `merchant_public` are listed."
},
"type": {
"enum": [
"interview",
"coaching",
"assessment"
],
"type": "string",
"example": "interview",
"description": "Product type of the interview. Changing it also re-derives `type_credit` (null for interview/assessment, otherwise interview_coach_manager) unless you send `type_credit` explicitly. Single-stage interviews only."
},
"status": {
"enum": [
"draft",
"active",
"archived",
"deleted",
"preparing",
"completed"
],
"type": "string",
"example": "active",
"description": "New lifecycle status. Applied through the same interview_set_status routine as job-interview-set-state (which is also where you manage the iframe embed key)."
},
"location": {
"type": [
"string",
"null"
],
"example": "remote",
"description": "Interview location (column `interview_location`). Blank is stored as null."
},
"questions": {
"type": "array",
"items": {
"type": "object",
"required": [
"question"
],
"properties": {
"id": {
"type": "string",
"description": "Identifier for this question. job-interview-get returns the question's real id here; send it back to job-interview-update so an unchanged question keeps its existing record (and with it its answer rules and any rendered avatar video). Also the handle another question references via conditional_question_main_id. On job-interview-create-from-array it is a caller-local value, only needed for those references."
},
"label": {
"type": [
"string",
"null"
],
"description": "Optional label/tag stored on the question."
},
"duration": {
"type": [
"number",
"null"
],
"description": "Answer duration in seconds for this question."
},
"question": {
"type": "string",
"description": "The question text shown to the candidate."
},
"is_expert": {
"type": [
"boolean",
"null"
],
"description": "Expert listening question (view 'with listening expert')."
},
"external_id": {
"type": [
"string",
"null"
],
"description": "External identifier stored on the question. job-interview-update matches on this first, so an ATS that owns stable ids can send its own array and have the diff line up without round-tripping our ids."
},
"is_skippable": {
"type": [
"boolean",
"null"
],
"example": false,
"description": "The candidate may skip this question: the recorded (offline avatar) interview shows a Skip button on it, and a skipped question is saved without an answer. Defaults to false."
},
"external_data": {
"type": [
"object",
"null"
],
"description": "Arbitrary JSON metadata stored on the question.",
"additionalProperties": {}
},
"is_conditional": {
"type": [
"boolean",
"null"
],
"description": "A yes/no question that decides which follow-ups are asked (view 'with listening conditional'). Its follow-ups point at it with conditional_question_main_id."
},
"knowledge_base_id": {
"type": [
"string",
"null"
],
"description": "Knowledge-base store id (uuid) the question draws context from."
},
"is_multiple_choice": {
"type": [
"boolean",
"null"
],
"description": "Multiple-choice question (view 'multiple choice')."
},
"is_without_scoring": {
"type": [
"boolean",
"null"
],
"description": "Question is asked but not scored (view 'without scoring')."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi",
null
],
"type": [
"string",
"null"
],
"example": "en",
"description": "Per-question language override (one of the platform-languages.json codes). Inherits the interview language when omitted."
},
"question_alternatives": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Alternative phrasings for the question."
},
"candidate_expectations_json": {
"type": [
"object",
"null"
],
"properties": {
"weak": {
"type": "array",
"items": {
"type": "string"
},
"description": "Baseline requirements every viable candidate should meet (table stakes)."
},
"strong": {
"type": "array",
"items": {
"type": "string"
},
"description": "High-bar requirements only standout candidates clear."
},
"moderate": {
"type": "array",
"items": {
"type": "string"
},
"description": "Requirements expected of a solid, competent candidate."
}
},
"description": "Per-question candidate expectations, bucketed by requirement level (weak/moderate/strong). Extra keys are preserved."
},
"conditional_question_answer": {
"type": [
"boolean",
"null"
],
"example": false,
"description": "With conditional_question_main_id: the answer to the yes/no question that shows this follow-up. `true` (the default) asks it after a yes, `false` after a no, so each answer can lead to its own follow-ups. Ignored on a question without conditional_question_main_id."
},
"conditional_question_main_id": {
"type": [
"string",
"null"
],
"description": "For a follow-up question, the id (the \"id\" field above) of the yes/no question (is_conditional) in this same array that decides whether it is asked. The yes/no question must appear earlier in the array. Use conditional_question_answer to choose the answer that shows it."
},
"is_candidate_asking_recruiter": {
"type": [
"boolean",
"null"
],
"description": "Candidate-asks-recruiter prompt (view 'candidate asking recruiter')."
}
},
"additionalProperties": {}
},
"minItems": 1,
"description": "OPTIONAL. Omit this field entirely and the interview's questions are left exactly as they are — this endpoint only touches questions when you send the array. When you do send it, send the COMPLETE list you want the interview to end up with, in order, in the same format job-interview-create-from-array accepts and job-interview-get returns: there is no way to change a single question on its own, so read the interview, edit that array, and send the whole thing back. An empty array is rejected. It is applied as a DIFF, not a replace, so resending the array job-interview-get gave you changes nothing at all. Each entry is matched against what is stored — first on `external_id`, then on `id`, then on identical content — and: an entry matching an unchanged question keeps that question exactly as it is, including its answer rules and any rendered avatar video; an entry matching a question whose content differs unlinks the old question and creates a new one in its place (fields you omit are carried over from the old one); an entry matching nothing is created; and a stored question no entry matches is unlinked. `questions_diff` in the response reports exactly what was decided. Questions are shared records, so nothing is ever deleted — removing one only unlinks it from this interview, and an edit is always unlink-old + create-new so the change cannot leak into another interview reusing the same question. The welcome, thank-you and instructional-video steps are not part of this array and are left in place. Single-stage interviews only; for a multi-stage position, update its interview stages individually."
},
"recording": {
"enum": [
"audio_first_5_answers",
"audio_all",
"video_all",
"video_first_5_answers",
null
],
"type": [
"string",
"null"
],
"example": "video_all",
"description": "Cheating/proctoring detection mode for candidate answers — this is NOT a full session recording. Video options also record the candidate. Omit to keep the current setting. A null is ignored: turn it off in the admin console. Options — `audio_first_5_answers`: Audio-only cheating detection, first 5 answers only. | `audio_all`: Audio-only cheating detection on every answer. | `video_all`: Audio + video cheating detection on every answer (candidate is recorded for all answers). | `video_first_5_answers`: Audio + video cheating detection, first 5 answers only.."
},
"coach_plan": {
"enum": [
"demo",
"screening",
"2nd",
"3rd",
"closing",
"job-specific",
"other",
null
],
"type": [
"string",
"null"
],
"example": "screening",
"description": "Coaching-plan stage this item belongs to, used by the coaching-plan progress view. Omit to keep the current stage. A null is ignored: use `other` when no stage fits. Options — `demo`: Demo session. | `screening`: Screening-interview practice. | `2nd`: Second-interview practice. | `3rd`: Third-interview practice. | `closing`: Closing / salary-negotiation practice. | `job-specific`: Job-specific coaching. | `other`: Anything that does not fit the other buckets.."
},
"visibility": {
"enum": [
"merchant_public",
"merchant_invite",
"merchant_unlisted"
],
"type": "string",
"example": "merchant_public",
"description": "Who can discover and access the interview. Options — `merchant_public`: Listed on the merchant's public interview list — anyone with the merchant link can find and start it. | `merchant_invite`: Invite-only — only candidates explicitly invited (by email/link) can access it; not listed anywhere. | `merchant_unlisted`: Reachable only via a direct link — not listed anywhere; share the link manually.."
},
"description": {
"type": [
"string",
"null"
],
"description": "Short interview description."
},
"environment": {
"enum": [
"production",
"uat",
"development",
"demo"
],
"type": "string",
"example": "production",
"description": "Which of your webhook environments results from this interview are delivered to. Defaults to production. Options — `production`: Live hiring. Results reach the webhooks configured as production. This is the default when the field is omitted. | `uat`: User-acceptance testing - an isolated environment for pre-release verification. | `development`: Development/testing. Use for interviews created by a test or preview app so their results never reach the production webhook. | `demo`: Demonstrations and sales trials.."
},
"position_id": {
"type": "string",
"format": "uuid",
"example": "00000000-0000-0000-0000-000000000000",
"minLength": 1,
"description": "Id of the interview definition (single-stage) or position definition (multi-stage) to update. The same id you pass to job-interview-get."
},
"result_view": {
"enum": [
"none",
"minimal",
"minimal_with_score",
"advanced",
"full",
"full_expand_scores"
],
"type": "string",
"example": "full",
"description": "Result screen shown to the candidate after finishing. With any value other than `none`, the candidate sees a results screen where they can provide feedback, record an intro video and edit the transcript, and must then submit the result; the value sets how much score/result detail is shown. Options — `none`: No results screen at all — the interview is submitted immediately when the candidate finishes (no feedback, intro video, transcript edit or manual submit step). | `minimal`: Minimal results layout, no score shown. | `minimal_with_score`: Minimal results layout including the overall score. | `advanced`: Advanced results layout with more detail. | `full`: Full results layout with all sections. | `full_expand_scores`: Full results with every score breakdown expanded.."
},
"type_credit": {
"enum": [
"resume_check",
"interview_coach_starter",
"interview_coach_contributor",
"interview_coach_manager",
"cover_letter",
null
],
"type": [
"string",
"null"
],
"example": "interview_coach_manager",
"description": "Credit bucket the session draws from. Only meaningful for candidate-paid coaching/persona sessions; hiring interviews and assessments are merchant-billed and carry null. Options — `resume_check`: Resume-check credits. | `interview_coach_starter`: Coaching credits — starter tier. | `interview_coach_contributor`: Coaching credits — contributor tier. | `interview_coach_manager`: Coaching credits — manager tier. | `cover_letter`: Cover-letter credits.."
},
"max_duration": {
"type": [
"number",
"null"
],
"example": 1200,
"description": "Live session limit in seconds. Also the basis for the credit multiplier."
},
"custom_fields": {
"type": "object",
"example": {
"cost_centre": "CC-100",
"contract_type": "permanent"
},
"description": "Company-defined position fields to set, {key: value}; a null value clears that field and keys you omit keep their value. Values are checked against the field definitions (option values, YYYY-MM-DD dates, {min, max, currency} ranges, user profile ids). Requires custom fields to be enabled for the company (else 403). Multi-stage positions only: on a single-stage interview the request is refused with 422.",
"additionalProperties": {}
},
"max_followups": {
"type": [
"integer",
"null"
],
"example": 2,
"maximum": 999,
"minimum": 0,
"description": "Maximum number of AI follow-up questions. 0 disables follow-ups; presets are 0-3 (none/low/normal/high) and custom values start at 4; 2 is the template default (Normal)."
},
"custom_scoring": {
"type": [
"object",
"null"
],
"description": "Result-scoring overrides (max_score, early_stop, speech_cadence, ai_pronunciation, sentiment_analysis, ai_assessment_answer, ai_assessment_resume, ai_assessment_session). Merged onto the stored configuration, so keys you omit keep their current value.",
"additionalProperties": {}
},
"interview_tone": {
"enum": [
"relaxed",
"simple",
"professional",
"persuasive",
"exact",
null
],
"type": [
"string",
"null"
],
"example": "professional",
"description": "Tone — configures the AI avatar's speaking style and the follow-up questions it generates. Stored in creation_parameters; existing questions are NOT regenerated. Case-insensitive; omit to default to relaxed. Options — `relaxed`: Friendly and conversational tone that helps candidates feel at ease. | `simple`: Plain language at CEFR A2 level — short sentences and simple words. | `professional`: Formal and business-like approach suitable for senior roles. | `persuasive`: Engaging style that encourages candidates to elaborate. | `exact`: Asks the questions exactly as provided, without rephrasing — for interviews built from your own questions (create_interview_from_questions) where the wording is a script.."
},
"interview_type": {
"enum": [
"pre-screening",
"pre-screening-with-test-questions",
"second-interview",
"remote-freelancer-verification",
"strength-based-interview",
"potential-based-interview",
"process-verification-from-knowledge-base",
"knockout-screening",
null
],
"type": [
"string",
"null"
],
"example": "pre-screening-with-test-questions",
"description": "Interview style — configures the AI avatar and the follow-up questions it generates during the interview. Stored in creation_parameters; existing questions are NOT regenerated. Options — `pre-screening`: Pre-screening — quick qualification check focusing on basic requirements and availability. | `pre-screening-with-test-questions`: Pre-screening with test questions — pre-screening plus practical questions to test relevant skills. | `second-interview`: Second round interview — deeper dive for candidates who passed initial screening. | `remote-freelancer-verification`: Remote worker verification — verify remote work capabilities and communication skills. | `strength-based-interview`: Strength-based interview — focus on what candidates enjoy and excel at to predict job satisfaction. | `potential-based-interview`: Potential-based interview — assess learning ability and growth potential rather than past experience. | `process-verification-from-knowledge-base`: Knowledge Base interview — generate questions from your knowledge base documents. | `knockout-screening`: Knock-out screening — a short (about 5 minute) check of the role's must-haves with closed questions; ends early, politely, when a must-have is clearly not met. On creation it defaults custom_scoring.early_stop to true (where the avatar supports early stop) and max_followups to 1 unless you send them.."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"description": "Cover image URL."
},
"seniority_level": {
"enum": [
"entry-level",
"intermediate",
"senior",
"managerial",
"director",
"executive",
null
],
"type": [
"string",
"null"
],
"example": "senior",
"description": "Target seniority level for the role; auto-detected from the job description when omitted. Options — `entry-level`: Early-career or graduate roles. | `intermediate`: Some experience required. | `senior`: Experienced professional. | `managerial`: Team or department lead. | `director`: Director-level responsibility. | `executive`: C-suite or executive role.."
},
"description_long": {
"type": [
"string",
"null"
],
"description": "Long-form interview description (column `interview_description_long`). Rendered as Markdown on the candidate-facing position page, including chips, callouts, cards, columns and buttons — formatting guide: https://developer.jobmojito.com/cookbooks/format-content-with-markdown"
},
"interview_salary": {
"type": [
"string",
"null"
],
"example": "$80k - $100k",
"description": "Salary range shown for the position. Blank is stored as null."
},
"hiring_for_company": {
"type": [
"object",
"null"
],
"example": {
"name": "undisclosed"
},
"properties": {
"name": {
"type": [
"string",
"null"
],
"example": "Unimo Enterprises",
"description": "End-employer name. Omit/null when hiring for yourself, 'undisclosed' for an unnamed external client, or the client's company name."
},
"sector": {
"type": [
"string",
"null"
],
"example": "Logistics and Supply Chain",
"description": "Industry / sector of the end employer."
},
"location": {
"type": [
"string",
"null"
],
"example": "Sri Lanka",
"description": "Primary location of the end employer."
},
"description": {
"type": [
"string",
"null"
],
"example": "Unimo Enterprises is a leading logistics and supply chain solutions provider.",
"description": "Short description of the end employer, used as background context by the agent."
},
"company_size": {
"type": [
"string",
"null"
],
"example": "100-200",
"description": "Approximate headcount of the end employer."
}
},
"description": "Who the position is really for. An object with name null/blank means hiring for yourself; { name: 'undisclosed' } for an unnamed external client; or { name: '<company>' } plus optional description/location/sector/company_size. Stored in creation_parameters.hiring_for_company."
},
"interview_attempts": {
"type": "number",
"example": 1,
"maximum": 20,
"minimum": 1,
"description": "Allowed candidate attempts (1-20). Stored as result_scoring.max_retries."
},
"country_availability": {
"type": [
"object",
"null"
],
"properties": {
"countries_allowed": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Za-z]{2}$"
},
"example": [
"PH",
"IN"
],
"description": "Available ONLY to candidates in these countries (ISO 3166-1 alpha-2)."
},
"countries_blocked": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Za-z]{2}$"
},
"example": [
"US"
],
"description": "NOT available to candidates in these countries (ISO 3166-1 alpha-2). Wins over the allowed side."
},
"countries_allowed_eu": {
"type": "boolean",
"description": "Adds every EU country (plus EEA and Switzerland) to countries_allowed."
},
"countries_blocked_eu": {
"type": "boolean",
"description": "Adds every EU country (plus EEA and Switzerland) to countries_blocked."
}
},
"description": "Which countries the interview / position is open to on the candidate portal, judged by the IP address the candidate opens it from (an unknown location only passes when countries_allowed / countries_allowed_eu are empty). From a closed country it is left out of the portal listings, and a direct link shows \"not available in your region\". Invited candidates (register_users / invite links) and candidates who already started keep access; API and MCP calls themselves are never restricted. An empty object {} = not used, every country."
},
"interview_department": {
"type": [
"string",
"null"
],
"example": "Engineering",
"description": "Department the position belongs to. Blank is stored as null."
},
"recruiter_profile_id": {
"type": [
"string",
"null"
],
"description": "Profile id of the recruiter owning this interview. Must be a merchant/merchant_owner/admin profile of the same merchant. An empty string clears it."
},
"interview_template_id": {
"type": "string",
"example": "46b98d37-1557-4391-beca-03037ead19f2",
"minLength": 1,
"description": "Id of the interview template (avatar/voice) to use. Must reference an existing interview_templates row. Also decides the modality — see list_avatars / merchant-avatar-list."
},
"candidate_expectations": {
"type": [
"string",
"null"
],
"description": "Free-text candidate expectations."
},
"pdf_export_auto_config": {
"type": [
"object",
"null"
],
"properties": {
"files": {
"type": "boolean",
"description": "Include uploaded candidate files."
},
"template": {
"enum": [
"classic",
"modern",
"one_pager"
],
"type": "string",
"description": "Report layout: classic, modern, or one_pager. Omit for the merchant default (modern when unset)."
},
"analytics": {
"type": "boolean",
"description": "Include analytics."
},
"transcript": {
"type": "boolean",
"description": "Include the interview transcript."
},
"contact_details": {
"type": "boolean",
"description": "Include candidate contact details."
},
"answer_recording": {
"type": "boolean",
"description": "Include per-answer recordings."
},
"ai_scoring_rubric": {
"type": "boolean",
"description": "Include the AI scoring rubric."
},
"group_by_question": {
"type": "boolean",
"description": "Group transcript answers by expected question."
},
"session_recording": {
"type": "boolean",
"description": "Include the full-session recording."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi"
],
"type": "string",
"example": "en",
"description": "Report language code (a platform-languages.json code)."
},
"ai_recruiter_assessment": {
"type": "boolean",
"description": "Include the AI recruiter assessment."
}
},
"description": "Auto-generate a candidate PDF report with these options once the interview completes. null disables auto-export."
},
"recording_full_session": {
"enum": [
"audio_all",
"video_all",
null
],
"type": [
"string",
"null"
],
"example": "video_all",
"description": "Full interview-session recording (includes the avatar and voice) produced as a single file. Independent of `recording`. Omit to keep the current setting. A null is ignored: turn it off in the admin console. Options — `audio_all`: Record the whole session audio (avatar + candidate voice) into a single file. Adds +0.2 credits. | `video_all`: Record the whole session video + audio (avatar + candidate) into a single file. Adds +0.4 credits.."
},
"required_pronunciation": {
"type": [
"boolean",
"null"
],
"description": "Require pronunciation assessment (restricts to pronunciation-capable languages)."
},
"knowledge_base_store_id": {
"type": [
"string",
"null"
],
"description": "Knowledge base store id the interview draws context from; validated for existence. An empty string unlinks it."
},
"questions_random_subset": {
"type": [
"number",
"null"
],
"example": 0.5,
"maximum": 0.9,
"minimum": 0.01,
"description": "Ask only a random subset of the questions, as a fraction between 0.01 and 0.9. null asks all questions."
},
"interview_available_till": {
"type": [
"string",
"null"
],
"example": "2026-12-31",
"description": "ISO date/time after which the interview is no longer available to candidates. An empty string keeps it always available."
},
"candidate_expectations_json": {
"type": [
"object",
"null"
],
"properties": {
"weak": {
"type": "array",
"items": {
"type": "string"
},
"description": "Baseline requirements every viable candidate should meet (table stakes)."
},
"strong": {
"type": "array",
"items": {
"type": "string"
},
"description": "High-bar requirements only standout candidates clear."
},
"moderate": {
"type": "array",
"items": {
"type": "string"
},
"description": "Requirements expected of a solid, competent candidate."
}
},
"description": "Structured candidate expectations (the scoring rubric), bucketed by requirement level (weak/moderate/strong). A null is ignored: send a new rubric to replace it, or regenerate_candidate_expectations together with `questions` to derive it again. Extra keys are preserved."
},
"candidate_video_introduction": {
"enum": [
"hidden",
"optional",
"required",
null
],
"type": [
"string",
"null"
],
"description": "Whether a candidate video introduction is hidden, optional or required. Send `hidden` to stop asking."
},
"interview_conversation_speed": {
"enum": [
"slower",
"normal",
"faster",
"fluent",
null
],
"type": [
"string",
"null"
],
"example": "normal",
"description": "Conversation pace of the AI avatar. Omit to keep the current pace; `normal` is the default speaking pace. Options — `slower`: The avatar speaks more slowly — easier to follow for non-native speakers. | `normal`: Default speaking pace. | `faster`: The avatar speaks more quickly for a snappier conversation. | `fluent`: Talking pace: the avatar answers 300 ms after the candidate stops speaking. A pause mid-sentence also ends their turn.."
},
"result_enable_edit_transcript": {
"type": [
"boolean",
"null"
],
"description": "Allow editing the transcript on the result view."
},
"candidate_notification_channel": {
"enum": [
"reminders",
"last_reminder",
"all",
null
],
"type": [
"string",
"null"
],
"description": "SMS / WhatsApp notifications to the candidate, sent alongside the e-mails: \"reminders\" = with both e-mail reminders (day 1 and day 3); \"last_reminder\" = only with the final day-3 reminder; \"all\" = invitation, both reminders and the pre-screening-accepted step. null switches them off. WhatsApp is tried first, SMS when the number is not on WhatsApp. Available on paid plans (Starter and above) and needs a phone number on the candidate."
},
"regenerate_candidate_expectations": {
"type": "boolean",
"example": false,
"description": "Re-derive the interview-level `candidate_expectations_json` scoring rubric from the resulting question list, the way job-interview-create-from-array derives it at creation time. Only applies when `questions` is sent, the interview type is `interview`, and `candidate_expectations_json` is not also being set explicitly (an explicit value wins)."
}
}
}{
"type": "object",
"required": [
"position_id",
"is_multistage",
"updated_fields"
],
"properties": {
"position_id": {
"type": [
"string",
"null"
],
"description": "The id that was updated."
},
"is_multistage": {
"type": [
"boolean",
"null"
],
"description": "True when the id resolved to a multi-stage position (position_def_set) rather than a single interview."
},
"questions_diff": {
"type": "object",
"required": [
"kept",
"added",
"removed",
"replaced",
"question_ids",
"reactivated"
],
"properties": {
"kept": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Questions left exactly as they were, record and all."
},
"added": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Ids of the questions created for entries that matched nothing stored."
},
"removed": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Ids unlinked because no entry in the request matched them. The question records themselves still exist."
},
"replaced": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": [
"string",
"null"
],
"description": "The id of the question created in its place."
},
"from": {
"type": [
"string",
"null"
],
"description": "The id that was unlinked."
}
}
},
"description": "Edited questions: the old record was unlinked and a new one created, so the change cannot leak into other interviews reusing it."
},
"reactivated": {
"type": [
"boolean",
"null"
],
"description": "True when the interview was already active and was re-published so the new questions go live."
},
"question_ids": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "The interview's full ordered step list after the update, including the welcome / thank-you / instructional steps this endpoint does not manage."
}
},
"description": "What the question diff decided, question by question. Absent when `questions` was not sent."
},
"updated_fields": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"name",
"tags"
],
"description": "Names of the stored columns that were written, plus `status` when the lifecycle status was changed and `questions` when the question list was diffed."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"description": "Confirmation of what was updated.",
"x-fastmcp-top-level-schema": "JobInterviewUpdateResponse"
}{
"title": "Update interview or position",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}set_interview_state — Change interview state
[Interviews] Change the state of an interview/position (e.g. open, closed). Changes the lifecycle status of an interview or position (draft, active, archived, preparing, completed, deleted) and/or manages its embed key. Provide at least one of `status` or `is_embedded`.
{
"type": "object",
"required": [
"position_id"
],
"properties": {
"status": {
"enum": [
"draft",
"active",
"archived",
"deleted",
"preparing",
"completed",
null
],
"type": [
"string",
"null"
],
"description": "New lifecycle status to apply."
},
"is_embedded": {
"type": [
"boolean",
"null"
],
"description": "Controls iframe embedding of the interview on an external page. When true, ensures an embed key exists (returns embed_id/embed_signing_key, used to authenticate/sign the iframe embed). When false, removes the embed keys (disables embedding). Where it runs depends on the type: interviews, assessments and interview role-plays embed on the interview portal (`https://<interview portal domain>/embed?embed_id=...`); coaching sessions and coaching personas embed on the consumer (coaching) portal (`https://<coaching portal domain>/embed?embed_id=...`, a link built on the interview portal is forwarded there). A signed coaching embed (name, email, signature) signs the visitor in as a learner of that portal, and the session is charged to the merchant's interview credits on completion, not to the learner's coaching credits; it cannot start while the merchant has none."
},
"position_id": {
"type": "string",
"format": "uuid",
"example": "00000000-0000-0000-0000-000000000000",
"minLength": 1,
"description": "Interview definition id or position id whose state should change."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"anyOf": [
{
"type": "object",
"required": [
"success"
],
"properties": {
"success": {
"type": [
"boolean",
"null"
]
}
}
},
{
"type": "object",
"required": [
"embed_id",
"embed_signing_key"
],
"properties": {
"embed_id": {
"type": [
"string",
"null"
]
},
"embed_signing_key": {
"type": [
"string",
"null"
]
}
}
}
],
"description": "Returns `{ embed_id, embed_signing_key }` when an embed key was created/returned (is_embedded=true), otherwise `{ success: true }`.",
"x-fastmcp-top-level-schema": "JobInterviewSetStateResponse"
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-wrap-result": true
}{
"title": "Change interview state",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}generate_interview_url — Generate shareable interview link
[Interviews] Generate a signed public interview URL/token. Generates a public, token-signed URL for an existing interview, profile, or result. The required id fields depend on `type`.
{
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"enum": [
"interview-for-profile",
"results-for-profile",
"interview-result-candidate",
"interview-result-talent-seeker",
"interview-results-for-position"
],
"type": "string",
"description": "Which kind of signed URL to generate."
},
"hide_menu": {
"anyOf": [
{
"type": "string"
},
{
"type": "boolean"
}
],
"description": "Pass the string 'false' to show the navigation menu; any other value hides it (default)."
},
"merchant_id": {
"type": "string",
"description": "Override merchant id (admin / sub-merchant only)."
},
"interview_id": {
"type": "string",
"description": "Interview or position id. Required for interview-for-profile and interview-results-for-position."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"interview_result_id": {
"type": "string",
"description": "Interview result id. Required for interview-result-candidate and interview-result-talent-seeker."
},
"interview_profile_id": {
"type": "string",
"description": "Candidate profile id. Required for interview-for-profile and results-for-profile."
}
}
}{
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"type": "string",
"example": "https://interviews.example.com/interview/take/<id>/?interview_token=<jwt>&view_hm=true",
"description": "The signed interview URL.",
"x-fastmcp-top-level-schema": "JobInterviewTokenResponse"
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-wrap-result": true
}{
"title": "Generate shareable interview link",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}register_users_for_interview — Register candidates for interview
[Interviews] Register candidates for an interview and return their personal interview links. Sends NO e-mail: to invite candidates by e-mail use invite_candidates. Registers (or updates) one or more candidate profiles for an interview and returns a one-time interview URL for each. Sends no e-mail: share the links yourself, or invite candidates with invite-users (type candidate), which e-mails the invitation. Accepts a Supabase user JWT (merchant/admin) or the service key (merchant_id then required).
{
"type": "object",
"required": [
"users",
"interview_id"
],
"properties": {
"users": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": [
"string",
"null"
],
"example": "Peter Parker",
"description": "Display name of the candidate."
},
"email": {
"type": [
"string",
"null"
],
"example": "peter.parker@example.com",
"description": "Email used to identify and invite the candidate. Validated per item, not request-level."
},
"phone": {
"type": [
"string",
"null"
],
"example": "+421903123456",
"description": "Optional mobile number in international format (E.164, e.g. +421903123456). Enables SMS / WhatsApp invitation and reminder notifications when the interview has candidate_notification_channel set. Never overwrites a phone already stored on the candidate."
},
"external_id": {
"type": [
"string",
"null"
],
"example": "abcd",
"description": "Optional caller-supplied id, stored on the candidate profile."
}
}
},
"example": [
{
"name": "Peter Parker",
"email": "peter.parker@example.com",
"external_id": "abcd"
},
{
"name": "Mary Jane Watson",
"email": "mary.jane@example.com"
}
],
"description": "Candidates to register for the interview. Each gets a one-time interview URL."
},
"is_test": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string"
},
{}
],
"description": "When true (or the string \"true\"), mints a test token that can take a draft/unpublished interview and flags the result as a test. For a DRAFT interview it also recompiles the definition (interview_recalc_definition) first, so the test session includes the current questions/welcome. This makes it a single-call \"trigger interview test\"."
},
"hide_menu": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string"
},
{}
],
"description": "When true (or the string \"true\"), the interview UI hides its menu (view_hm)."
},
"hide_iframe": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string"
},
{}
],
"description": "When true (or the string \"true\"), the interview UI hides its iframe chrome (view_hi)."
},
"merchant_id": {
"type": [
"string",
"null"
],
"format": "uuid",
"example": "b5201178-46dd-4a20-a1af-7ffd647f834b",
"description": "Merchant id. Required when authenticating with the service key; for user tokens it is optional and only honored for admin / sub-merchant accounts."
},
"interview_id": {
"type": "string",
"format": "uuid",
"example": "04e09fa5-3fb8-4236-b37d-fca2bcd1cb66",
"minLength": 1,
"description": "Interview definition (interview_def_set) or position (position_def_set) id to register users for."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": [
"string",
"null"
]
},
"email": {
"type": [
"string",
"null"
]
},
"phone": {
"type": [
"string",
"null"
]
},
"result": {
"type": [
"string",
"null"
],
"example": "ok",
"description": "\"ok\" on success, otherwise an \"Error: ...\" message for that user."
},
"external_id": {
"type": [
"string",
"null"
]
},
"interview_url": {
"type": [
"string",
"null"
],
"description": "One-time interview URL containing the minted interview_token. Present only on success."
}
},
"additionalProperties": {}
},
"description": "The input users, each annotated with its registration result and (on success) interview URL.",
"x-fastmcp-top-level-schema": "JobInterviewRegisterUsersResponse"
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-wrap-result": true
}{
"title": "Register candidates for interview",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}list_interviews — List interviews
[Interviews] List the merchant's interview definitions. Paginated list of a merchant's interview definitions (the admin-portal interview list), scoped to your token's merchant (or a merchant_id override). Capped at 1000 records per page.
{
"type": "object",
"required": [],
"properties": {
"type": {
"enum": [
"interview",
"coaching",
"assessment",
"public_avatar",
"persona",
"persona_interview"
],
"type": "string",
"default": "interview",
"example": "interview",
"description": "Product type of interviews to list."
},
"limit": {
"type": "integer",
"default": 50,
"example": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1–1000)."
},
"offset": {
"type": "integer",
"default": 0,
"example": 0,
"minimum": 0,
"description": "Number of records to skip from the start of the result set."
},
"status": {
"enum": [
"draft",
"active",
"archived",
"deleted",
"preparing",
"completed"
],
"type": "string",
"example": "active",
"description": "Filter by lifecycle status. Omit to include all statuses."
},
"show_demo": {
"enum": [
"true",
"false"
],
"type": "string",
"default": "false",
"example": "false",
"description": "Include demo/sample interviews."
},
"filter_text": {
"type": "string",
"description": "Case-insensitive search on the interview name."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"show_public": {
"enum": [
"true",
"false"
],
"type": "string",
"default": "false",
"example": "false",
"description": "Include interviews shared publicly across merchants (coaching/avatars)."
},
"filter_emoji": {
"type": "string",
"description": "Filter by the interview emoji marker."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"data",
"pagination"
],
"properties": {
"data": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "Interview definition id."
},
"name": {
"type": [
"string",
"null"
],
"description": "Interview name."
},
"type": {
"type": [
"string",
"null"
],
"description": "Product type (interview/coaching/…)."
},
"emoji": {
"type": [
"string",
"null"
],
"description": "Emoji marker."
},
"is_ats": {
"type": [
"boolean",
"null"
],
"description": "Whether the interview is ATS-integrated."
},
"status": {
"type": [
"string",
"null"
],
"description": "Lifecycle status."
},
"recording": {
"type": [
"string",
"null"
],
"description": "Per-answer recording/proctoring mode."
},
"created_at": {
"type": [
"string",
"null"
],
"description": "Creation timestamp (ISO 8601)."
},
"updated_at": {
"type": [
"string",
"null"
],
"description": "Last update timestamp (ISO 8601)."
},
"visibility": {
"type": [
"string",
"null"
],
"description": "Visibility (merchant_public/invite/unlisted/…)."
},
"environment": {
"type": [
"string",
"null"
],
"description": "Environment (production/test/…)."
},
"merchant_id": {
"type": [
"string",
"null"
],
"description": "Owning merchant id."
},
"type_avatar": {
"type": [
"string",
"null"
],
"description": "Avatar type used by the interview."
},
"calc_duration": {
"type": [
"number",
"null"
],
"description": "Calculated duration."
},
"interview_last": {
"type": [
"string",
"null"
],
"description": "Timestamp of the most recent interview (ISO 8601)."
},
"is_multi_stage": {
"type": [
"boolean",
"null"
],
"description": "Whether the interview is multi-stage."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"description": "Cover image URL."
},
"interview_count": {
"type": [
"number",
"null"
],
"description": "Number of interviews run against this definition."
},
"mojito_language_code": {
"type": [
"string",
"null"
],
"description": "Platform language code."
},
"speech_language_code": {
"type": [
"string",
"null"
],
"description": "Speech language code."
},
"recording_full_session": {
"type": [
"string",
"null"
],
"description": "Full-session recording mode."
},
"billing_single_position": {
"type": [
"boolean",
"null"
],
"description": "Single-position billing flag."
},
"billing_single_position_end_at": {
"type": [
"string",
"null"
],
"description": "Single-position billing end (ISO 8601)."
}
},
"additionalProperties": {}
},
"description": "The merchant's interview definitions for this page, newest-updated first."
},
"pagination": {
"type": "object",
"required": [
"total",
"limit",
"offset",
"has_more"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"example": 50,
"description": "Page size used for this response."
},
"total": {
"type": [
"integer",
"null"
],
"example": 137,
"description": "Total matching records across all pages."
},
"offset": {
"type": [
"integer",
"null"
],
"example": 0,
"description": "Offset used for this response."
},
"has_more": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "True when more records exist beyond this page (offset + returned < total)."
}
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "MerchantInterviewListResponse"
}{
"title": "List interviews",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}create_catalogue_directory — Create coaching catalogue directory
[Coaching catalogue] Create a directory (page) in the coaching portal catalogue. A directory nests other directories (`tags_sub`), lists coaching sessions whose own `tags` match its `tags_interview_set_filter`, and can carry a fully custom Markdown page (`content_md`) with `[sessions]`, `[directory:…]`, `[session:…]` and `[plan-progress]` directives. One directory can hold several languages: the top-level fields are the default language, `translations` adds the others. Coaching-platform feature. Creates a directory (page) in the coaching portal catalogue. A directory nests other directories through `tags_sub`, lists coaching sessions through `tags_interview_set_filter`, and can replace the default grid with a custom Markdown page through `content_md`. The id you choose is the catalogue URL segment and cannot be changed afterwards.
{
"type": "object",
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "string",
"example": "sales-coaching-en",
"pattern": "^[a-z0-9]+(?:(?:-|_)+[a-z0-9]+)*$",
"minLength": 1,
"description": "Directory id — also the catalogue URL segment (/catalogue/<id>) and the value other directories reference in their `tags_sub`. Lowercase letters, digits and single - or _ separators. Convention is to end language-specific directories with the language code, e.g. `sales-coaching-en`."
},
"name": {
"type": "string",
"example": "Sales coaching",
"minLength": 1,
"description": "Display name of the directory, shown as the page title and on its card."
},
"status": {
"enum": [
"draft",
"active",
"archived",
null
],
"type": [
"string",
"null"
],
"example": "active",
"description": "Lifecycle status of the catalogue directory. Options — `draft`: Not published — the directory exists but is not served to visitors. | `active`: Published and served in the catalogue. | `archived`: Retired — kept for reference but no longer served.."
},
"tags_sub": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"sales-coaching-objections-en",
"sales-coaching-closing-en"
],
"description": "Ids of the directories nested under this one, in display order. Replaces the whole list — send the full set, not just the additions. A referenced directory only appears if it exists and is visible to the viewer."
},
"coach_plan": {
"enum": [
"demo",
"screening",
"2nd",
"3rd",
"closing",
"job-specific",
"other",
null
],
"type": [
"string",
"null"
],
"example": "screening",
"description": "Coaching-plan stage this item belongs to, used by the coaching-plan progress view. Omit/null to leave it out of any plan. Options — `demo`: Demo session. | `screening`: Screening-interview practice. | `2nd`: Second-interview practice. | `3rd`: Third-interview practice. | `closing`: Closing / salary-negotiation practice. | `job-specific`: Job-specific coaching. | `other`: Anything that does not fit the other buckets.."
},
"content_md": {
"type": [
"string",
"null"
],
"example": "## Sales coaching\n\nPick a session to practise with.\n\n[chip:6 sessions] [chip:Beginner,tone=accent]\n\n[sessions:filter=objection-handling,limit=6]\n",
"description": "Markdown for a custom directory page. When set (even as an empty string) the markdown replaces the default grid and decides the layout itself; null renders the plain grid of sub-directories and sessions. Alongside normal Markdown you can place these directives, each ALONE on its own line: `[plan-progress]` (the learner's coaching-plan progress), `[directory:<tag-id>]` (a card for one sub-directory), `[session:<interview-id>]` (a card for one session), `[sessions]` (every session in this directory), `[sessions:<term>]` (sessions matching a term), `[sessions:filter=<term>,limit=<n>]` (a filtered, capped list). A directive on a line with other text is rendered as ordinary text. Chips, callouts, cards, columns and buttons are available here too — full vocabulary: https://developer.jobmojito.com/cookbooks/format-content-with-markdown"
},
"parent_tag": {
"type": [
"string",
"null"
],
"example": "home-employee-en",
"description": "Id of an existing directory to nest this new one under: the new id is appended to that directory's `tags_sub`. Omit to create a top-level directory (reachable via a direct link, or by adding it to another directory later)."
},
"visibility": {
"enum": [
"public",
"merchant_public",
"merchant_invite",
"merchant_unlisted",
null
],
"type": [
"string",
"null"
],
"example": "merchant_public",
"description": "Who can see the catalogue directory. Options — `public`: Shared across every merchant. Platform admins only — a merchant caller is rejected by row-level security. | `merchant_public`: Listed in the merchant's own catalogue — the normal choice. | `merchant_invite`: Owned by the merchant but not listed; reachable only for invited users. | `merchant_unlisted`: Owned by the merchant but not listed; reachable only via a direct link.."
},
"description": {
"type": [
"string",
"null"
],
"example": "Practice discovery, objection handling and closing.",
"description": "Short description shown on the directory card."
},
"merchant_id": {
"type": [
"string",
"null"
],
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Merchant that owns the directory. Admin / sub-merchant callers only; otherwise taken from your token."
},
"translations": {
"type": [
"object",
"null"
],
"example": {
"sk": {
"name": "Obchodný koučing",
"tags_interview_set_filter": [
"sales-sk"
]
}
},
"description": "Other language versions of the directory, keyed by mojito language code. The top-level fields are the default language (`mojito_language_code`); each version is a page of its own (name, Markdown, session filter, sub-directories) shown to learners who pick that language. A key may not equal `mojito_language_code`.",
"additionalProperties": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"example": "Obchodný koučing",
"minLength": 1,
"description": "Page title and card name in this language."
},
"tags_sub": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"sales-coaching-objections-en",
"sales-coaching-closing-en"
],
"description": "Ids of the directories nested under this one, in display order. Replaces the whole list — send the full set, not just the additions. A referenced directory only appears if it exists and is visible to the viewer."
},
"content_md": {
"type": [
"string",
"null"
],
"example": "## Sales coaching\n\nPick a session to practise with.\n\n[chip:6 sessions] [chip:Beginner,tone=accent]\n\n[sessions:filter=objection-handling,limit=6]\n",
"description": "Markdown for a custom directory page. When set (even as an empty string) the markdown replaces the default grid and decides the layout itself; null renders the plain grid of sub-directories and sessions. Alongside normal Markdown you can place these directives, each ALONE on its own line: `[plan-progress]` (the learner's coaching-plan progress), `[directory:<tag-id>]` (a card for one sub-directory), `[session:<interview-id>]` (a card for one session), `[sessions]` (every session in this directory), `[sessions:<term>]` (sessions matching a term), `[sessions:filter=<term>,limit=<n>]` (a filtered, capped list). A directive on a line with other text is rendered as ordinary text. Chips, callouts, cards, columns and buttons are available here too — full vocabulary: https://developer.jobmojito.com/cookbooks/format-content-with-markdown"
},
"description": {
"type": [
"string",
"null"
],
"description": "Card description in this language."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"description": "Cover image for this language. Omit to show the default language's cover."
},
"tags_interview_set_filter": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"sales",
"objection-handling"
],
"description": "Tag filter selecting which coaching sessions this directory lists: a session appears when its own `tags` contain EVERY tag here (an AND, not an OR). Only `active` coaching/persona sessions with visibility `public` or `merchant_public` are listed. Set the matching tags on the session with the create-interview / job-interview-update `tags` field."
}
},
"additionalProperties": false
}
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"description": "Cover image URL shown on the directory card."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi",
null
],
"type": [
"string",
"null"
],
"example": "en",
"description": "Language of the directory (one of the platform-languages.json codes). The catalogue groups directories by language; defaults to `en` when omitted."
},
"tags_interview_set_filter": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"sales",
"objection-handling"
],
"description": "Tag filter selecting which coaching sessions this directory lists: a session appears when its own `tags` contain EVERY tag here (an AND, not an OR). Only `active` coaching/persona sessions with visibility `public` or `merchant_public` are listed. Set the matching tags on the session with the create-interview / job-interview-update `tags` field."
}
}
}{
"type": "object",
"required": [
"id",
"merchant_id",
"parent_tag",
"catalogue_url"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"example": "sales-coaching-en",
"description": "Id of the created directory."
},
"parent_tag": {
"type": [
"string",
"null"
],
"description": "The directory this one was nested under, when `parent_tag` was supplied."
},
"merchant_id": {
"type": [
"string",
"null"
],
"description": "Owning merchant id. Null for a platform-wide (`public`) directory."
},
"catalogue_url": {
"type": [
"string",
"null"
],
"example": "https://coaching.example.com/catalogue/sales-coaching-en",
"description": "Public URL of the directory page, when the merchant has a coaching-portal domain configured. Null otherwise."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"description": "The created catalogue directory.",
"x-fastmcp-top-level-schema": "CatalogueTagCreateResponse"
}{
"title": "Create coaching catalogue directory",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}update_catalogue_directory — Update coaching catalogue directory
[Coaching catalogue] Update a coaching catalogue directory: rename it, change which sessions it lists (`tags_interview_set_filter`), re-order its sub-directories (`tags_sub`), author its custom Markdown page (`content_md`), or add, replace or remove a language version (`translations`, per language; null removes one). Only the fields you send are changed. A top-level null counts as not sent, except `content_md: null`, the only way to remove the custom page and restore the default grid: leave `content_md` out unless that is what you mean. Coaching-platform feature. Updates a directory (page) of the coaching portal catalogue. Only the fields present in the request body are written — everything else keeps its current value. A top-level null argument counts as not sent, except on content_md. Use it to rename a directory, re-point which sessions it lists (`tags_interview_set_filter`), re-order or replace its sub-directories (`tags_sub`), author its custom Markdown page (`content_md`), or add, replace and remove its other language versions (`translations`).
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"example": "sales-coaching-en",
"pattern": "^[a-z0-9]+(?:(?:-|_)+[a-z0-9]+)*$",
"minLength": 1,
"description": "Id of the directory to update (the catalogue URL segment). The id itself is not changed here — rename a directory in the admin catalogue editor, which moves every link to it."
},
"name": {
"type": "string",
"example": "Sales coaching",
"minLength": 1,
"description": "Display name of the directory."
},
"status": {
"enum": [
"draft",
"active",
"archived"
],
"type": "string",
"example": "active",
"description": "Lifecycle status of the catalogue directory. Options — `draft`: Not published — the directory exists but is not served to visitors. | `active`: Published and served in the catalogue. | `archived`: Retired — kept for reference but no longer served.."
},
"tags_sub": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"sales-coaching-objections-en",
"sales-coaching-closing-en"
],
"description": "Ids of the directories nested under this one, in display order. Replaces the whole list — send the full set, not just the additions. A referenced directory only appears if it exists and is visible to the viewer."
},
"coach_plan": {
"enum": [
"demo",
"screening",
"2nd",
"3rd",
"closing",
"job-specific",
"other",
null
],
"type": [
"string",
"null"
],
"example": "screening",
"description": "Coaching-plan stage this item belongs to, used by the coaching-plan progress view. Omit to keep the current stage. A null is ignored: use `other` when no stage fits. Options — `demo`: Demo session. | `screening`: Screening-interview practice. | `2nd`: Second-interview practice. | `3rd`: Third-interview practice. | `closing`: Closing / salary-negotiation practice. | `job-specific`: Job-specific coaching. | `other`: Anything that does not fit the other buckets.."
},
"content_md": {
"type": [
"string",
"null"
],
"example": "## Sales coaching\n\nPick a session to practise with.\n\n[chip:6 sessions] [chip:Beginner,tone=accent]\n\n[sessions:filter=objection-handling,limit=6]\n",
"description": "Markdown for the custom directory page. Sending null removes the custom page and restores the default grid; sending a string replaces the whole page. Directives, each ALONE on its own line: `[plan-progress]`, `[directory:<tag-id>]`, `[session:<interview-id>]`, `[sessions]`, `[sessions:<term>]`, `[sessions:filter=<term>,limit=<n>]`. Chips, callouts, cards, columns and buttons are available too — formatting guide: https://developer.jobmojito.com/cookbooks/format-content-with-markdown"
},
"visibility": {
"enum": [
"public",
"merchant_public",
"merchant_invite",
"merchant_unlisted"
],
"type": "string",
"example": "merchant_public",
"description": "Who can see the catalogue directory. Options — `public`: Shared across every merchant. Platform admins only — a merchant caller is rejected by row-level security. | `merchant_public`: Listed in the merchant's own catalogue — the normal choice. | `merchant_invite`: Owned by the merchant but not listed; reachable only for invited users. | `merchant_unlisted`: Owned by the merchant but not listed; reachable only via a direct link.."
},
"description": {
"type": [
"string",
"null"
],
"description": "Short description shown on the directory card."
},
"translations": {
"type": [
"object",
"null"
],
"example": {
"sk": {
"name": "Obchodný koučing",
"tags_interview_set_filter": [
"sales-sk"
]
}
},
"description": "The directory's OTHER language versions, keyed by mojito language code. The top-level fields are the default language (`mojito_language_code`); each version here is a page of its own with its own name, Markdown, session filter and sub-directories, shown to learners who pick that language in the catalogue (falling back to English, then the default). A key may not equal `mojito_language_code`. Only the languages you send change: an object replaces that version, null removes it. To remove every version, send null for each language.",
"additionalProperties": {
"type": [
"object",
"null"
],
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"example": "Obchodný koučing",
"minLength": 1,
"description": "Page title and card name in this language."
},
"tags_sub": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"sales-coaching-objections-en",
"sales-coaching-closing-en"
],
"description": "Ids of the directories nested under this one, in display order. Replaces the whole list — send the full set, not just the additions. A referenced directory only appears if it exists and is visible to the viewer."
},
"content_md": {
"type": [
"string",
"null"
],
"example": "## Sales coaching\n\nPick a session to practise with.\n\n[chip:6 sessions] [chip:Beginner,tone=accent]\n\n[sessions:filter=objection-handling,limit=6]\n",
"description": "Markdown for a custom directory page. When set (even as an empty string) the markdown replaces the default grid and decides the layout itself; null renders the plain grid of sub-directories and sessions. Alongside normal Markdown you can place these directives, each ALONE on its own line: `[plan-progress]` (the learner's coaching-plan progress), `[directory:<tag-id>]` (a card for one sub-directory), `[session:<interview-id>]` (a card for one session), `[sessions]` (every session in this directory), `[sessions:<term>]` (sessions matching a term), `[sessions:filter=<term>,limit=<n>]` (a filtered, capped list). A directive on a line with other text is rendered as ordinary text. Chips, callouts, cards, columns and buttons are available here too — full vocabulary: https://developer.jobmojito.com/cookbooks/format-content-with-markdown"
},
"description": {
"type": [
"string",
"null"
],
"description": "Card description in this language."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"description": "Cover image for this language. Omit to show the default language's cover."
},
"tags_interview_set_filter": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"sales",
"objection-handling"
],
"description": "Tag filter selecting which coaching sessions this directory lists: a session appears when its own `tags` contain EVERY tag here (an AND, not an OR). Only `active` coaching/persona sessions with visibility `public` or `merchant_public` are listed. Set the matching tags on the session with the create-interview / job-interview-update `tags` field."
}
},
"additionalProperties": false
}
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"description": "Cover image URL shown on the directory card. An empty string clears it."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi",
null
],
"type": [
"string",
"null"
],
"example": "en",
"description": "Language of the directory (one of the platform-languages.json codes). The catalogue groups directories by language."
},
"tags_interview_set_filter": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"sales",
"objection-handling"
],
"description": "Tag filter selecting which coaching sessions this directory lists: a session appears when its own `tags` contain EVERY tag here (an AND, not an OR). Only `active` coaching/persona sessions with visibility `public` or `merchant_public` are listed. Set the matching tags on the session with the create-interview / job-interview-update `tags` field."
}
}
}{
"type": "object",
"required": [
"id",
"updated_fields",
"catalogue_url"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"example": "sales-coaching-en",
"description": "Id of the updated directory."
},
"catalogue_url": {
"type": [
"string",
"null"
],
"example": "https://coaching.example.com/catalogue/sales-coaching-en",
"description": "Public URL of the directory page, when the merchant has a coaching-portal domain configured. Null otherwise."
},
"updated_fields": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"content_md",
"tags_interview_set_filter"
],
"description": "Names of the fields that were written."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"description": "Confirmation of what was updated.",
"x-fastmcp-top-level-schema": "CatalogueTagUpdateResponse"
}{
"title": "Update coaching catalogue directory",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}get_catalogue_directory — Get coaching catalogue directory
[Coaching catalogue] Read one catalogue directory in full: its settings, its custom Markdown page (`content_md`), its resolved sub-directories, and the coaching sessions its tag filter currently matches — which is how you verify that a session's `tags` actually place it in this directory. Read before updating: `content_md`, `tags_sub` and `tags_interview_set_filter` are replaced wholesale, so you need the current value to extend it. Other language versions of the page are in `translations` (`languages` lists them, default first). Reads one catalogue directory in full: its settings, its custom Markdown page (`content_md`), the sub-directories it nests, and the coaching sessions its `tags_interview_set_filter` currently matches. Read a directory before updating it — `content_md`, `tags_sub` and `tags_interview_set_filter` are replaced wholesale by catalogue-tag-update, so you need the current value to extend rather than overwrite it.
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"example": "sales-coaching-en",
"minLength": 1,
"description": "Id of the catalogue directory to read (the catalogue URL segment). The old id of a renamed directory also works; the response carries its current `id`."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi"
],
"type": "string",
"example": "en",
"description": "Show `sub_directories` and `matched_sessions` for this language version of the directory (one of its `languages`) instead of the default one - how you verify a translation lists the right sessions."
}
}
}{
"type": "object",
"required": [
"id",
"name",
"description",
"mojito_language_code",
"visibility",
"status",
"coach_plan",
"cover_image_url",
"merchant_id",
"tags_sub",
"tags_interview_set_filter",
"content_md",
"translations",
"languages",
"viewed_language",
"merged_into",
"created_at",
"updated_at",
"catalogue_url",
"is_start_directory",
"sub_directories",
"matched_sessions",
"matched_sessions_truncated"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"example": "sales-coaching-en",
"description": "Directory id — also the catalogue URL segment."
},
"name": {
"type": [
"string",
"null"
],
"description": "Display name."
},
"status": {
"type": [
"string",
"null"
],
"description": "Lifecycle status."
},
"tags_sub": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"sales-coaching-objections-en",
"sales-coaching-closing-en"
],
"description": "Ids of the directories nested under this one, in display order. Replaces the whole list — send the full set, not just the additions. A referenced directory only appears if it exists and is visible to the viewer."
},
"languages": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"en",
"sk"
],
"description": "Every language the directory has, the default (`mojito_language_code`) first. `matched_sessions` and `sub_directories` describe the default language unless the request names another one in `mojito_language_code`."
},
"coach_plan": {
"type": [
"string",
"null"
],
"description": "Coaching-plan stage, when the directory belongs to one."
},
"content_md": {
"type": [
"string",
"null"
],
"example": "## Sales coaching\n\nPick a session to practise with.\n\n[chip:6 sessions] [chip:Beginner,tone=accent]\n\n[sessions:filter=objection-handling,limit=6]\n",
"description": "Markdown for a custom directory page. When set (even as an empty string) the markdown replaces the default grid and decides the layout itself; null renders the plain grid of sub-directories and sessions. Alongside normal Markdown you can place these directives, each ALONE on its own line: `[plan-progress]` (the learner's coaching-plan progress), `[directory:<tag-id>]` (a card for one sub-directory), `[session:<interview-id>]` (a card for one session), `[sessions]` (every session in this directory), `[sessions:<term>]` (sessions matching a term), `[sessions:filter=<term>,limit=<n>]` (a filtered, capped list). A directive on a line with other text is rendered as ordinary text. Chips, callouts, cards, columns and buttons are available here too — full vocabulary: https://developer.jobmojito.com/cookbooks/format-content-with-markdown"
},
"created_at": {
"type": [
"string",
"null"
],
"description": "Creation timestamp (ISO 8601)."
},
"updated_at": {
"type": [
"string",
"null"
],
"description": "Last update timestamp (ISO 8601)."
},
"visibility": {
"type": [
"string",
"null"
],
"description": "Who can see it."
},
"description": {
"type": [
"string",
"null"
],
"description": "Short description shown on the directory card."
},
"merchant_id": {
"type": [
"string",
"null"
],
"description": "Owning merchant id. Null for a platform-wide directory."
},
"merged_into": {
"type": [
"string",
"null"
],
"description": "Set when this directory was folded into another as one of its language versions: the directory to edit instead. Its old URL redirects there."
},
"translations": {
"type": [
"object",
"null"
],
"description": "The directory's OTHER language versions, keyed by mojito language code. The top-level fields are the default language (`mojito_language_code`); each version here is a page of its own with its own name, Markdown, session filter and sub-directories, shown to learners who pick that language in the catalogue (falling back to English, then the default). A key may not equal `mojito_language_code`.",
"additionalProperties": {
"type": "object",
"properties": {},
"additionalProperties": {}
}
},
"catalogue_url": {
"type": [
"string",
"null"
],
"example": "https://coaching.example.com/catalogue/sales-coaching-en",
"description": "Public URL of this directory page, when the merchant has a coaching-portal domain configured."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"description": "Cover image URL."
},
"sub_directories": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"id",
"name",
"description",
"status",
"visibility",
"mojito_language_code"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "Sub-directory id."
},
"name": {
"type": [
"string",
"null"
],
"description": "Display name."
},
"status": {
"type": [
"string",
"null"
],
"description": "Lifecycle status."
},
"visibility": {
"type": [
"string",
"null"
],
"description": "Visibility."
},
"description": {
"type": [
"string",
"null"
],
"description": "Short description."
},
"mojito_language_code": {
"type": [
"string",
"null"
],
"description": "Directory language."
}
},
"additionalProperties": {}
},
"description": "The directories listed in `tags_sub`, resolved and in display order. An id in `tags_sub` that does not resolve (deleted, or not visible to you) is simply absent here — compare the two to spot a broken link."
},
"viewed_language": {
"type": [
"string",
"null"
],
"example": "sk",
"description": "The language version `sub_directories` and `matched_sessions` were resolved for: the requested `mojito_language_code`, else the default."
},
"matched_sessions": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"id",
"name",
"type",
"type_avatar",
"status",
"visibility",
"type_credit",
"tags"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "Interview definition id of the coaching/persona session."
},
"name": {
"type": [
"string",
"null"
],
"description": "Session name."
},
"tags": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "The session's own tags — the superset that matched this directory's filter."
},
"type": {
"type": [
"string",
"null"
],
"description": "Product type — `coaching` or `persona`."
},
"status": {
"type": [
"string",
"null"
],
"description": "Lifecycle status (always `active` here — nothing else is listed)."
},
"visibility": {
"type": [
"string",
"null"
],
"description": "Visibility."
},
"type_avatar": {
"type": [
"string",
"null"
],
"description": "Avatar/voice modality of the session."
},
"type_credit": {
"type": [
"string",
"null"
],
"description": "Credit bucket the session draws from."
}
},
"additionalProperties": {}
},
"description": "The coaching/persona sessions this directory currently lists, applying the same rules as the live portal: `active`, type `coaching` or `persona`, visibility `public` or `merchant_public` for this merchant, and `tags` containing every tag of `tags_interview_set_filter`. Empty when the filter is null. Capped at 200."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
},
"is_start_directory": {
"type": [
"boolean",
"null"
],
"description": "True when this is the directory the merchant's catalogue opens on."
},
"mojito_language_code": {
"type": [
"string",
"null"
],
"example": "en",
"description": "Directory language."
},
"tags_interview_set_filter": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"sales",
"objection-handling"
],
"description": "Tag filter selecting which coaching sessions this directory lists: a session appears when its own `tags` contain EVERY tag here (an AND, not an OR). Only `active` coaching/persona sessions with visibility `public` or `merchant_public` are listed. Set the matching tags on the session with the create-interview / job-interview-update `tags` field."
},
"matched_sessions_truncated": {
"type": [
"boolean",
"null"
],
"description": "True when more sessions match than the 200 returned — narrow `tags_interview_set_filter` if you see this."
}
},
"description": "One catalogue directory with its content page, sub-directories and matched sessions.",
"x-fastmcp-top-level-schema": "CatalogueTagGetResponse"
}{
"title": "Get coaching catalogue directory",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}list_catalogue_directories — List coaching catalogue directories
[Coaching catalogue] List the coaching-catalogue directories you can see (your merchant's own plus the platform-wide public ones). Start here to find a directory id, to pick a parent for a new one, or to walk the tree with `parent_tag`; `is_start_directory` marks the page the catalogue opens on. The custom Markdown page is not included — read it with get_catalogue_directory. Returns 25 at a time; page with `offset` while `pagination.has_more` is true. Paginated list of the coaching-catalogue directories visible to you: your merchant's own, plus the platform-wide `public` ones unless you set include_public=false. Use it to find a directory id before updating one, to pick a `parent_tag`, or to walk the tree with `parent_tag`. The custom Markdown page is not included — fetch it per directory with catalogue-tag-get.
{
"type": "object",
"required": [],
"properties": {
"limit": {
"type": "integer",
"default": 25,
"example": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1–1000)."
},
"offset": {
"type": "integer",
"default": 0,
"example": 0,
"minimum": 0,
"description": "Number of records to skip from the start of the result set."
},
"status": {
"enum": [
"draft",
"active",
"archived"
],
"type": "string",
"example": "active",
"description": "Filter by lifecycle status. Omit to include every status you can see (deleted directories are never returned)."
},
"parent_tag": {
"type": "string",
"description": "Return only the directories nested directly under this one (its `tags_sub`), in the parent's own order. Combine with the other filters to narrow further."
},
"visibility": {
"enum": [
"public",
"merchant_public",
"merchant_invite",
"merchant_unlisted"
],
"type": "string",
"example": "merchant_public",
"description": "Filter by visibility."
},
"filter_text": {
"type": "string",
"description": "Case-insensitive search on the directory id, name and description."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"include_public": {
"enum": [
"true",
"false"
],
"type": "string",
"default": "true",
"example": "true",
"description": "Include the platform-wide `public` directories shared across merchants."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi"
],
"type": "string",
"example": "en",
"description": "Filter by language: directories whose default language or one of whose language versions is this one. Omit for all languages."
}
}
}{
"type": "object",
"required": [
"data",
"pagination"
],
"properties": {
"data": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"id",
"languages",
"has_content_page",
"is_start_directory"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"example": "sales-coaching-en",
"description": "Directory id — also the catalogue URL segment and the value referenced in a parent's `tags_sub`."
},
"name": {
"type": [
"string",
"null"
],
"description": "Display name."
},
"status": {
"type": [
"string",
"null"
],
"description": "Lifecycle status (draft/active/archived)."
},
"tags_sub": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Ids of the directories nested under this one, in display order."
},
"languages": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"en",
"sk"
],
"description": "Every language the directory has, its default (`mojito_language_code`) first. Read a version with catalogue-tag-get (`translations`)."
},
"coach_plan": {
"type": [
"string",
"null"
],
"description": "Coaching-plan stage, when the directory belongs to one."
},
"created_at": {
"type": [
"string",
"null"
],
"description": "Creation timestamp (ISO 8601)."
},
"updated_at": {
"type": [
"string",
"null"
],
"description": "Last update timestamp (ISO 8601)."
},
"visibility": {
"type": [
"string",
"null"
],
"description": "Who can see it (public/merchant_public/merchant_invite/merchant_unlisted)."
},
"description": {
"type": [
"string",
"null"
],
"description": "Short description shown on the directory card."
},
"merchant_id": {
"type": [
"string",
"null"
],
"description": "Owning merchant id. Null for a platform-wide directory."
},
"merged_into": {
"type": [
"string",
"null"
],
"description": "Set when this directory was folded into another as one of its language versions (it is archived; its URL redirects there)."
},
"cover_image_url": {
"type": [
"string",
"null"
],
"description": "Cover image URL."
},
"has_content_page": {
"type": [
"boolean",
"null"
],
"description": "True when the directory has a custom Markdown page. Read the Markdown itself with catalogue-tag-get."
},
"is_start_directory": {
"type": [
"boolean",
"null"
],
"description": "True for the directory the merchant's catalogue opens on — the root to navigate down from."
},
"mojito_language_code": {
"type": [
"string",
"null"
],
"example": "en",
"description": "Directory language."
},
"tags_interview_set_filter": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Tag filter selecting the coaching sessions this directory lists: a session appears when its own `tags` contain every tag here."
}
},
"additionalProperties": {}
},
"description": "The catalogue directories for this page, ordered by id."
},
"pagination": {
"type": "object",
"required": [
"total",
"limit",
"offset",
"has_more"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"example": 50,
"description": "Page size used for this response."
},
"total": {
"type": [
"integer",
"null"
],
"example": 137,
"description": "Total matching records across all pages."
},
"offset": {
"type": [
"integer",
"null"
],
"example": 0,
"description": "Offset used for this response."
},
"has_more": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "True when more records exist beyond this page (offset + returned < total)."
}
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "CatalogueTagListResponse"
}{
"title": "List coaching catalogue directories",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}get_interview_result_details — Get interview result details
[Results] Get full interview result details including transcript and scores. Scores are assistive output for a human reviewer. One result is a large record, so `view` controls how much of it comes back — the default (`standard`) omits only the raw machine assessment data. Returns an interview result with its full transcript and AI assessment. Optionally attaches signed recording URLs.
{
"type": "object",
"required": [
"interview_result_id"
],
"properties": {
"view": {
"enum": [
"summary",
"standard",
"full"
],
"type": "string",
"default": "standard",
"description": "How much of the record to return. Handled by the MCP server, not the JobMojito API — it only narrows the response, never the query.\n\n- `summary`: scores, the overall AI analysis, and each question with the candidate's answer — no per-answer AI commentary, recording paths or raw assessment data. Use this to review or compare candidates.\n- `standard`: everything a human reviewer reads: the full transcript with per-answer analysis, scores and recordings, minus the raw machine assessment blobs. This is the default.\n- `full`: every field, including the per-answer raw assessment data — condensed: pronunciation and sentiment scores per recognised segment with the lowest-scoring words, reading/gaze statistics, STT reliability, proctor status and seconds per detected speaker. Word-, character- and frame-level arrays are left out (listed in `_omitted`). Only ask for this if you need those assessment details."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"interview_result_id": {
"type": "string",
"format": "uuid",
"example": "93c98d21-e04d-4a84-9afa-ed154cf73636",
"minLength": 1,
"description": "The interview result to fetch the transcript and details for."
},
"get_signed_recordings": {
"enum": [
"true",
"false"
],
"type": "string",
"default": "false",
"example": "false",
"description": "When true, includes short-lived signed recording URLs for the session, the video introduction, and each transcript answer."
}
}
}{
"type": "object",
"properties": {
"end": {
"type": [
"string",
"null"
],
"description": "Interview end timestamp (training_end, ISO 8601)."
},
"score": {
"type": [
"number",
"null"
],
"description": "Overall interview score."
},
"start": {
"type": [
"string",
"null"
],
"description": "Interview start timestamp (training_start, ISO 8601)."
},
"status": {
"type": [
"string",
"null"
],
"description": "Coach/interview status (coach_status), e.g. started, completed."
},
"duration": {
"type": [
"number",
"null"
],
"description": "Total duration in deciseconds (duration_ds)."
},
"score_text": {
"type": [
"string",
"null"
],
"description": "Human-readable score summary."
},
"transcript": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "interview_result_question id."
},
"view": {
"type": [
"string",
"null"
],
"description": "Question view/type (e.g. with listening, multiple choice, without scoring)."
},
"score": {
"type": [
"number",
"null"
],
"description": "Score for this interaction."
},
"answer": {
"type": [
"string",
"null"
],
"description": "The candidate answer transcript text."
},
"duration": {
"type": [
"number",
"null"
],
"description": "Duration of this interaction in deciseconds (duration_ds)."
},
"is_answer": {
"type": [
"boolean",
"null"
],
"description": "True for a candidate answer turn, false for an avatar/question turn."
},
"ai_analysis": {
"type": [
"string",
"null"
],
"description": "AI analysis of this answer."
},
"external_id": {
"type": [
"string",
"null"
],
"description": "External identifier carried from the question definition."
},
"external_data": {
"description": "External data carried from the question definition."
},
"recording_url": {
"type": [
"string",
"null"
],
"description": "Signed URL for recording_local_path; present only when get_signed_recordings=true."
},
"question_asked": {
"type": [
"string",
"null"
],
"description": "The question text actually asked to the candidate."
},
"score_answer_ai": {
"type": [
"number",
"null"
],
"description": "AI answer score."
},
"score_simulation": {
"type": [
"number",
"null"
],
"description": "Simulated score for this interaction."
},
"question_original": {
"type": [
"string",
"null"
],
"description": "Original question text from the definition (when question_original_id is set)."
},
"recording_is_video": {
"type": [
"boolean",
"null"
],
"description": "Whether this answer recording is video (vs audio)."
},
"mojito_language_code": {
"type": [
"string",
"null"
],
"description": "Platform language code of this interaction."
},
"question_original_id": {
"type": [
"string",
"null"
],
"description": "Source interview_def_question id (null for generated follow-ups)."
},
"recording_local_path": {
"type": [
"string",
"null"
],
"description": "Storage path of this answer recording (private-recordings bucket)."
},
"ai_analysis_recruiter": {
"type": [
"string",
"null"
],
"description": "Recruiter-facing AI analysis of this answer."
},
"answer_assessment_raw_data": {
"description": "Raw per-answer assessment data (pronunciation/sentiment/etc.)."
},
"score_answer_ai_simulation": {
"type": [
"number",
"null"
],
"description": "Simulated AI answer score."
}
},
"additionalProperties": {}
},
"description": "Ordered interactions (questions + answers) of the interview."
},
"ai_analysis": {
"type": [
"string",
"null"
],
"description": "Candidate-facing AI analysis of the whole interview."
},
"score_answer": {
"type": [
"number",
"null"
],
"description": "Aggregate answer sub-score."
},
"recording_url": {
"type": [
"string",
"null"
],
"description": "Signed session-recording URL; present only when get_signed_recordings=true."
},
"recruiter_risks": {
"description": "Detected recruiter risk flags (jsonb)."
},
"score_sentiment": {
"type": [
"number",
"null"
],
"description": "Sentiment sub-score."
},
"score_simulation": {
"type": [
"number",
"null"
],
"description": "Overall simulated score."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
},
"ai_analysis_other": {
"description": "Session-level AI analyses keyed by analysis name (jsonb). `strengths_concerns`: { strengths: string[], concerns: string[] } (0-4 items each, recruiter-facing; called `hiring_reasons` with `reasons_to_hire` / `reasons_not_to_hire` inside before 2026-09-15, and rows written before then may still carry that shape). `takeaways`: { what_went_well: string[], next_steps: { text, priority: impact | quick | longterm, guidance: { kind: say | structure | length | exercise | perspective, text }, proof? }[], fixed?: string[] } (coaching / persona sessions, learner-facing; `proof` entries are { kind: quote | ai_summary | transcript_ref | document, text, ref?: { result_question_id } }; `fixed` lists the steps from the previous attempt that the learner did this time). `multi_department`: the multi-department-selection analysis. `score_rationale`: the rubric bracket of the session score and evidenced counts. `key_statement`: { quote, result_question_id }, the one verbatim quotation a recruiter would remember the candidate by (interview / persona_interview, absent when nothing distinctive was said). Further keys may be added."
},
"recording_is_video": {
"type": [
"boolean",
"null"
],
"description": "Whether the session recording is video."
},
"score_pronunciation": {
"type": [
"number",
"null"
],
"description": "Pronunciation sub-score."
},
"ai_completion_reason": {
"type": [
"string",
"null"
],
"description": "Why the interview completed (ai_completed_reason)."
},
"recording_local_path": {
"type": [
"string",
"null"
],
"description": "Storage path of the full session recording. When the candidate restarted, this is the latest session."
},
"ai_analysis_recruiter": {
"type": [
"string",
"null"
],
"description": "Recruiter-facing AI analysis of the whole interview."
},
"recording_url_history": {
"type": [
"array",
"null"
],
"items": {
"type": [
"string",
"null"
]
},
"description": "Signed URLs of the earlier sessions, in the same order as recording_local_path_history; null for a file that could not be signed. Present only when get_signed_recordings=true and the candidate restarted."
},
"score_words_per_minute": {
"type": [
"number",
"null"
],
"description": "Speaking-pace (words per minute) sub-score."
},
"video_introduction_url": {
"type": [
"string",
"null"
],
"description": "Signed video-introduction URL; present only when get_signed_recordings=true."
},
"user_feedback_recruiter": {
"type": [
"string",
"null"
],
"description": "Recruiter-entered feedback note."
},
"recording_local_path_history": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Storage paths of the earlier sessions when the candidate restarted the interview, oldest first. Null when there is only one recording."
},
"video_introduction_local_path": {
"type": [
"string",
"null"
],
"description": "Storage path of the candidate video introduction."
},
"ai_analysis_recruiter_why_hire": {
"description": "Reasons to hire (list). Deprecated: a copy of ai_analysis_other.strengths_concerns.strengths, kept for existing integrations."
},
"ai_interview_coverage_percentage": {
"type": [
"number",
"null"
],
"description": "Percentage of the intended interview the AI judged to be covered."
},
"ai_analysis_recruiter_why_not_hire": {
"description": "Reasons not to hire (list). Deprecated: a copy of ai_analysis_other.strengths_concerns.concerns, kept for existing integrations."
}
},
"description": "The interview result with its transcript, produced by the interview_transcript RPC. Additional fields may be present (passthrough).",
"additionalProperties": {},
"x-fastmcp-top-level-schema": "JobInterviewDetailsResponse"
}{
"title": "Get interview result details",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}generate_interview_report — Generate interview report
[Results] Generate an interview result report (HTML/PDF/JSON) for a completed interview. The report is decision-support material for a human reviewer, not an automated hiring decision. Generates an interview result report as a PDF (returns a signed URL), raw HTML, or structured JSON. Provide either `interview_result_id` for a single result or `interview_result_ids` for a combined multi-result report.
{
"type": "object",
"required": [
"export_type"
],
"properties": {
"template": {
"allOf": [
{
"enum": [
"classic",
"modern",
"one_pager"
],
"type": "string",
"example": "modern",
"description": "Report layout for this export. Same values as the top-level `template`; the top-level field wins when both are given. Kept here so a saved auto-export configuration carries its template."
},
{
"description": "Report layout: `classic`, `modern` or `one_pager`. Omit for the merchant default (modern when unset)."
}
]
},
"store_file": {
"type": "boolean",
"description": "When true (pdf only), persist the file to storage and return a signed URL."
},
"export_type": {
"enum": [
"pdf",
"html",
"json"
],
"type": "string"
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"interview_result_id": {
"type": "string",
"format": "uuid",
"example": "93c98d21-e04d-4a84-9afa-ed154cf73636"
},
"interview_result_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"description": "Generate a single combined report for multiple results."
},
"export_features_result": {
"type": "object",
"properties": {
"files": {
"type": "boolean",
"default": true,
"description": "Include the candidate's uploaded files (resume and cover letter) as signed page URLs."
},
"template": {
"enum": [
"classic",
"modern",
"one_pager"
],
"type": "string",
"example": "modern",
"description": "Report layout for this export. Same values as the top-level `template`; the top-level field wins when both are given. Kept here so a saved auto-export configuration carries its template."
},
"analytics": {
"type": "boolean",
"default": true,
"description": "Include speech analytics for each answer (speech accuracy, fluency, recognition confidence, sentiment, cadence / words-per-minute)."
},
"transcript": {
"type": "boolean",
"default": true,
"description": "Include the full interview transcript: each question, the candidate answer, and the per-answer AI analysis."
},
"meeting_notes": {
"type": "boolean",
"description": "Candidate packet: add the notes and evaluation forms filled in on the meeting steps of the application, after the interview."
},
"contact_details": {
"type": "boolean",
"default": true,
"description": "Include the candidate's contact details (email, LinkedIn, phone number, physical address)."
},
"job_description": {
"type": "boolean",
"description": "Candidate packet: start the report with the job description of the position (or of the interview when it has no position)."
},
"answer_recording": {
"type": "boolean",
"default": false,
"description": "Include a signed recording URL (audio/video) for each individual answer."
},
"ai_scoring_rubric": {
"type": "boolean",
"default": false,
"description": "Include the AI scoring rubric — the candidate-expectation buckets (strong / moderate / weak). Only takes effect when ai_recruiter_assessment is also true."
},
"group_by_question": {
"type": "boolean",
"default": false,
"description": "Group answers under their expected questions (instead of chronological order), including questions that were not reached."
},
"session_recording": {
"type": "boolean",
"default": false,
"description": "Include the full interview session recording and the video introduction as signed URLs."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi"
],
"type": "string",
"example": "en",
"description": "Report language. Optional: if omitted, the report uses each result's own language code. If provided and it differs from the result's language, the report content is translated, which costs 0.1 credit per result."
},
"ai_recruiter_assessment": {
"type": "boolean",
"default": true,
"description": "Include the AI recruiter assessment: resume analysis, overall AI analysis, and the why-hire / why-not-hire summaries."
}
}
}
}
}{
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"anyOf": [
{
"type": "object",
"title": "PDF export",
"required": [
"pdf_export_url",
"pdf_export_valid_until"
],
"properties": {
"pdf_export_url": {
"type": [
"string",
"null"
],
"format": "uri",
"description": "Signed URL of the generated PDF."
},
"pdf_export_valid_until": {
"type": [
"string",
"null"
],
"example": "2026-09-01T12:00:00.000Z",
"description": "ISO timestamp until which the signed URL is valid."
}
},
"description": "Returned when `export_type` is `pdf`."
},
{
"type": "object",
"title": "HTML export",
"required": [
"html_export"
],
"properties": {
"html_export": {
"type": [
"string",
"null"
],
"description": "The full report rendered as an HTML document string."
}
},
"description": "Returned when `export_type` is `html`."
},
{
"type": "object",
"title": "JSON export",
"required": [
"json_export"
],
"properties": {
"json_export": {
"type": [
"object",
"null"
],
"required": [
"results"
],
"properties": {
"table": {
"type": [
"string",
"null"
],
"description": "Overview table HTML, present only for multi-result exports."
},
"results": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"data",
"files"
],
"properties": {
"data": {
"type": [
"object",
"null"
],
"additionalProperties": {}
},
"files": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"type",
"page",
"page_signed"
],
"properties": {
"page": {
"type": [
"number",
"null"
]
},
"type": {
"type": [
"string",
"null"
]
},
"page_signed": {
"type": [
"string",
"null"
]
}
}
}
}
}
}
}
}
}
},
"description": "Returned when `export_type` is `json`."
}
],
"description": "The generated report. Exactly one of the three shapes below is returned, selected by the request `export_type`: `pdf` → `{ pdf_export_url, pdf_export_valid_until }`; `html` → `{ html_export }`; `json` → `{ json_export }`.",
"x-fastmcp-top-level-schema": "JobInterviewPdfResponse"
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-wrap-result": true
}{
"title": "Generate interview report",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}request_another_interview_attempt — Re-open interview for another attempt
[Results] Re-open a submitted interview result so the candidate can retry. Company owners, recruiters and platform admins only. Marks a submitted (active + completed) interview result as unsuccessful so the candidate can retake it. Resets the result to draft and clears the recruiter decision.
{
"type": "object",
"required": [
"interview_result_id"
],
"properties": {
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"interview_result_id": {
"type": "string",
"format": "uuid",
"example": "93c98d21-e04d-4a84-9afa-ed154cf73636",
"minLength": 1,
"description": "The interview result to reopen for another attempt."
}
}
}{
"type": "object",
"properties": {
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"description": "Empty object on success.",
"x-fastmcp-top-level-schema": "RequestAnotherAttemptResponse"
}{
"title": "Re-open interview for another attempt",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}set_assessment_result — Record an external assessment result
[Results] Record the result of a candidate's external assessment step (status, score, pass / fail, report link). The step then waits for the recruiter's decision. Records the result of an external assessment step of an application (status, score, pass / fail, report link). A result never decides on its own: the step then waits for the recruiter to pass or fail it. Requires the token of a merchant or merchant owner of the company; available when external assessments are enabled for the company.
{
"type": "object",
"required": [
"position_result_step_id",
"status"
],
"properties": {
"note": {
"type": "string",
"maxLength": 2000,
"description": "Note for the recruiter."
},
"score": {
"type": "number",
"example": 78,
"description": "Score (completed)."
},
"passed": {
"type": "boolean",
"description": "Pass / fail as the assessment reports it. Without it the step's pass mark decides it from the score."
},
"status": {
"enum": [
"started",
"completed",
"expired",
"cancelled"
],
"type": "string",
"description": "started, completed (a result), expired (not taken in time) or cancelled."
},
"score_max": {
"type": "number",
"example": 100,
"minimum": 0,
"description": "Maximum score.",
"exclusiveMinimum": 1
},
"report_url": {
"type": "string",
"format": "uri",
"maxLength": 2000,
"description": "Link to the full report (https)."
},
"result_label": {
"type": "string",
"example": "B2",
"maxLength": 200,
"description": "Result label, for example a language level."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"position_result_step_id": {
"type": "string",
"format": "uuid",
"example": "6a0f2d4e-1c3b-4e5f-8a7b-9c0d1e2f3a4b",
"minLength": 1,
"description": "The external assessment step of the application (position_result_step id)."
}
}
}{
"type": "object",
"required": [
"outcome"
],
"properties": {
"score": {
"type": [
"number",
"null"
]
},
"passed": {
"type": [
"boolean",
"null"
]
},
"status": {
"type": [
"string",
"null"
],
"description": "Status of the assessment after the call."
},
"outcome": {
"enum": [
"applied",
"duplicate",
"ignored_final",
null
],
"type": [
"string",
"null"
],
"description": "applied, or ignored_final for a cancelled assessment."
},
"score_max": {
"type": [
"number",
"null"
]
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"description": "The recorded result. The step now waits for the recruiter's decision.",
"x-fastmcp-top-level-schema": "AssessmentResultSetResponse"
}{
"title": "Record an external assessment result",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}list_interview_results — List interview results
[Results] List the merchant's interview results. Rows carry the candidate's scores and recruiter risk flags, so this returns 20 at a time; page with `offset` while `pagination.has_more` is true, or narrow with `tab`/`interview_id`/`filter_text`. Paginated list of a merchant's interview results (the admin-portal results list), scoped to your token's merchant (or a merchant_id override). Capped at 1000 records per page.
{
"type": "object",
"required": [],
"properties": {
"tab": {
"enum": [
"decided-rejected",
"undecided",
"shortlist",
"decided-selected",
"completed",
"incomplete",
"archived-interview",
"public_avatar",
"archived-public_avatar",
""
],
"type": "string",
"example": "undecided",
"description": "Filter by decision/completion state. Omit (or empty) to include all."
},
"step": {
"enum": [
"pre-screening",
"interview",
"meeting"
],
"type": "string",
"example": "interview",
"description": "Filter by pipeline step type (pre-screening, interview or meeting). Omit for all."
},
"type": {
"enum": [
"interview",
"coaching",
"assessment",
"public_avatar",
"persona",
"persona_interview"
],
"type": "string",
"default": "interview",
"example": "interview",
"description": "Product type of results to list."
},
"limit": {
"type": "integer",
"default": 20,
"example": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1–1000)."
},
"risks": {
"type": "string",
"description": "Comma-separated list of recruiter-risk keys to filter by (matches any)."
},
"offset": {
"type": "integer",
"default": 0,
"example": 0,
"minimum": 0,
"description": "Number of records to skip from the start of the result set."
},
"order_by": {
"enum": [
"score",
"created_at_newest",
"created_at_oldest",
"updated_at_newest"
],
"type": "string",
"default": "created_at_newest",
"example": "created_at_newest",
"description": "Sort order of the result set."
},
"filter_text": {
"type": "string",
"description": "Case-insensitive search on candidate name or email."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"filter_emoji": {
"type": "string",
"description": "Filter by the candidate emoji marker."
},
"interview_id": {
"type": "string",
"format": "uuid",
"description": "Filter to a single interview definition id."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"profile_interview_id": {
"type": "string",
"format": "uuid",
"description": "Filter to a single candidate (profile_interview) id."
}
}
}{
"type": "object",
"required": [
"data",
"pagination"
],
"properties": {
"data": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "Position result row id."
},
"type": {
"type": [
"string",
"null"
],
"description": "Product type."
},
"emoji": {
"type": [
"string",
"null"
],
"description": "Emoji marker."
},
"score": {
"type": [
"number",
"null"
],
"description": "Overall score."
},
"status": {
"type": [
"string",
"null"
],
"description": "Row status."
},
"is_test": {
"type": [
"boolean",
"null"
],
"description": "Whether a test result."
},
"created_at": {
"type": [
"string",
"null"
],
"description": "Creation timestamp (ISO 8601)."
},
"profile_id": {
"type": [
"string",
"null"
],
"description": "Candidate profile id."
},
"updated_at": {
"type": [
"string",
"null"
],
"description": "Last update timestamp (ISO 8601)."
},
"duration_ds": {
"type": [
"number",
"null"
],
"description": "Duration in deciseconds."
},
"external_id": {
"type": [
"string",
"null"
],
"description": "External identifier."
},
"merchant_id": {
"type": [
"string",
"null"
],
"description": "Owning merchant id."
},
"coach_status": {
"type": [
"string",
"null"
],
"description": "Interview/coach status (started/completed/…)."
},
"definition_id": {
"type": [
"string",
"null"
],
"description": "Interview definition id."
},
"position_name": {
"type": [
"string",
"null"
],
"description": "Position / interview name."
},
"position_step": {
"type": [
"string",
"null"
],
"description": "Pipeline step."
},
"candidate_name": {
"type": [
"string",
"null"
],
"description": "Candidate name."
},
"is_multi_stage": {
"type": [
"boolean",
"null"
],
"description": "Whether multi-stage."
},
"candidate_email": {
"type": [
"string",
"null"
],
"description": "Candidate email."
},
"decision_status": {
"type": [
"string",
"null"
],
"description": "Hiring decision (selected/rejected/…)."
},
"recruiter_risks": {
"description": "Detected recruiter risk flags (jsonb)."
},
"score_answer_ai": {
"type": [
"number",
"null"
],
"description": "AI answer sub-score."
},
"score_sentiment": {
"type": [
"number",
"null"
],
"description": "Sentiment sub-score."
},
"processing_error": {
"type": [
"string",
"null"
],
"description": "Processing error, if any."
},
"score_simulation": {
"type": [
"number",
"null"
],
"description": "Simulated overall score."
},
"interview_started": {
"type": [
"boolean",
"null"
],
"description": "Whether the interview was started."
},
"processing_status": {
"type": [
"string",
"null"
],
"description": "Processing status."
},
"position_step_name": {
"type": [
"string",
"null"
],
"description": "Custom name of the position step, null when the step uses its default label."
},
"score_interview_ai": {
"type": [
"number",
"null"
],
"description": "AI interview sub-score."
},
"interview_result_id": {
"type": [
"string",
"null"
],
"description": "Interview result id (use with merchant-... detail endpoints)."
},
"position_step_count": {
"type": [
"number",
"null"
],
"description": "Number of steps in the position process."
},
"position_step_index": {
"type": [
"number",
"null"
],
"description": "1-based position of the step in the position process."
},
"recruiter_shortlist": {
"type": [
"boolean",
"null"
],
"description": "Whether recruiter-shortlisted."
},
"score_pronunciation": {
"type": [
"number",
"null"
],
"description": "Pronunciation sub-score."
},
"mojito_language_code": {
"type": [
"string",
"null"
],
"description": "Platform language code."
},
"position_def_step_id": {
"type": [
"string",
"null"
],
"description": "Position step definition id the row belongs to (null for single-stage interviews)."
},
"profile_interview_id": {
"type": [
"string",
"null"
],
"description": "Candidate (profile_interview) id."
},
"kyc_processing_status": {
"type": [
"string",
"null"
],
"description": "Deprecated, always null: the retired identity verification (KYC) is no longer returned."
},
"score_words_per_minute": {
"type": [
"number",
"null"
],
"description": "Speaking-pace sub-score."
},
"position_result_step_id": {
"type": [
"string",
"null"
],
"description": "Position result step id."
},
"interview_result_pre_screening_id": {
"type": [
"string",
"null"
],
"description": "Pre-screening result id, if any."
}
},
"additionalProperties": {}
},
"description": "The merchant's interview results for this page."
},
"pagination": {
"type": "object",
"required": [
"total",
"limit",
"offset",
"has_more"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"example": 50,
"description": "Page size used for this response."
},
"total": {
"type": [
"integer",
"null"
],
"example": 137,
"description": "Total matching records across all pages."
},
"offset": {
"type": [
"integer",
"null"
],
"example": 0,
"description": "Offset used for this response."
},
"has_more": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "True when more records exist beyond this page (offset + returned < total)."
}
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "MerchantResultListResponse"
}{
"title": "List interview results",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}get_merchant_analytics — Get merchant analytics
[Results] Get the merchant's daily event analytics. Daily event-count time-series for a merchant over a date range (the admin-portal analytics events graph), scoped to your token's merchant (or a merchant_id override). Optionally drilled to a single interview. Capped at 1000 records per page. Note: only day/event combinations with a non-zero count are returned — any day/event pair absent from the response should be treated as a count of 0 by the caller.
{
"type": "object",
"required": [
"date_from",
"date_to"
],
"properties": {
"limit": {
"type": "integer",
"default": 50,
"example": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1–1000)."
},
"offset": {
"type": "integer",
"default": 0,
"example": 0,
"minimum": 0,
"description": "Number of records to skip from the start of the result set."
},
"date_to": {
"type": "string",
"example": "2026-01-01",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "End of the date range (inclusive), YYYY-MM-DD."
},
"date_from": {
"type": "string",
"example": "2026-01-01",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Start of the date range (inclusive), YYYY-MM-DD."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"interview_id": {
"type": "string",
"format": "uuid",
"description": "Optional interview (interview_def_set) or position (position_def_set) id to drill the event counts down. The type is detected automatically: a position aggregates the daily counts across every interview that makes up the position; an interview filters to that single definition."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"data",
"pagination"
],
"properties": {
"data": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"day",
"event",
"event_number"
],
"properties": {
"day": {
"type": [
"string",
"null"
],
"example": "2026-01-15",
"description": "Calendar day (YYYY-MM-DD)."
},
"event": {
"type": [
"string",
"null"
],
"description": "Event type (e.g. interview, interview_invitation, interview_page_visit)."
},
"event_number": {
"type": [
"number",
"null"
],
"example": 12,
"description": "Count of this event on this day. Always greater than 0 — zero-count day/event combinations are omitted."
}
},
"additionalProperties": {}
},
"description": "Daily event counts for the requested range, ordered by day ascending. Only day/event combinations with a non-zero count are returned; missing combinations should be treated as 0 by the caller."
},
"pagination": {
"type": "object",
"required": [
"total",
"limit",
"offset",
"has_more"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"example": 50,
"description": "Page size used for this response."
},
"total": {
"type": [
"integer",
"null"
],
"example": 137,
"description": "Total matching records across all pages."
},
"offset": {
"type": [
"integer",
"null"
],
"example": 0,
"description": "Offset used for this response."
},
"has_more": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "True when more records exist beyond this page (offset + returned < total)."
}
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "MerchantAnalyticsResponse"
}{
"title": "Get merchant analytics",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}get_merchant_credit_usage — Get merchant credit usage
[Results] Get the merchant's credit usage. Per-event credit-usage ledger for a merchant: every billable analytics event (interview, pre-screening, public avatar, simulation, …) that consumed credits, ordered most recent first. Scoped to your token's merchant, or a merchant_id override for admins / sub-merchant operators. The credits consumed by each event are in stats.credit_amount. Capped at 1000 records per page.
{
"type": "object",
"required": [],
"properties": {
"limit": {
"type": "integer",
"default": 50,
"example": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1–1000)."
},
"offset": {
"type": "integer",
"default": 0,
"example": 0,
"minimum": 0,
"description": "Number of records to skip from the start of the result set."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"interview_id": {
"type": "string",
"format": "uuid",
"description": "Optional interview (interview_def_set) or position (position_def_set) id to drill the credit-usage ledger down to a single interview or position. The type is detected automatically: for a position the response combines pre-screening and interview-result credits across the whole position; for an interview it returns that interview's credit events (including simulations and report translations)."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"data",
"pagination"
],
"properties": {
"data": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"id",
"event_date",
"event",
"credit_amount",
"candidate_name",
"profile_id",
"profile_interview_id",
"object_uuid",
"parent_object_uuid"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"format": "uuid",
"description": "Analytics event id."
},
"event": {
"type": [
"string",
"null"
],
"example": "interview",
"description": "Billable event type that consumed the credit (e.g. interview, interview_reviewing, interview_failed, interview_incomplete, pre_screening, candidate_report_translation, candidate_kyc, public_avatar, interview_simulation)."
},
"stats": {
"description": "Event stats JSON. Contains credit_amount (the credits consumed by this event) plus event-specific fields."
},
"event_date": {
"type": [
"string",
"null"
],
"example": "2026-01-15T09:30:00.000Z",
"description": "When the credit-consuming event occurred (ISO 8601)."
},
"profile_id": {
"type": [
"string",
"null"
],
"format": "uuid",
"description": "Underlying person (profile) id, when linked."
},
"object_uuid": {
"type": [
"string",
"null"
],
"format": "uuid",
"description": "Primary object the event refers to (e.g. the interview result / attempt)."
},
"credit_amount": {
"type": [
"number",
"null"
],
"example": 1,
"description": "Number of credits deducted by this event, surfaced from stats.credit_amount for convenience. Null when the amount is absent."
},
"candidate_name": {
"type": [
"string",
"null"
],
"description": "Candidate display name from the linked profile interview, when available."
},
"parent_object_uuid": {
"type": [
"string",
"null"
],
"format": "uuid",
"description": "Parent object of the event (e.g. the interview definition), when applicable."
},
"profile_interview_id": {
"type": [
"string",
"null"
],
"format": "uuid",
"description": "Candidate enrolment (profile interview) id, when linked."
}
},
"additionalProperties": {}
},
"description": "Credit-consuming analytics events for the merchant, most recent first. Each row is one billable event; the credits it consumed are in stats.credit_amount."
},
"pagination": {
"type": "object",
"required": [
"total",
"limit",
"offset",
"has_more"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"example": 50,
"description": "Page size used for this response."
},
"total": {
"type": [
"integer",
"null"
],
"example": 137,
"description": "Total matching records across all pages."
},
"offset": {
"type": [
"integer",
"null"
],
"example": 0,
"description": "Offset used for this response."
},
"has_more": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "True when more records exist beyond this page (offset + returned < total)."
}
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "MerchantCreditsUsedResponse"
}{
"title": "Get merchant credit usage",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}list_report_fields — List report datasets and fields
[Reports] List what a custom report can use on this account: datasets (interview results, invitations, position pipeline, ATS pipeline, credits, coaching sessions - only those the account and your role include), their dimensions (group by / conditions, with allowed values) and measures. Call this first: a report definition may only use ids listed here. Custom reports are switched on per account by JobMojito; while they are off, every report tool is refused with 403 'Custom reports are not enabled for this account.' Tell the user to contact JobMojito rather than retrying. Everything a custom report may use on this account: the datasets (interview results, invitations, position pipeline, ATS pipeline, credits, coaching sessions - only those your account and role include), their dimensions (group by / filter) and measures (numbers), block types, condition operators and period presets. Read this before writing a report definition: ids not listed here are refused. Pass dataset to get only that dataset (the whole catalogue of a large account is long).
{
"type": "object",
"required": [],
"properties": {
"dataset": {
"type": "string",
"example": "positions",
"pattern": "^[a-z][a-z0-9_]{0,40}$",
"description": "Only this dataset (e.g. positions, interview_results); the other keys are returned as usual. Unknown ids return no datasets."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"schema",
"datasets",
"block_types",
"where_ops",
"period_presets",
"limits",
"can_share"
],
"properties": {
"limits": {
"type": [
"object",
"null"
],
"additionalProperties": {}
},
"schema": {
"type": [
"string",
"null"
]
},
"datasets": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"additionalProperties": {}
},
"description": "Datasets you may use: id, label, description, snapshot (ignores the period), and fields: id, kind (dimension | measure), type, label, description, values (allowed values of a text dimension), grains (date), table_only, format."
},
"can_share": {
"type": [
"boolean",
"null"
],
"description": "false for hiring managers (read-only role)."
},
"where_ops": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"block_types": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"period_presets": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"additionalProperties": {},
"x-fastmcp-top-level-schema": "ReportCatalog"
}{
"title": "List report datasets and fields",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}list_reports — List custom reports
[Reports] List custom reports you can open (yours, shared with you) and the JobMojito templates, with id, title, visibility, your access and version. Custom reports you can open on this account (yours, the ones shared with you) and the JobMojito templates. Pinned first, then most recently updated.
{
"type": "object",
"required": [],
"properties": {
"scope": {
"enum": [
"all",
"mine",
"shared",
"templates"
],
"type": "string",
"default": "all",
"description": "all (default), mine, shared (by colleagues) or templates."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"id",
"title",
"visibility",
"access",
"created_via",
"version",
"has_draft",
"block_count",
"datasets",
"pinned",
"updated_at"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"format": "uuid"
},
"owner": {},
"title": {
"type": [
"string",
"null"
]
},
"access": {
"enum": [
"owner",
"edit",
"view",
null
],
"type": [
"string",
"null"
]
},
"pinned": {
"type": [
"boolean",
"null"
]
},
"is_mine": {},
"version": {
"type": [
"integer",
"null"
]
},
"datasets": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"has_draft": {
"type": [
"boolean",
"null"
]
},
"updated_at": {
"type": [
"string",
"null"
]
},
"visibility": {
"enum": [
"private",
"merchant",
"people",
"template",
null
],
"type": [
"string",
"null"
],
"description": "private (only the owner), merchant (everyone at the company), people (chosen people), template (JobMojito template)."
},
"block_count": {
"type": [
"integer",
"null"
]
},
"created_via": {
"type": [
"string",
"null"
]
},
"description": {}
},
"additionalProperties": {}
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "ReportListResponse"
}{
"title": "List custom reports",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}get_report — Get custom report
[Reports] Get a custom report's JSON definition, visibility, your access and its version. Read it before update_report: the definition is replaced whole and the version must match. The report's JSON definition, visibility, your access (owner, edit, view) and its version (needed to update it). People who can edit also get the unpublished draft, if any.
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"description": "Report id (from list_reports)."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"id",
"title",
"visibility",
"access",
"version",
"admin_url"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"format": "uuid"
},
"title": {
"type": [
"string",
"null"
]
},
"access": {
"enum": [
"owner",
"edit",
"view",
null
],
"type": [
"string",
"null"
]
},
"version": {
"type": [
"integer",
"null"
]
},
"admin_url": {
"type": [
"string",
"null"
]
},
"definition": {
"description": "The published ReportDefinition."
},
"visibility": {
"enum": [
"private",
"merchant",
"people",
"template",
null
],
"type": [
"string",
"null"
]
},
"draft_definition": {
"description": "Unpublished edits (only for people who can edit), or null."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"additionalProperties": {},
"x-fastmcp-top-level-schema": "ReportDetail"
}{
"title": "Get custom report",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}run_report — Run custom report
[Reports] Run a saved custom report and return each block's rows (aggregated numbers, at most 500 rows per block). Optional `filters` JSON overrides the period / positions / interviews for this run only. Report numbers are decision-support material for a human reviewer. Runs every block of a saved report and returns its rows. Filters start from the report's defaults, then your own saved filters, then `filters` if you pass it. Each block runs on its own: a block that fails returns ok=false with an error while the others still return data. Numbers are aggregated; no block returns more than 500 rows.
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"description": "Report id (from list_reports)."
},
"draft": {
"enum": [
"true",
"false"
],
"type": "string",
"default": "false",
"example": "false",
"description": "Run the unpublished draft instead (only if you can edit the report)."
},
"filters": {
"type": "string",
"description": "Optional JSON ReportFilters to apply on top, e.g. {\"period\":{\"preset\":\"last_month\"}}."
},
"block_id": {
"type": "string",
"description": "Run only this block."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"filters",
"blocks"
],
"properties": {
"title": {
"type": [
"string",
"null"
]
},
"blocks": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"block_id",
"ok"
],
"properties": {
"ok": {
"type": [
"boolean",
"null"
],
"description": "false when this block could not run; see error. Other blocks are unaffected."
},
"rows": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"additionalProperties": {}
},
"description": "One object per row, keyed by column id."
},
"error": {
"type": [
"string",
"null"
]
},
"period": {
"type": [
"object",
"null"
],
"description": "The period the block covered: from, to (inclusive), snapshot (true when the dataset ignores the period).",
"additionalProperties": {}
},
"columns": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"additionalProperties": {}
},
"description": "Each column: id, kind (dimension | measure), type, label, and for measures a format."
},
"block_id": {
"type": [
"string",
"null"
]
},
"previous": {
"type": [
"object",
"null"
],
"description": "compare previous_period: the same measures for the period before.",
"additionalProperties": {}
},
"truncated": {
"type": [
"boolean",
"null"
],
"description": "More rows exist than the block limit."
}
},
"additionalProperties": {}
}
},
"filters": {
"type": [
"object",
"null"
],
"description": "The filters the blocks ran with (report defaults, then your saved filters, then the ones you passed).",
"additionalProperties": {}
},
"version": {
"type": [
"integer",
"null"
]
},
"report_id": {
"type": [
"string",
"null"
],
"format": "uuid"
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"additionalProperties": {},
"x-fastmcp-top-level-schema": "ReportRunResponse"
}{
"title": "Run custom report",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}preview_report — Check and preview a report definition
[Reports] Validate a report definition against the catalogue and run it without saving anything. Use it before create_report / update_report: a block that is wrong comes back with ok=false and an error naming the field and the valid ids. Validates a report definition against the catalogue and runs it, without storing anything. Use it to check a definition before create_report or update_report. A 422 names the block and field that is wrong and lists the valid ids. Format: https://developer.jobmojito.com/api-reference/reports/check-and-run-an-unsaved-report.md
{
"type": "object",
"required": [
"definition"
],
"properties": {
"filters": {
"allOf": [
{
"type": "object",
"properties": {
"period": {
"anyOf": [
{
"type": "object",
"required": [
"preset"
],
"properties": {
"preset": {
"enum": [
"last_7_days",
"last_30_days",
"last_90_days",
"this_month",
"last_month",
"this_quarter",
"last_quarter",
"this_year",
"last_year",
"all_time"
],
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"from": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
}
},
"additionalProperties": false
}
],
"example": {
"preset": "last_30_days"
},
"description": "Either a preset, or from and to dates (YYYY-MM-DD, both inclusive, UTC). Default: last_30_days."
},
"include_test": {
"type": "boolean",
"description": "Include test runs. Default false."
},
"position_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 200,
"description": "Positions to show (position ids from list_interviews). position_ids, interview_ids and without_position are one pick list: a row shows when it belongs to ANY pick. Nothing picked = all."
},
"interview_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 200,
"description": "Interviews to show (interview ids from list_interviews), combined with the other picks by OR. Nothing picked = all."
},
"without_position": {
"type": "boolean",
"description": "Also pick interviews that are not part of any position (stand-alone interviews, and coaching sessions). Combined with the other picks by OR."
}
},
"description": "Filters for every block of the report. Each viewer can override them for themselves.",
"additionalProperties": false
},
{
"description": "Applied on top of definition.filters."
}
]
},
"block_id": {
"type": "string",
"description": "Run only this block."
},
"definition": {
"type": "object",
"example": {
"title": "Weekly hiring pulse",
"blocks": [
{
"id": "headline",
"type": "kpi",
"compare": "previous_period",
"dataset": "interview_results",
"measures": [
"completed",
"avg_score",
"shortlisted"
]
},
{
"id": "trend",
"type": "line",
"title": "Completed per week",
"width": 8,
"dataset": "interview_results",
"measures": [
"completed"
],
"dimensions": [
{
"field": "date",
"grain": "week"
}
]
},
{
"id": "by_position",
"sort": [
{
"dir": "desc",
"field": "completed"
}
],
"type": "table",
"limit": 25,
"title": "By position",
"dataset": "interview_results",
"measures": [
"completed",
"avg_score",
"shortlisted"
],
"dimensions": [
"position"
]
}
],
"schema": "jobmojito.report/v1",
"filters": {
"period": {
"preset": "last_30_days"
}
}
},
"required": [
"schema",
"title",
"blocks"
],
"properties": {
"title": {
"type": "string",
"maxLength": 120,
"minLength": 1
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"type"
],
"properties": {
"id": {
"type": "string",
"example": "by_position",
"pattern": "^[A-Za-z0-9_-]{1,40}$"
},
"sort": {
"type": "array",
"items": {
"type": "object",
"required": [
"field"
],
"properties": {
"dir": {
"enum": [
"asc",
"desc"
],
"type": "string"
},
"field": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$",
"description": "A dimension or measure the block selects."
}
},
"additionalProperties": false
},
"maxItems": 3
},
"text": {
"type": "string",
"maxLength": 4000,
"description": "Text block only: the note (plain text, line breaks kept)."
},
"type": {
"enum": [
"kpi",
"table",
"bar",
"stacked_bar",
"line",
"funnel",
"text"
],
"type": "string",
"description": "kpi: one total per measure (no dimensions; add compare previous_period for a change). funnel: 2+ measures as steps (no dimensions). table: 0-2 dimensions. bar: 1-2 dimensions. stacked_bar: exactly 2 dimensions. line: first dimension a date. text: a note, only `text`."
},
"limit": {
"type": "integer",
"maximum": 500,
"minimum": 1,
"description": "Rows for a table / bar / line. Default 100 (line 500)."
},
"title": {
"type": "string",
"maxLength": 120
},
"where": {
"type": "array",
"items": {
"type": "object",
"required": [
"field",
"op"
],
"properties": {
"op": {
"enum": [
"eq",
"neq",
"in",
"not_in",
"gt",
"gte",
"lt",
"lte",
"between",
"is_empty",
"is_not_empty"
],
"type": "string"
},
"field": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$",
"description": "A dimension of the block's dataset."
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
}
}
],
"description": "One value for eq / neq / gt / gte / lt / lte, a list for in / not_in, [low, high] for between, nothing for is_empty / is_not_empty."
}
},
"additionalProperties": false
},
"maxItems": 10
},
"width": {
"type": "integer",
"maximum": 12,
"minimum": 1,
"description": "Columns of a 12-column grid. Default 12."
},
"compare": {
"enum": [
"previous_period"
],
"type": "string",
"description": "kpi / funnel only: also return the same numbers for the period before."
},
"dataset": {
"type": "string",
"example": "interview_results",
"description": "Dataset id from report-catalog. Not used by a text block."
},
"measures": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$"
},
"example": [
"completed",
"avg_score"
],
"maxItems": 6,
"minItems": 1,
"description": "1 to 6 measure ids of the dataset."
},
"dimensions": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$"
},
{
"type": "object",
"required": [
"field"
],
"properties": {
"field": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$"
},
"grain": {
"enum": [
"day",
"week",
"month",
"quarter",
"year"
],
"type": "string",
"description": "For a date field: day (default), week, month, quarter or year."
}
},
"additionalProperties": false
}
],
"description": "A dimension field id, or {\"field\", \"grain\"} for a date."
},
"maxItems": 2
}
},
"additionalProperties": false
},
"maxItems": 12
},
"schema": {
"enum": [
"jobmojito.report/v1"
],
"type": "string",
"description": "Always \"jobmojito.report/v1\"."
},
"filters": {
"type": "object",
"properties": {
"period": {
"anyOf": [
{
"type": "object",
"required": [
"preset"
],
"properties": {
"preset": {
"enum": [
"last_7_days",
"last_30_days",
"last_90_days",
"this_month",
"last_month",
"this_quarter",
"last_quarter",
"this_year",
"last_year",
"all_time"
],
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"from": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
}
},
"additionalProperties": false
}
],
"example": {
"preset": "last_30_days"
},
"description": "Either a preset, or from and to dates (YYYY-MM-DD, both inclusive, UTC). Default: last_30_days."
},
"include_test": {
"type": "boolean",
"description": "Include test runs. Default false."
},
"position_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 200,
"description": "Positions to show (position ids from list_interviews). position_ids, interview_ids and without_position are one pick list: a row shows when it belongs to ANY pick. Nothing picked = all."
},
"interview_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 200,
"description": "Interviews to show (interview ids from list_interviews), combined with the other picks by OR. Nothing picked = all."
},
"without_position": {
"type": "boolean",
"description": "Also pick interviews that are not part of any position (stand-alone interviews, and coaching sessions). Combined with the other picks by OR."
}
},
"description": "Filters for every block of the report. Each viewer can override them for themselves.",
"additionalProperties": false
},
"description": {
"type": "string",
"maxLength": 1000
}
},
"description": "A custom report: report-wide filters and up to 12 blocks. Read report-catalog first for the dataset, field and measure ids (every block needs an id and a type; measures are measure ids as strings, at least one). Format, rules and examples: https://developer.jobmojito.com/api-reference/reports/create-a-custom-report.md",
"additionalProperties": false
},
"merchant_id": {
"type": "string",
"format": "uuid",
"description": "Optional merchant to act on. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"filters",
"blocks"
],
"properties": {
"title": {
"type": [
"string",
"null"
]
},
"blocks": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"block_id",
"ok"
],
"properties": {
"ok": {
"type": [
"boolean",
"null"
],
"description": "false when this block could not run; see error. Other blocks are unaffected."
},
"rows": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"additionalProperties": {}
},
"description": "One object per row, keyed by column id."
},
"error": {
"type": [
"string",
"null"
]
},
"period": {
"type": [
"object",
"null"
],
"description": "The period the block covered: from, to (inclusive), snapshot (true when the dataset ignores the period).",
"additionalProperties": {}
},
"columns": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"additionalProperties": {}
},
"description": "Each column: id, kind (dimension | measure), type, label, and for measures a format."
},
"block_id": {
"type": [
"string",
"null"
]
},
"previous": {
"type": [
"object",
"null"
],
"description": "compare previous_period: the same measures for the period before.",
"additionalProperties": {}
},
"truncated": {
"type": [
"boolean",
"null"
],
"description": "More rows exist than the block limit."
}
},
"additionalProperties": {}
}
},
"filters": {
"type": [
"object",
"null"
],
"description": "The filters the blocks ran with (report defaults, then your saved filters, then the ones you passed).",
"additionalProperties": {}
},
"version": {
"type": [
"integer",
"null"
]
},
"report_id": {
"type": [
"string",
"null"
],
"format": "uuid"
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"additionalProperties": {},
"x-fastmcp-top-level-schema": "ReportRunResponse"
}{
"title": "Check and preview a report definition",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}create_report — Create custom report
[Reports] Save a new custom report from a definition (schema jobmojito.report/v1). It is created as Only me: tell the user they can share it from the returned admin_url. Check the definition with preview_report first. Stores a new custom report. It is created as "Only me" (private to you); share it from the JobMojito admin. Returns its id, version and the admin link to open it. Check the definition with preview_report first. Format, rules and examples: https://developer.jobmojito.com/api-reference/reports/create-a-custom-report.md
{
"type": "object",
"required": [
"definition"
],
"properties": {
"definition": {
"type": "object",
"example": {
"title": "Weekly hiring pulse",
"blocks": [
{
"id": "headline",
"type": "kpi",
"compare": "previous_period",
"dataset": "interview_results",
"measures": [
"completed",
"avg_score",
"shortlisted"
]
},
{
"id": "trend",
"type": "line",
"title": "Completed per week",
"width": 8,
"dataset": "interview_results",
"measures": [
"completed"
],
"dimensions": [
{
"field": "date",
"grain": "week"
}
]
},
{
"id": "by_position",
"sort": [
{
"dir": "desc",
"field": "completed"
}
],
"type": "table",
"limit": 25,
"title": "By position",
"dataset": "interview_results",
"measures": [
"completed",
"avg_score",
"shortlisted"
],
"dimensions": [
"position"
]
}
],
"schema": "jobmojito.report/v1",
"filters": {
"period": {
"preset": "last_30_days"
}
}
},
"required": [
"schema",
"title",
"blocks"
],
"properties": {
"title": {
"type": "string",
"maxLength": 120,
"minLength": 1
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"type"
],
"properties": {
"id": {
"type": "string",
"example": "by_position",
"pattern": "^[A-Za-z0-9_-]{1,40}$"
},
"sort": {
"type": "array",
"items": {
"type": "object",
"required": [
"field"
],
"properties": {
"dir": {
"enum": [
"asc",
"desc"
],
"type": "string"
},
"field": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$",
"description": "A dimension or measure the block selects."
}
},
"additionalProperties": false
},
"maxItems": 3
},
"text": {
"type": "string",
"maxLength": 4000,
"description": "Text block only: the note (plain text, line breaks kept)."
},
"type": {
"enum": [
"kpi",
"table",
"bar",
"stacked_bar",
"line",
"funnel",
"text"
],
"type": "string",
"description": "kpi: one total per measure (no dimensions; add compare previous_period for a change). funnel: 2+ measures as steps (no dimensions). table: 0-2 dimensions. bar: 1-2 dimensions. stacked_bar: exactly 2 dimensions. line: first dimension a date. text: a note, only `text`."
},
"limit": {
"type": "integer",
"maximum": 500,
"minimum": 1,
"description": "Rows for a table / bar / line. Default 100 (line 500)."
},
"title": {
"type": "string",
"maxLength": 120
},
"where": {
"type": "array",
"items": {
"type": "object",
"required": [
"field",
"op"
],
"properties": {
"op": {
"enum": [
"eq",
"neq",
"in",
"not_in",
"gt",
"gte",
"lt",
"lte",
"between",
"is_empty",
"is_not_empty"
],
"type": "string"
},
"field": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$",
"description": "A dimension of the block's dataset."
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
}
}
],
"description": "One value for eq / neq / gt / gte / lt / lte, a list for in / not_in, [low, high] for between, nothing for is_empty / is_not_empty."
}
},
"additionalProperties": false
},
"maxItems": 10
},
"width": {
"type": "integer",
"maximum": 12,
"minimum": 1,
"description": "Columns of a 12-column grid. Default 12."
},
"compare": {
"enum": [
"previous_period"
],
"type": "string",
"description": "kpi / funnel only: also return the same numbers for the period before."
},
"dataset": {
"type": "string",
"example": "interview_results",
"description": "Dataset id from report-catalog. Not used by a text block."
},
"measures": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$"
},
"example": [
"completed",
"avg_score"
],
"maxItems": 6,
"minItems": 1,
"description": "1 to 6 measure ids of the dataset."
},
"dimensions": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$"
},
{
"type": "object",
"required": [
"field"
],
"properties": {
"field": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$"
},
"grain": {
"enum": [
"day",
"week",
"month",
"quarter",
"year"
],
"type": "string",
"description": "For a date field: day (default), week, month, quarter or year."
}
},
"additionalProperties": false
}
],
"description": "A dimension field id, or {\"field\", \"grain\"} for a date."
},
"maxItems": 2
}
},
"additionalProperties": false
},
"maxItems": 12
},
"schema": {
"enum": [
"jobmojito.report/v1"
],
"type": "string",
"description": "Always \"jobmojito.report/v1\"."
},
"filters": {
"type": "object",
"properties": {
"period": {
"anyOf": [
{
"type": "object",
"required": [
"preset"
],
"properties": {
"preset": {
"enum": [
"last_7_days",
"last_30_days",
"last_90_days",
"this_month",
"last_month",
"this_quarter",
"last_quarter",
"this_year",
"last_year",
"all_time"
],
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"from": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
}
},
"additionalProperties": false
}
],
"example": {
"preset": "last_30_days"
},
"description": "Either a preset, or from and to dates (YYYY-MM-DD, both inclusive, UTC). Default: last_30_days."
},
"include_test": {
"type": "boolean",
"description": "Include test runs. Default false."
},
"position_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 200,
"description": "Positions to show (position ids from list_interviews). position_ids, interview_ids and without_position are one pick list: a row shows when it belongs to ANY pick. Nothing picked = all."
},
"interview_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 200,
"description": "Interviews to show (interview ids from list_interviews), combined with the other picks by OR. Nothing picked = all."
},
"without_position": {
"type": "boolean",
"description": "Also pick interviews that are not part of any position (stand-alone interviews, and coaching sessions). Combined with the other picks by OR."
}
},
"description": "Filters for every block of the report. Each viewer can override them for themselves.",
"additionalProperties": false
},
"description": {
"type": "string",
"maxLength": 1000
}
},
"description": "A custom report: report-wide filters and up to 12 blocks. Read report-catalog first for the dataset, field and measure ids (every block needs an id and a type; measures are measure ids as strings, at least one). Format, rules and examples: https://developer.jobmojito.com/api-reference/reports/create-a-custom-report.md",
"additionalProperties": false
},
"merchant_id": {
"type": "string",
"format": "uuid",
"description": "Optional merchant to act on. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"id",
"version",
"published",
"admin_url"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"format": "uuid"
},
"version": {
"type": [
"integer",
"null"
],
"description": "Send it back with the next update."
},
"admin_url": {
"type": [
"string",
"null"
],
"description": "Open the report in the JobMojito admin."
},
"published": {
"type": [
"boolean",
"null"
],
"description": "false when the change was stored as a draft the owner publishes in the admin."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"additionalProperties": {},
"x-fastmcp-top-level-schema": "ReportSaveResponse"
}{
"title": "Create custom report",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}update_report — Update custom report
[Reports] Replace a custom report's definition (send the whole definition and the version from get_report). On a report shared with other people the change is saved as a draft that the owner publishes in the admin; a private report updates at once. Replaces the definition of a report you own or may edit. Send the version you read; if someone changed the report since, you get 409 and should get it again. A report shared with others keeps your change as a draft until it is published in the admin, so viewers never see a half-finished report. Format, rules and examples: https://developer.jobmojito.com/api-reference/reports/update-a-custom-report.md
{
"type": "object",
"required": [
"id",
"version",
"definition"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"description": "Report id (from list_reports)."
},
"target": {
"enum": [
"published",
"draft"
],
"type": "string",
"description": "published (default) or draft. A private report always publishes. Through the MCP server a shared report always gets a draft that its owner publishes in the admin."
},
"version": {
"type": "integer",
"minimum": 1,
"description": "The version you read (get_report / list_reports). A stale version is refused with 409."
},
"definition": {
"allOf": [
{
"type": "object",
"example": {
"title": "Weekly hiring pulse",
"blocks": [
{
"id": "headline",
"type": "kpi",
"compare": "previous_period",
"dataset": "interview_results",
"measures": [
"completed",
"avg_score",
"shortlisted"
]
},
{
"id": "trend",
"type": "line",
"title": "Completed per week",
"width": 8,
"dataset": "interview_results",
"measures": [
"completed"
],
"dimensions": [
{
"field": "date",
"grain": "week"
}
]
},
{
"id": "by_position",
"sort": [
{
"dir": "desc",
"field": "completed"
}
],
"type": "table",
"limit": 25,
"title": "By position",
"dataset": "interview_results",
"measures": [
"completed",
"avg_score",
"shortlisted"
],
"dimensions": [
"position"
]
}
],
"schema": "jobmojito.report/v1",
"filters": {
"period": {
"preset": "last_30_days"
}
}
},
"required": [
"schema",
"title",
"blocks"
],
"properties": {
"title": {
"type": "string",
"maxLength": 120,
"minLength": 1
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"type"
],
"properties": {
"id": {
"type": "string",
"example": "by_position",
"pattern": "^[A-Za-z0-9_-]{1,40}$"
},
"sort": {
"type": "array",
"items": {
"type": "object",
"required": [
"field"
],
"properties": {
"dir": {
"enum": [
"asc",
"desc"
],
"type": "string"
},
"field": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$",
"description": "A dimension or measure the block selects."
}
},
"additionalProperties": false
},
"maxItems": 3
},
"text": {
"type": "string",
"maxLength": 4000,
"description": "Text block only: the note (plain text, line breaks kept)."
},
"type": {
"enum": [
"kpi",
"table",
"bar",
"stacked_bar",
"line",
"funnel",
"text"
],
"type": "string",
"description": "kpi: one total per measure (no dimensions; add compare previous_period for a change). funnel: 2+ measures as steps (no dimensions). table: 0-2 dimensions. bar: 1-2 dimensions. stacked_bar: exactly 2 dimensions. line: first dimension a date. text: a note, only `text`."
},
"limit": {
"type": "integer",
"maximum": 500,
"minimum": 1,
"description": "Rows for a table / bar / line. Default 100 (line 500)."
},
"title": {
"type": "string",
"maxLength": 120
},
"where": {
"type": "array",
"items": {
"type": "object",
"required": [
"field",
"op"
],
"properties": {
"op": {
"enum": [
"eq",
"neq",
"in",
"not_in",
"gt",
"gte",
"lt",
"lte",
"between",
"is_empty",
"is_not_empty"
],
"type": "string"
},
"field": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$",
"description": "A dimension of the block's dataset."
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
}
}
],
"description": "One value for eq / neq / gt / gte / lt / lte, a list for in / not_in, [low, high] for between, nothing for is_empty / is_not_empty."
}
},
"additionalProperties": false
},
"maxItems": 10
},
"width": {
"type": "integer",
"maximum": 12,
"minimum": 1,
"description": "Columns of a 12-column grid. Default 12."
},
"compare": {
"enum": [
"previous_period"
],
"type": "string",
"description": "kpi / funnel only: also return the same numbers for the period before."
},
"dataset": {
"type": "string",
"example": "interview_results",
"description": "Dataset id from report-catalog. Not used by a text block."
},
"measures": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$"
},
"example": [
"completed",
"avg_score"
],
"maxItems": 6,
"minItems": 1,
"description": "1 to 6 measure ids of the dataset."
},
"dimensions": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$"
},
{
"type": "object",
"required": [
"field"
],
"properties": {
"field": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$"
},
"grain": {
"enum": [
"day",
"week",
"month",
"quarter",
"year"
],
"type": "string",
"description": "For a date field: day (default), week, month, quarter or year."
}
},
"additionalProperties": false
}
],
"description": "A dimension field id, or {\"field\", \"grain\"} for a date."
},
"maxItems": 2
}
},
"additionalProperties": false
},
"maxItems": 12
},
"schema": {
"enum": [
"jobmojito.report/v1"
],
"type": "string",
"description": "Always \"jobmojito.report/v1\"."
},
"filters": {
"type": "object",
"properties": {
"period": {
"anyOf": [
{
"type": "object",
"required": [
"preset"
],
"properties": {
"preset": {
"enum": [
"last_7_days",
"last_30_days",
"last_90_days",
"this_month",
"last_month",
"this_quarter",
"last_quarter",
"this_year",
"last_year",
"all_time"
],
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"from": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
}
},
"additionalProperties": false
}
],
"example": {
"preset": "last_30_days"
},
"description": "Either a preset, or from and to dates (YYYY-MM-DD, both inclusive, UTC). Default: last_30_days."
},
"include_test": {
"type": "boolean",
"description": "Include test runs. Default false."
},
"position_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 200,
"description": "Positions to show (position ids from list_interviews). position_ids, interview_ids and without_position are one pick list: a row shows when it belongs to ANY pick. Nothing picked = all."
},
"interview_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 200,
"description": "Interviews to show (interview ids from list_interviews), combined with the other picks by OR. Nothing picked = all."
},
"without_position": {
"type": "boolean",
"description": "Also pick interviews that are not part of any position (stand-alone interviews, and coaching sessions). Combined with the other picks by OR."
}
},
"description": "Filters for every block of the report. Each viewer can override them for themselves.",
"additionalProperties": false
},
"description": {
"type": "string",
"maxLength": 1000
}
},
"description": "A custom report: report-wide filters and up to 12 blocks. Read report-catalog first for the dataset, field and measure ids (every block needs an id and a type; measures are measure ids as strings, at least one). Format, rules and examples: https://developer.jobmojito.com/api-reference/reports/create-a-custom-report.md",
"additionalProperties": false
},
{
"description": "The whole new definition (it replaces the old one)."
}
]
},
"merchant_id": {
"type": "string",
"format": "uuid",
"description": "Optional merchant to act on. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"id",
"version",
"published",
"admin_url"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"format": "uuid"
},
"version": {
"type": [
"integer",
"null"
],
"description": "Send it back with the next update."
},
"admin_url": {
"type": [
"string",
"null"
],
"description": "Open the report in the JobMojito admin."
},
"published": {
"type": [
"boolean",
"null"
],
"description": "false when the change was stored as a draft the owner publishes in the admin."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"additionalProperties": {},
"x-fastmcp-top-level-schema": "ReportSaveResponse"
}{
"title": "Update custom report",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}list_candidates — List candidates
[Candidates] List the merchant's candidates. Paginated list of a merchant's candidates (the admin-portal candidates list), scoped to your token's merchant (or a merchant_id override). Capped at 1000 records per page.
{
"type": "object",
"required": [],
"properties": {
"tab": {
"enum": [
"interview",
"pre-screening",
"no-action",
"leads",
""
],
"type": "string",
"example": "interview",
"description": "Filter candidates by recent activity. Omit (or empty) to include all."
},
"limit": {
"type": "integer",
"default": 50,
"example": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1–1000)."
},
"offset": {
"type": "integer",
"default": 0,
"example": 0,
"minimum": 0,
"description": "Number of records to skip from the start of the result set."
},
"source": {
"enum": [
"invited",
"registered"
],
"type": "string",
"example": "registered",
"description": "Filter by how the candidate entered: invited or self-registered. Omit for both."
},
"order_by": {
"enum": [
"name_accending",
"name_decending",
"created_at_newest",
"created_at_oldest"
],
"type": "string",
"default": "created_at_newest",
"example": "created_at_newest",
"description": "Sort order of the result set."
},
"filter_text": {
"type": "string",
"description": "Search on candidate name, email, external id, phone and CV skills. Every word must match (case and accents ignored; longer words tolerate typos). Without order_by, the best matches come first."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"filter_emoji": {
"type": "string",
"description": "Filter by the candidate emoji marker."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"data",
"pagination"
],
"properties": {
"data": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "Candidate (profile_interview) id."
},
"name": {
"type": [
"string",
"null"
],
"description": "Candidate name."
},
"email": {
"type": [
"string",
"null"
],
"description": "Candidate email."
},
"emoji": {
"type": [
"string",
"null"
],
"description": "Emoji marker."
},
"skills": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Skills read from the candidate's latest CV; null until extracted."
},
"status": {
"type": [
"string",
"null"
],
"description": "Status (active/invited/deleted)."
},
"linked_in": {
"type": [
"string",
"null"
],
"description": "LinkedIn URL."
},
"avatar_url": {
"type": [
"string",
"null"
],
"description": "Avatar image URL."
},
"created_at": {
"type": [
"string",
"null"
],
"description": "Creation timestamp (ISO 8601)."
},
"deleted_at": {
"type": [
"string",
"null"
],
"description": "Deletion timestamp (ISO 8601), if deleted."
},
"email_auth": {
"type": [
"string",
"null"
],
"description": "Auth email (login)."
},
"profile_id": {
"type": [
"string",
"null"
],
"description": "Underlying profile id."
},
"updated_at": {
"type": [
"string",
"null"
],
"description": "Last update timestamp (ISO 8601)."
},
"external_id": {
"type": [
"string",
"null"
],
"description": "External identifier."
},
"merchant_id": {
"type": [
"string",
"null"
],
"description": "Owning merchant id."
},
"phone_number": {
"type": [
"string",
"null"
],
"description": "Phone number."
},
"custom_fields": {
"type": [
"object",
"null"
],
"example": {
"employee_level": "senior"
},
"description": "Company-defined candidate fields (key: value), when custom fields are enabled for the company.",
"additionalProperties": {}
},
"last_login_at": {
"type": [
"string",
"null"
],
"description": "Last login timestamp (ISO 8601)."
},
"interview_last": {
"type": [
"string",
"null"
],
"description": "Most recent interview timestamp (ISO 8601)."
},
"interview_count": {
"type": [
"number",
"null"
],
"description": "Number of interviews taken."
},
"physical_address": {
"type": [
"string",
"null"
],
"description": "Physical address."
},
"avatar_local_path": {
"type": [
"string",
"null"
],
"description": "Avatar storage path."
},
"pre_screening_count": {
"type": [
"number",
"null"
],
"description": "Number of pre-screenings taken."
},
"public_avatar_count": {
"type": [
"number",
"null"
],
"description": "Number of public-avatar conversations."
},
"kyc_image_local_path": {
"type": [
"string",
"null"
],
"description": "Deprecated, always null: the retired identity verification (KYC) is no longer returned."
},
"mojito_language_code": {
"type": [
"string",
"null"
],
"description": "Platform language code."
},
"kyc_processing_status": {
"type": [
"string",
"null"
],
"description": "Deprecated, always null: the retired identity verification (KYC) is no longer returned."
}
},
"additionalProperties": {}
},
"description": "The merchant's candidates for this page."
},
"pagination": {
"type": "object",
"required": [
"total",
"limit",
"offset",
"has_more"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"example": 50,
"description": "Page size used for this response."
},
"total": {
"type": [
"integer",
"null"
],
"example": 137,
"description": "Total matching records across all pages."
},
"offset": {
"type": [
"integer",
"null"
],
"example": 0,
"description": "Offset used for this response."
},
"has_more": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "True when more records exist beyond this page (offset + returned < total)."
}
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "MerchantCandidateListResponse"
}{
"title": "List candidates",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}upload_knowledge_base_document — Upload knowledge base document
[Knowledge base] Upload and process a knowledge base document (base64 file). The optional interview_ids limits it to chosen interviews on the same knowledge base: leave it out for every interview, [] for none. Returns the limit it set. Uploads a document to a knowledge base store and queues it for processing. Accepts the file either as multipart/form-data (binary `file`) or as application/json (base64 `file`). Use `interview_ids` to limit the document to chosen interviews.
{
"type": "object",
"required": [
"knowledge_base_store_id",
"file"
],
"properties": {
"file": {
"type": "string",
"minLength": 1,
"description": "The document file, base64-encoded."
},
"name": {
"type": "string",
"description": "Document name (with extension). Falls back to the uploaded file name for multipart."
},
"merchant_id": {
"type": "string",
"description": "Override merchant id (admin / sub-merchant only)."
},
"interview_ids": {
"type": [
"array",
"null"
],
"items": {
"type": "string",
"format": "uuid"
},
"example": [
"de300000-0000-4000-8001-000000000007"
],
"description": "The interviews that may use this document, in the live session and when generating questions. Omit it or send null to use the document in every interview on the knowledge base. An empty list limits it to no interview. Each interview must belong to the same company and use this knowledge base. With multipart/form-data, repeat the field or send one JSON array such as `[\"<id>\"]`, and write an empty list as `[]`."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"knowledge_base_store_id": {
"type": "string",
"example": "d9d6f121-1230-48fd-b3a7-8b5a1e8db052",
"minLength": 1,
"description": "The knowledge base store to add the document to."
}
}
}{
"type": "object",
"required": [
"knowledge_base_id",
"limit_to_interviews"
],
"properties": {
"interview_ids": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "The interviews this document is limited to. Present only when `limit_to_interviews` is true; an empty list means no interview can use it."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
},
"knowledge_base_id": {
"type": [
"string",
"null"
],
"description": "Id of the created knowledge_base record."
},
"limit_to_interviews": {
"type": [
"boolean",
"null"
],
"description": "True when the document is limited to the interviews in `interview_ids`. False when every interview on the knowledge base can use it."
}
},
"x-fastmcp-top-level-schema": "KnowledgeBaseDocumentUploadResponse"
}{
"title": "Upload knowledge base document",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}list_knowledge_base_documents — List knowledge base documents
[Knowledge base] List the company's knowledge base documents, optionally for one store, with the interviews each is limited to. Pass knowledge_base_id to read one document's limit. Paginated list of your company's knowledge base documents (uploaded files, URLs and written documents), each with the interviews it is limited to. Pass `knowledge_base_id` to read one document's limit. Capped at 1000 records per page.
{
"type": "object",
"required": [],
"properties": {
"limit": {
"type": "integer",
"default": 50,
"example": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1–1000)."
},
"offset": {
"type": "integer",
"default": 0,
"example": 0,
"minimum": 0,
"description": "Number of records to skip from the start of the result set."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"knowledge_base_id": {
"type": "string",
"format": "uuid",
"example": "44444444-4444-4444-8444-444444444444",
"description": "Read this one document. Answers 404 if your token cannot see it, including when it is another company's. `merchant_id` and `offset` are ignored."
},
"knowledge_base_store_id": {
"type": "string",
"format": "uuid",
"example": "d9d6f121-1230-48fd-b3a7-8b5a1e8db052",
"description": "Only the documents of this knowledge base store. Omit for every store of the company."
}
}
}{
"type": "object",
"required": [
"data",
"pagination"
],
"properties": {
"data": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"knowledge_base_id",
"knowledge_base_store_id",
"merchant_id",
"name",
"type",
"status",
"processing_status",
"created_at",
"limit_to_interviews"
],
"properties": {
"name": {
"type": [
"string",
"null"
],
"description": "Document name."
},
"type": {
"type": [
"string",
"null"
],
"description": "Where it came from: `file`, `url` or `markdown`."
},
"status": {
"type": [
"string",
"null"
],
"description": "Lifecycle status."
},
"created_at": {
"type": [
"string",
"null"
],
"description": "Creation timestamp (ISO 8601)."
},
"merchant_id": {
"type": [
"string",
"null"
],
"description": "The company that owns the document."
},
"interview_ids": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "The interviews this document is limited to. Present only when `limit_to_interviews` is true; an empty list means no interview can use it."
},
"knowledge_base_id": {
"type": [
"string",
"null"
],
"description": "Document id, for knowledge-base-document-set-interviews."
},
"processing_status": {
"type": [
"string",
"null"
],
"description": "Parsing progress of a file or URL. Written documents have none."
},
"limit_to_interviews": {
"type": [
"boolean",
"null"
],
"description": "True when the document is limited to the interviews in `interview_ids`. False when every interview on the knowledge base can use it."
},
"knowledge_base_store_id": {
"type": [
"string",
"null"
],
"description": "The knowledge base store the document belongs to."
}
}
},
"description": "The documents for this page with the interview limit of each, newest first."
},
"pagination": {
"type": "object",
"required": [
"total",
"limit",
"offset",
"has_more"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"example": 50,
"description": "Page size used for this response."
},
"total": {
"type": [
"integer",
"null"
],
"example": 137,
"description": "Total matching records across all pages."
},
"offset": {
"type": [
"integer",
"null"
],
"example": 0,
"description": "Offset used for this response."
},
"has_more": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "True when more records exist beyond this page (offset + returned < total)."
}
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "KnowledgeBaseDocumentListResponse"
}{
"title": "List knowledge base documents",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}set_knowledge_base_document_interviews — Change which interviews can use a document
[Knowledge base] Replace the interview limit of an existing knowledge base document. interview_ids is required: a list limits it to those interviews on the same knowledge base, [] to none, and null opens it to every interview on the knowledge base. Replaces the interview limit of an existing knowledge base document: a list of interviews, an empty list for none, or null for every interview on the knowledge base. Takes effect for the next live session and the next generation; the document is not processed again and costs no credits.
{
"type": "object",
"required": [
"knowledge_base_id",
"interview_ids"
],
"properties": {
"interview_ids": {
"type": [
"array",
"null"
],
"items": {
"type": "string",
"format": "uuid"
},
"example": [
"de300000-0000-4000-8001-000000000007"
],
"description": "The whole new list, which replaces the old one. A list limits the document to those interviews, an empty list to no interview, and null opens it to every interview on the knowledge base. Required, so that leaving it out can never open a document. Each interview must belong to the same company as the document and use the same knowledge base."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"knowledge_base_id": {
"type": "string",
"format": "uuid",
"example": "44444444-4444-4444-8444-444444444444",
"minLength": 1,
"description": "The document to change: `knowledge_base_id` from the upload response or from knowledge-base-document-list."
}
}
}{
"type": "object",
"required": [
"knowledge_base_id",
"limit_to_interviews"
],
"properties": {
"interview_ids": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "The interviews this document is limited to. Present only when `limit_to_interviews` is true; an empty list means no interview can use it."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
},
"knowledge_base_id": {
"type": [
"string",
"null"
],
"description": "The document that was changed."
},
"limit_to_interviews": {
"type": [
"boolean",
"null"
],
"description": "True when the document is limited to the interviews in `interview_ids`. False when every interview on the knowledge base can use it."
}
},
"x-fastmcp-top-level-schema": "KnowledgeBaseDocumentSetInterviewsResponse"
}{
"title": "Change which interviews can use a document",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}list_sub_merchants — List sub-merchants
[Admin] List sub-merchants under the merchant account. Paginated list of the sub-merchants the caller administers (the admin-portal sub-merchants list). Visibility is enforced by row-level security. Capped at 1000 records per page.
{
"type": "object",
"required": [],
"properties": {
"limit": {
"type": "integer",
"default": 50,
"example": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1–1000)."
},
"offset": {
"type": "integer",
"default": 0,
"example": 0,
"minimum": 0,
"description": "Number of records to skip from the start of the result set."
},
"order_by": {
"enum": [
"name",
"created_at_newest",
"created_at_oldest"
],
"type": "string",
"default": "created_at_newest",
"example": "created_at_newest",
"description": "Sort order of the result set."
},
"filter_text": {
"type": "string",
"description": "Case-insensitive search on the sub-merchant name."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"data",
"pagination"
],
"properties": {
"data": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "Sub-merchant id."
},
"name": {
"type": [
"string",
"null"
],
"description": "Sub-merchant name."
},
"users": {
"description": "Users linked to this sub-merchant (jsonb)."
},
"status": {
"type": [
"string",
"null"
],
"description": "Status."
},
"created_at": {
"type": [
"string",
"null"
],
"description": "Creation timestamp (ISO 8601)."
},
"info_notes": {
"type": [
"string",
"null"
],
"description": "Internal notes."
},
"business_type": {
"type": [
"string",
"null"
],
"description": "Business type."
},
"contact_email": {
"type": [
"string",
"null"
],
"description": "Primary contact email."
},
"admin_profiles": {
"description": "Admin profiles linked to this sub-merchant (jsonb)."
},
"contact_person": {
"type": [
"string",
"null"
],
"description": "Primary contact name."
},
"image_logo_url": {
"type": [
"string",
"null"
],
"description": "Logo image URL."
},
"billing_country": {
"type": [
"string",
"null"
],
"description": "Billing country."
},
"frontend_domain": {
"type": [
"string",
"null"
],
"description": "Consumer frontend domain."
},
"interview_count": {
"type": [
"number",
"null"
],
"description": "Total interviews run."
},
"register_domain": {
"type": [
"string",
"null"
],
"description": "Registration domain."
},
"credits_used_30d": {
"type": [
"number",
"null"
],
"description": "Credits used in the last 30 days."
},
"interview_latest": {
"type": [
"string",
"null"
],
"description": "Most recent interview timestamp (ISO 8601)."
},
"merchant_features": {
"description": "Enabled feature flags (jsonb)."
},
"credits_used_total": {
"type": [
"number",
"null"
],
"description": "Total credits used."
},
"consumer_webanalytics": {
"type": [
"string",
"null"
],
"description": "Consumer web-analytics id."
},
"interview_webanalytics": {
"type": [
"string",
"null"
],
"description": "Interview web-analytics id."
},
"interview_frontend_domain": {
"type": [
"string",
"null"
],
"description": "Interview frontend domain."
},
"interview_subscription_type": {
"type": [
"string",
"null"
],
"description": "Interview subscription type."
},
"interview_subscription_status": {
"type": [
"string",
"null"
],
"description": "Interview subscription status."
},
"stripe_interview_subscription_start": {
"type": [
"string",
"null"
],
"description": "Subscription start (ISO 8601)."
}
},
"additionalProperties": {}
},
"description": "The sub-merchants the caller administers for this page."
},
"pagination": {
"type": "object",
"required": [
"total",
"limit",
"offset",
"has_more"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"example": 50,
"description": "Page size used for this response."
},
"total": {
"type": [
"integer",
"null"
],
"example": 137,
"description": "Total matching records across all pages."
},
"offset": {
"type": [
"integer",
"null"
],
"example": 0,
"description": "Offset used for this response."
},
"has_more": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "True when more records exist beyond this page (offset + returned < total)."
}
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "MerchantSubMerchantListResponse"
}{
"title": "List sub-merchants",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}list_avatars — List avatars and voice templates
[Admin] List available avatar/voice templates. Each item's `type` decides the interview modality: `interactive_elevenlabs` = voice-only (no video avatar); `interactive_spatius` = realtime interactive 3D avatar (rendered in the candidate's browser, 1.25 credits, no early stop); `interactive_heygen` = premium realtime interactive video avatar; `offline_heygen` = pre-recorded, non-interactive avatar. An item's `id` is the `interview_template_id` you pass to the create-interview tools, so pick the template whose type matches the experience you want. Note: `offline_elai` and `offline_synthesia` are legacy integrations that may still appear here but cannot be used to create new interviews. Rows are large, so this returns 15 at a time; page with `offset` while `pagination.has_more` is true, or narrow with `type`/`filter_text`. Paginated list of a merchant's avatar templates (the admin-portal avatars list), scoped to your token's merchant (or a merchant_id override). Capped at 1000 records per page.
{
"type": "object",
"required": [],
"properties": {
"type": {
"enum": [
"interactive_heygen",
"interactive_elevenlabs",
"interactive_spatius",
"offline_heygen",
"offline_elai",
"offline_synthesia"
],
"type": "string",
"example": "interactive_heygen",
"description": "Filter by avatar template type. Omit for all."
},
"limit": {
"type": "integer",
"default": 15,
"example": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1–1000)."
},
"offset": {
"type": "integer",
"default": 0,
"example": 0,
"minimum": 0,
"description": "Number of records to skip from the start of the result set."
},
"status": {
"enum": [
"active",
"draft",
"archived",
"deleted"
],
"type": "string",
"example": "active",
"description": "Filter by status. Omit for all except archived (see include_archived)."
},
"filter_text": {
"type": "string",
"description": "Case-insensitive search on template name or voice language name."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"include_public": {
"enum": [
"true",
"false"
],
"type": "string",
"default": "false",
"example": "false",
"description": "Also include public templates shared across merchants, in addition to this merchant's own."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
},
"include_archived": {
"enum": [
"true",
"false"
],
"type": "string",
"default": "false",
"example": "false",
"description": "Include archived templates (excluded by default)."
},
"mojito_language_code": {
"enum": [
"ar",
"bg",
"zh",
"hr",
"cs",
"da",
"nl",
"en",
"fil",
"fi",
"fr",
"de",
"el",
"hi",
"hu",
"id",
"it",
"ja",
"ko",
"ms",
"no",
"pl",
"pt",
"br",
"ro",
"ru",
"sk",
"es",
"sv",
"ta",
"th",
"tr",
"uk",
"vi"
],
"type": "string",
"example": "en",
"description": "Filter by platform language code. Omit for all languages."
}
}
}{
"type": "object",
"required": [
"data",
"pagination"
],
"properties": {
"data": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "Avatar template id."
},
"name": {
"type": [
"string",
"null"
],
"description": "Template name."
},
"type": {
"type": [
"string",
"null"
],
"description": "Avatar template type."
},
"status": {
"type": [
"string",
"null"
],
"description": "Status (active/draft/archived/deleted)."
},
"media_url": {
"type": [
"string",
"null"
],
"description": "Avatar media URL."
},
"created_at": {
"type": [
"string",
"null"
],
"description": "Creation timestamp (ISO 8601)."
},
"updated_at": {
"type": [
"string",
"null"
],
"description": "Last update timestamp (ISO 8601)."
},
"visibility": {
"type": [
"string",
"null"
],
"description": "Visibility (public/merchant_…)."
},
"merchant_id": {
"type": [
"string",
"null"
],
"description": "Owning merchant id."
},
"background_url": {
"type": [
"string",
"null"
],
"description": "Background image URL."
},
"background_blur": {
"type": [
"number",
"null"
],
"description": "Background blur intensity (0–1); higher is blurrier."
},
"mojito_language_code": {
"type": [
"string",
"null"
],
"description": "Platform language code."
},
"video_avatar_image_url": {
"type": [
"string",
"null"
],
"description": "Avatar still-image URL."
},
"video_background_remove": {
"type": [
"boolean",
"null"
],
"description": "Whether the video background is removed."
},
"video_avatar_aspect_ratio": {
"type": [
"string",
"null"
],
"description": "Avatar aspect ratio."
},
"video_voice_language_name": {
"type": [
"string",
"null"
],
"description": "Voice language display name."
}
},
"additionalProperties": {}
},
"description": "The merchant's avatar templates for this page, newest-updated first."
},
"pagination": {
"type": "object",
"required": [
"total",
"limit",
"offset",
"has_more"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"example": 50,
"description": "Page size used for this response."
},
"total": {
"type": [
"integer",
"null"
],
"example": 137,
"description": "Total matching records across all pages."
},
"offset": {
"type": [
"integer",
"null"
],
"example": 0,
"description": "Offset used for this response."
},
"has_more": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "True when more records exist beyond this page (offset + returned < total)."
}
}
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "MerchantAvatarListResponse"
}{
"title": "List avatars and voice templates",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}get_merchant_status — Get merchant status
[Admin] Get a merchant status snapshot: credit balances, subscription, pending-work counts, candidate/result totals, and invitation headroom. Status snapshot for a merchant: interview-credit balances, subscription type/status, pending-work counts (undecided / ongoing / uncredited interviews), candidate & result totals with 14-day history, and invitation headroom. Scoped to your token's merchant (or a merchant_id override for admins / sub-merchant operators). Also echoes the caller's profile_id and default_merchant_id from the token, plus the effective merchant_id.
{
"type": "object",
"required": [],
"properties": {
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"profile_id",
"default_merchant_id",
"merchant_id",
"credits_interview_monthly",
"credits_interview_monthly_limit",
"credits_interview_extra",
"credits_interview_single",
"definitions_active",
"interviews_without_credits",
"interviews_undecided",
"interviews_ongoing",
"interview_results",
"candidates",
"candidates_history",
"interview_result_history",
"subscription_type",
"subscription_status",
"invitations_sent",
"invitations_limit",
"invitations_available"
],
"properties": {
"candidates": {
"type": [
"number",
"null"
],
"example": 212,
"description": "Total candidates (non-archived profile_interview rows) for the merchant."
},
"profile_id": {
"type": [
"string",
"null"
],
"example": "9b2c1e54-7a3d-4f8e-9c10-2b6a4d5e7f01",
"description": "The calling user's profile id (auth user id), taken from the JWT."
},
"merchant_id": {
"type": [
"string",
"null"
],
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "The merchant this status is scoped to — the default_merchant_id unless an admin / sub-merchant operator overrode it via the merchant_id query param."
},
"invitations_sent": {
"type": [
"number",
"null"
],
"example": 18,
"description": "Interview invitations sent during the current subscription period."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
},
"interview_results": {
"type": [
"number",
"null"
],
"example": 137,
"description": "Total non-archived interview results for the merchant."
},
"invitations_limit": {
"type": [
"number",
"null"
],
"example": 40,
"description": "Maximum invitations allowed this period (4x available credits). Null for unlimited (Special) plans."
},
"subscription_type": {
"type": [
"string",
"null"
],
"example": "Growth",
"description": "Subscription plan name (e.g. Free, Starter, Growth, Special)."
},
"candidates_history": {
"type": [
"array",
"null"
],
"items": {
"type": "number"
},
"example": [
3,
5,
0,
2,
8,
1,
0,
4,
6,
2,
1,
0,
3,
5
],
"description": "Daily new-candidate counts for the last 14 days, most recent first."
},
"definitions_active": {
"type": [
"number",
"null"
],
"example": 4,
"description": "Count of active interview + position definitions."
},
"interviews_ongoing": {
"type": [
"number",
"null"
],
"example": 1,
"description": "Interviews currently in progress (coach_status = started)."
},
"default_merchant_id": {
"type": [
"string",
"null"
],
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "The caller's home merchant id pinned in the JWT (app_metadata.merchant_id). Null if the token carries no merchant."
},
"subscription_status": {
"type": [
"string",
"null"
],
"example": "active",
"description": "Subscription status (e.g. active, past_due, canceled). Null when no subscription."
},
"interviews_undecided": {
"type": [
"number",
"null"
],
"example": 3,
"description": "Completed interviews awaiting a recruiter decision."
},
"invitations_available": {
"type": [
"number",
"null"
],
"example": 22,
"description": "Remaining invitations this period. Null for unlimited (Special) plans."
},
"credits_interview_extra": {
"type": [
"number",
"null"
],
"example": 25,
"description": "Extra (top-up) interview credits available on top of the monthly allowance."
},
"credits_interview_single": {
"type": [
"number",
"null"
],
"example": 0,
"description": "Single-position interview credits available."
},
"interview_result_history": {
"type": [
"array",
"null"
],
"items": {
"type": "number"
},
"example": [
1,
2,
0,
1,
3,
0,
0,
2,
1,
1,
0,
0,
1,
2
],
"description": "Daily new-interview-result counts for the last 14 days, most recent first."
},
"credits_interview_monthly": {
"type": [
"number",
"null"
],
"example": 8,
"description": "Remaining monthly interview credits."
},
"interviews_without_credits": {
"type": [
"number",
"null"
],
"example": 2,
"description": "Completed interviews that have not yet consumed a credit."
},
"credits_interview_monthly_limit": {
"type": [
"number",
"null"
],
"example": 10,
"description": "Monthly interview-credit allowance for the current plan."
}
},
"additionalProperties": {},
"x-fastmcp-top-level-schema": "MerchantStatus"
}{
"title": "Get merchant status",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}list_languages — List supported languages
[Admin] List supported platform (mojito) languages: the `code` to pass as `mojito_language_code`, English/local names, SVG flag URL, per-interface enablement flags, and Azure speech accents. Returns all supported platform (mojito) languages: the mojito language code, English and local names, and an SVG flag image URL. Use the `code` as `mojito_language_code` when creating interviews or personas.
{
"type": "object",
"required": [],
"properties": {
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"languages"
],
"properties": {
"languages": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"code",
"name_english",
"name_local",
"flag_image",
"flag_emoji",
"flag_unicode",
"coaching_interface",
"interview_interface",
"admin_interface",
"accents"
],
"properties": {
"code": {
"type": [
"string",
"null"
],
"example": "en",
"description": "Mojito platform language code. Pass this as `mojito_language_code` when creating interviews / personas."
},
"accents": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"code",
"name"
],
"properties": {
"code": {
"type": [
"string",
"null"
],
"example": "en-US",
"description": "Azure speech locale code for the accent/regional variant."
},
"name": {
"type": [
"string",
"null"
],
"example": "English (United States)",
"description": "Human-readable accent name."
}
}
},
"description": "Azure speech regional accents / variants available for this language, each with its locale `code` and `name`."
},
"flag_emoji": {
"type": [
"string",
"null"
],
"example": "🇺🇸",
"description": "Flag emoji for the language."
},
"flag_image": {
"type": [
"string",
"null"
],
"example": "https://cdn.jsdelivr.net/npm/country-flag-emoji-json@2.0.0/dist/images/US.svg",
"description": "URL to an SVG flag image for the language (same source the app UI renders)."
},
"name_local": {
"type": [
"string",
"null"
],
"example": "English",
"description": "Language name written in the language itself."
},
"flag_unicode": {
"type": [
"string",
"null"
],
"example": "U+1F1FA U+1F1F8",
"description": "Unicode code points for the flag emoji."
},
"name_english": {
"type": [
"string",
"null"
],
"example": "English",
"description": "Language name in English."
},
"admin_interface": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "Whether this language is enabled on the admin interface."
},
"coaching_interface": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "Whether this language is enabled on the coaching (consumer) interface."
},
"interview_interface": {
"type": [
"boolean",
"null"
],
"example": true,
"description": "Whether this language is enabled on the interview interface."
}
}
},
"description": "Supported platform languages, sorted by English name."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "PlatformLanguagesListResponse"
}{
"title": "List supported languages",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}get_portal_configuration — Get portal configuration
[Admin] Read the company's portal configuration as editable documents: `branding` (portal name, style, colours, logos, e-mail sender name), `interview-portal` (languages, home texts), `coaching-portal` (vertical, feature toggles, labels, languages, home blocks) and one `jobmojito.portal-site` document per portal (every page's draft content and the theme CSS). Narrow with `sections` (branding, interview-portal, coaching-portal, interview-pages, coaching-pages): the page documents are the large ones. This is the read step of every portal change: read the section(s) involved, change only what the user asked for, and send those documents back with update_portal_configuration. Field reference: https://developer.jobmojito.com/cookbooks/portal-branding/branding, https://developer.jobmojito.com/cookbooks/portal-branding/interview-portal, https://developer.jobmojito.com/cookbooks/portal-branding/coaching-portal, https://developer.jobmojito.com/cookbooks/portal-branding/portal-pages. Reads the company's portal configuration as import documents: branding, the interview portal, the coaching portal, and the pages + theme CSS of each portal. Every returned document can be edited and sent back to portal-configuration-update unchanged in shape (preview with `dry_run: true` first). Document formats: https://developer.jobmojito.com/cookbooks/portal-branding/branding, https://developer.jobmojito.com/cookbooks/portal-branding/interview-portal, https://developer.jobmojito.com/cookbooks/portal-branding/coaching-portal, https://developer.jobmojito.com/cookbooks/portal-branding/portal-pages.
{
"type": "object",
"required": [],
"properties": {
"sections": {
"type": "string",
"example": "branding,interview-pages",
"description": "Comma-separated sections to return (default: all): branding, interview-portal, coaching-portal, interview-pages, coaching-pages. A portal the company does not use is left out of the default; name its sections to read them anyway."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"merchant_id",
"documents"
],
"properties": {
"documents": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"branding",
"interview-portal",
"coaching-portal",
null
],
"type": [
"string",
"null"
],
"example": "branding",
"description": "Portal configuration document type. `branding`: portal name, style, colours, logos, e-mail sender name, speech term boosting. `interview-portal`: interview portal languages and the per-language home texts. `coaching-portal`: coaching portal vertical (`use_case`), feature toggles, label overrides, coaching plan, catalogue start directory, languages and per-language home blocks. A portal site document declares `format` instead."
},
"format": {
"enum": [
"jobmojito.portal-site",
"jobmojito.portal-page",
null
],
"type": [
"string",
"null"
],
"example": "jobmojito.portal-site",
"description": "Portal site document type. `jobmojito.portal-site`: `{ portal, theme: { css }, pages: [Page] }`, a whole site (pages in every language, the menu, the footer and the theme CSS). `jobmojito.portal-page`: `{ page: Page }`, one page. Page = `{ slug, show_in_menu, sort_order, languages: [{ mojito_language_code, title, description, blocks }] }`; slug \"\" is the home page, \"_footer\", \"_support\", \"_jobs\" etc. are the built-in pages."
},
"version": {
"example": 1,
"description": "Document format version. Always 1."
}
},
"description": "One configuration document: declares either `kind` (branding, interview-portal, coaching-portal) or `format` (jobmojito.portal-site, jobmojito.portal-page), plus that type's fields. Field reference: branding https://developer.jobmojito.com/cookbooks/portal-branding/branding, interview portal https://developer.jobmojito.com/cookbooks/portal-branding/interview-portal, coaching portal https://developer.jobmojito.com/cookbooks/portal-branding/coaching-portal, portal pages https://developer.jobmojito.com/cookbooks/portal-branding/portal-pages. Unknown fields are ignored with a warning; wrong types and enum values are errors.",
"additionalProperties": true
},
"description": "One document per section, each a valid input for portal-configuration-update: `kind: \"branding\"`, `kind: \"interview-portal\"`, `kind: \"coaching-portal\"`, and one `format: \"jobmojito.portal-site\"` per portal (`portal: \"interview\"` for interview-pages, `\"consumer\"` for coaching-pages) with every page's DRAFT content and the theme CSS draft. Pages also carry `published_at` (read-only, ignored on import; null = never published). The support / application-tracking / interview-completed / signed-in home texts are built-in pages (\"_support\", \"_tracking\", \"_completion\", \"_signed_in_home\") and appear only in the portal-site documents."
},
"merchant_id": {
"type": [
"string",
"null"
],
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Merchant the configuration belongs to."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "PortalConfigurationGetResponse"
}{
"title": "Get portal configuration",
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}update_portal_configuration — Update portal configuration
[Admin] Import portal configuration documents, the same ones get_portal_configuration returns: branding, interview portal, coaching portal, and portal sites (pages, menu, footer, theme CSS). Read first, edit only the fields the user asked about, then send the documents with `dry_run: true` and show the user the returned summary, warnings and changes; repeat without `dry_run` to apply. Fields a document omits keep their value, and languages are only ever added. Validation is all-or-nothing: a 422 lists every error and nothing is written. Pages land as drafts the company reviews in the admin; set `publish: true` only when the user asked to publish the pages or theme, because it puts them live at once. Field reference: https://developer.jobmojito.com/cookbooks/portal-branding/branding, https://developer.jobmojito.com/cookbooks/portal-branding/interview-portal, https://developer.jobmojito.com/cookbooks/portal-branding/coaching-portal, https://developer.jobmojito.com/cookbooks/portal-branding/portal-pages. Imports portal configuration documents in one call: **branding** (portal name, style, colours, logos, e-mail sender name, speech term boosting), the **interview portal** (languages, home texts, support / application-tracking / interview-completed texts), the **coaching portal** (vertical, feature toggles, labels, coaching plan, catalogue start directory, languages, home blocks) and **portal sites** (pages in every language with their blocks, menu, footer and the theme CSS). Document formats: https://developer.jobmojito.com/cookbooks/portal-branding/branding, https://developer.jobmojito.com/cookbooks/portal-branding/interview-portal, https://developer.jobmojito.com/cookbooks/portal-branding/coaching-portal, https://developer.jobmojito.com/cookbooks/portal-branding/portal-pages. Read the current configuration with portal-configuration-get first: it returns these same documents, so you can edit them and send them back. Fields a document omits keep their value. Run with `dry_run: true` to preview the summary, warnings and changes, then repeat without it to apply. Validation is all-or-nothing: if any document has an error nothing is written and the 422 lists every error. Pages land as drafts unless `publish` is true; the support / tracking / completion / signed-in home texts of the portal documents always land as drafts of those built-in pages.
{
"type": "object",
"required": [
"documents"
],
"properties": {
"kind": {
"enum": [
"branding",
"interview-portal",
"coaching-portal",
"portal-site"
],
"type": "string",
"description": "Optional: lock the call to one document type, as one admin import tab does. Every document must then be that type (`portal-site` accepts both portal-site and portal-page formats), and a document that declares neither `kind` nor `format` is read as this type with a warning. Without it, every document must declare its own `kind` or `format`."
},
"portal": {
"enum": [
"interview",
"consumer"
],
"type": "string",
"description": "Where portal-site pages and theme CSS go: `interview` (candidate-facing interview / careers portal) or `consumer` (coaching portal). Default: the portal-site document's own `portal`, else `interview`. A document made for the other portal is imported here with a warning."
},
"dry_run": {
"type": "boolean",
"default": false,
"description": "Validate and report what would change (summary, warnings, changes) without writing anything. Use it to preview an import before applying it."
},
"publish": {
"type": "boolean",
"default": false,
"description": "Also publish the imported pages and theme CSS (live at once, as the admin's \"Publish after import\"). Default false: pages land as drafts the company reviews in the admin. Built-in page drafts produced by interview-portal / coaching-portal documents are never published. With page versions switched on, only a company owner or admin may publish."
},
"documents": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"branding",
"interview-portal",
"coaching-portal"
],
"type": "string",
"example": "branding",
"description": "Portal configuration document type. `branding`: portal name, style, colours, logos, e-mail sender name, speech term boosting. `interview-portal`: interview portal languages and the per-language home texts. `coaching-portal`: coaching portal vertical (`use_case`), feature toggles, label overrides, coaching plan, catalogue start directory, languages and per-language home blocks. A portal site document declares `format` instead."
},
"format": {
"enum": [
"jobmojito.portal-site",
"jobmojito.portal-page"
],
"type": "string",
"example": "jobmojito.portal-site",
"description": "Portal site document type. `jobmojito.portal-site`: `{ portal, theme: { css }, pages: [Page] }`, a whole site (pages in every language, the menu, the footer and the theme CSS). `jobmojito.portal-page`: `{ page: Page }`, one page. Page = `{ slug, show_in_menu, sort_order, languages: [{ mojito_language_code, title, description, blocks }] }`; slug \"\" is the home page, \"_footer\", \"_support\", \"_jobs\" etc. are the built-in pages."
},
"version": {
"example": 1,
"description": "Document format version. Always 1."
}
},
"description": "One configuration document: declares either `kind` (branding, interview-portal, coaching-portal) or `format` (jobmojito.portal-site, jobmojito.portal-page), plus that type's fields. Field reference: branding https://developer.jobmojito.com/cookbooks/portal-branding/branding, interview portal https://developer.jobmojito.com/cookbooks/portal-branding/interview-portal, coaching portal https://developer.jobmojito.com/cookbooks/portal-branding/coaching-portal, portal pages https://developer.jobmojito.com/cookbooks/portal-branding/portal-pages. Unknown fields are ignored with a warning; wrong types and enum values are errors.",
"additionalProperties": true
},
"maxItems": 20,
"minItems": 1,
"description": "1 to 20 documents, applied in order (a later document wins where two set the same thing). Every field a document omits keeps its current value; languages are only ever added, never removed."
},
"merchant_id": {
"type": "string",
"format": "uuid",
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Optional merchant to configure. Admins and sub-merchant operators only; other callers always use their token's merchant."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"merchant_id",
"dry_run",
"documents",
"changes"
],
"properties": {
"changes": {
"type": "object",
"required": [
"merchant_fields",
"pages_created",
"pages_updated",
"pages_unchanged",
"theme_css",
"published",
"portal",
"page_changes"
],
"properties": {
"portal": {
"enum": [
"interview",
"consumer",
null
],
"type": [
"string",
"null"
],
"description": "Portal the portal-site pages and theme CSS went to."
},
"published": {
"type": [
"boolean",
"null"
],
"description": "Whether anything is published by this call."
},
"theme_css": {
"type": [
"boolean",
"null"
],
"description": "Whether the theme CSS changes."
},
"page_changes": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"portal",
"slug",
"action",
"published"
],
"properties": {
"slug": {
"type": [
"string",
"null"
],
"description": "Page slug (\"\" is the home page)."
},
"action": {
"enum": [
"create",
"update",
"unchanged",
null
],
"type": [
"string",
"null"
],
"description": "What happens to the page."
},
"portal": {
"enum": [
"interview",
"consumer",
null
],
"type": [
"string",
"null"
],
"description": "Portal of the page."
},
"published": {
"type": [
"boolean",
"null"
],
"description": "Whether this write publishes the page."
}
}
},
"description": "Every page touched, with its portal (built-in page drafts from a coaching-portal document go to the consumer portal)."
},
"pages_created": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"our-history"
],
"description": "Slugs of pages created."
},
"pages_updated": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"about",
"_support"
],
"description": "Slugs of existing pages whose draft (or published version) changes."
},
"merchant_fields": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"homepage_name",
"template_colors.c_primary",
"interview_homepage_texts.de"
],
"description": "Company settings that change: column names, with the key for the branding / feature jsonb and the language for home texts."
},
"pages_unchanged": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Slugs of pages the documents carry that already hold the same content (not written)."
}
},
"description": "What changed (for a dry run: what would change)."
},
"dry_run": {
"type": [
"boolean",
"null"
],
"description": "True when nothing was written."
},
"documents": {
"type": [
"array",
"null"
],
"items": {
"type": "object",
"required": [
"index",
"kind",
"summary",
"warnings"
],
"properties": {
"kind": {
"enum": [
"branding",
"interview-portal",
"coaching-portal",
"portal-site",
null
],
"type": [
"string",
"null"
],
"description": "How the document was read."
},
"index": {
"type": [
"integer",
"null"
],
"example": 0,
"description": "Position of the document in the request."
},
"summary": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"example": [
"3 colour(s)",
"style \"minimal\"",
"portal name"
],
"description": "What the document sets, in the lines the admin dialog shows (\"+ /slug\" a new page, \"↻ /slug\" a replaced one)."
},
"warnings": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Parts that were ignored or adjusted. Warnings never block the import."
}
}
}
},
"merchant_id": {
"type": [
"string",
"null"
],
"example": "28106cba-1c27-4e53-b149-32113e5e8e31",
"description": "Merchant that was configured."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"x-fastmcp-top-level-schema": "PortalConfigurationUpdateResponse"
}{
"title": "Update portal configuration",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}submit_feature_request — Submit feature request
[Feedback] Send a feature request to the JobMojito product team. It is posted on the public feature request board under the signed-in user's name, where they get status updates; the company is not named. Write a short title and a description of the need and why (no candidate data or company names). Confirm the wording with the user before submitting; at most 5 a day. Sends a feature request to the JobMojito product team. It is posted on the public feature request board under your name (from your account e-mail), where you get status updates and others can vote on it; your company is not named. At most 5 requests per user in 24 hours. Requires a user token (admin console session or MCP sign-in) of a merchant, merchant owner or admin.
{
"type": "object",
"required": [
"title",
"description"
],
"properties": {
"title": {
"type": "string",
"example": "Export all results of a position as one CSV",
"maxLength": 120,
"minLength": 5,
"description": "A short summary of the feature, shown as the post title."
},
"description": {
"type": "string",
"example": "We review applications in a spreadsheet every Friday. Today we export each interview separately; one CSV per position would save an hour a week.",
"maxLength": 4000,
"minLength": 10,
"description": "What the user needs and why: the problem, how they work around it today, who it would help. Plain text; blank lines separate paragraphs. The board is public: do not include candidate data, company names or other personal information."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"id",
"status"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "Id of the stored request."
},
"status": {
"enum": [
"posted",
"queued",
null
],
"type": [
"string",
"null"
],
"description": "`posted`: it is on the feature request board. `queued`: stored, it is posted shortly (the board could not be reached right now)."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"description": "The submitted feature request.",
"x-fastmcp-top-level-schema": "FeatureRequestSubmitResponse"
}{
"title": "Submit feature request",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}report_technical_issue — Report technical issue
[Feedback] Report a technical problem to JobMojito support. It opens a support conversation from the signed-in user's account (their e-mail), with their company and the interview or result ids given; support answers there. Describe what was done, what was expected and what happened (error text as shown). Confirm the wording with the user first; at most 5 a day. Reports a technical problem to JobMojito support. It opens a support conversation from your account (your e-mail), with your company and the ids you give; support answers there. At most 5 reports per user in 24 hours. Requires a user token (admin console session or MCP sign-in) of a merchant, merchant owner or admin.
{
"type": "object",
"required": [
"description"
],
"properties": {
"result_id": {
"type": [
"string",
"null"
],
"format": "uuid",
"description": "The interview result the problem is about, if any."
},
"description": {
"type": "string",
"example": "The PDF report of a finished interview stays empty since this morning. Other results open fine.",
"maxLength": 4000,
"minLength": 10,
"description": "What went wrong: what you did, what you expected, what happened instead (error messages as shown), and since when. Plain text; blank lines separate paragraphs. Do not include passwords or API keys."
},
"interview_id": {
"type": [
"string",
"null"
],
"format": "uuid",
"description": "The interview (definition) the problem is about, if any."
},
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
}
}{
"type": "object",
"required": [
"id",
"status",
"reference"
],
"properties": {
"id": {
"type": [
"string",
"null"
],
"description": "Id of the stored report."
},
"status": {
"enum": [
"sent",
"queued",
null
],
"type": [
"string",
"null"
],
"description": "`sent`: the support team has it. `queued`: stored, it is sent shortly (support could not be reached right now)."
},
"reference": {
"type": [
"string",
"null"
],
"example": "#592",
"description": "The support conversation number, once sent (e.g. `#592`)."
},
"_mcp_instructions": {
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The server-issued conversation identifier."
}
},
"description": "Server-issued metadata for this conversation."
}
},
"description": "The reported technical issue.",
"x-fastmcp-top-level-schema": "TechnicalIssueReportResponse"
}{
"title": "Report technical issue",
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}jobmojito_configuration — Choose a JobMojito merchant
Show the interactive JobMojito merchant picker (UI). ALWAYS call this when the user wants to choose, switch, or set a merchant, or when a tool needs a `merchant_id` and none is selected. It renders a searchable picker with clickable options. Do NOT list merchants as text or ask the user to type a name — render this picker instead. After calling it, STOP and wait for the user's selection; then pass `merchant_id=<chosen id>` on every JobMojito call (omit it for the user's own account).
{
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"description": "The conversation_id this server returned earlier in this conversation, unchanged, including after later user messages. Omit it if you have none. Recorded for the server developer's analytics."
}
},
"additionalProperties": false
}{
"title": "Choose a JobMojito merchant",
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}Prompts
0 advertised
Resources
1 advertised
Prefab Renderer (jobmojito_configuration)
ui://prefab/tool/8bb716989559/renderer.html90-day uptime
A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.
- 2026-11-06: 1 of 1 checks answered
- 2026-11-07: 1 of 1 checks answered
30-day availability
Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.
- Successful checks
- 1 of 1 (100%)
- Average latency
- 2566 ms
- 95th percentile latency
- 2566 ms
Maintenance history
Public check and source-refresh events. Editorial and ownership records remain private.
- · probe · passed · 2566 ms · metadata changed · 44 tool(s), 1 resource(s), 0 resource template(s), 0 prompt(s)
Connect from your client
Each snippet registers the same endpoint; pick the client you use.
Claude Code
claude mcp add --transport http jobmojito-mcp https://mcp.jobmojito.com/mcpCodex
codex mcp add jobmojito-mcp --url https://mcp.jobmojito.com/mcpCursor
{
"mcpServers": {
"jobmojito-mcp": {
"url": "https://mcp.jobmojito.com/mcp"
}
}
}Cline
{
"mcpServers": {
"jobmojito-mcp": {
"type": "streamableHttp",
"url": "https://mcp.jobmojito.com/mcp",
"disabled": false
}
}
}VS Code
{
"servers": {
"jobmojito-mcp": {
"type": "http",
"url": "https://mcp.jobmojito.com/mcp"
}
}
}Gemini CLI
gemini mcp add --transport http jobmojito-mcp https://mcp.jobmojito.com/mcpClaude Desktop
https://mcp.jobmojito.com/mcpWindsurf
{
"mcpServers": {
"jobmojito-mcp": {
"serverUrl": "https://mcp.jobmojito.com/mcp"
}
}
}Let your agent find its tools
Find servers like JobMojito MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.
Other productivity and knowledge servers
Do you run JobMojito MCP?
Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.