> ## Documentation Index
> Fetch the complete documentation index at: https://developer.jobmojito.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create an interview

> Generate an AI interview from a job position, from your own questions, or in one shot for a candidate.

There are four ways to create an interview, depending on how much you want JobMojito to generate for you:

| You provide | Use | MCP tool |
| - | - | - |
| A job description → AI generates questions | `POST /job-interview-create` | `create_interview` |
| Your own list of questions | `POST /job-interview-create-from-array` | `create_interview_from_questions` |
| A candidate + position, get a ready link back | `POST /job-interview-create-for-candidate-with-token` | — (HTTP only, not an MCP tool) |
| A role-play persona (avatar plays a role, no Q\&A) — two variants: coaching persona, or an interview-portal interview role-play | `POST /persona-create` | `create_persona` |

<Note>
  You'll need an `interview_template_id`. List your available templates with `GET /merchant-avatar-list` (MCP tool `list_avatars`) and pick one.
</Note>

## The template picks the interview modality

The `interview_template_id` is not just cosmetic — **the template's `type` decides whether the candidate gets a voice-only or an avatar experience.** Choose it deliberately; there is no separate "voice or avatar" flag on the create call.

| Template `type` (from `list_avatars`) | Candidate experience |
| - | - |
| `interactive_elevenlabs` | **Voice-only** — realtime conversation, no video avatar. |
| `interactive_heygen` | **Realtime interactive avatar** — a talking video avatar. |
| `offline_heygen` | **Non-interactive avatar** — pre-recorded avatar video (no live back-and-forth). |

<Note>
  `offline_elai` and `offline_synthesia` are **legacy** integrations. They may still appear in `list_avatars` for older accounts, but they **cannot be used to create new interviews** — only `offline_heygen` is supported for the pre-recorded modality.
</Note>

<Tip>
  Filter directly to the modality you want: `GET /merchant-avatar-list?type=interactive_elevenlabs` for voice-only, or `?type=interactive_heygen` for a realtime avatar. The item's `id` is the `interview_template_id` you pass below.
</Tip>

<Warning>
  Some options only work on avatar templates. On a **voice-only** (`interactive_elevenlabs`) template, `early_stop` scoring and `instructional_video` are unsupported and the create call returns **422**. Pick an `interactive_heygen` template if you need those.
</Warning>

To confirm the modality of an interview you've already created, read it back with `get_interview_definition` (`POST /job-interview-get`) — the response includes `interview_template_type` and a convenience `is_voice_only` boolean.

## Option A — from a job position (AI-generated questions)

JobMojito writes the description, questions, and candidate expectations for you.

<Steps>
  <Step title="Choose a template">
    Call `GET /merchant-avatar-list` and note an `interview_template_id`.
  </Step>

  <Step title="Create the interview">
    Send the position details. The required fields are `name`, `location`, `interview_template_id`, `mojito_language_code`, `status`, `type`, and `visibility`.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://cool.jobmojito.com/functions/v1/job-interview-create \
        -H "Authorization: Bearer $SUPABASE_JWT" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "Senior Backend Engineer",
          "location": "Remote (EU)",
          "interview_template_id": "TEMPLATE_UUID",
          "mojito_language_code": "en",
          "status": "active",
          "type": "interview",
          "visibility": "merchant_invite",
          "interview_type": "pre-screening",
          "seniority_level": "senior",
          "interview_length": 8,
          "description": "Backend role focused on Python, async APIs, and AWS.",
          "candidate_expectations": "5+ yrs Python, REST/async, cloud, SQL."
        }'
      ```
    </CodeGroup>
  </Step>

  <Step title="Personalize the candidate experience & scoring (optional but recommended)">
    The defaults are generic. In the **same create call**, set these fields to tailor the interview — they are easy to forget:

    * `welcome_message` — the custom message shown to the candidate **before** they start. Set it to greet the candidate and set expectations.
    * `thank_you_message` — the custom message shown **after** they finish.
    * `custom_scoring` — an object of result-scoring overrides (your custom rubric). It is merged over the platform defaults and any template overrides, so you only need to include what you want to change. Omit it to use the template/default scoring.

    ```json theme={null}
    {
      "welcome_message": "Welcome! This is a 15-minute screen for the Senior Backend role. Answer out loud; there are no trick questions.",
      "thank_you_message": "Thanks for your time — our team will review your responses and be in touch within a week.",
      "custom_scoring": {
        "criteria": [
          { "name": "System design", "weight": 40 },
          { "name": "Coding fundamentals", "weight": 40 },
          { "name": "Communication", "weight": 20 }
        ]
      }
    }
    ```

    <Note>
      `custom_scoring` is a free-form overrides object — its exact shape follows your account's scoring model. Fetch an existing interview with `get_interview_definition` to see the current scoring structure before overriding it.
    </Note>
  </Step>

  <Step title="Capture the interview id">
    The response returns `interview_def_set_id` — keep it; you'll use it to invite candidates and review results. Note it is passed back to other endpoints under different field names (`position_id`, `interview_id`) — see [Identifiers & admin links](/mcp/identifiers).

    ```json theme={null}
    { "interview_def_set_id": "9c1b…e4f2" }
    ```
  </Step>
</Steps>

### Key fields

| Field | Required | Notes |
| - | - | - |
| `name`, `location` | ✓ | Position title and location. **Plain UTF-8 text** — displayed to the candidate as-is, so no Markdown or HTML (any `#`, `*`, or `<tags>` show up literally). |
| `interview_template_id` | ✓ | Template id from `list_avatars`. **Its `type` sets the modality** (voice-only vs avatar) — see [The template picks the interview modality](#the-template-picks-the-interview-modality). |
| `mojito_language_code` | ✓ | Platform language code (e.g. `en`). |
| `status` | ✓ | `draft`, `active`, `archived`, or `deleted`. |
| `type` | ✓ | Product type, e.g. `interview`. |
| `visibility` | ✓ | Who can access it, e.g. `merchant_invite`, `merchant_public`. |
| `description` | – | Short, **two-sentence** job description **shown to the candidate**, as **plain UTF-8 text** — no Markdown or HTML (unlike `description_long`; markup here renders literally). Provide it to use it as-is, or leave it null/blank and it is **AI-generated** from the position name (and any other context). Also fed into question generation. |
| `description_long` | – | Full job description in **Markdown** (Job Purpose, Responsibilities, Required & Preferred Qualifications). This is the **only** create field that accepts Markdown; `name` and `description` are plain text. Provide it to use it as-is, or leave it null/blank and it is **AI-generated**. |
| `candidate_expectations` | – | Free-text candidate expectations, fed into AI question generation. |
| `interview_length` | – | Number of questions to generate (1-40), **capped by `max_duration`** — see [How many questions fit](#how-many-questions-fit). Omit it and the AI picks 5-8. |
| `welcome_message`, `thank_you_message` | – | Custom candidate-facing messages — **set these; the defaults are generic.** |
| `custom_scoring` | – | Custom scoring-rubric overrides (object), merged over defaults. |
| `knowledge_base_store_id` | – | Ground questions in a [knowledge base](/cookbooks/manage-knowledge-base). |

### How many questions fit

`interview_length` is capped by `max_duration`, which allows **one question per 2 minutes** — enough for the question, the answer, and automatic follow-ups.

| `max_duration` | Interview length | Max questions |
| - | - | - |
| `1200` (default) | 20 min | 10 |
| `1800` | 30 min | 15 |
| `2700` | 45 min | 22 |
| `3600` | 60 min | 30 |
| `4800` | 80 min | 40 |

Asking for more than the cap is not an error — you get the cap. The create response returns `questions_generated` with the real count, so check it if the number matters to you.

### Additional configuration

These optional fields fine-tune the position, the conversation, and what candidates see. They apply to **`job-interview-create` and `job-interview-create-from-array`** (and, where noted, `job-interview-create-for-candidate-with-token`). All are optional — omit any you don't need.

| Field | Type | Notes |
| - | - | - |
| `interview_department` | string | Department the position belongs to. |
| `interview_salary` | string | Salary range shown for the position. |
| `interview_available_till` | ISO date/time | When the interview stops being available to candidates. |
| `interview_conversation_speed` | `slower` \| `normal` \| `faster` | Speaking pace of the AI avatar. |
| `max_followups` | int `0`–`999` | Max AI follow-up questions. `0` disables follow-ups; omit for the template default. |
| `questions_random_subset` | number `0.01`–`0.9` | Ask only a random fraction of questions (e.g. `0.5` = 50%). |
| `required_pronunciation` | boolean | Require pronunciation assessment (restricts to pronunciation-capable languages). |
| `result_enable_edit_transcript` | boolean | Allow editing the transcript on the result view (default `true`). |
| `tags` | string\[] | Free-form tags stored on the interview. |
| `recruiter_profile_id` | uuid | Recruiter (merchant profile) that owns the interview. |
| `hiring_for_company` | object | Who the role is really for (see below). Omit (or `name` null/blank) for yourself, `{ "name": "undisclosed" }` for an unnamed client, or `{ "name": "<company>" }` plus optional `description`/`location`/`sector`/`company_size` for a named client. |
| `pdf_export_auto_config` | object | Auto-generate a candidate PDF report when the interview completes (see below). |

`hiring_for_company` describes the end employer so the AI agent can answer candidate questions accurately. All fields are optional; `name` carries the original meaning:

```json theme={null}
{
  "hiring_for_company": {
    "name": "JobMojito",
    "description": "World leading AI assessment provider.",
    "location": "Estonia",
    "sector": "AI Interview",
    "company_size": "100-200"
  }
}
```

* Omit the field entirely, or send `name` null/blank → hiring for yourself (no external-client note).
* `{ "name": "undisclosed" }` → external client whose name is withheld from the candidate.
* `{ "name": "<company>" }` → named external client; `description`, `location`, `sector`, and `company_size` are surfaced to the agent as background context.

`pdf_export_auto_config` mirrors the report options — every key is optional:

```json theme={null}
{
  "pdf_export_auto_config": {
    "mojito_language_code": "en",
    "contact_details": true,
    "ai_recruiter_assessment": true,
    "ai_scoring_rubric": false,
    "analytics": true,
    "transcript": true,
    "files": true,
    "answer_recording": false,
    "session_recording": false,
    "group_by_question": false
  }
}
```

<Note>
  On `job-interview-create-for-candidate-with-token` these same fields are honoured (it creates the interview through the multistage flow). They require the `interview_create_multi_stage` migration to be deployed.
</Note>

## Option B — from your own questions

Supply an ordered `questions` array. Same required interview fields as Option A, plus a `description`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://cool.jobmojito.com/functions/v1/job-interview-create-from-array \
    -H "Authorization: Bearer $SUPABASE_JWT" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Support Specialist",
      "location": "Remote",
      "interview_template_id": "TEMPLATE_UUID",
      "mojito_language_code": "en",
      "description": "Customer support screening",
      "status": "active",
      "type": "interview",
      "visibility": "merchant_invite",
      "questions": [
        { "question": "Tell me about a time you de-escalated an angry customer.", "duration": 120 },
        { "question": "How do you prioritize a full support queue?", "duration": 90 },
        { "question": "Rate your written English.", "is_multiple_choice": true,
          "question_alternatives": ["Native", "Fluent", "Intermediate"] }
      ]
    }'
  ```
</CodeGroup>

Each `questions` item requires `question` (the text). Useful per-question flags: `duration` (seconds), `is_without_scoring`, `is_multiple_choice`, `question_alternatives`, `is_conditional` + `conditional_question_main_id` (for follow-ups), and `knowledge_base_id` (ask grounded questions — see [Manage a knowledge base](/cookbooks/manage-knowledge-base)).

The personalization fields from Option A — `welcome_message`, `thank_you_message`, and `custom_scoring` — plus everything in [Additional configuration](#additional-configuration) apply here too. Set them in the same call so the candidate experience and rubric aren't left on the generic defaults.

You may also pass `candidate_expectations_json` — a structured expectations object bucketed by requirement level (`{ "weak": [...], "moderate": [...], "strong": [...] }`). Omit it and JobMojito auto-generates one for `type: "interview"`.

Returns `interview_def_set_id`.

<Tip>
  This same `questions` format comes back from `GET /job-interview-get` and is accepted by `POST /job-interview-update`, so you can change the questions of an interview later without recreating it — see [Edit interview questions](/cookbooks/edit-interview-questions).
</Tip>

## Option C — one call per candidate (returns a link)

Create the position (or reuse one), enrol a candidate, run pre-screening, and get a tokenised interview URL back — all in one request.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://cool.jobmojito.com/functions/v1/job-interview-create-for-candidate-with-token \
    -H "Authorization: Bearer $SUPABASE_JWT" \
    -H "Content-Type: application/json" \
    -d '{
      "candidate_name": "Peter Parker",
      "candidate_email": "peter@parker.com",
      "candidate_country_code": "US",
      "candidate_resume": "Full plaintext résumé here…",
      "position_name": "Senior Backend Engineer",
      "position_location": "Remote (EU)",
      "position_country_code": "DE",
      "mojito_language_code": "en"
    }'
  ```
</CodeGroup>

If you already have a position, pass `position_def_set_id` instead of the `position_*` fields. The response includes `status` (e.g. `ai_accept`, `recruiter_action`, `ai_reject`), `interview_url`, and the created `interview_def_set_id` / `position_def_set_id`.

<Tip>
  Need to publish, archive, or unpublish later? Use `POST /job-interview-set-state` (`set_interview_state`) with the `position_id` and a `status` of `active`, `draft`, `archived`, or `deleted`. Retrieve a definition — questions included — with `POST /job-interview-get` (`get_interview_definition`), and change its settings or questions with `POST /job-interview-update` (`update_interview`).
</Tip>

## Option D — a role-play persona (no Q\&A)

A **persona** is a different product: the AI avatar plays a defined role in a free-form conversation instead of asking a scored list of questions. Use it for sales role-plays, mock-customer practice, or any "act as X" scenario. There is **no AI question generation** — the role fields you supply *are* the configuration.

The session flows as: a welcome message → the avatar's **opening line** → the candidate replies and the role-play begins → a closing message. The `opening_line` is the literal first thing the avatar says; it's the only scripted line, so set it in character (it defaults to a bare "Hello" if you omit it).

### Two variants

`POST /persona-create` creates one of two variants, chosen with the optional `portal` parameter. Both run the identical session mechanics — free-form role-play, no question list, the same `persona_*` and `opening_line` fields, and the same persona scoring — but they live on different portals and are billed differently.

| | **Coaching persona** | **Interview role-play** |
| - | - | - |
| `portal` | `coaching` (the default) | `interview` |
| Portal | Coaching portal only | Recruiter / interview portal |
| Who takes it | Mentees on the coaching platform | Candidates, **invited through the normal invitation flow** |
| Results | Visible to the coaching platform | Visible to **recruiters**, alongside regular interview results |
| Billing | Consumer coaching credits | **Merchant credits**, using the standard interview cost formula |
| Attempt limit | Unlimited | `interview_attempts` (default `3`) |
| `type` in API responses | `persona` | `persona_interview` |

<Note>
  `portal` defaults to `coaching`, so **existing callers are unaffected** — a `persona-create` call that doesn't mention `portal` behaves exactly as it did before. The **coaching persona** is otherwise unchanged: coaching portal only, billed against consumer coaching credits, with no attempt limit.
</Note>

The **interview role-play** (`portal: "interview"`) behaves like a persona in the session but like an interview commercially and operationally:

* **Recruiters see the results.** Interview role-plays are scored on the persona scoring path — you get per-answer and session-level feedback, the same as a coaching persona — and completed sessions appear in the recruiter results list alongside regular interviews. See [Review results](/cookbooks/review-results).
* **Candidates are invited normally.** Use the standard invitation flow (`POST /job-interview-register-users`) to generate links or send email invites — see [Invite candidates](/cookbooks/invite-candidates).
* **It draws on merchant credits.** Cost is the standard interview formula — base format cost × duration multiplier, plus any recording add-ons — not consumer coaching credits. Each **completed** session is billed, so a candidate who uses every attempt costs a multiple of the single-session price. See [How credits work](/how-credits-work).
* **Attempts are capped.** `interview_attempts` limits how many times a candidate may take the role-play; it defaults to **3**.

<Tip>
  The cap is not a hard ceiling. A recruiter can always grant one more go with `POST /job-interview-result-request-another-attempt` (`request_another_interview_attempt`) — a released attempt no longer counts against the cap.
</Tip>

The first example creates a **coaching persona**, the second the **interview-portal interview role-play**. The role fields are identical for either variant.

<CodeGroup>
  ```bash Coaching persona theme={null}
  curl -X POST https://cool.jobmojito.com/functions/v1/persona-create \
    -H "Authorization: Bearer $SUPABASE_JWT" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Objection-handling role-play",
      "interview_template_id": "TEMPLATE_UUID",
      "mojito_language_code": "en",
      "status": "active",
      "visibility": "merchant_invite",
      "persona_role_avatar": "is to act as a busy procurement manager who is skeptical about switching vendors",
      "persona_role_user": "is to be a sales rep trying to book a follow-up demo",
      "persona_avatar_who_is": "Dana Reyes, procurement lead at a mid-size manufacturer. Runs a tight ship and is proud of it. She wants to stop firefighting supplier problems, and she is quietly worried that switching vendors mid-year will be blamed on her if it goes wrong. She will not engage with a pitch until someone acknowledges how much of her week the current vendor already costs her. She does not know that migration is handled by the supplier, not her team.",
      "persona_avatar_knowledge": "Current contract renews in 3 months. Budget is frozen until Q1. Two missed deliveries in the last quarter, on 14 March and 2 May. Her director, Tom, signs anything over 40k. She will raise price first, then switching risk. She would only mention that she is covering a colleague on parental leave, and is stretched thin, once the conversation feels genuinely useful to her.",
      "persona_avatar_progress": "Mode: turning point\nInitial hold: will not discuss switching while the conversation is about price.\nUnlocks when: the rep asks about the missed deliveries or the cost of managing the current vendor, rather than answering the price objection.\nAfter unlock: openly discusses the renewal timeline and will agree to a follow-up with Tom.\nHard limits: never commits to a contract on this call, and never accepts a discount as the reason to switch.\nNo goalposts: once unlocked, stays unlocked — no new objection is invented.\nGates: does not mention covering for a colleague on parental leave until after unlock.",
      "persona_avatar_end_conditions": "End once progress is complete — the follow-up with Tom is agreed — or once the conversation has clearly broken down.",
      "opening_line": "Look, I have about two minutes. We are pretty happy with our current vendor, so what is it you actually want?",
      "candidate_expectations": "Should uncover the renewal timeline and handle the price objection."
    }'
  ```

  ```bash Interview role-play (interview portal) theme={null}
  curl -X POST https://cool.jobmojito.com/functions/v1/persona-create \
    -H "Authorization: Bearer $SUPABASE_JWT" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Objection-handling role-play",
      "portal": "interview",
      "interview_attempts": 3,
      "interview_template_id": "TEMPLATE_UUID",
      "mojito_language_code": "en",
      "status": "active",
      "visibility": "merchant_invite",
      "persona_role_avatar": "is to act as a busy procurement manager who is skeptical about switching vendors",
      "persona_role_user": "is to be a sales rep trying to book a follow-up demo",
      "persona_avatar_who_is": "Dana Reyes, procurement lead at a mid-size manufacturer. Runs a tight ship and is proud of it. She wants to stop firefighting supplier problems, and she is quietly worried that switching vendors mid-year will be blamed on her if it goes wrong. She will not engage with a pitch until someone acknowledges how much of her week the current vendor already costs her. She does not know that migration is handled by the supplier, not her team.",
      "persona_avatar_knowledge": "Current contract renews in 3 months. Budget is frozen until Q1. Two missed deliveries in the last quarter, on 14 March and 2 May. Her director, Tom, signs anything over 40k. She will raise price first, then switching risk. She would only mention that she is covering a colleague on parental leave, and is stretched thin, once the conversation feels genuinely useful to her.",
      "persona_avatar_progress": "Mode: turning point\nInitial hold: will not discuss switching while the conversation is about price.\nUnlocks when: the rep asks about the missed deliveries or the cost of managing the current vendor, rather than answering the price objection.\nAfter unlock: openly discusses the renewal timeline and will agree to a follow-up with Tom.\nHard limits: never commits to a contract on this call, and never accepts a discount as the reason to switch.\nNo goalposts: once unlocked, stays unlocked — no new objection is invented.\nGates: does not mention covering for a colleague on parental leave until after unlock.",
      "persona_avatar_end_conditions": "End once progress is complete — the follow-up with Tom is agreed — or once the conversation has clearly broken down.",
      "opening_line": "Look, I have about two minutes. We are pretty happy with our current vendor, so what is it you actually want?",
      "candidate_expectations": "Should uncover the renewal timeline and handle the price objection."
    }'
  ```
</CodeGroup>

Returns `interview_def_set_id` (and `embed_id` / `embed_signing_key` when `is_embedded` is true).

### Key fields

| Field | Required | Notes |
| - | - | - |
| `name` | ✓ | Persona / session name. **Plain UTF-8 text** — shown to the candidate as-is, so no Markdown or HTML. |
| `interview_template_id` | ✓ | Avatar template id from `list_avatars`. Its `type` sets the modality (voice vs avatar). |
| `mojito_language_code` | ✓ | Platform language code (e.g. `en`). |
| `status` | ✓ | `draft` or `active`. |
| `visibility` | ✓ | Who can access it, e.g. `merchant_invite`. |
| `description` | – | Short persona description shown to the candidate on the pre-session poster. **Candidate-visible — keep to max 2 sentences, plain UTF-8 text (no Markdown or HTML).** Never AI-generated (personas run no generation); omit it and the poster shows no description. |
| `persona_role_avatar` | ✓ | The role the **avatar** plays. **Candidate-visible** ("Role of the agent" on the poster) — keep to max 2 sentences. |
| `persona_role_user` | ✓ | The role the **candidate** plays. **Candidate-visible** ("Your role" on the poster) — keep to max 2 sentences. |
| `opening_line` | – | The avatar's **first spoken line** (the literal words it says to open the scene, in character). Omit and it defaults to a generic "Hello" — set it for anything other than a neutral greeting. |
| `persona_avatar_who_is` | – | Who the avatar represents: name, role, context and personality, woven together with what drives them underneath — motive, fear, what they refuse to move past until it is heard, and what they only learn when told. One continuous description of a person; **no labelled subsections**. |
| `persona_avatar_knowledge` | – | The private facts the avatar may use: numbers, dates, names, the objections it will raise. Personal details it should only share after the conversation has progressed go here too, with the timing written **inline** rather than as a labelled subsection. Those personal details must never be something the candidate has to extract in order to reach the goal. |
| `persona_avatar_progress` | – | **How the conversation is allowed to move forward.** Plain text whose first line is either `Mode: turning point` (a scene about resistance) or `Mode: steps` (a conversation run to a protocol), followed by that mode's labelled lines — one per line. See [Progression](#progression). Omit it and the avatar has no arc: it tends to either concede on the first polite question or never concede at all. |
| `persona_avatar_end_conditions` | – | When the avatar should end the session. Write it to **refer back to** `persona_avatar_progress` rather than restate it: wrap up once progress is complete (turning point unlocked and a plan accepted, or the `Done when` state reached), or once the conversation has clearly broken down. |
| `candidate_expectations` | – | What the person must achieve, and what a strong performance looks like (≤ 2100 chars). Technically optional, but this is the yardstick the session is scored against — it is passed to the grader as the goal of the session. Omit it and the scoring has nothing to measure the transcript against, so set it on anything you intend to assess. |
| `portal` | – | **Decide this first.** `interview` for anything that assesses or screens candidates (recruiter-visible, invited, merchant-billed, attempt-capped). `coaching` is the **default when omitted** and creates the coaching-portal practice persona instead — so pass `interview` explicitly for recruiting use cases. See [Two variants](#two-variants). |
| `interview_attempts` | – | Allowed candidate attempts (1-20), default `3` — the same field and behaviour as on `POST /job-interview-create`. **Only meaningful when `portal` is `interview`**; coaching personas are unlimited. |
| `welcome_message`, `thank_you_message` | – | Candidate-facing messages spoken before the role-play starts and shown after it ends — **set these; the defaults are generic.** `welcome_message` sets the scene; the avatar's first in-character line is `opening_line`, which follows it. |

Common optional fields also apply: `code`, `cover_image_url`, `interview_location`, `max_duration` (seconds, default `1200`), `recording`, `recording_full_session`, `result_view`, `interview_conversation_speed`, `candidate_video_introduction`, `tags`, `recruiter_profile_id`, `merchant_id`, and `is_embedded`.

On the **interview role-play** variant (`portal: "interview"`), `max_duration`, `recording`, and `recording_full_session` drive the merchant-credit cost exactly as they do on a standard interview.

### Progression

`persona_avatar_progress` is what stops a role-play from being either trivially easy or unwinnable. It is one plain-text value: the first line picks the mode, and the following lines are that mode's labels, one per line. **Use one mode, not both.**

Pick **turning point** for a scene built on resistance the candidate has to work through, and **steps** for a difficult conversation the candidate is expected to run to a protocol.

<CodeGroup>
  ```text Mode: turning point theme={null}
  Mode: turning point
  Initial hold: what the avatar will not budge on at the start.
  Unlocks when: the specific, observable thing the candidate must do.
  After unlock: how the avatar behaves once it happens.
  Hard limits: what it never agrees to, regardless.
  No goalposts: once unlocked it stays unlocked; no new obstacle is invented.
  Gates: which later personal details stay unsaid until after unlock.
  ```

  ```text Mode: steps theme={null}
  Mode: steps
  Framework: the protocol being assessed.
  Steps: the ordered steps the candidate is expected to take.
  Done when: the observable state that ends the scene.
  Hard limits: what the avatar never agrees to, regardless.
  Gates: which later personal details stay unsaid until progress is made.
  ```
</CodeGroup>

Two rules make the difference between a scenario that assesses something and one that does not:

* **`Unlocks when` / `Done when` must be observable.** "The rep asks about the missed deliveries" is something an assessor can point at in the transcript. "The rep builds rapport" is not, and the avatar will apply it inconsistently.
* **No moving goalposts.** Once the candidate has earned progress, the avatar must not invent a fresh obstacle. Without the `No goalposts` line, a role-play that is supposed to be winnable often is not.

The `Gates` line is the link back to `persona_avatar_knowledge`: anything listed there should be one of the later personal details you wrote in that field, and none of them should be required to reach the goal.

The same progression is also fed to the grader, so it scores whether the candidate actually did the thing that was meant to unlock progress — not whether the avatar happened to soften.

## Next steps

<CardGroup cols={2}>
  <Card title="Invite candidates" icon="user-plus" href="/cookbooks/invite-candidates">
    Turn your new interview into links or email invitations.
  </Card>

  <Card title="Review results" icon="clipboard-check" href="/cookbooks/review-results">
    Read transcripts, scores, and export reports once candidates finish.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.