---
title: "Begin a spoken interview with one of your end-users, or hand back one they have not finished with."
method: POST
path: "/v1/start_voice_interview"
tags: ["Voice Interviews"]
---

# Begin a spoken interview with one of your end-users, or hand back one they have not finished with.

`POST /v1/start_voice_interview`

## Request

- Base URL: `https://partner.mindupload.app`
- URL: `https://partner.mindupload.app/v1/start_voice_interview`
- Auth: API key in header `X-Partner-Key`

Starts an interview and returns its first question as both text and a playable audio URL. Pick the question set with `template_key`; the whole interview — every question and the final write-up — is produced in the language you send as `preferred_language`. If this user already has an unfinished interview on the same clone, it is resumed instead of restarted and `is_resumed` is true, so a person who closed your app halfway through picks up where they stopped. If instead they finished one recently and never asked for the write-up, that session is handed back: `is_complete` is true, there is no question, and `interview_session_id` is the one to pass to `generate_voice_interview_report`. Neither case counts against the daily limit, because neither starts an interview. Hold on to `interview_session_id`: every later call needs it.

## Request body

- object
  - `preferred_language` string, nullable: BCP-47-ish language code (e.g. 'en', 'zh-cn') for localized messages.
  - `username` string, nullable: The end-user's username.
  - `password` string, nullable: The end-user's password on first sign-in. After login/register, send the returned session token (JWT) here instead — the field accepts either, and reusing the token keeps raw passwords out of your storage.
  - `codename` string, nullable: Ask for a consciousness by name instead of writing up an interview: returns the third-person description of that consciousness, if one has been produced. Any authenticated caller may ask. Only the third-person form is available this way; the first- and second-person forms address the interviewee and are theirs alone.
  - `clone_id` string, nullable: AI consciousness id as returned by clone operations.
  - `template_key` 'mind_upload_first_time' | 'memory_refresh' | 'personality_refresh' | 'matchmaking' | 'guided_reflection', nullable: Which question set to run. `mind_upload_first_time` is a long first-run interview whose write-up is meant for a mind-data upload; `memory_refresh` and `personality_refresh` are short repeatable ones that ask something different each time; `matchmaking` surfaces what someone values; `guided_reflection` gives them somewhere to think out loud, and is not counseling.
  - `voice_id` 'female_warm' | 'female_gentle' | 'female_soft' | 'female_bright' | 'female_clear' | 'female_smooth' | 'female_easygoing' | 'female_breezy' | 'female_upbeat' | 'female_youthful' | 'female_firm' | 'female_forward' | 'female_mature' | 'male_friendly' | 'male_casual' | 'male_easygoing' | 'male_smooth' | 'male_clear' | 'male_even' | 'male_informative' | 'male_knowledgeable' | 'male_upbeat' | 'male_lively' | 'male_excitable' | 'male_firm' | 'male_breathy' | 'male_gravelly', nullable: Optional. Which voice the interviewer speaks in, one of the `voices` a start returns. Sending it keeps it for this consciousness, and an interview in progress answers with its current question spoken in it, so the voice is heard as it is chosen. Left out, the consciousness keeps the voice it has.
  - `interview_session_id` string, nullable: Optional. The interview a `voice_id` is for. With it, the call only switches that interview's voice while it is in progress, and is refused once it has ended rather than starting or resuming another.

## Response `200`

Result envelope — check the success field.

- object
  - `success` boolean: Whether the call succeeded.
  - `error_message` string, nullable: Human-readable reason when success is false.
  - `error_code` string
  - `interview_session_id` string, nullable: Identifies one interview. Returned by start_voice_interview and required by every later call.
  - `template_key` 'mind_upload_first_time' | 'memory_refresh' | 'personality_refresh' | 'matchmaking' | 'guided_reflection', nullable: Which question set to run.
  - `language` string, nullable
  - `turn_index` integer, nullable: Which question this call is about. Send back the one you were last given; it makes the call safe to retry.
  - `question_text` string, nullable: The question the user is being asked, in the interview language.
  - `question_audio_url` string, nullable: A time-limited URL for the spoken question. Play it to the user.
  - `question_audio_content_type` string, nullable: Media type of the question audio, so a player can be chosen without sniffing.
  - `progress` number, nullable: How far through the interview the user is, from 0 to 1.
  - `is_complete` boolean, nullable: True when there are no further questions and the write-up can be requested.
  - `is_resumed` boolean, nullable: True when this picked up an interview the user had already started rather than beginning a new one.
  - `status` 'in_progress' | 'safety_paused' | 'completed' | 'aborted' | 'halted_for_safety', nullable: Where the interview itself stands: `in_progress` while there are questions left, `completed` once the write-up can be asked for, `aborted` when the user ended it and the transcript was discarded, and `halted_for_safety` when an answer suggested the person needed help rather than another question -- on which show `safety_message` and stop asking. The new first interview instead uses `safety_paused`: its earlier confirmed answers are kept, the triggering answer is excluded, and questions remain paused.
  - `voice_id` 'female_warm' | 'female_gentle' | 'female_soft' | 'female_bright' | 'female_clear' | 'female_smooth' | 'female_easygoing' | 'female_breezy' | 'female_upbeat' | 'female_youthful' | 'female_firm' | 'female_forward' | 'female_mature' | 'male_friendly' | 'male_casual' | 'male_easygoing' | 'male_smooth' | 'male_clear' | 'male_even' | 'male_informative' | 'male_knowledgeable' | 'male_upbeat' | 'male_lively' | 'male_excitable' | 'male_firm' | 'male_breathy' | 'male_gravelly', nullable: The voice this consciousness's interviews speak in now.
  - `voices` object[], nullable: Every voice on offer, each with its `voice_id`, its `gender` (`female`, `male`) and one word for its `character`.
    - `voice_id` string, required
    - `gender` string, required
    - `character` string, required
  - `spoken_lines` object[], nullable: Present only on an interview made of spoken lines. Every line this reply says, in the order to play them, each with its `line_id` (null for a question written for this answer), its `line_role` (`anchor`, `chaser`, `statement`, `pivot`, `acknowledgement`, `backchannel`, `waiting`, `mirror`, `closing`, `follow_up`), its `text` in the interview language, and its `audio_url` and `audio_content_type`; a line with an empty `audio_url` is shown as text. The last line is the question, which `question_text` and `question_audio_url` carry too. Skip the first line when you have already played that `line_id`.
    - `line_id` string, nullable, required
    - `line_role` string, required
    - `text` string, required
    - `audio_url` string, required
    - `audio_content_type` string, required
  - `chapter_key` string, nullable: Present only on an interview made of spoken lines. Which chapter the question is in.
  - `chapter_title` string, nullable: Present only on an interview made of spoken lines. The chapter's title, in the interview language.
  - `chapter_index` integer, nullable: Present only on an interview made of spoken lines. Where the chapter comes, counting from 0; a new value means a new chapter has begun.
  - `chapter_count` integer, nullable: Present only on an interview made of spoken lines. How many chapters it has.
  - `minutes_left_estimate` integer, nullable: Present only on an interview made of spoken lines. Roughly how many minutes of answering are left, rounded up, from how long this user's answers have taken so far; 0 once it has ended.
  - `review_mode` 'auto' | 'confirm', nullable: Present only on an interview made of spoken lines. How the user accepts the transcript of their answer to the question: `auto` lets it through after a short pause unless they touch it, and `confirm` waits for them, as for a name or a date of birth, and for every answer once they have corrected one. Null when nothing is asked.
  - `learned_facts` object[], nullable: Present only on an interview made of spoken lines. The basic facts of their life the user has told it so far, each with its `slot`, its `display_value`, its `visibility` (`shared`, `general_only`, `owner_only`) and its `certainty` (`said`, `inferred`, `needs_confirmation`). This is the owner's review snapshot. Confirmed facts become consciousness knowledge; legacy visibility labels do not control what it may disclose.
    - `slot` string, required
    - `display_value` string, required
    - `visibility` string, required
    - `certainty` string, required
  - `ambient_clips` object[], nullable: Present only on an interview made of spoken lines. Lines to download now and play without another call when their moment comes, shaped as `spoken_lines` are: the answer to the user's first hello, the short sounds made as an answer ends and while a reply is slow, the lines said while a longer reply is being prepared and in its place should it not come, and the line said on finishing early. A line with an empty `audio_url` is shown as text.
    - `line_id` string, nullable, required
    - `line_role` string, required
    - `text` string, required
    - `audio_url` string, required
    - `audio_content_type` string, required
  - `answered_count` integer, nullable
  - `safety_paused` boolean, nullable
  - `safety_message` string, nullable: Present only when the interview stopped because an answer suggested the person needed help rather than another question. Show it, and do not continue the interview.

## Other responses

- `400`: Invalid request payload.
- `401`: Missing or invalid partner key.
- `403`: Partner is not authorized for this operation.
- `429`: Rate limit exceeded — retry later.
- `500`: Internal server error.

## Changes

- **2026-10-19** `0371bdf78533`: 1 warning, 12 info
  - added the new `safety_paused` enum value to the `status` response property for the response status `200`
  - added the optional property `ambient_clips` to the response with the `200` status
  - added the optional property `answered_count` to the response with the `200` status
  - added the optional property `chapter_count` to the response with the `200` status
  - …9 more

[Operation history](https://skmtc.dev/mindupload/apis/mind-upload-partner-api/changes/v1/start_voice_interview/post.md)

---

[API](https://skmtc.dev/mindupload/apis/mind-upload-partner-api.md) · [All operations](https://skmtc.dev/mindupload/apis/mind-upload-partner-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mindupload/apis/mind-upload-partner-api/revisions/0371bdf78533?raw)
