{"openapi":"3.1.0","info":{"title":"JobMojito API","version":"1.0.0","description":"Public API for JobMojito, served by Supabase Edge Functions. Authenticate with a Supabase JWT access token via the Authorization header."},"servers":[{"url":"https://cool.jobmojito.com/functions/v1","description":"Production"}],"tags":[{"name":"Interviews","description":"Create, configure and manage interview / coaching / assessment definitions."},{"name":"Coaching catalogue","description":"Directories (pages) of the coaching portal catalogue that group coaching sessions and carry custom content pages."},{"name":"Results","description":"Interview results, transcripts, reports, re-attempt requests and analytics."},{"name":"Candidates","description":"List and manage candidates."},{"name":"Knowledge base","description":"Upload documents used to generate knowledge-base interviews."},{"name":"Resume & Form verification","description":"Pre-screen candidates from resumes and forms."},{"name":"Admin","description":"Account administration — invite team/coaching users, manage sub-merchants and avatar templates."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Supabase JWT — `Authorization: Bearer <token>`."}},"schemas":{"JobInterviewCreateResponse":{"type":"object","properties":{"interview_def_set_id":{"type":"string","description":"Id of the newly created interview definition set.","example":"00000000-0000-0000-0000-000000000000"},"embed_id":{"type":"string","description":"Embed id, present only when is_embedded=true."},"embed_signing_key":{"type":"string","description":"Embed signing key, present only when is_embedded=true."},"questions_generated":{"type":"number","description":"Number of questions actually generated. May be lower than the requested interview_length, which is capped by max_duration.","example":8},"max_duration":{"type":"number","description":"Live session limit in seconds stored on the interview — the value sent, or the 1200 default when omitted.","example":1200}},"required":["interview_def_set_id","questions_generated","max_duration"],"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."},"Error":{"type":"object","properties":{"error":{"type":"string","example":"Field is required."},"name":{"type":"string","example":"interview_result_id"}},"required":["error"]},"JobInterviewCreateRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Interview / position name.","example":"Project manager"},"location":{"type":"string","minLength":1,"description":"Job location.","example":"remote"},"interview_template_id":{"type":"string","minLength":1,"format":"uuid","description":"Id of the interview template to base this interview on.","example":"baa3bf7c-6926-46fd-9005-16738a31f72d"},"mojito_language_code":{"type":"string","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"],"description":"Platform language code used for the interview. Must be one of the platform-languages.json codes.","example":"en"},"status":{"type":"string","enum":["draft","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..","example":"active"},"type":{"type":"string","enum":["interview","coaching","assessment"],"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..","example":"interview"},"visibility":{"type":"string","enum":["merchant_public","merchant_invite","merchant_unlisted"],"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..","example":"merchant_public"},"recording":{"type":"string","nullable":true,"enum":["audio_first_5_answers","audio_all","video_all","video_first_5_answers"],"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..","example":"video_all"},"recording_full_session":{"type":"string","nullable":true,"enum":["audio_all","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..","example":"video_all"},"interview_type":{"type":"string","enum":["pre-screening","pre-screening-with-test-questions","second-interview","remote-freelancer-verification","strength-based-interview","potential-based-interview","process-verification-from-knowledge-base"],"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..","example":"pre-screening-with-test-questions"},"seniority_level":{"type":"string","nullable":true,"enum":["entry-level","intermediate","senior","managerial","director","executive"],"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..","example":"senior"},"result_view":{"type":"string","nullable":true,"enum":["none","minimal","minimal_with_score","advanced","full","full_expand_scores"],"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..","example":"full"},"interview_conversation_speed":{"type":"string","nullable":true,"enum":["slower","normal","faster"],"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..","example":"normal"},"candidate_video_introduction":{"type":"string","nullable":true,"enum":["optional","required"],"description":"Whether a candidate video introduction is optional or required.","example":"optional"},"interview_tone":{"type":"string","nullable":true,"description":"Interview tone — configures the AI avatar's speaking style and the tone of the AI-generated questions and follow-ups; omit to default to relaxed. Suggested values — `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.. Case-insensitive; other strings are accepted but unknown tones fall back to the default.","example":"professional"},"interview_length":{"anyOf":[{"type":"number"},{"type":"string"},{"nullable":true}],"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.","example":8},"interview_attempts":{"type":"number","nullable":true,"minimum":1,"maximum":20,"description":"Allowed candidate attempts (1-20).","example":1},"max_duration":{"type":"number","nullable":true,"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.","example":1200},"max_followups":{"type":"integer","nullable":true,"minimum":0,"maximum":999,"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).","example":2},"questions_random_subset":{"type":"number","nullable":true,"minimum":0.01,"maximum":0.9,"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.","example":0.5},"code":{"type":"string","nullable":true,"description":"Optional external code/reference for the interview.","example":"my code"},"cover_image_url":{"type":"string","nullable":true,"description":"Cover image URL.","example":"https://example.com/cover.png"},"interview_department":{"type":"string","nullable":true,"description":"Department the position belongs to.","example":"Engineering"},"interview_salary":{"type":"string","nullable":true,"description":"Salary range shown for the position.","example":"$80k - $100k"},"interview_available_till":{"type":"string","nullable":true,"description":"ISO date/time after which the interview is no longer available to candidates. null keeps it always available.","example":"2026-12-31"},"recruiter_profile_id":{"type":"string","nullable":true,"description":"Profile id of the recruiter owning this interview. Must be a merchant/merchant_owner/admin profile of the same merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"hiring_for_company":{"type":"object","nullable":true,"properties":{"name":{"type":"string","nullable":true,"description":"End-employer name. Omit/null when hiring for yourself, 'undisclosed' for an unnamed external client, or the client's company name.","example":"Unimo Enterprises"},"description":{"type":"string","nullable":true,"description":"Short description of the end employer, used as background context by the agent.","example":"Unimo Enterprises is a leading logistics and supply chain solutions provider."},"location":{"type":"string","nullable":true,"description":"Primary location of the end employer.","example":"Sri Lanka"},"sector":{"type":"string","nullable":true,"description":"Industry / sector of the end employer.","example":"Logistics and Supply Chain"},"company_size":{"type":"string","nullable":true,"description":"Approximate headcount of the end employer.","example":"100-200"}},"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.","example":{"name":"undisclosed"}},"tags":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["interview-practice","sales"]},"merchant_id":{"type":"string","nullable":true,"description":"Merchant id. Admin / sub-merchant callers only; otherwise taken from your token.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"knowledge_base_store_id":{"type":"string","nullable":true,"description":"Knowledge base store id to source additional context from."},"description":{"type":"string","nullable":true,"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.","example":"Backend role focused on Python, async APIs, and AWS."},"description_long":{"type":"string","nullable":true,"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)."},"candidate_expectations":{"type":"string","nullable":true,"description":"Free-text candidate expectations folded into AI generation."},"welcome_message":{"type":"string","nullable":true,"description":"Custom welcome message shown to the candidate."},"thank_you_message":{"type":"string","nullable":true,"description":"Custom thank-you message shown after the interview."},"additional_context":{"type":"object","nullable":true,"additionalProperties":{"nullable":true},"description":"Arbitrary additional context object merged into AI generation."},"instructional_video_custom_text":{"type":"string","nullable":true,"description":"Custom narration text for the instructional video."},"is_embedded":{"type":"boolean","nullable":true,"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.","example":false},"include_rapport_question":{"type":"boolean","nullable":true,"description":"Include an opening rapport question. Defaults to false.","example":true},"include_closing_prompt":{"type":"boolean","nullable":true,"description":"Include a closing prompt. Defaults to true.","example":true},"instructional_video":{"type":"boolean","nullable":true,"description":"Show an instructional video before approval. Defaults to false.","example":true},"use_enhanced_expectations":{"type":"boolean","nullable":true,"description":"Reserved flag passed through to generation."},"required_pronunciation":{"type":"boolean","nullable":true,"description":"Require pronunciation assessment (restricts to pronunciation-capable languages). Defaults to false.","example":false},"result_enable_edit_transcript":{"type":"boolean","nullable":true,"description":"Allow editing the transcript on the result view. Defaults to true.","example":true},"custom_scoring":{"type":"object","nullable":true,"additionalProperties":{"nullable":true},"description":"Custom result-scoring overrides merged with defaults and template overrides."},"pdf_export_auto_config":{"type":"object","nullable":true,"properties":{"mojito_language_code":{"type":"string","description":"Report language code (platform-languages.json code).","example":"en"},"contact_details":{"type":"boolean","description":"Include candidate contact details."},"ai_recruiter_assessment":{"type":"boolean","description":"Include the AI recruiter assessment."},"ai_scoring_rubric":{"type":"boolean","description":"Include the AI scoring rubric."},"analytics":{"type":"boolean","description":"Include analytics."},"files":{"type":"boolean","description":"Include uploaded candidate files."},"transcript":{"type":"boolean","description":"Include the interview transcript."},"answer_recording":{"type":"boolean","description":"Include per-answer recordings."},"session_recording":{"type":"boolean","description":"Include the full-session recording."},"group_by_question":{"type":"boolean","description":"Group transcript answers by expected question."}},"description":"Auto-generate a candidate PDF report with these options once the interview completes. null disables auto-export."}},"required":["name","location","interview_template_id","mojito_language_code","status","type","visibility"]},"JobInterviewCreateFromArrayResponse":{"type":"object","properties":{"interview_def_set_id":{"type":"string","description":"Id of the newly created interview definition set."},"embed_id":{"type":"string","description":"Embed id, present only when is_embedded=true."},"embed_signing_key":{"type":"string","description":"Embed signing key, present only when is_embedded=true."}},"required":["interview_def_set_id"],"description":"The created interview definition set id, plus embed credentials when is_embedded=true."},"JobInterviewCreateFromArrayQuestion":{"type":"object","properties":{"question":{"type":"string","description":"The question text shown to the candidate."},"id":{"type":"string","description":"Caller-local identifier for this question. Only needed when another question references it via conditional_question_main_id (the mapping is resolved within this array)."},"duration":{"type":"number","nullable":true,"description":"Answer duration in seconds for this question."},"mojito_language_code":{"type":"string","nullable":true,"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"],"description":"Per-question language override (one of the platform-languages.json codes). Inherits the interview language when omitted.","example":"en"},"label":{"type":"string","nullable":true,"description":"Optional label/tag stored on the question."},"is_conditional":{"type":"boolean","nullable":true,"description":"Conditional follow-up question (view 'with listening conditional'). Use with conditional_question_main_id."},"is_without_scoring":{"type":"boolean","nullable":true,"description":"Question is asked but not scored (view 'without scoring')."},"is_candidate_asking_recruiter":{"type":"boolean","nullable":true,"description":"Candidate-asks-recruiter prompt (view 'candidate asking recruiter')."},"is_expert":{"type":"boolean","nullable":true,"description":"Expert listening question (view 'with listening expert')."},"is_multiple_choice":{"type":"boolean","nullable":true,"description":"Multiple-choice question (view 'multiple choice')."},"question_alternatives":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Alternative phrasings for the question."},"conditional_question_main_id":{"type":"string","nullable":true,"description":"For a conditional question, the id (the local \"id\" field above) of the parent question in this same array that triggers it."},"knowledge_base_id":{"type":"string","nullable":true,"description":"Knowledge-base store id (uuid) the question draws context from."},"external_id":{"type":"string","nullable":true,"description":"External identifier stored on the question."},"external_data":{"type":"object","nullable":true,"additionalProperties":{"nullable":true},"description":"Arbitrary JSON metadata stored on the question."},"candidate_expectations_json":{"type":"object","nullable":true,"properties":{"weak":{"type":"array","items":{"type":"string"},"description":"Baseline requirements every viable candidate should meet (table stakes)."},"moderate":{"type":"array","items":{"type":"string"},"description":"Requirements expected of a solid, competent candidate."},"strong":{"type":"array","items":{"type":"string"},"description":"High-bar requirements only standout candidates clear."}},"description":"Per-question candidate expectations, bucketed by requirement level (weak/moderate/strong). Extra keys are preserved."}},"required":["question"],"additionalProperties":{"nullable":true}},"JobInterviewCreateFromArrayRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Interview/position name.","example":"Project manager"},"location":{"type":"string","minLength":1,"description":"Interview location.","example":"remote"},"interview_template_id":{"type":"string","minLength":1,"description":"Id of the interview template to use. Must reference an existing interview_templates row.","example":"46b98d37-1557-4391-beca-03037ead19f2"},"mojito_language_code":{"type":"string","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"],"description":"Platform language code (one of the platform-languages.json codes); must also resolve to a supported language with an Azure speech mapping.","example":"en"},"description":{"type":"string","minLength":1,"description":"Short interview description.","example":"Project manager role"},"status":{"type":"string","enum":["draft","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..","example":"active"},"type":{"type":"string","enum":["interview","coaching","assessment"],"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..","example":"interview"},"visibility":{"type":"string","enum":["merchant_public","merchant_invite","merchant_unlisted"],"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..","example":"merchant_public"},"questions":{"type":"array","items":{"$ref":"#/components/schemas/JobInterviewCreateFromArrayQuestion"},"minItems":1,"description":"Ordered list of interview questions to create as steps."},"recording":{"type":"string","nullable":true,"enum":["audio_first_5_answers","audio_all","video_all","video_first_5_answers"],"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..","example":"video_all"},"recording_full_session":{"type":"string","nullable":true,"enum":["audio_all","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..","example":"video_all"},"interview_type":{"type":"string","nullable":true,"enum":["pre-screening","pre-screening-with-test-questions","second-interview","remote-freelancer-verification","strength-based-interview","potential-based-interview","process-verification-from-knowledge-base"],"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..","example":"pre-screening-with-test-questions"},"seniority_level":{"type":"string","nullable":true,"enum":["entry-level","intermediate","senior","managerial","director","executive"],"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..","example":"senior"},"result_view":{"type":"string","nullable":true,"enum":["none","minimal","minimal_with_score","advanced","full","full_expand_scores"],"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..","example":"full"},"interview_conversation_speed":{"type":"string","nullable":true,"enum":["slower","normal","faster"],"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..","example":"normal"},"candidate_video_introduction":{"type":"string","nullable":true,"enum":["optional","required"],"description":"Whether a candidate video introduction is optional or required."},"interview_tone":{"type":"string","nullable":true,"description":"Tone — configures the AI avatar's speaking style and the follow-up questions it generates; the base `questions` you supply are not affected. Omit to default to relaxed. Suggested values — `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.. Case-insensitive; other strings are accepted but unknown tones fall back to the default.","example":"professional"},"interview_attempts":{"type":"number","nullable":true,"description":"Allowed attempts, 1-20.","example":1},"questions_random_subset":{"type":"number","nullable":true,"description":"Fraction of questions to randomly ask, between 0.01 and 0.9."},"max_followups":{"type":"integer","nullable":true,"minimum":0,"maximum":999,"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).","example":2},"code":{"type":"string","nullable":true,"description":"Optional external code/reference."},"cover_image_url":{"type":"string","nullable":true,"description":"Cover image URL."},"interview_department":{"type":"string","nullable":true,"description":"Department the position belongs to.","example":"Engineering"},"interview_salary":{"type":"string","nullable":true,"description":"Salary range shown for the position.","example":"$80k - $100k"},"interview_available_till":{"type":"string","nullable":true,"description":"ISO date/time after which the interview is no longer available to candidates. null keeps it always available.","example":"2026-12-31"},"recruiter_profile_id":{"type":"string","nullable":true,"description":"Profile id of the recruiter owning this interview. Must be a merchant/merchant_owner/admin profile of the same merchant."},"hiring_for_company":{"type":"object","nullable":true,"properties":{"name":{"type":"string","nullable":true,"description":"End-employer name. Omit/null when hiring for yourself, 'undisclosed' for an unnamed external client, or the client's company name.","example":"Unimo Enterprises"},"description":{"type":"string","nullable":true,"description":"Short description of the end employer, used as background context by the agent.","example":"Unimo Enterprises is a leading logistics and supply chain solutions provider."},"location":{"type":"string","nullable":true,"description":"Primary location of the end employer.","example":"Sri Lanka"},"sector":{"type":"string","nullable":true,"description":"Industry / sector of the end employer.","example":"Logistics and Supply Chain"},"company_size":{"type":"string","nullable":true,"description":"Approximate headcount of the end employer.","example":"100-200"}},"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.","example":{"name":"undisclosed"}},"tags":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["interview-practice","sales"]},"required_pronunciation":{"type":"boolean","nullable":true,"description":"Require pronunciation assessment (restricts to pronunciation-capable languages). Defaults to false.","example":false},"result_enable_edit_transcript":{"type":"boolean","nullable":true,"description":"Allow editing the transcript on the result view. Defaults to true.","example":true},"pdf_export_auto_config":{"type":"object","nullable":true,"properties":{"mojito_language_code":{"type":"string","description":"Report language code (platform-languages.json code).","example":"en"},"contact_details":{"type":"boolean","description":"Include candidate contact details."},"ai_recruiter_assessment":{"type":"boolean","description":"Include the AI recruiter assessment."},"ai_scoring_rubric":{"type":"boolean","description":"Include the AI scoring rubric."},"analytics":{"type":"boolean","description":"Include analytics."},"files":{"type":"boolean","description":"Include uploaded candidate files."},"transcript":{"type":"boolean","description":"Include the interview transcript."},"answer_recording":{"type":"boolean","description":"Include per-answer recordings."},"session_recording":{"type":"boolean","description":"Include the full-session recording."},"group_by_question":{"type":"boolean","description":"Group transcript answers by expected question."}},"description":"Auto-generate a candidate PDF report with these options once the interview completes. null disables auto-export."},"knowledge_base_store_id":{"type":"string","nullable":true,"description":"Optional knowledge base store id; validated for existence."},"merchant_id":{"type":"string","nullable":true,"description":"Target merchant id (admins / sub-merchant only)."},"description_long":{"type":"string","nullable":true,"description":"Long-form interview description."},"candidate_expectations":{"type":"string","nullable":true,"description":"Free-text candidate expectations."},"candidate_expectations_json":{"type":"object","nullable":true,"properties":{"weak":{"type":"array","items":{"type":"string"},"description":"Baseline requirements every viable candidate should meet (table stakes)."},"moderate":{"type":"array","items":{"type":"string"},"description":"Requirements expected of a solid, competent candidate."},"strong":{"type":"array","items":{"type":"string"},"description":"High-bar requirements only standout candidates clear."}},"description":"Pre-generated candidate expectations, bucketed by requirement level (weak/moderate/strong); auto-generated when omitted for type=interview. Extra keys are preserved."},"custom_scoring":{"type":"object","nullable":true,"additionalProperties":{"nullable":true},"description":"Custom scoring overrides merged with defaults."},"additional_context":{"type":"object","nullable":true,"additionalProperties":{"nullable":true},"description":"Extra context forwarded to expectation generation."},"instructional_video":{"type":"boolean","nullable":true,"description":"Enable an instructional video before approval."},"instructional_video_custom_text":{"type":"string","nullable":true,"description":"Custom text for the instructional video."},"disable_deduplication":{"type":"boolean","nullable":true,"description":"When true, skip step deduplication on insert."},"welcome_message":{"type":"string","nullable":true,"description":"Custom welcome message."},"thank_you_message":{"type":"string","nullable":true,"description":"Custom thank-you message."},"is_embedded":{"type":"boolean","nullable":true,"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."}},"required":["name","location","interview_template_id","mojito_language_code","description","status","type","visibility","questions"]},"JobInterviewCreateForCandidateWithTokenResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ai_accept","recruiter_accept","recruiter_action","ai_reject","recruiter_reject","already-applied"],"description":"Decision / lifecycle status for the candidate against this position."},"position_def_set_id":{"type":"string","description":"Created/resolved position id."},"interview_def_set_id":{"type":"string","description":"Created/resolved interview definition set id."},"profile_interview_id":{"type":"string","description":"Candidate profile_interview id."},"interview_url":{"type":"string","description":"URL the candidate should open to continue."},"reason":{"type":"string","description":"Human-readable reason, present on reject / already-applied branches."},"embed_id":{"type":"string","description":"Embed id, present only when is_embedded=true."},"embed_signing_key":{"type":"string","description":"Embed signing key, present only when is_embedded=true."}},"required":["status","interview_url"],"description":"Result of creating (or resolving) the position and enrolling the candidate. Fields present depend on the decision branch."},"JobInterviewCreateForCandidateWithTokenRequest":{"type":"object","properties":{"position_def_set_id":{"type":"string","nullable":true,"description":"Existing position_def_set id. When provided, the position is looked up instead of created, and position_name/position_location/position_country_code/mojito_language_code become optional.","example":"00000000-0000-0000-0000-000000000000"},"position_external_id":{"type":"string","nullable":true,"description":"External position code used to look up an existing position.","example":"JOB-123"},"interview_template_id":{"type":"string","nullable":true,"description":"Interview template id to base the interview on. Defaults to the platform template when omitted.","example":"5d8ea38b-eec4-4866-aa6c-b2ea5bc6e45b"},"merchant_id":{"type":"string","nullable":true,"description":"Merchant id. Only honoured for admin / sub-merchant tokens; otherwise the token merchant is used.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"position_name":{"type":"string","nullable":true,"description":"Position / job title. Required when position_def_set_id is not provided.","example":"Entry level coffee boy"},"position_location":{"type":"string","nullable":true,"description":"Position location. Required when position_def_set_id is not provided.","example":"remote"},"position_country_code":{"type":"string","nullable":true,"description":"ISO country code of the position. Required when position_def_set_id is not provided.","example":"SK"},"mojito_language_code":{"type":"string","nullable":true,"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"],"description":"Interview language code (one of the platform-languages.json codes). Required when position_def_set_id is not provided.","example":"en"},"position_description":{"type":"string","nullable":true,"description":"Short, two-sentence position 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."},"position_description_long":{"type":"string","nullable":true,"description":"Full position 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."},"candidate_name":{"type":"string","minLength":1,"description":"Candidate full name.","example":"John"},"candidate_email":{"type":"string","minLength":1,"description":"Candidate email (used as auth identity).","example":"peterson6@hello.com"},"candidate_country_code":{"type":"string","minLength":1,"description":"ISO country code of the candidate.","example":"HR"},"candidate_resume":{"type":"string","minLength":1,"description":"Candidate resume parsed text.","example":"Curriculum Vitae ..."},"candidate_external_id":{"type":"string","nullable":true,"description":"External candidate id stored on the profile."},"candidate_linkedin_url":{"type":"string","nullable":true,"description":"Candidate LinkedIn URL."},"candidate_video_introduction":{"type":"string","nullable":true,"enum":["optional","required"],"description":"Whether a candidate video introduction is optional or required."},"interview_attempts":{"type":"number","nullable":true,"minimum":1,"maximum":20,"description":"Number of allowed interview attempts (1-20).","example":3},"seniority_level":{"type":"string","nullable":true,"enum":["entry-level","intermediate","senior","managerial","director","executive"],"description":"Seniority level of the position. Auto-detected when omitted."},"result_view":{"type":"string","nullable":true,"enum":["none","minimal","minimal_with_score","advanced","full","full_expand_scores"],"description":"How interview results are shown to the candidate. Defaults to minimal."},"custom_scoring":{"type":"object","nullable":true,"additionalProperties":{"nullable":true},"description":"Optional custom scoring overrides merged with platform defaults."},"use_enhanced_expectations":{"type":"boolean","nullable":true,"description":"Reserved flag for enhanced candidate expectations generation."},"include_rapport_question":{"type":"boolean","nullable":true,"description":"Include an opening rapport question. Defaults to false."},"include_closing_prompt":{"type":"boolean","nullable":true,"description":"Include a closing prompt. Defaults to true."},"instructional_video":{"type":"boolean","nullable":true,"description":"Show an instructional video before the interview. Defaults to false."},"instructional_video_custom_text":{"type":"string","nullable":true,"description":"Custom text shown with the instructional video."},"welcome_message":{"type":"string","nullable":true,"description":"Custom welcome message for the interview."},"thank_you_message":{"type":"string","nullable":true,"description":"Custom thank-you message shown after the interview."},"max_duration":{"type":"number","nullable":true,"description":"Maximum interview duration in seconds. Scopes how many questions are generated 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.","example":1200},"is_embedded":{"type":"boolean","nullable":true,"description":"Set true when the interview will be embedded as an iframe on an external page. Ensures an embed key exists and returns embed_id/embed_signing_key, used to authenticate/sign the iframe embed."},"interview_department":{"type":"string","nullable":true,"description":"Department the position belongs to.","example":"Engineering"},"interview_salary":{"type":"string","nullable":true,"description":"Salary range shown for the position.","example":"$80k - $100k"},"interview_available_till":{"type":"string","nullable":true,"description":"ISO date/time after which the interview is no longer available to candidates. null keeps it always available.","example":"2026-12-31"},"interview_conversation_speed":{"type":"string","nullable":true,"enum":["slower","normal","faster"],"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..","example":"normal"},"max_followups":{"type":"integer","nullable":true,"minimum":0,"maximum":999,"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).","example":2},"questions_random_subset":{"type":"number","nullable":true,"minimum":0.01,"maximum":0.9,"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.","example":0.5},"required_pronunciation":{"type":"boolean","nullable":true,"description":"Require pronunciation assessment (restricts to pronunciation-capable languages). Defaults to false.","example":false},"result_enable_edit_transcript":{"type":"boolean","nullable":true,"description":"Allow editing the transcript on the result view. Defaults to true.","example":true},"tags":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Free-form tags stored on the interview.","example":["engineering","remote"]},"recruiter_profile_id":{"type":"string","nullable":true,"description":"Profile id of the recruiter owning this interview. Must be a merchant/merchant_owner/admin profile of the same merchant."},"hiring_for_company":{"type":"object","nullable":true,"properties":{"name":{"type":"string","nullable":true,"description":"End-employer name. Omit/null when hiring for yourself, 'undisclosed' for an unnamed external client, or the client's company name.","example":"Unimo Enterprises"},"description":{"type":"string","nullable":true,"description":"Short description of the end employer, used as background context by the agent.","example":"Unimo Enterprises is a leading logistics and supply chain solutions provider."},"location":{"type":"string","nullable":true,"description":"Primary location of the end employer.","example":"Sri Lanka"},"sector":{"type":"string","nullable":true,"description":"Industry / sector of the end employer.","example":"Logistics and Supply Chain"},"company_size":{"type":"string","nullable":true,"description":"Approximate headcount of the end employer.","example":"100-200"}},"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.","example":{"name":"undisclosed"}},"pdf_export_auto_config":{"type":"object","nullable":true,"properties":{"mojito_language_code":{"type":"string","description":"Report language code (platform-languages.json code).","example":"en"},"contact_details":{"type":"boolean","description":"Include candidate contact details."},"ai_recruiter_assessment":{"type":"boolean","description":"Include the AI recruiter assessment."},"ai_scoring_rubric":{"type":"boolean","description":"Include the AI scoring rubric."},"analytics":{"type":"boolean","description":"Include analytics."},"files":{"type":"boolean","description":"Include uploaded candidate files."},"transcript":{"type":"boolean","description":"Include the interview transcript."},"answer_recording":{"type":"boolean","description":"Include per-answer recordings."},"session_recording":{"type":"boolean","description":"Include the full-session recording."},"group_by_question":{"type":"boolean","description":"Group transcript answers by expected question."}},"description":"Auto-generate a candidate PDF report with these options once the interview completes. null disables auto-export."}},"required":["candidate_name","candidate_email","candidate_country_code","candidate_resume"]},"PersonaCreateResponse":{"type":"object","properties":{"interview_def_set_id":{"type":"string","description":"Id of the newly created persona definition set.","example":"00000000-0000-0000-0000-000000000000"},"embed_id":{"type":"string","description":"Embed id, present only when is_embedded=true."},"embed_signing_key":{"type":"string","description":"Embed signing key, present only when is_embedded=true."}},"required":["interview_def_set_id"],"description":"Id of the created persona. Includes embed_id/embed_signing_key when is_embedded=true."},"PersonaCreateRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Persona / session name.","example":"Difficult customer role-play"},"interview_template_id":{"type":"string","minLength":1,"format":"uuid","description":"Id of the interview template (avatar) the persona uses.","example":"baa3bf7c-6926-46fd-9005-16738a31f72d"},"mojito_language_code":{"type":"string","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"],"description":"Platform language code used for the conversation. Must be one of the platform-languages.json codes.","example":"en"},"status":{"type":"string","enum":["draft","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..","example":"active"},"visibility":{"type":"string","enum":["merchant_public","merchant_invite","merchant_unlisted"],"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..","example":"merchant_public"},"description":{"type":"string","nullable":true,"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.","example":"A quick sales role-play with an unhappy customer. Your goal is to resolve their complaint and keep the account."},"persona_role_avatar":{"type":"string","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'.","example":"is to act as a happy customer responding to questions"},"persona_role_user":{"type":"string","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'.","example":"is to be a sales person trying to sell an additional product to the customer"},"persona_avatar_who_is":{"type":"string","nullable":true,"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."},"persona_avatar_knowledge":{"type":"string","nullable":true,"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."},"persona_avatar_end_conditions":{"type":"string","nullable":true,"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."},"persona_avatar_progress":{"type":"string","nullable":true,"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.","example":"Mode: turning point\nInitial hold: ...\nUnlocks when: ...\nAfter unlock: ...\nHard limits: ...\nNo goalposts: ...\nGates: ..."},"opening_line":{"type":"string","nullable":true,"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.","example":"Oh, finally — someone actually comes over! I've been waiting for ages. Are you going to help me or not?"},"candidate_expectations":{"type":"string","nullable":true,"maxLength":2100,"description":"Mentee assessment goals — free-text describing what the candidate is expected to achieve (max 2100 chars)."},"recording":{"type":"string","nullable":true,"enum":["audio_first_5_answers","audio_all","video_all","video_first_5_answers"],"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..","example":"video_all"},"recording_full_session":{"type":"string","nullable":true,"enum":["audio_all","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..","example":"video_all"},"result_view":{"type":"string","nullable":true,"enum":["none","minimal","minimal_with_score","advanced","full","full_expand_scores"],"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..","example":"full"},"interview_conversation_speed":{"type":"string","nullable":true,"enum":["slower","normal","faster"],"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..","example":"normal"},"candidate_video_introduction":{"type":"string","nullable":true,"enum":["optional","required"],"description":"Whether a candidate video introduction is optional or required.","example":"optional"},"max_duration":{"type":"number","nullable":true,"description":"Maximum conversation duration in seconds. Defaults to 1200 (20 min) when omitted.","example":1200},"code":{"type":"string","nullable":true,"description":"Optional external code/reference for the persona.","example":"my code"},"cover_image_url":{"type":"string","nullable":true,"description":"Cover image URL.","example":"https://example.com/cover.png"},"interview_location":{"type":"string","nullable":true,"description":"Optional location label shown for the session.","example":"remote"},"merchant_id":{"type":"string","nullable":true,"description":"Merchant id. Admin / sub-merchant callers only; otherwise taken from your token.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"recruiter_profile_id":{"type":"string","nullable":true,"description":"Profile id of the recruiter owning this persona. Must be a merchant/merchant_owner/admin profile of the same merchant."},"welcome_message":{"type":"string","nullable":true,"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.","example":"Welcome! You are about to speak with a customer who is unhappy about a recent price increase."},"thank_you_message":{"type":"string","nullable":true,"description":"Custom thank-you message shown after the session — set it; omitting it leaves a generic platform default.","example":"Thanks — your role-play has been recorded and will be reviewed by the hiring team."},"tags":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["interview-practice","sales"]},"portal":{"type":"string","nullable":true,"enum":["coaching","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.","example":"interview"},"interview_attempts":{"type":"number","nullable":true,"minimum":1,"maximum":20,"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.","example":3},"is_embedded":{"type":"boolean","nullable":true,"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.","example":false}},"required":["name","interview_template_id","mojito_language_code","status","visibility","persona_role_avatar","persona_role_user"]},"JobInterviewGetResponse":{"type":"object","properties":{"name":{"type":"string","nullable":true,"description":"Interview or position name."},"created_at":{"type":"string","nullable":true,"example":"2026-01-15T09:30:00.000Z"},"updated_at":{"type":"string","nullable":true,"example":"2026-01-20T14:05:00.000Z"},"calc_definition_json":{"nullable":true,"description":"The compiled interview definition JSON (structure varies by interview type)."},"is_multistage":{"type":"boolean","description":"True when the id resolved to a multi-stage position rather than a single interview."},"status":{"type":"string","enum":["draft","active","archived","deleted","preparing","completed"],"description":"Lifecycle status of the interview/position."},"visibility":{"type":"string","enum":["public","merchant_public","merchant_invite","merchant_unlisted","sub_item"],"description":"Who can see and access the interview/position."},"stage":{"type":"string","nullable":true,"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"],"description":"Hiring-pipeline stage. Null for multi-stage positions."},"type":{"type":"string","nullable":true,"enum":["coaching","interview","persona","persona_interview","public_avatar","assessment"],"description":"Interview type. Null for multi-stage positions."},"code":{"type":"string","nullable":true,"description":"Caller-supplied external code/reference."},"interview_location":{"type":"string","nullable":true,"description":"Location (create field `location`)."},"cover_image_url":{"type":"string","nullable":true,"description":"Cover image URL."},"merchant_id":{"type":"string","nullable":true,"description":"Owning merchant id."},"interview_template_id":{"type":"string","nullable":true,"description":"Interview template id. For multi-stage positions this is the first interview step's template."},"interview_template_type":{"type":"string","nullable":true,"enum":["offline_elai","offline_synthesia","interactive_heygen","offline_heygen","interactive_elevenlabs"],"description":"Type of the linked interview template. `interactive_elevenlabs` is voice-only; the others (`interactive_heygen`, `offline_heygen`, `offline_elai`, `offline_synthesia`) are avatar-based. Null when the template could not be resolved."},"is_voice_only":{"type":"boolean","nullable":true,"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."},"knowledge_base_store_id":{"type":"string","nullable":true,"description":"Linked knowledge base store id. Null for multi-stage positions."},"mojito_language_code":{"type":"string","nullable":true,"description":"Platform (mojito) language code.","example":"en"},"speech_language_code":{"type":"string","nullable":true,"description":"Azure speech language code. Null for multi-stage positions."},"speech_language_name":{"type":"string","nullable":true,"description":"Azure speech language display name. Null for multi-stage positions."},"recording":{"type":"string","nullable":true,"enum":["audio_first_5_answers","audio_all","video_all","video_first_5_answers"],"description":"Per-answer recording mode. Null for multi-stage positions."},"recording_full_session":{"type":"string","nullable":true,"enum":["audio_first_5_answers","audio_all","video_all","video_first_5_answers"],"description":"Full-session recording mode. Null for multi-stage positions."},"type_credit":{"type":"string","nullable":true,"enum":["resume_check","interview_coach_starter","interview_coach_contributor","interview_coach_manager","cover_letter"],"description":"Credit bucket the interview draws from. Null for multi-stage positions."},"result_view":{"type":"string","nullable":true,"enum":["minimal","advanced","full","full_expand_scores","minimal_with_score","none"],"description":"Result view level. Null for multi-stage positions."},"candidate_video_introduction":{"type":"string","nullable":true,"enum":["hidden","optional","required"],"description":"Whether a candidate video introduction is hidden/optional/required. Null for multi-stage positions."},"description":{"type":"string","nullable":true,"description":"Short description."},"interview_description_long":{"type":"string","nullable":true,"description":"Long description (create/update field `description_long`)."},"candidate_expectations":{"type":"string","nullable":true,"description":"Free-text candidate expectations. Null for multi-stage positions."},"candidate_expectations_json":{"nullable":true,"description":"Structured candidate expectations JSON — the scoring rubric. Null for multi-stage positions."},"result_scoring":{"nullable":true,"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."},"creation_parameters":{"nullable":true,"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)."},"interview_department":{"type":"string","nullable":true,"description":"Department the position belongs to.","example":"Engineering"},"interview_salary":{"type":"string","nullable":true,"description":"Salary range shown for the position.","example":"$80k - $100k"},"interview_available_till":{"type":"string","nullable":true,"description":"ISO date/time after which the interview is no longer available to candidates. Null means always available."},"recruiter_profile_id":{"type":"string","nullable":true,"description":"Profile id of the recruiter owning this interview/position."},"slug":{"type":"string","nullable":true,"description":"URL slug of the public listing, when one was generated."},"tags":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["interview-practice","sales"]},"coach_plan":{"type":"string","nullable":true,"enum":["demo","screening","2nd","3rd","closing","job-specific","other"],"description":"Coaching-plan stage this session belongs to. Null for multi-stage positions and for sessions outside any plan."},"interview_conversation_speed":{"type":"string","nullable":true,"description":"Conversation pace of the AI avatar (slower/normal/faster). Null keeps the template default. Null for multi-stage positions."},"max_followups":{"type":"number","nullable":true,"description":"Maximum number of AI follow-up questions; null uses the template default. Null for multi-stage positions."},"max_duration":{"type":"number","nullable":true,"description":"Live session limit in seconds. Null for multi-stage positions."},"questions_random_subset":{"type":"number","nullable":true,"description":"Fraction of the questions actually asked (0.01-0.9); null asks all of them. Null for multi-stage positions."},"required_pronunciation":{"type":"boolean","nullable":true,"description":"Whether a pronunciation assessment is required. Null for multi-stage positions."},"result_enable_edit_transcript":{"type":"boolean","nullable":true,"description":"Whether the candidate may edit the transcript on the result view. Null for multi-stage positions."},"pdf_export_auto_config":{"nullable":true,"description":"Auto-PDF-report options applied when the interview completes; null when auto-export is off. Null for multi-stage positions."}},"required":["name","created_at","updated_at","is_multistage","status","visibility","stage","type","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","recruiter_profile_id","slug","tags","coach_plan","interview_conversation_speed","max_followups","max_duration","questions_random_subset","required_pronunciation","result_enable_edit_transcript"]},"JobInterviewUpdateResponse":{"type":"object","properties":{"position_id":{"type":"string","description":"The id that was updated."},"is_multistage":{"type":"boolean","description":"True when the id resolved to a multi-stage position (position_def_set) rather than a single interview."},"updated_fields":{"type":"array","items":{"type":"string"},"description":"Names of the stored columns that were written, plus `status` when the lifecycle status was changed.","example":["name","tags"]}},"required":["position_id","is_multistage","updated_fields"],"description":"Confirmation of what was updated."},"JobInterviewUpdateRequest":{"type":"object","properties":{"position_id":{"type":"string","minLength":1,"format":"uuid","description":"Id of the interview definition (single-stage) or position definition (multi-stage) to update. The same id you pass to job-interview-get.","example":"00000000-0000-0000-0000-000000000000"},"status":{"type":"string","enum":["draft","active","archived","deleted","preparing","completed"],"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).","example":"active"},"visibility":{"type":"string","enum":["merchant_public","merchant_invite","merchant_unlisted"],"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..","example":"merchant_public"},"name":{"type":"string","nullable":true,"description":"Interview / position name.","example":"Project manager"},"code":{"type":"string","nullable":true,"description":"External code/reference. Blank is stored as null."},"location":{"type":"string","nullable":true,"description":"Interview location (column `interview_location`). Blank is stored as null.","example":"remote"},"cover_image_url":{"type":"string","nullable":true,"description":"Cover image URL."},"description":{"type":"string","nullable":true,"description":"Short interview description."},"description_long":{"type":"string","nullable":true,"description":"Long-form interview description (column `interview_description_long`), Markdown."},"interview_department":{"type":"string","nullable":true,"description":"Department the position belongs to. Blank is stored as null.","example":"Engineering"},"interview_salary":{"type":"string","nullable":true,"description":"Salary range shown for the position. Blank is stored as null.","example":"$80k - $100k"},"interview_available_till":{"type":"string","nullable":true,"description":"ISO date/time after which the interview is no longer available to candidates. null keeps it always available.","example":"2026-12-31"},"recruiter_profile_id":{"type":"string","nullable":true,"description":"Profile id of the recruiter owning this interview. Must be a merchant/merchant_owner/admin profile of the same merchant. null clears it."},"tags":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["interview-practice","sales"]},"type":{"type":"string","enum":["interview","coaching","assessment"],"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.","example":"interview"},"type_credit":{"type":"string","nullable":true,"enum":["resume_check","interview_coach_starter","interview_coach_contributor","interview_coach_manager","cover_letter"],"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..","example":"interview_coach_manager"},"coach_plan":{"type":"string","nullable":true,"enum":["demo","screening","2nd","3rd","closing","job-specific","other"],"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..","example":"screening"},"interview_template_id":{"type":"string","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.","example":"46b98d37-1557-4391-beca-03037ead19f2"},"knowledge_base_store_id":{"type":"string","nullable":true,"description":"Knowledge base store id the interview draws context from; validated for existence. null unlinks it."},"recording":{"type":"string","nullable":true,"enum":["audio_first_5_answers","audio_all","video_all","video_first_5_answers"],"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..","example":"video_all"},"recording_full_session":{"type":"string","nullable":true,"enum":["audio_all","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..","example":"video_all"},"result_view":{"type":"string","enum":["none","minimal","minimal_with_score","advanced","full","full_expand_scores"],"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..","example":"full"},"candidate_video_introduction":{"type":"string","nullable":true,"enum":["hidden","optional","required"],"description":"Whether a candidate video introduction is hidden, optional or required. null is treated like hidden."},"interview_conversation_speed":{"type":"string","nullable":true,"enum":["slower","normal","faster"],"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..","example":"normal"},"max_followups":{"type":"integer","nullable":true,"minimum":0,"maximum":999,"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).","example":2},"max_duration":{"type":"number","nullable":true,"description":"Live session limit in seconds. Also the basis for the credit multiplier.","example":1200},"questions_random_subset":{"type":"number","nullable":true,"minimum":0.01,"maximum":0.9,"description":"Ask only a random subset of the questions, as a fraction between 0.01 and 0.9. null asks all questions.","example":0.5},"interview_attempts":{"type":"number","minimum":1,"maximum":20,"description":"Allowed candidate attempts (1-20). Stored as result_scoring.max_retries.","example":1},"required_pronunciation":{"type":"boolean","nullable":true,"description":"Require pronunciation assessment (restricts to pronunciation-capable languages)."},"result_enable_edit_transcript":{"type":"boolean","nullable":true,"description":"Allow editing the transcript on the result view."},"candidate_expectations":{"type":"string","nullable":true,"description":"Free-text candidate expectations."},"candidate_expectations_json":{"type":"object","nullable":true,"properties":{"weak":{"type":"array","items":{"type":"string"},"description":"Baseline requirements every viable candidate should meet (table stakes)."},"moderate":{"type":"array","items":{"type":"string"},"description":"Requirements expected of a solid, competent candidate."},"strong":{"type":"array","items":{"type":"string"},"description":"High-bar requirements only standout candidates clear."}},"description":"Structured candidate expectations (the scoring rubric), bucketed by requirement level (weak/moderate/strong). null clears the rubric. Extra keys are preserved."},"custom_scoring":{"type":"object","nullable":true,"additionalProperties":{"nullable":true},"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."},"pdf_export_auto_config":{"type":"object","nullable":true,"properties":{"mojito_language_code":{"type":"string","description":"Report language code (platform-languages.json code).","example":"en"},"contact_details":{"type":"boolean","description":"Include candidate contact details."},"ai_recruiter_assessment":{"type":"boolean","description":"Include the AI recruiter assessment."},"ai_scoring_rubric":{"type":"boolean","description":"Include the AI scoring rubric."},"analytics":{"type":"boolean","description":"Include analytics."},"files":{"type":"boolean","description":"Include uploaded candidate files."},"transcript":{"type":"boolean","description":"Include the interview transcript."},"answer_recording":{"type":"boolean","description":"Include per-answer recordings."},"session_recording":{"type":"boolean","description":"Include the full-session recording."},"group_by_question":{"type":"boolean","description":"Group transcript answers by expected question."}},"description":"Auto-generate a candidate PDF report with these options once the interview completes. null disables auto-export."},"interview_type":{"type":"string","nullable":true,"enum":["pre-screening","pre-screening-with-test-questions","second-interview","remote-freelancer-verification","strength-based-interview","potential-based-interview","process-verification-from-knowledge-base"],"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..","example":"pre-screening-with-test-questions"},"interview_tone":{"type":"string","nullable":true,"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. Suggested values — `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.. Case-insensitive; other strings are accepted but unknown tones fall back to the default.","example":"professional"},"seniority_level":{"type":"string","nullable":true,"enum":["entry-level","intermediate","senior","managerial","director","executive"],"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..","example":"senior"},"hiring_for_company":{"type":"object","nullable":true,"properties":{"name":{"type":"string","nullable":true,"description":"End-employer name. Omit/null when hiring for yourself, 'undisclosed' for an unnamed external client, or the client's company name.","example":"Unimo Enterprises"},"description":{"type":"string","nullable":true,"description":"Short description of the end employer, used as background context by the agent.","example":"Unimo Enterprises is a leading logistics and supply chain solutions provider."},"location":{"type":"string","nullable":true,"description":"Primary location of the end employer.","example":"Sri Lanka"},"sector":{"type":"string","nullable":true,"description":"Industry / sector of the end employer.","example":"Logistics and Supply Chain"},"company_size":{"type":"string","nullable":true,"description":"Approximate headcount of the end employer.","example":"100-200"}},"description":"Who the position is really for. null (or 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.","example":{"name":"undisclosed"}}},"required":["position_id"]},"JobInterviewSetStateResponse":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]},{"type":"object","properties":{"embed_id":{"type":"string"},"embed_signing_key":{"type":"string"}},"required":["embed_id","embed_signing_key"]}],"description":"Returns `{ embed_id, embed_signing_key }` when an embed key was created/returned (is_embedded=true), otherwise `{ success: true }`."},"JobInterviewSetStateRequest":{"type":"object","properties":{"position_id":{"type":"string","minLength":1,"format":"uuid","description":"Interview definition id or position id whose state should change.","example":"00000000-0000-0000-0000-000000000000"},"status":{"type":"string","nullable":true,"enum":["draft","active","archived","deleted","preparing","completed"],"description":"New lifecycle status to apply."},"is_embedded":{"type":"boolean","nullable":true,"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)."}},"required":["position_id"]},"JobInterviewTokenResponse":{"type":"string","description":"The signed interview URL.","example":"https://interviews.example.com/interview/take/<id>/?interview_token=<jwt>&view_hm=true"},"JobInterviewTokenRequest":{"type":"object","properties":{"type":{"type":"string","enum":["interview-for-profile","results-for-profile","interview-result-candidate","interview-result-talent-seeker","interview-results-for-position"],"description":"Which kind of signed URL to generate."},"merchant_id":{"type":"string","description":"Override merchant id (admin / sub-merchant only)."},"interview_profile_id":{"type":"string","description":"Candidate profile id. Required for interview-for-profile and results-for-profile."},"interview_id":{"type":"string","description":"Interview or position id. Required for interview-for-profile and interview-results-for-position."},"interview_result_id":{"type":"string","description":"Interview result id. Required for interview-result-candidate and interview-result-talent-seeker."},"hide_menu":{"anyOf":[{"type":"string"},{"type":"boolean"}],"description":"Pass the string 'false' to show the navigation menu; any other value hides it (default)."}},"required":["type"]},"JobInterviewRegisterUsersResponse":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","nullable":true},"email":{"type":"string"},"external_id":{"type":"string","nullable":true},"result":{"type":"string","description":"\"ok\" on success, otherwise an \"Error: ...\" message for that user.","example":"ok"},"interview_url":{"type":"string","description":"One-time interview URL containing the minted interview_token. Present only on success."}},"additionalProperties":{"nullable":true}},"description":"The input users, each annotated with its registration result and (on success) interview URL."},"RegisterUser":{"type":"object","properties":{"name":{"type":"string","nullable":true,"description":"Display name of the candidate.","example":"Peter Parker"},"email":{"type":"string","nullable":true,"description":"Email used to identify and invite the candidate. Validated per item, not request-level.","example":"jozo@jozo.sk"},"external_id":{"type":"string","nullable":true,"description":"Optional caller-supplied id, stored on the candidate profile.","example":"abcd"}}},"JobInterviewRegisterUsersRequest":{"type":"object","properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/RegisterUser"},"description":"Candidates to register for the interview. Each gets a one-time interview URL.","example":[{"name":"Peter Parker","email":"jozo@jozo.sk","external_id":"abcd"},{"name":"mr beast","email":"hi@jozefbalaz.com"}]},"interview_id":{"type":"string","minLength":1,"format":"uuid","description":"Interview definition (interview_def_set) or position (position_def_set) id to register users for.","example":"04e09fa5-3fb8-4236-b37d-fca2bcd1cb66"},"merchant_id":{"type":"string","nullable":true,"format":"uuid","description":"Merchant id. Required when authenticating with the service key; for user tokens it is optional and only honored for admin / sub-merchant accounts.","example":"b5201178-46dd-4a20-a1af-7ffd647f834b"},"send_email":{"type":"boolean","nullable":true,"description":"Whether to send an invitation email to each registered candidate."},"hide_menu":{"anyOf":[{"type":"boolean"},{"type":"string"},{"nullable":true}],"description":"When true (or the string \"true\"), the interview UI hides its menu (view_hm)."},"hide_iframe":{"anyOf":[{"type":"boolean"},{"type":"string"},{"nullable":true}],"description":"When true (or the string \"true\"), the interview UI hides its iframe chrome (view_hi)."},"is_test":{"anyOf":[{"type":"boolean"},{"type":"string"},{"nullable":true}],"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\"."}},"required":["users","interview_id"]},"Pagination":{"type":"object","properties":{"total":{"type":"integer","description":"Total matching records across all pages.","example":137},"limit":{"type":"integer","description":"Page size used for this response.","example":50},"offset":{"type":"integer","description":"Offset used for this response.","example":0},"has_more":{"type":"boolean","description":"True when more records exist beyond this page (offset + returned < total).","example":true}},"required":["total","limit","offset","has_more"]},"MerchantInterviewListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Interview definition id."},"merchant_id":{"type":"string","nullable":true,"description":"Owning merchant id."},"name":{"type":"string","nullable":true,"description":"Interview name."},"emoji":{"type":"string","nullable":true,"description":"Emoji marker."},"type":{"type":"string","nullable":true,"description":"Product type (interview/coaching/…)."},"type_avatar":{"type":"string","nullable":true,"description":"Avatar type used by the interview."},"status":{"type":"string","nullable":true,"description":"Lifecycle status."},"visibility":{"type":"string","nullable":true,"description":"Visibility (merchant_public/invite/unlisted/…)."},"mojito_language_code":{"type":"string","nullable":true,"description":"Platform language code."},"speech_language_code":{"type":"string","nullable":true,"description":"Speech language code."},"environment":{"type":"string","nullable":true,"description":"Environment (production/test/…)."},"is_ats":{"type":"boolean","nullable":true,"description":"Whether the interview is ATS-integrated."},"is_multi_stage":{"type":"boolean","nullable":true,"description":"Whether the interview is multi-stage."},"recording":{"type":"string","nullable":true,"description":"Per-answer recording/proctoring mode."},"recording_full_session":{"type":"string","nullable":true,"description":"Full-session recording mode."},"billing_single_position":{"type":"boolean","nullable":true,"description":"Single-position billing flag."},"billing_single_position_end_at":{"type":"string","nullable":true,"description":"Single-position billing end (ISO 8601)."},"cover_image_url":{"type":"string","nullable":true,"description":"Cover image URL."},"interview_count":{"type":"number","nullable":true,"description":"Number of interviews run against this definition."},"interview_last":{"type":"string","nullable":true,"description":"Timestamp of the most recent interview (ISO 8601)."},"calc_duration":{"type":"number","nullable":true,"description":"Calculated duration."},"created_at":{"type":"string","nullable":true,"description":"Creation timestamp (ISO 8601)."},"updated_at":{"type":"string","nullable":true,"description":"Last update timestamp (ISO 8601)."}},"required":["id"],"additionalProperties":{"nullable":true}},"description":"The merchant's interview definitions for this page, newest-updated first."},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"CatalogueTagCreateResponse":{"type":"object","properties":{"id":{"type":"string","description":"Id of the created directory.","example":"sales-coaching-en"},"merchant_id":{"type":"string","nullable":true,"description":"Owning merchant id. Null for a platform-wide (`public`) directory."},"parent_tag":{"type":"string","nullable":true,"description":"The directory this one was nested under, when `parent_tag` was supplied."},"catalogue_url":{"type":"string","nullable":true,"description":"Public URL of the directory page, when the merchant has a coaching-portal domain configured. Null otherwise.","example":"https://coaching.example.com/catalogue/sales-coaching-en"}},"required":["id","merchant_id","parent_tag","catalogue_url"],"description":"The created catalogue directory."},"CatalogueTagCreateRequest":{"type":"object","properties":{"id":{"type":"string","minLength":1,"pattern":"^[a-z0-9]+(?:(?:-|_)+[a-z0-9]+)*$","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`.","example":"sales-coaching-en"},"name":{"type":"string","minLength":1,"description":"Display name of the directory, shown as the page title and on its card.","example":"Sales coaching"},"description":{"type":"string","nullable":true,"description":"Short description shown on the directory card.","example":"Practice discovery, objection handling and closing."},"mojito_language_code":{"type":"string","nullable":true,"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"],"description":"Language of the directory (one of the platform-languages.json codes). The catalogue groups directories by language; defaults to `en` when omitted.","example":"en"},"visibility":{"type":"string","nullable":true,"enum":["public","merchant_public","merchant_invite","merchant_unlisted"],"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..","example":"merchant_public"},"status":{"type":"string","nullable":true,"enum":["draft","active","archived"],"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..","example":"active"},"cover_image_url":{"type":"string","nullable":true,"description":"Cover image URL shown on the directory card."},"coach_plan":{"type":"string","nullable":true,"enum":["demo","screening","2nd","3rd","closing","job-specific","other"],"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..","example":"screening"},"tags_sub":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["sales-coaching-objections-en","sales-coaching-closing-en"]},"tags_interview_set_filter":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["sales","objection-handling"]},"content_md":{"type":"string","nullable":true,"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.","example":"## Sales coaching\n\nPick a session to practise with.\n\n[sessions:filter=objection-handling,limit=6]\n"},"parent_tag":{"type":"string","nullable":true,"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).","example":"home-employee-en"},"merchant_id":{"type":"string","nullable":true,"description":"Merchant that owns the directory. Admin / sub-merchant callers only; otherwise taken from your token.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"}},"required":["id","name"]},"CatalogueTagUpdateResponse":{"type":"object","properties":{"id":{"type":"string","description":"Id of the updated directory.","example":"sales-coaching-en"},"updated_fields":{"type":"array","items":{"type":"string"},"description":"Names of the fields that were written.","example":["content_md","tags_interview_set_filter"]},"catalogue_url":{"type":"string","nullable":true,"description":"Public URL of the directory page, when the merchant has a coaching-portal domain configured. Null otherwise.","example":"https://coaching.example.com/catalogue/sales-coaching-en"}},"required":["id","updated_fields","catalogue_url"],"description":"Confirmation of what was updated."},"CatalogueTagUpdateRequest":{"type":"object","properties":{"id":{"type":"string","minLength":1,"pattern":"^[a-z0-9]+(?:(?:-|_)+[a-z0-9]+)*$","description":"Id of the directory to update (the catalogue URL segment). The id itself cannot be changed — create a new directory instead.","example":"sales-coaching-en"},"name":{"type":"string","minLength":1,"description":"Display name of the directory.","example":"Sales coaching"},"description":{"type":"string","nullable":true,"description":"Short description shown on the directory card."},"mojito_language_code":{"type":"string","nullable":true,"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"],"description":"Language of the directory (one of the platform-languages.json codes). The catalogue groups directories by language.","example":"en"},"visibility":{"type":"string","enum":["public","merchant_public","merchant_invite","merchant_unlisted"],"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..","example":"merchant_public"},"status":{"type":"string","enum":["draft","active","archived"],"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..","example":"active"},"cover_image_url":{"type":"string","nullable":true,"description":"Cover image URL shown on the directory card. null clears it."},"coach_plan":{"type":"string","nullable":true,"enum":["demo","screening","2nd","3rd","closing","job-specific","other"],"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..","example":"screening"},"tags_sub":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["sales-coaching-objections-en","sales-coaching-closing-en"]},"tags_interview_set_filter":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["sales","objection-handling"]},"content_md":{"type":"string","nullable":true,"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>]`.","example":"## Sales coaching\n\nPick a session to practise with.\n\n[sessions:filter=objection-handling,limit=6]\n"}},"required":["id"]},"CatalogueTagGetResponse":{"type":"object","properties":{"id":{"type":"string","description":"Directory id — also the catalogue URL segment.","example":"sales-coaching-en"},"name":{"type":"string","nullable":true,"description":"Display name."},"description":{"type":"string","nullable":true,"description":"Short description shown on the directory card."},"mojito_language_code":{"type":"string","nullable":true,"description":"Directory language.","example":"en"},"visibility":{"type":"string","nullable":true,"description":"Who can see it."},"status":{"type":"string","nullable":true,"description":"Lifecycle status."},"coach_plan":{"type":"string","nullable":true,"description":"Coaching-plan stage, when the directory belongs to one."},"cover_image_url":{"type":"string","nullable":true,"description":"Cover image URL."},"merchant_id":{"type":"string","nullable":true,"description":"Owning merchant id. Null for a platform-wide directory."},"tags_sub":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["sales-coaching-objections-en","sales-coaching-closing-en"]},"tags_interview_set_filter":{"type":"array","nullable":true,"items":{"type":"string"},"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.","example":["sales","objection-handling"]},"content_md":{"type":"string","nullable":true,"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.","example":"## Sales coaching\n\nPick a session to practise with.\n\n[sessions:filter=objection-handling,limit=6]\n"},"created_at":{"type":"string","nullable":true,"description":"Creation timestamp (ISO 8601)."},"updated_at":{"type":"string","nullable":true,"description":"Last update timestamp (ISO 8601)."},"catalogue_url":{"type":"string","nullable":true,"description":"Public URL of this directory page, when the merchant has a coaching-portal domain configured.","example":"https://coaching.example.com/catalogue/sales-coaching-en"},"is_start_directory":{"type":"boolean","description":"True when this is the directory the merchant's catalogue opens on."},"sub_directories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Sub-directory id."},"name":{"type":"string","nullable":true,"description":"Display name."},"description":{"type":"string","nullable":true,"description":"Short description."},"status":{"type":"string","nullable":true,"description":"Lifecycle status."},"visibility":{"type":"string","nullable":true,"description":"Visibility."},"mojito_language_code":{"type":"string","nullable":true,"description":"Directory language."}},"required":["id","name","description","status","visibility","mojito_language_code"],"additionalProperties":{"nullable":true}},"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."},"matched_sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Interview definition id of the coaching/persona session."},"name":{"type":"string","nullable":true,"description":"Session name."},"type":{"type":"string","nullable":true,"description":"Product type — `coaching` or `persona`."},"type_avatar":{"type":"string","nullable":true,"description":"Avatar/voice modality of the session."},"status":{"type":"string","nullable":true,"description":"Lifecycle status (always `active` here — nothing else is listed)."},"visibility":{"type":"string","nullable":true,"description":"Visibility."},"type_credit":{"type":"string","nullable":true,"description":"Credit bucket the session draws from."},"tags":{"type":"array","nullable":true,"items":{"type":"string"},"description":"The session's own tags — the superset that matched this directory's filter."}},"required":["id","name","type","type_avatar","status","visibility","type_credit","tags"],"additionalProperties":{"nullable":true}},"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."},"matched_sessions_truncated":{"type":"boolean","description":"True when more sessions match than the 200 returned — narrow `tags_interview_set_filter` if you see this."}},"required":["id","name","description","mojito_language_code","visibility","status","coach_plan","cover_image_url","merchant_id","tags_sub","tags_interview_set_filter","content_md","created_at","updated_at","catalogue_url","is_start_directory","sub_directories","matched_sessions","matched_sessions_truncated"],"description":"One catalogue directory with its content page, sub-directories and matched sessions."},"CatalogueTagListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Directory id — also the catalogue URL segment and the value referenced in a parent's `tags_sub`.","example":"sales-coaching-en"},"name":{"type":"string","nullable":true,"description":"Display name."},"description":{"type":"string","nullable":true,"description":"Short description shown on the directory card."},"mojito_language_code":{"type":"string","nullable":true,"description":"Directory language.","example":"en"},"visibility":{"type":"string","nullable":true,"description":"Who can see it (public/merchant_public/merchant_invite/merchant_unlisted)."},"status":{"type":"string","nullable":true,"description":"Lifecycle status (draft/active/archived)."},"coach_plan":{"type":"string","nullable":true,"description":"Coaching-plan stage, when the directory belongs to one."},"cover_image_url":{"type":"string","nullable":true,"description":"Cover image URL."},"merchant_id":{"type":"string","nullable":true,"description":"Owning merchant id. Null for a platform-wide directory."},"tags_sub":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Ids of the directories nested under this one, in display order."},"tags_interview_set_filter":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Tag filter selecting the coaching sessions this directory lists: a session appears when its own `tags` contain every tag here."},"has_content_page":{"type":"boolean","description":"True when the directory has a custom Markdown page. Read the Markdown itself with catalogue-tag-get."},"is_start_directory":{"type":"boolean","description":"True for the directory the merchant's catalogue opens on — the root to navigate down from."},"created_at":{"type":"string","nullable":true,"description":"Creation timestamp (ISO 8601)."},"updated_at":{"type":"string","nullable":true,"description":"Last update timestamp (ISO 8601)."}},"required":["id","has_content_page","is_start_directory"],"additionalProperties":{"nullable":true}},"description":"The catalogue directories for this page, ordered by id."},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"JobInterviewDetailsResponse":{"type":"object","properties":{"score":{"type":"number","nullable":true,"description":"Overall interview score."},"score_simulation":{"type":"number","nullable":true,"description":"Overall simulated score."},"score_text":{"type":"string","nullable":true,"description":"Human-readable score summary."},"score_sentiment":{"type":"number","nullable":true,"description":"Sentiment sub-score."},"score_words_per_minute":{"type":"number","nullable":true,"description":"Speaking-pace (words per minute) sub-score."},"score_pronunciation":{"type":"number","nullable":true,"description":"Pronunciation sub-score."},"score_answer":{"type":"number","nullable":true,"description":"Aggregate answer sub-score."},"status":{"type":"string","nullable":true,"description":"Coach/interview status (coach_status), e.g. started, completed."},"start":{"type":"string","nullable":true,"description":"Interview start timestamp (training_start, ISO 8601)."},"end":{"type":"string","nullable":true,"description":"Interview end timestamp (training_end, ISO 8601)."},"duration":{"type":"number","nullable":true,"description":"Total duration in deciseconds (duration_ds)."},"ai_interview_coverage_percentage":{"type":"number","nullable":true,"description":"Percentage of the intended interview the AI judged to be covered."},"ai_completion_reason":{"type":"string","nullable":true,"description":"Why the interview completed (ai_completed_reason)."},"ai_analysis":{"type":"string","nullable":true,"description":"Candidate-facing AI analysis of the whole interview."},"ai_analysis_recruiter":{"type":"string","nullable":true,"description":"Recruiter-facing AI analysis of the whole interview."},"ai_analysis_recruiter_why_hire":{"nullable":true,"description":"Reasons to hire (list/text)."},"ai_analysis_recruiter_why_not_hire":{"nullable":true,"description":"Reasons not to hire (list/text)."},"recruiter_risks":{"nullable":true,"description":"Detected recruiter risk flags (jsonb)."},"user_feedback_recruiter":{"type":"string","nullable":true,"description":"Recruiter-entered feedback note."},"recording_local_path":{"type":"string","nullable":true,"description":"Storage path of the full session recording."},"recording_is_video":{"type":"boolean","nullable":true,"description":"Whether the session recording is video."},"video_introduction_local_path":{"type":"string","nullable":true,"description":"Storage path of the candidate video introduction."},"recording_url":{"type":"string","nullable":true,"description":"Signed session-recording URL; present only when get_signed_recordings=true."},"video_introduction_url":{"type":"string","nullable":true,"description":"Signed video-introduction URL; present only when get_signed_recordings=true."},"transcript":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"interview_result_question id."},"is_answer":{"type":"boolean","description":"True for a candidate answer turn, false for an avatar/question turn."},"view":{"type":"string","nullable":true,"description":"Question view/type (e.g. with listening, multiple choice, without scoring)."},"question_original_id":{"type":"string","nullable":true,"description":"Source interview_def_question id (null for generated follow-ups)."},"question_original":{"type":"string","nullable":true,"description":"Original question text from the definition (when question_original_id is set)."},"question_asked":{"type":"string","nullable":true,"description":"The question text actually asked to the candidate."},"answer":{"type":"string","nullable":true,"description":"The candidate answer transcript text."},"ai_analysis":{"type":"string","nullable":true,"description":"AI analysis of this answer."},"ai_analysis_recruiter":{"type":"string","nullable":true,"description":"Recruiter-facing AI analysis of this answer."},"score":{"type":"number","nullable":true,"description":"Score for this interaction."},"score_simulation":{"type":"number","nullable":true,"description":"Simulated score for this interaction."},"score_answer_ai":{"type":"number","nullable":true,"description":"AI answer score."},"score_answer_ai_simulation":{"type":"number","nullable":true,"description":"Simulated AI answer score."},"duration":{"type":"number","nullable":true,"description":"Duration of this interaction in deciseconds (duration_ds)."},"mojito_language_code":{"type":"string","nullable":true,"description":"Platform language code of this interaction."},"answer_assessment_raw_data":{"nullable":true,"description":"Raw per-answer assessment data (pronunciation/sentiment/etc.)."},"recording_local_path":{"type":"string","nullable":true,"description":"Storage path of this answer recording (private-recordings bucket)."},"recording_is_video":{"type":"boolean","nullable":true,"description":"Whether this answer recording is video (vs audio)."},"external_data":{"nullable":true,"description":"External data carried from the question definition."},"external_id":{"type":"string","nullable":true,"description":"External identifier carried from the question definition."},"recording_url":{"type":"string","nullable":true,"description":"Signed URL for recording_local_path; present only when get_signed_recordings=true."}},"additionalProperties":{"nullable":true}},"description":"Ordered interactions (questions + answers) of the interview."}},"additionalProperties":{"nullable":true},"description":"The interview result with its transcript, produced by the interview_transcript RPC. Additional fields may be present (passthrough)."},"JobInterviewPdfPdfExport":{"type":"object","properties":{"pdf_export_url":{"type":"string","format":"uri","description":"Signed URL of the generated PDF."},"pdf_export_valid_until":{"type":"string","description":"ISO timestamp until which the signed URL is valid.","example":"2026-09-01T12:00:00.000Z"}},"required":["pdf_export_url","pdf_export_valid_until"],"description":"Returned when `export_type` is `pdf`.","title":"PDF export"},"JobInterviewPdfHtmlExport":{"type":"object","properties":{"html_export":{"type":"string","description":"The full report rendered as an HTML document string."}},"required":["html_export"],"description":"Returned when `export_type` is `html`.","title":"HTML export"},"JobInterviewPdfJsonExport":{"type":"object","properties":{"json_export":{"type":"object","properties":{"table":{"type":"string","description":"Overview table HTML, present only for multi-result exports."},"results":{"type":"array","items":{"type":"object","properties":{"data":{"type":"object","additionalProperties":{"nullable":true}},"files":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"page":{"type":"number","nullable":true},"page_signed":{"type":"string","nullable":true}},"required":["type","page","page_signed"]}}},"required":["data","files"]}}},"required":["results"]}},"required":["json_export"],"description":"Returned when `export_type` is `json`.","title":"JSON export"},"JobInterviewPdfResponse":{"anyOf":[{"$ref":"#/components/schemas/JobInterviewPdfPdfExport"},{"$ref":"#/components/schemas/JobInterviewPdfHtmlExport"},{"$ref":"#/components/schemas/JobInterviewPdfJsonExport"}],"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 }`."},"InterviewPdfExportFeatures":{"type":"object","properties":{"mojito_language_code":{"type":"string","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"],"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.","example":"en"},"contact_details":{"type":"boolean","default":true,"description":"Include the candidate's contact details (email, LinkedIn, phone number, physical address)."},"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."},"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."},"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."},"files":{"type":"boolean","default":true,"description":"Include the candidate's uploaded files (resume and cover letter) as signed page URLs."},"answer_recording":{"type":"boolean","default":false,"description":"Include a signed recording URL (audio/video) for each individual answer."},"session_recording":{"type":"boolean","default":false,"description":"Include the full interview session recording and the video introduction as signed URLs."},"group_by_question":{"type":"boolean","default":false,"description":"Group answers under their expected questions (instead of chronological order), including questions that were not reached."}}},"JobInterviewPdfRequest":{"type":"object","properties":{"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_type":{"type":"string","enum":["pdf","html","json"]},"export_features_result":{"$ref":"#/components/schemas/InterviewPdfExportFeatures"},"store_file":{"type":"boolean","description":"When true (pdf only), persist the file to storage and return a signed URL."}},"required":["export_type"]},"RequestAnotherAttemptResponse":{"type":"object","properties":{},"description":"Empty object on success."},"RequestAnotherAttemptRequest":{"type":"object","properties":{"interview_result_id":{"type":"string","minLength":1,"format":"uuid","description":"The interview result to reopen for another attempt.","example":"93c98d21-e04d-4a84-9afa-ed154cf73636"}},"required":["interview_result_id"]},"MerchantResultListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Position result row id."},"interview_result_id":{"type":"string","nullable":true,"description":"Interview result id (use with merchant-... detail endpoints)."},"interview_result_pre_screening_id":{"type":"string","nullable":true,"description":"Pre-screening result id, if any."},"merchant_id":{"type":"string","nullable":true,"description":"Owning merchant id."},"profile_id":{"type":"string","nullable":true,"description":"Candidate profile id."},"profile_interview_id":{"type":"string","nullable":true,"description":"Candidate (profile_interview) id."},"definition_id":{"type":"string","nullable":true,"description":"Interview definition id."},"candidate_name":{"type":"string","nullable":true,"description":"Candidate name."},"candidate_email":{"type":"string","nullable":true,"description":"Candidate email."},"position_name":{"type":"string","nullable":true,"description":"Position / interview name."},"position_step":{"type":"string","nullable":true,"description":"Pipeline step."},"type":{"type":"string","nullable":true,"description":"Product type."},"emoji":{"type":"string","nullable":true,"description":"Emoji marker."},"external_id":{"type":"string","nullable":true,"description":"External identifier."},"mojito_language_code":{"type":"string","nullable":true,"description":"Platform language code."},"status":{"type":"string","nullable":true,"description":"Row status."},"coach_status":{"type":"string","nullable":true,"description":"Interview/coach status (started/completed/…)."},"decision_status":{"type":"string","nullable":true,"description":"Hiring decision (selected/rejected/…)."},"interview_started":{"type":"boolean","nullable":true,"description":"Whether the interview was started."},"is_multi_stage":{"type":"boolean","nullable":true,"description":"Whether multi-stage."},"is_test":{"type":"boolean","nullable":true,"description":"Whether a test result."},"recruiter_shortlist":{"type":"boolean","nullable":true,"description":"Whether recruiter-shortlisted."},"recruiter_risks":{"nullable":true,"description":"Detected recruiter risk flags (jsonb)."},"duration_ds":{"type":"number","nullable":true,"description":"Duration in deciseconds."},"score":{"type":"number","nullable":true,"description":"Overall score."},"score_simulation":{"type":"number","nullable":true,"description":"Simulated overall score."},"score_sentiment":{"type":"number","nullable":true,"description":"Sentiment sub-score."},"score_words_per_minute":{"type":"number","nullable":true,"description":"Speaking-pace sub-score."},"score_interview_ai":{"type":"number","nullable":true,"description":"AI interview sub-score."},"score_answer_ai":{"type":"number","nullable":true,"description":"AI answer sub-score."},"score_pronunciation":{"type":"number","nullable":true,"description":"Pronunciation sub-score."},"processing_status":{"type":"string","nullable":true,"description":"Processing status."},"processing_error":{"type":"string","nullable":true,"description":"Processing error, if any."},"kyc_processing_status":{"type":"string","nullable":true,"description":"KYC processing status."},"position_result_step_id":{"type":"string","nullable":true,"description":"Position result step id."},"created_at":{"type":"string","nullable":true,"description":"Creation timestamp (ISO 8601)."},"updated_at":{"type":"string","nullable":true,"description":"Last update timestamp (ISO 8601)."}},"required":["id"],"additionalProperties":{"nullable":true}},"description":"The merchant's interview results for this page."},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"MerchantAnalyticsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","description":"Calendar day (YYYY-MM-DD).","example":"2026-01-15"},"event":{"type":"string","description":"Event type (e.g. interview, interview_invitation, interview_page_visit)."},"event_number":{"type":"number","description":"Count of this event on this day. Always greater than 0 — zero-count day/event combinations are omitted.","example":12}},"required":["day","event","event_number"],"additionalProperties":{"nullable":true}},"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":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"MerchantCreditsUsedResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Analytics event id."},"event_date":{"type":"string","nullable":true,"description":"When the credit-consuming event occurred (ISO 8601).","example":"2026-01-15T09:30:00.000Z"},"event":{"type":"string","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).","example":"interview"},"credit_amount":{"type":"number","nullable":true,"description":"Number of credits deducted by this event, surfaced from stats.credit_amount for convenience. Null when the amount is absent.","example":1},"stats":{"nullable":true,"description":"Event stats JSON. Contains credit_amount (the credits consumed by this event) plus event-specific fields."},"candidate_name":{"type":"string","nullable":true,"description":"Candidate display name from the linked profile interview, when available."},"profile_id":{"type":"string","nullable":true,"format":"uuid","description":"Underlying person (profile) id, when linked."},"profile_interview_id":{"type":"string","nullable":true,"format":"uuid","description":"Candidate enrolment (profile interview) id, when linked."},"object_uuid":{"type":"string","nullable":true,"format":"uuid","description":"Primary object the event refers to (e.g. the interview result / attempt)."},"parent_object_uuid":{"type":"string","nullable":true,"format":"uuid","description":"Parent object of the event (e.g. the interview definition), when applicable."}},"required":["id","event_date","event","credit_amount","candidate_name","profile_id","profile_interview_id","object_uuid","parent_object_uuid"],"additionalProperties":{"nullable":true}},"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":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"MerchantCandidateListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Candidate (profile_interview) id."},"profile_id":{"type":"string","nullable":true,"description":"Underlying profile id."},"merchant_id":{"type":"string","nullable":true,"description":"Owning merchant id."},"name":{"type":"string","nullable":true,"description":"Candidate name."},"email":{"type":"string","nullable":true,"description":"Candidate email."},"email_auth":{"type":"string","nullable":true,"description":"Auth email (login)."},"emoji":{"type":"string","nullable":true,"description":"Emoji marker."},"external_id":{"type":"string","nullable":true,"description":"External identifier."},"status":{"type":"string","nullable":true,"description":"Status (active/invited/deleted)."},"phone_number":{"type":"string","nullable":true,"description":"Phone number."},"linked_in":{"type":"string","nullable":true,"description":"LinkedIn URL."},"physical_address":{"type":"string","nullable":true,"description":"Physical address."},"mojito_language_code":{"type":"string","nullable":true,"description":"Platform language code."},"avatar_url":{"type":"string","nullable":true,"description":"Avatar image URL."},"avatar_local_path":{"type":"string","nullable":true,"description":"Avatar storage path."},"kyc_image_local_path":{"type":"string","nullable":true,"description":"KYC image storage path."},"kyc_processing_status":{"type":"string","nullable":true,"description":"KYC processing status."},"interview_count":{"type":"number","nullable":true,"description":"Number of interviews taken."},"pre_screening_count":{"type":"number","nullable":true,"description":"Number of pre-screenings taken."},"public_avatar_count":{"type":"number","nullable":true,"description":"Number of public-avatar conversations."},"interview_last":{"type":"string","nullable":true,"description":"Most recent interview timestamp (ISO 8601)."},"last_login_at":{"type":"string","nullable":true,"description":"Last login timestamp (ISO 8601)."},"created_at":{"type":"string","nullable":true,"description":"Creation timestamp (ISO 8601)."},"updated_at":{"type":"string","nullable":true,"description":"Last update timestamp (ISO 8601)."},"deleted_at":{"type":"string","nullable":true,"description":"Deletion timestamp (ISO 8601), if deleted."}},"required":["id"],"additionalProperties":{"nullable":true}},"description":"The merchant's candidates for this page."},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"KnowledgeBaseDocumentUploadResponse":{"type":"object","properties":{"knowledge_base_id":{"type":"string","description":"Id of the created knowledge_base record."}},"required":["knowledge_base_id"]},"KnowledgeBaseDocumentUploadMultipart":{"type":"object","properties":{"knowledge_base_store_id":{"type":"string","minLength":1,"description":"The knowledge base store to add the document to.","example":"d9d6f121-1230-48fd-b3a7-8b5a1e8db052"},"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)."},"file":{"type":"string","description":"The document file (binary).","format":"binary"}},"required":["knowledge_base_store_id","file"]},"KnowledgeBaseDocumentUploadJson":{"type":"object","properties":{"knowledge_base_store_id":{"type":"string","minLength":1,"description":"The knowledge base store to add the document to.","example":"d9d6f121-1230-48fd-b3a7-8b5a1e8db052"},"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)."},"file":{"type":"string","minLength":1,"description":"The document file, base64-encoded."}},"required":["knowledge_base_store_id","file"]},"PreScreeningCreateResponse":{"type":"object","properties":{"created":{"type":"boolean","description":"True when a new pre-screening position was created."},"updated":{"type":"boolean","description":"True when an existing pre-screening position was updated."},"position_def_set_id":{"type":"string","nullable":true,"description":"The position_def_set id created or updated."},"interview_pre_screening_id":{"type":"string","nullable":true,"description":"The pre-screening definition id created or updated."}},"description":"On create returns `{ created: true, position_def_set_id, interview_pre_screening_id }`; on update returns `{ updated: true, position_def_set_id, interview_pre_screening_id }`."},"PreScreeningAssessmentRule":{"type":"object","properties":{"id":{"type":"string","description":"Rule identifier. Standard form rules: form_nationality, form_residency, form_languages, form_education (or form_education_by_country), form_visa, form_age, form_gender, form_cover_letter, form_national_id. Standard resume rules: resume_match, resume_technical_experience, resume_education (or resume_education_by_country). Custom fields use custom_<timestamp>.","example":"form_education"},"type":{"type":"string","enum":["optional","required","screening"],"description":"How the field is treated. \"screening\" = the rule is enforced and can pass / mark-for-review / reject the candidate. \"required\" / \"optional\" only affect whether the candidate must fill the field — they are NOT enforced as screening. resume_match and resume_technical_experience ignore this key (always enforced).","example":"screening"},"action":{"type":"string","enum":["mark_for_review","reject"],"description":"Outcome applied when a screening rule is not satisfied. Defaults to \"mark_for_review\".","example":"reject"},"country":{"type":"array","items":{"type":"string"},"description":"form_nationality / form_residency: allowlist of ISO country codes. Combined with country_eu as a logical OR.","example":["US","CA"]},"country_eu":{"type":"boolean","description":"form_nationality / form_residency: when true, any EU country is accepted (in addition to `country`)."},"languages":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Platform language code.","example":"en"},"level":{"type":"string","enum":["beginner","intermediate","advanced","fluent"],"description":"Minimum required proficiency.","example":"advanced"}},"required":["code","level"]},"description":"form_languages: each listed language must be known at (at least) the given level."},"education":{"type":"string","description":"form_education / resume_education: minimum acceptable education level. Standard keys: [\"primary school\",\"high school no graduation\",\"high school\",\"vocational\",\"bachelors\",\"masters\",\"doctorate\"]. Country-specific rules (form_education_by_country / resume_education_by_country) use the keys from that country's list.","example":"bachelors"},"visa":{"type":"array","items":{"type":"string"},"description":"form_visa: allowlist of visa keys. Keys are country-specific (only defined for some countries, e.g. KW, TR)."},"age_min":{"type":"integer","description":"form_age: minimum acceptable age (inclusive).","example":18},"age_max":{"type":"integer","description":"form_age: maximum acceptable age (inclusive).","example":60},"gender":{"type":"array","items":{"type":"string"},"description":"form_gender: allowlist of gender keys. Standard keys: male, female, other.","example":["female"]},"technical_experience_years":{"type":"integer","description":"resume_technical_experience: minimum years of relevant experience the AI must detect in the resume.","example":3},"resume_score_reject":{"type":"integer","minimum":0,"maximum":10,"description":"resume_match: reject the candidate when the AI match score (0-10) is below this value. 0 disables the reject threshold.","example":2},"resume_score_accept":{"type":"integer","minimum":0,"maximum":10,"description":"resume_match: below this value (but at/above resume_score_reject) the candidate is marked for review; at/above it the candidate passes.","example":7}},"required":["id"],"description":"A single pre-screening assessment rule. The keys used depend on `id` (see each key's description)."},"PreScreeningFormField":{"type":"object","properties":{"id":{"type":"string","description":"Field id. Standard fields reuse the rule id (e.g. form_education). Custom fields use custom_<timestamp>.","example":"custom_1772004521633"},"mandatory":{"type":"boolean","description":"Standard fields: whether the candidate must answer. Derived from the matching rule type (required/screening → true, optional → false)."},"title":{"type":"string","description":"Custom fields: the question/label shown to the candidate.","example":"Do you hold a valid work permit?"},"description":{"type":"string","description":"Custom fields: optional helper text shown under the title."},"type":{"type":"string","enum":["optional","required","screening"],"description":"Custom fields: \"required\" or \"optional\" (custom fields are never \"screening\").","example":"required"},"field_type":{"type":"string","enum":["text","file","radio"],"description":"Custom fields: input widget — \"text\" (free text), \"file\" (upload) or \"radio\" (single choice from `options`).","example":"radio"},"options":{"type":"array","items":{"type":"string"},"description":"Custom fields: the choices when field_type is \"radio\" (at least 2 required).","example":["Yes","No"]}},"required":["id"],"description":"A single candidate-form field: either a standard field reference or a custom field."},"PreScreeningCreateRequest":{"type":"object","properties":{"update_position_def_set_id":{"type":"string","nullable":true,"description":"When set to an existing position id, the request updates that pre-screening position instead of creating a new one. When absent/empty, a new pre-screening position is created and the create-only mandatory fields are required.","example":"00000000-0000-0000-0000-000000000000"},"position_name":{"type":"string","nullable":true,"description":"Position name. Required when creating (no update_position_def_set_id).","example":"Customer Support Specialist"},"position_location":{"type":"string","nullable":true,"description":"Interview/position location. Required when creating.","example":"Remote"},"position_country_code":{"type":"string","nullable":true,"description":"ISO country code; stored upper-cased. Required when creating.","example":"US"},"position_description":{"type":"string","nullable":true,"description":"Short position description.","example":"Handle inbound customer requests."},"position_description_long":{"type":"string","nullable":true,"description":"Long position description."},"mojito_language_code":{"type":"string","nullable":true,"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"],"description":"Mojito language code (one of the platform-languages.json codes). Required when creating.","example":"en"},"merchant_id":{"type":"string","nullable":true,"description":"Target merchant id. Only honored for admin or sub-merchant users; otherwise the caller's own merchant is used."},"type":{"type":"string","nullable":true,"enum":["resume","form","resume_with_form"],"description":"Pre-screening type. Defaults to \"resume\" on create."},"status":{"type":"string","nullable":true,"enum":["draft","active"],"description":"When \"active\", the pre-screening position is activated after create/update."},"assessment_rules":{"type":"array","nullable":true,"items":{"$ref":"#/components/schemas/PreScreeningAssessmentRule"},"description":"Ordered array of assessment rules. Only rules with `type: \"screening\"` (plus resume_match / resume_technical_experience) are enforced; other entries just declare which fields the form collects. Keep in sync with `form_fields`: a standard form field enabled here should have a matching `{ id, mandatory }` entry in form_fields.","example":[{"id":"form_education","type":"screening","action":"reject","education":"bachelors"},{"id":"form_languages","type":"screening","action":"mark_for_review","languages":[{"code":"en","level":"advanced"}]},{"id":"form_nationality","type":"required"},{"id":"resume_match","resume_score_reject":2,"resume_score_accept":7},{"id":"resume_technical_experience","action":"mark_for_review","technical_experience_years":3}]},"form_fields":{"type":"array","nullable":true,"items":{"$ref":"#/components/schemas/PreScreeningFormField"},"description":"Array describing the candidate form: standard field references ({ id, mandatory }) and/or custom fields ({ id: \"custom_…\", title, field_type, … }). For `form` and `resume_with_form` types at least one entry is required.","example":[{"id":"form_education","mandatory":true},{"id":"form_languages","mandatory":true},{"id":"form_nationality","mandatory":true},{"id":"custom_1772004521633","title":"Do you hold a valid work permit?","type":"required","field_type":"radio","options":["Yes","No"]}]},"candidate_expectations":{"type":"string","nullable":true,"description":"Candidate expectations text."},"visibility":{"type":"string","nullable":true,"description":"Position visibility. Defaults to \"merchant_invite\" on create."},"kombo_parameters":{"nullable":true,"description":"Kombo integration parameters (any JSON).","example":{"kombo_job_id":"7QzbCBaxX6fa4BMXBRuD7Lwm","kombo_stage_id":"CVjhZwxbtcer5P15gakmEhsM","kombo_sync_all_results":true,"kombo_selected_stage_id":null,"kombo_sync_all_results_stage_id":"9c5VZXrcMRmyr5vr1nvZWXSX"}},"interview_available_till":{"type":"string","nullable":true,"description":"Timestamp until which the interview is available."},"creation_parameters":{"nullable":true,"description":"Creation parameters, any JSON (create only)."}}},"PreScreeningResumeTextResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"decision_status":{"type":"string","description":"Applied decision status for the pre-screening step.","example":"recruiter_action"},"recommendation":{"type":"string","enum":["ai_accept","ai_reject","recruiter_action"],"description":"AI recommendation derived from the pre-screening status.","example":"recruiter_action"},"profile_interview_id":{"type":"string","description":"Candidate profile_interview id."},"interview_result_pre_screening_id":{"type":"string","description":"Inserted interview_result_pre_screening id."},"interview_file_id":{"type":"string","description":"Stored resume interview_file id."},"pre_screening_score":{"type":"number","nullable":true,"description":"AI match score."},"resume_ai_analysis":{"type":"string","nullable":true,"description":"AI resume analysis feedback."},"resume_ai_education":{"type":"string","nullable":true,"description":"AI-detected education level of the candidate."},"resume_ai_technical_experience":{"type":"number","nullable":true,"description":"AI-estimated years of relevant experience."},"resume_ai_is_switching_profession":{"type":"boolean","nullable":true,"description":"Whether the candidate is switching profession."},"processing_reason":{"type":"string","nullable":true,"description":"Reason the candidate failed screening, when applicable."}},"required":["success","decision_status","recommendation","profile_interview_id","interview_result_pre_screening_id","interview_file_id","pre_screening_score","resume_ai_analysis","resume_ai_education","resume_ai_technical_experience","resume_ai_is_switching_profession","processing_reason"],"description":"Result of the pre-screening analysis. When the candidate is not in the pre-screening candidate_action state, a `{ decision_status, message }` object is returned instead."},"PreScreeningNotApplicable":{"type":"object","properties":{"decision_status":{"type":"string","example":"unknown"},"message":{"type":"string"}},"required":["decision_status","message"]},"PreScreeningResumeTextRequest":{"type":"object","properties":{"position_id":{"type":"string","minLength":1,"description":"Position (position_def_set) id the candidate is being screened against.","example":"00000000-0000-0000-0000-000000000000"},"candidate_name":{"type":"string","minLength":1,"description":"Candidate full name.","example":"Jane Doe"},"candidate_email":{"type":"string","minLength":1,"description":"Candidate email (normalised to trimmed lowercase).","example":"jane.doe@example.com"},"candidate_country_code":{"type":"string","description":"Optional ISO country code, used as the default residency/nationality when those are not supplied in `form`. If the position has a `form_nationality` or `form_residency` screening rule and neither this field nor the corresponding `form` value is provided, the candidate is screened with no nationality/residency and will fail that rule (marked for review or rejected per its action).","example":"US"},"candidate_resume":{"type":"string","minLength":1,"description":"Plaintext candidate resume.","example":"Experienced software engineer with 8 years ..."},"candidate_external_id":{"type":"string","description":"Optional external identifier stored on the candidate profile.","example":"ATS-12345"},"candidate_linkedin_url":{"type":"string","description":"Optional LinkedIn profile URL stored on the candidate profile.","example":"https://www.linkedin.com/in/jane-doe"},"form":{"type":"object","properties":{"education":{"type":"string","description":"Candidate's highest education level. Must be one of the education keys used by the position's form_education rule: \"primary school\", \"high school no graduation\", \"high school\", \"vocational\", \"bachelors\", \"masters\", \"doctorate\".","example":"masters"},"languages":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Platform language code.","example":"en"},"level":{"type":"string","description":"Proficiency level key: \"beginner\", \"intermediate\", \"advanced\" or \"fluent\".","example":"advanced"}}},"description":"Candidate language proficiencies, matched against the position's form_languages rule."},"residency":{"type":"string","description":"ISO country code of residency. Defaults to candidate_country_code when omitted.","example":"US"},"nationality":{"type":"string","description":"ISO country code of nationality. Defaults to candidate_country_code when omitted.","example":"US"},"visa":{"type":"string","description":"Visa key, matched against the position's form_visa allowlist (keys are country-specific).","example":"18_transferable"},"age":{"type":"integer","description":"Candidate age in years.","example":30},"gender":{"type":"string","description":"Candidate gender key (standard values: male, female, other).","example":"male"}},"additionalProperties":{"nullable":true},"description":"Optional structured pre-screening form used by the assessment rules. Custom fields (defined via form_fields) are also accepted."}},"required":["position_id","candidate_name","candidate_email","candidate_resume"]},"PreScreeningResumeBinarySuccess":{"type":"object","properties":{"success":{"type":"boolean"},"decision_status":{"type":"string"},"recommendation":{"type":"string","nullable":true},"profile_interview_id":{"type":"string"},"interview_result_pre_screening_id":{"type":"string"},"interview_file_id":{"type":"string"},"pre_screening_score":{"type":"number","nullable":true},"resume_ai_analysis":{"type":"string","nullable":true},"resume_ai_education":{"type":"string","nullable":true},"resume_ai_technical_experience":{"type":"number","nullable":true},"resume_ai_is_switching_profession":{"type":"boolean","nullable":true},"processing_reason":{"type":"string","nullable":true}},"required":["success","decision_status","recommendation","profile_interview_id","interview_result_pre_screening_id","interview_file_id","pre_screening_score","resume_ai_analysis","resume_ai_education","resume_ai_technical_experience","resume_ai_is_switching_profession","processing_reason"]},"PreScreeningResumeBinaryEarlyExit":{"type":"object","properties":{"decision_status":{"type":"string"},"message":{"type":"string"}},"required":["decision_status","message"]},"PreScreeningResumeBinaryResponse":{"anyOf":[{"$ref":"#/components/schemas/PreScreeningResumeBinarySuccess"},{"$ref":"#/components/schemas/PreScreeningResumeBinaryEarlyExit"}],"description":"Either the completed pre-screening result, or an early-exit status message when the candidate is not in the pre-screening candidate_action state."},"PreScreeningResumeBinaryRequest":{"type":"object","properties":{"position_id":{"type":"string","minLength":1,"description":"The position the candidate is being pre-screened against."},"candidate_name":{"type":"string","minLength":1,"description":"Full name of the candidate."},"candidate_email":{"type":"string","minLength":1,"description":"Candidate email (normalized to lowercase, trimmed)."},"candidate_country_code":{"type":"string","description":"Optional ISO country code used as the residency/nationality fallback when those are not supplied in `form`. If the position has a `form_nationality` or `form_residency` screening rule and neither this field nor the corresponding `form` value is provided, the candidate is screened with no nationality/residency and will fail that rule (marked for review or rejected per its action)."},"candidate_external_id":{"type":"string","description":"Optional external identifier for the candidate."},"candidate_linkedin_url":{"type":"string","description":"Optional candidate LinkedIn profile URL."},"file":{"type":"string","description":"Resume file (pdf, doc, docx, txt, etc.)","format":"binary"},"form":{"type":"string","description":"Optional JSON string with the candidate form answers, same shape as the `form` object of job-interview-pre-screening-api-resume-text: education (level key), languages ([{ code, level }]), residency/nationality (ISO codes), visa (key), age (int), gender (key). Custom fields (custom_<timestamp>) may also be included.","example":"{\"education\":\"masters\",\"languages\":[{\"code\":\"en\",\"level\":\"fluent\"}],\"residency\":\"US\"}"}},"required":["position_id","candidate_name","candidate_email","file"]},"InviteUsersResponse":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string","nullable":true},"type":{"type":"string","nullable":true},"interview_id":{"type":"string","nullable":true},"result":{"type":"string","description":"Outcome, e.g. 'Invited', 'Activated', 'SubMerchantAdded', 'Skipped', or 'Error: <message>'."},"profile_id":{"type":"string","nullable":true},"profile_interview_id":{"type":"string","nullable":true}},"required":["email"],"additionalProperties":{"nullable":true}},"description":"The input users array, each augmented with the invitation result fields."},"InviteUsersRequest":{"type":"object","properties":{"users":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string","nullable":true,"description":"Email address of the person to invite. Validated per item; an invalid/missing value produces a per-item error result, not a request-level 422.","example":"hello@google.com"},"name":{"type":"string","nullable":true,"description":"Display name for the invited profile.","example":"mr. Brown"},"type":{"type":"string","nullable":true,"description":"Invite type — selects which kind of account is created. Defaults to 'user'. Admin / merchant-team roles (manage the recruiting dashboard): `merchant_owner`, `merchant`, `merchant_selection`. Coaching-portal users: `user`, `mentor`. Interview candidate: `candidate` (requires `interview_id`). An invalid value yields a per-item error result rather than a request-level failure.","example":"merchant"},"interview_id":{"type":"string","nullable":true,"format":"uuid","description":"Interview/position id. Mandatory when type is 'candidate'.","example":"d1c00b60-7e75-4292-9190-7037b95b349a"}},"additionalProperties":{"nullable":true}},"description":"List of users to invite."},"force_invite":{"type":"boolean","nullable":true,"description":"When true, re-sends the invite email for still-pending (invited) profiles that did not otherwise produce a result.","example":false},"merchant_id":{"type":"string","nullable":true,"format":"uuid","description":"Target merchant id. Required with a service-role key; for admins / sub-merchant users it overrides the token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"mojito_language_code":{"type":"string","nullable":true,"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"],"description":"Language code applied to created profiles (one of the platform-languages.json codes). Defaults to 'en' when omitted.","example":"en"},"message_from_recruiter":{"type":"string","nullable":true,"description":"Optional custom message included in the invitation email."}},"required":["users"]},"MerchantSubMerchantListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Sub-merchant id."},"name":{"type":"string","nullable":true,"description":"Sub-merchant name."},"status":{"type":"string","nullable":true,"description":"Status."},"contact_person":{"type":"string","nullable":true,"description":"Primary contact name."},"contact_email":{"type":"string","nullable":true,"description":"Primary contact email."},"billing_country":{"type":"string","nullable":true,"description":"Billing country."},"business_type":{"type":"string","nullable":true,"description":"Business type."},"image_logo_url":{"type":"string","nullable":true,"description":"Logo image URL."},"frontend_domain":{"type":"string","nullable":true,"description":"Consumer frontend domain."},"interview_frontend_domain":{"type":"string","nullable":true,"description":"Interview frontend domain."},"register_domain":{"type":"string","nullable":true,"description":"Registration domain."},"info_notes":{"type":"string","nullable":true,"description":"Internal notes."},"merchant_features":{"nullable":true,"description":"Enabled feature flags (jsonb)."},"interview_subscription_status":{"type":"string","nullable":true,"description":"Interview subscription status."},"interview_subscription_type":{"type":"string","nullable":true,"description":"Interview subscription type."},"stripe_interview_subscription_start":{"type":"string","nullable":true,"description":"Subscription start (ISO 8601)."},"consumer_webanalytics":{"type":"string","nullable":true,"description":"Consumer web-analytics id."},"interview_webanalytics":{"type":"string","nullable":true,"description":"Interview web-analytics id."},"admin_profiles":{"nullable":true,"description":"Admin profiles linked to this sub-merchant (jsonb)."},"users":{"nullable":true,"description":"Users linked to this sub-merchant (jsonb)."},"interview_count":{"type":"number","nullable":true,"description":"Total interviews run."},"interview_latest":{"type":"string","nullable":true,"description":"Most recent interview timestamp (ISO 8601)."},"credits_used_30d":{"type":"number","nullable":true,"description":"Credits used in the last 30 days."},"credits_used_total":{"type":"number","nullable":true,"description":"Total credits used."},"created_at":{"type":"string","nullable":true,"description":"Creation timestamp (ISO 8601)."}},"required":["id"],"additionalProperties":{"nullable":true}},"description":"The sub-merchants the caller administers for this page."},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"MerchantAvatarListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Avatar template id."},"merchant_id":{"type":"string","nullable":true,"description":"Owning merchant id."},"name":{"type":"string","nullable":true,"description":"Template name."},"type":{"type":"string","nullable":true,"description":"Avatar template type."},"status":{"type":"string","nullable":true,"description":"Status (active/draft/archived/deleted)."},"visibility":{"type":"string","nullable":true,"description":"Visibility (public/merchant_…)."},"mojito_language_code":{"type":"string","nullable":true,"description":"Platform language code."},"video_voice_language_name":{"type":"string","nullable":true,"description":"Voice language display name."},"media_url":{"type":"string","nullable":true,"description":"Avatar media URL."},"background_url":{"type":"string","nullable":true,"description":"Background image URL."},"background_blur":{"type":"number","nullable":true,"description":"Background blur intensity (0–1); higher is blurrier."},"video_background_remove":{"type":"boolean","nullable":true,"description":"Whether the video background is removed."},"video_avatar_image_url":{"type":"string","nullable":true,"description":"Avatar still-image URL."},"video_avatar_aspect_ratio":{"type":"string","nullable":true,"description":"Avatar aspect ratio."},"created_at":{"type":"string","nullable":true,"description":"Creation timestamp (ISO 8601)."},"updated_at":{"type":"string","nullable":true,"description":"Last update timestamp (ISO 8601)."}},"required":["id"],"additionalProperties":{"nullable":true}},"description":"The merchant's avatar templates for this page, newest-updated first."},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]},"MerchantStatus":{"type":"object","properties":{"profile_id":{"type":"string","description":"The calling user's profile id (auth user id), taken from the JWT.","example":"9b2c1e54-7a3d-4f8e-9c10-2b6a4d5e7f01"},"default_merchant_id":{"type":"string","nullable":true,"description":"The caller's home merchant id pinned in the JWT (app_metadata.merchant_id). Null if the token carries no merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"merchant_id":{"type":"string","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.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"credits_interview_monthly":{"type":"number","nullable":true,"description":"Remaining monthly interview credits.","example":8},"credits_interview_monthly_limit":{"type":"number","nullable":true,"description":"Monthly interview-credit allowance for the current plan.","example":10},"credits_interview_extra":{"type":"number","nullable":true,"description":"Extra (top-up) interview credits available on top of the monthly allowance.","example":25},"credits_interview_single":{"type":"number","nullable":true,"description":"Single-position interview credits available.","example":0},"definitions_active":{"type":"number","nullable":true,"description":"Count of active interview + position definitions.","example":4},"interviews_without_credits":{"type":"number","description":"Completed interviews that have not yet consumed a credit.","example":2},"interviews_undecided":{"type":"number","description":"Completed interviews awaiting a recruiter decision.","example":3},"interviews_ongoing":{"type":"number","description":"Interviews currently in progress (coach_status = started).","example":1},"interview_results":{"type":"number","description":"Total non-archived interview results for the merchant.","example":137},"candidates":{"type":"number","description":"Total candidates (non-archived profile_interview rows) for the merchant.","example":212},"candidates_history":{"type":"array","items":{"type":"number"},"description":"Daily new-candidate counts for the last 14 days, most recent first.","example":[3,5,0,2,8,1,0,4,6,2,1,0,3,5]},"interview_result_history":{"type":"array","items":{"type":"number"},"description":"Daily new-interview-result counts for the last 14 days, most recent first.","example":[1,2,0,1,3,0,0,2,1,1,0,0,1,2]},"subscription_type":{"type":"string","nullable":true,"description":"Subscription plan name (e.g. Free, Starter, Growth, Special).","example":"Growth"},"subscription_status":{"type":"string","nullable":true,"description":"Subscription status (e.g. active, past_due, canceled). Null when no subscription.","example":"active"},"invitations_sent":{"type":"number","description":"Interview invitations sent during the current subscription period.","example":18},"invitations_limit":{"type":"number","nullable":true,"description":"Maximum invitations allowed this period (4x available credits). Null for unlimited (Special) plans.","example":40},"invitations_available":{"type":"number","nullable":true,"description":"Remaining invitations this period. Null for unlimited (Special) plans.","example":22}},"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"],"additionalProperties":{"nullable":true}},"LanguageAccent":{"type":"object","properties":{"code":{"type":"string","description":"Azure speech locale code for the accent/regional variant.","example":"en-US"},"name":{"type":"string","description":"Human-readable accent name.","example":"English (United States)"}},"required":["code","name"]},"PlatformLanguage":{"type":"object","properties":{"code":{"type":"string","description":"Mojito platform language code. Pass this as `mojito_language_code` when creating interviews / personas.","example":"en"},"name_english":{"type":"string","description":"Language name in English.","example":"English"},"name_local":{"type":"string","description":"Language name written in the language itself.","example":"English"},"flag_image":{"type":"string","description":"URL to an SVG flag image for the language (same source the app UI renders).","example":"https://cdn.jsdelivr.net/npm/country-flag-emoji-json@2.0.0/dist/images/US.svg"},"flag_emoji":{"type":"string","description":"Flag emoji for the language.","example":"🇺🇸"},"flag_unicode":{"type":"string","description":"Unicode code points for the flag emoji.","example":"U+1F1FA U+1F1F8"},"coaching_interface":{"type":"boolean","description":"Whether this language is enabled on the coaching (consumer) interface.","example":true},"interview_interface":{"type":"boolean","description":"Whether this language is enabled on the interview interface.","example":true},"admin_interface":{"type":"boolean","description":"Whether this language is enabled on the admin interface.","example":true},"accents":{"type":"array","items":{"$ref":"#/components/schemas/LanguageAccent"},"description":"Azure speech regional accents / variants available for this language, each with its locale `code` and `name`."}},"required":["code","name_english","name_local","flag_image","flag_emoji","flag_unicode","coaching_interface","interview_interface","admin_interface","accents"]},"PlatformLanguagesListResponse":{"type":"object","properties":{"languages":{"type":"array","items":{"$ref":"#/components/schemas/PlatformLanguage"},"description":"Supported platform languages, sorted by English name."}},"required":["languages"]}},"parameters":{}},"paths":{"/job-interview-create":{"post":{"tags":["Interviews"],"summary":"Create a new interview","description":"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.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewCreateRequest"}}}},"responses":{"200":{"description":"Interview created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewCreateResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error (missing required field, invalid enum value, out-of-range number, or unresolved template/language/knowledge-base id).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures and unhandled exceptions).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-create-from-array":{"post":{"tags":["Interviews"],"summary":"Create an interview from an array of questions","description":"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.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewCreateFromArrayRequest"}}}},"responses":{"200":{"description":"Interview created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewCreateFromArrayResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures and unresolved template/knowledge-base/language ids).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-create-for-candidate-with-token":{"post":{"tags":["Interviews"],"summary":"Create an interview for a candidate and return an access token URL","description":"Creates (or resolves an existing) position and interview for a merchant, enrols the candidate, runs pre-screening, and returns a tokenised interview URL. Position fields are required only when position_def_set_id is not provided.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewCreateForCandidateWithTokenRequest"}}}},"responses":{"200":{"description":"Candidate enrolled. Decision status, ids, and a tokenised interview URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewCreateForCandidateWithTokenResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The requested position_def_set_id could not be found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error (missing/invalid fields, unresolved merchant/language/template).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures and unhandled exceptions).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/persona-create":{"post":{"tags":["Interviews"],"summary":"Create a role-play — coaching persona or interview-portal role-play (choose with `portal`)","description":"FIRST DECIDE `portal`. This endpoint creates two different products and the default is NOT the recruiter one:\n- `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.\n- `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.\nThe conversation itself behaves identically in both.\n\nThe 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.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PersonaCreateRequest"}}}},"responses":{"200":{"description":"Persona created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PersonaCreateResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error (missing required field, invalid enum value, out-of-range number, or unresolved template/language id).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures and unhandled exceptions).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-get":{"get":{"tags":["Interviews"],"summary":"Get interview definition","description":"Retrieves the interview definition for a given interview-definition id or position id. Returns the compiled `calc_definition_json` plus basic metadata. Access is subject to the caller's row-level security.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"format":"uuid","description":"Identifier of either an interview definition (single-stage) or a position definition (multi-stage). The function resolves whichever matches.","example":"00000000-0000-0000-0000-000000000000"},"required":true,"name":"position_id","in":"query"}],"responses":{"200":{"description":"The resolved interview definition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewGetResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No interview or position found for the given id (or hidden by row-level security).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-update":{"post":{"tags":["Interviews"],"summary":"Update an interview or position","description":"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, and sending null clears a nullable field. The question list is out of scope: questions, and the welcome / thank-you messages that are stored as questions, are not changed by this endpoint. For the same reason `mojito_language_code` cannot be changed — the existing questions stay in the language they were written in — 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.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewUpdateRequest"}}}},"responses":{"200":{"description":"Interview / position updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewUpdateResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No interview or position found for the given id (or hidden by row-level security).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error (unknown enum value, out-of-range number, unresolved template/language/knowledge-base id, or an interview-only field sent for a multi-stage position).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-set-state":{"post":{"tags":["Interviews"],"summary":"Set interview state","description":"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`.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewSetStateRequest"}}}},"responses":{"200":{"description":"State updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewSetStateResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures and unresolved position/interview ids).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-token":{"post":{"tags":["Interviews"],"summary":"Generate a signed interview URL","description":"Generates a public, token-signed URL for an existing interview, profile, or result. The required id fields depend on `type`.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewTokenRequest"}}}},"responses":{"200":{"description":"The signed URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewTokenResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error (invalid type, missing conditional fields, unauthenticated, or interview not found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-register-users":{"post":{"tags":["Interviews"],"summary":"Register users for an interview","description":"Registers (or updates) one or more candidate profiles for an interview and returns a one-time interview URL for each. Accepts a Supabase user JWT (merchant/admin) or the service key (merchant_id then required).","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewRegisterUsersRequest"}}}},"responses":{"200":{"description":"Per-user registration results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewRegisterUsersResponse"}}}},"401":{"description":"Unauthenticated user (user token with no resolvable user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error (missing required fields, missing merchant_id with service key, or interview not found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/merchant-interview-list":{"get":{"tags":["Interviews"],"summary":"List interview definitions","description":"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.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"required":false,"name":"merchant_id","in":"query"},{"schema":{"type":"string","enum":["interview","coaching","assessment","public_avatar","persona","persona_interview"],"default":"interview","description":"Product type of interviews to list.","example":"interview"},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","enum":["draft","active","archived","deleted","preparing","completed"],"description":"Filter by lifecycle status. Omit to include all statuses.","example":"active"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","enum":["true","false"],"default":"false","description":"Include interviews shared publicly across merchants (coaching/avatars).","example":"false"},"required":false,"name":"show_public","in":"query"},{"schema":{"type":"string","enum":["true","false"],"default":"false","description":"Include demo/sample interviews.","example":"false"},"required":false,"name":"show_demo","in":"query"},{"schema":{"type":"string","description":"Case-insensitive search on the interview name."},"required":false,"name":"filter_text","in":"query"},{"schema":{"type":"string","description":"Filter by the interview emoji marker."},"required":false,"name":"filter_emoji","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":50,"description":"Maximum number of records to return (1–1000).","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"default":0,"description":"Number of records to skip from the start of the result set.","example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"A page of interview definitions with pagination metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantInterviewListResponse"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Caller is not permitted to read this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalogue-tag-create":{"post":{"tags":["Coaching catalogue"],"summary":"Create a coaching catalogue directory","description":"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.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogueTagCreateRequest"}}}},"responses":{"200":{"description":"Directory created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogueTagCreateResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A directory with this id already exists. Use catalogue-tag-update to change it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error (malformed id, unknown enum value, unresolved parent_tag).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalogue-tag-update":{"post":{"tags":["Coaching catalogue"],"summary":"Update a coaching catalogue directory","description":"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, and sending null clears a nullable field. 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`), or author its custom Markdown page (`content_md`).","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogueTagUpdateRequest"}}}},"responses":{"200":{"description":"Directory updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogueTagUpdateResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No directory with this id (or hidden / not writable by row-level security).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error (malformed id, unknown enum value, or no updatable field supplied).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalogue-tag-get":{"get":{"tags":["Coaching catalogue"],"summary":"Get a coaching catalogue directory","description":"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.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Id of the catalogue directory to read (the catalogue URL segment).","example":"sales-coaching-en"},"required":true,"name":"id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"required":false,"name":"merchant_id","in":"query"}],"responses":{"200":{"description":"The resolved catalogue directory.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogueTagGetResponse"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Caller is not permitted to read this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No directory with this id (or hidden by row-level security).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalogue-tag-list":{"get":{"tags":["Coaching catalogue"],"summary":"List coaching catalogue directories","description":"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.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"required":false,"name":"merchant_id","in":"query"},{"schema":{"type":"string","enum":["draft","active","archived"],"description":"Filter by lifecycle status. Omit to include every status you can see (deleted directories are never returned).","example":"active"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","enum":["public","merchant_public","merchant_invite","merchant_unlisted"],"description":"Filter by visibility.","example":"merchant_public"},"required":false,"name":"visibility","in":"query"},{"schema":{"type":"string","description":"Filter by the directory language (a platform-languages.json code, e.g. `en`)."},"required":false,"name":"mojito_language_code","in":"query"},{"schema":{"type":"string","description":"Case-insensitive search on the directory id, name and description."},"required":false,"name":"filter_text","in":"query"},{"schema":{"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."},"required":false,"name":"parent_tag","in":"query"},{"schema":{"type":"string","enum":["true","false"],"default":"true","description":"Include the platform-wide `public` directories shared across merchants.","example":"true"},"required":false,"name":"include_public","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":50,"description":"Maximum number of records to return (1–1000).","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"default":0,"description":"Number of records to skip from the start of the result set.","example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"A page of catalogue directories with pagination metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogueTagListResponse"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Caller is not permitted to read this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-details":{"get":{"tags":["Results"],"summary":"Get interview result details with transcript","description":"Returns an interview result with its full transcript and AI assessment. Optionally attaches signed recording URLs.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"format":"uuid","description":"The interview result to fetch the transcript and details for.","example":"93c98d21-e04d-4a84-9afa-ed154cf73636"},"required":true,"name":"interview_result_id","in":"query"},{"schema":{"type":"string","enum":["true","false"],"default":"false","description":"When true, includes short-lived signed recording URLs for the session, the video introduction, and each transcript answer.","example":"false"},"required":false,"name":"get_signed_recordings","in":"query"}],"responses":{"200":{"description":"The interview result and transcript.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewDetailsResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No interview result exists for the given interview_result_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-pdf":{"post":{"tags":["Results"],"summary":"Generate an interview report","description":"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.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewPdfRequest"}}}},"responses":{"200":{"description":"The generated report. The body is one of three shapes, selected by the request `export_type`: `pdf` → `{ pdf_export_url, pdf_export_valid_until }`; `html` → `{ html_export }`; `json` → `{ json_export }`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobInterviewPdfResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-result-request-another-attempt":{"post":{"tags":["Results"],"summary":"Request another interview attempt","description":"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.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestAnotherAttemptRequest"}}}},"responses":{"200":{"description":"The result was reset for another attempt.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestAnotherAttemptResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error: missing/invalid id, result not found, or the result is not in an active+submitted state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/merchant-result-list":{"get":{"tags":["Results"],"summary":"List interview results","description":"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.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"required":false,"name":"merchant_id","in":"query"},{"schema":{"type":"string","enum":["interview","coaching","assessment","public_avatar","persona","persona_interview"],"default":"interview","description":"Product type of results to list.","example":"interview"},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","enum":["decided-rejected","undecided","shortlist","decided-selected","completed","incomplete","archived-interview","public_avatar","archived-public_avatar",""],"description":"Filter by decision/completion state. Omit (or empty) to include all.","example":"undecided"},"required":false,"name":"tab","in":"query"},{"schema":{"type":"string","enum":["pre-screening","interview"],"description":"Filter by pipeline step (pre-screening vs interview). Omit for both.","example":"interview"},"required":false,"name":"step","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter to a single interview definition id."},"required":false,"name":"interview_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter to a single candidate (profile_interview) id."},"required":false,"name":"profile_interview_id","in":"query"},{"schema":{"type":"string","description":"Comma-separated list of recruiter-risk keys to filter by (matches any)."},"required":false,"name":"risks","in":"query"},{"schema":{"type":"string","enum":["score","created_at_newest","created_at_oldest","updated_at_newest"],"default":"created_at_newest","description":"Sort order of the result set.","example":"created_at_newest"},"required":false,"name":"order_by","in":"query"},{"schema":{"type":"string","description":"Case-insensitive search on candidate name or email."},"required":false,"name":"filter_text","in":"query"},{"schema":{"type":"string","description":"Filter by the candidate emoji marker."},"required":false,"name":"filter_emoji","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":50,"description":"Maximum number of records to return (1–1000).","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"default":0,"description":"Number of records to skip from the start of the result set.","example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"A page of interview results with pagination metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantResultListResponse"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Caller is not permitted to read this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/merchant-analytics":{"get":{"tags":["Results"],"summary":"Get daily event analytics","description":"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.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"required":false,"name":"merchant_id","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Start of the date range (inclusive), YYYY-MM-DD.","example":"2026-01-01"},"required":true,"name":"date_from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"End of the date range (inclusive), YYYY-MM-DD.","example":"2026-01-01"},"required":true,"name":"date_to","in":"query"},{"schema":{"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."},"required":false,"name":"interview_id","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":50,"description":"Maximum number of records to return (1–1000).","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"default":0,"description":"Number of records to skip from the start of the result set.","example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"A page of daily event counts with pagination metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantAnalyticsResponse"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Caller is not permitted to read this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/merchant-analytics-credits-used":{"get":{"tags":["Results"],"summary":"Get merchant credit usage","description":"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.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"required":false,"name":"merchant_id","in":"query"},{"schema":{"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)."},"required":false,"name":"interview_id","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":50,"description":"Maximum number of records to return (1–1000).","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"default":0,"description":"Number of records to skip from the start of the result set.","example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"A page of credit-usage events with pagination metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantCreditsUsedResponse"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Caller is not permitted to read this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/merchant-candidate-list":{"get":{"tags":["Candidates"],"summary":"List candidates","description":"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.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"required":false,"name":"merchant_id","in":"query"},{"schema":{"type":"string","enum":["interview","pre-screening","no-action","leads",""],"description":"Filter candidates by recent activity. Omit (or empty) to include all.","example":"interview"},"required":false,"name":"tab","in":"query"},{"schema":{"type":"string","enum":["invited","registered"],"description":"Filter by how the candidate entered: invited or self-registered. Omit for both.","example":"registered"},"required":false,"name":"source","in":"query"},{"schema":{"type":"string","enum":["name_accending","name_decending","created_at_newest","created_at_oldest"],"default":"created_at_newest","description":"Sort order of the result set.","example":"created_at_newest"},"required":false,"name":"order_by","in":"query"},{"schema":{"type":"string","description":"Case-insensitive search on candidate name or email."},"required":false,"name":"filter_text","in":"query"},{"schema":{"type":"string","description":"Filter by the candidate emoji marker."},"required":false,"name":"filter_emoji","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":50,"description":"Maximum number of records to return (1–1000).","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"default":0,"description":"Number of records to skip from the start of the result set.","example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"A page of candidates with pagination metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantCandidateListResponse"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Caller is not permitted to read this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/knowledge-base-document-upload":{"post":{"tags":["Knowledge base"],"summary":"Upload a knowledge base document","description":"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`).","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/KnowledgeBaseDocumentUploadMultipart"}},"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeBaseDocumentUploadJson"}}}},"responses":{"200":{"description":"The document was uploaded and queued for processing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeBaseDocumentUploadResponse"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures and a missing/invalid file or store).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pre-screening-create":{"post":{"tags":["Resume & Form verification"],"summary":"Create or update a pre-screening position","description":"Creates a new pre-screening position (and optionally activates it) or, when update_position_def_set_id is provided, updates an existing pre-screening position. Only merchant_owner, merchant, or admin users may call this.\n\nThe screening behaviour is driven by two related arrays:\n- `assessment_rules` — the rules enforced on each candidate. Only rules with `type: \"screening\"` (plus `resume_match` / `resume_technical_experience`) actually pass/reject; other entries just declare collected fields.\n- `form_fields` — the candidate-facing form (standard field references and custom fields).\n\nFor `form` and `resume_with_form` types, enable a standard field in BOTH arrays: add the rule to `assessment_rules` and a matching `{ id, mandatory }` entry to `form_fields`. `resume`-only positions use `assessment_rules` (resume_* rules) and ignore `form_fields`. Both arrays are replaced wholesale on update — send the complete array, not a delta.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreScreeningCreateRequest"}}}},"responses":{"200":{"description":"The pre-screening position was created or updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreScreeningCreateResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Pre-screening step not found for the given position (update path).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error or unresolved merchant_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error (includes authorization failures).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-pre-screening-api-resume-text":{"post":{"tags":["Resume & Form verification"],"summary":"Run pre-screening on a plaintext resume","description":"Stores a plaintext candidate resume, runs AI pre-screening against the position definition, records the result and returns the AI recommendation and analysis. If the candidate is not in the pre-screening candidate_action state, an early `{ decision_status, message }` object is returned with HTTP 200.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreScreeningResumeTextRequest"}}}},"responses":{"200":{"description":"Pre-screening completed, or an early `{ decision_status, message }` result when the candidate is not in the pre-screening candidate_action state.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/PreScreeningResumeTextResponse"},{"$ref":"#/components/schemas/PreScreeningNotApplicable"}]}}}},"401":{"description":"Missing or invalid authentication.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated user is not a merchant user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Position not found or does not belong to this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error (missing required field or unresolved merchant_id).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/job-interview-pre-screening-api-resume-binary":{"post":{"tags":["Resume & Form verification"],"summary":"Pre-screen a candidate from a binary resume upload","description":"Accepts a multipart/form-data upload with candidate details and a binary resume file, converts the resume to text, runs AI pre-screening against the position, stores the result, and returns the decision.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/PreScreeningResumeBinaryRequest"}}}},"responses":{"200":{"description":"Pre-screening result, or an early-exit status message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreScreeningResumeBinaryResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden: only merchant users can access this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Position not found or does not belong to this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/invite-users":{"post":{"tags":["Admin"],"summary":"Invite admin / merchant-team or coaching users","description":"Invites people to a merchant account and emails each one an invitation. Use it to add **admin / merchant-team users** who manage the recruiting dashboard (`merchant_owner`, `merchant`, `merchant_selection`), **coaching-portal users** (`user`, `mentor`), or **interview candidates** (`candidate`). Candidate invites require an `interview_id` and are gated by the merchant's available interview credits; admin and coaching invites are not. Each user in the array is processed independently and returned with an individual result, so one failure does not block the rest. Callable with a merchant JWT (merchant_owner/merchant/admin) or a service-role key (requires merchant_id).","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteUsersRequest"}}}},"responses":{"200":{"description":"The users array with per-item invitation results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteUsersResponse"}}}},"403":{"description":"The caller does not have access to the requested merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error, missing merchant_id for service role, or unauthenticated user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/merchant-sub-merchant-list":{"get":{"tags":["Admin"],"summary":"List sub-merchants","description":"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.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"required":false,"name":"merchant_id","in":"query"},{"schema":{"type":"string","enum":["name","created_at_newest","created_at_oldest"],"default":"created_at_newest","description":"Sort order of the result set.","example":"created_at_newest"},"required":false,"name":"order_by","in":"query"},{"schema":{"type":"string","description":"Case-insensitive search on the sub-merchant name."},"required":false,"name":"filter_text","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":50,"description":"Maximum number of records to return (1–1000).","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"default":0,"description":"Number of records to skip from the start of the result set.","example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"A page of sub-merchants with pagination metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantSubMerchantListResponse"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Caller is not permitted to read sub-merchants.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/merchant-avatar-list":{"get":{"tags":["Admin"],"summary":"List avatar templates","description":"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.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"required":false,"name":"merchant_id","in":"query"},{"schema":{"type":"string","enum":["interactive_heygen","interactive_elevenlabs","offline_heygen","offline_elai","offline_synthesia"],"description":"Filter by avatar template type. Omit for all.","example":"interactive_heygen"},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","enum":["active","draft","archived","deleted"],"description":"Filter by status. Omit for all except archived (see include_archived).","example":"active"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","description":"Filter by platform language code."},"required":false,"name":"mojito_language_code","in":"query"},{"schema":{"type":"string","description":"Case-insensitive search on template name or voice language name."},"required":false,"name":"filter_text","in":"query"},{"schema":{"type":"string","enum":["true","false"],"default":"false","description":"Include archived templates (excluded by default).","example":"false"},"required":false,"name":"include_archived","in":"query"},{"schema":{"type":"string","enum":["true","false"],"default":"false","description":"Also include public templates shared across merchants, in addition to this merchant's own.","example":"false"},"required":false,"name":"include_public","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":50,"description":"Maximum number of records to return (1–1000).","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"default":0,"description":"Number of records to skip from the start of the result set.","example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"A page of avatar templates with pagination metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantAvatarListResponse"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Caller is not permitted to read this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/merchant-status":{"get":{"tags":["Admin"],"summary":"Get merchant status","description":"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.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Optional merchant to scope to. Admins and sub-merchant operators only; other callers always use their token's merchant.","example":"28106cba-1c27-4e53-b149-32113e5e8e31"},"required":false,"name":"merchant_id","in":"query"}],"responses":{"200":{"description":"The merchant status snapshot.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantStatus"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Caller is not permitted to read this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/platform-languages-list":{"get":{"tags":["Admin"],"summary":"List platform languages","description":"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.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The list of platform languages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformLanguagesListResponse"}}}},"401":{"description":"Missing, expired or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}