New

Now in Claude, ChatGPT, Cursor & more with our MCP server

Back to docs
API Reference

Sending Messages via API

Understand how messages flow between your application and Koji during an API-started interview.

Sending Messages via API

Once you have started an interview, your application needs to handle the back-and-forth conversation between the respondent and Koji's interviewer. This article explains how messages flow in API-started interviews and how to integrate the conversational experience into your own UI.


How Message Flow Works

An API-started interview follows a structured conversational pattern:

  1. You start the interview via POST /api/v1/interviews/start. The response includes an initial_message — this is the first thing the interviewer says to the respondent.
  2. The respondent replies. Your application collects the respondent's input (text or voice) and sends it to Koji.
  3. Koji processes the response and generates the next interviewer message, including follow-up questions that adapt to what the respondent said.
  4. The cycle repeats until the interview reaches a natural conclusion or you explicitly complete it.

This exchange happens through the interview session, with messages flowing through Koji's conversation engine.


Text-Based Message Flow

For text-mode interviews, messages are exchanged through the interview session endpoint:

POST /api/v1/interviews/:interview_id/messages

Headers

HeaderValueRequired
AuthorizationBearer your_api_keyYes
X-Session-Tokensession_token_from_startYes
Content-Typeapplication/jsonYes

Request Body

{
  "content": "The respondent's message text goes here.",
  "type": "respondent"
}

Response

{
  "message_id": "msg_abc123",
  "interviewer_response": {
    "content": "That's really interesting! Can you tell me more about why you felt that way?",
    "message_id": "msg_def456"
  },
  "interview_status": "active",
  "messages_count": 4
}

The response includes the interviewer's next message, which you display to the respondent. The interview_status field tells you whether the interview is still active or has reached a natural stopping point.


Voice-Based Message Flow

Voice interviews use a different mechanism. When you start a voice interview, the response includes voice_credentials with a WebSocket URL and authentication token.

Establishing the Voice Connection

const ws = new WebSocket(voice_credentials.server_url);

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: 'auth',
    token: voice_credentials.token
  }));
};

Once connected and authenticated, audio streams bidirectionally over the WebSocket. The voice service handles speech-to-text, processes the response through Koji's conversation engine, and streams synthesized speech back.

Voice Events

The WebSocket sends JSON events alongside audio data:

Event TypeDescription
transcriptReal-time transcription of the respondent's speech
interviewer_startThe interviewer has begun speaking
interviewer_endThe interviewer has finished speaking
interview_statusStatus update (active, completing, completed)
errorAn error occurred in the voice session

Handle these events to update your UI — for example, showing a transcript as the respondent speaks or displaying a visual indicator when the interviewer is responding.


Retrieving the Conversation

At any point during or after the interview, you can retrieve the full conversation:

GET /api/v1/interviews/:interview_id

Headers

HeaderValueRequired
AuthorizationBearer your_api_keyYes

The response includes the full transcript, each message with its sender, timestamp, and content. After the interview is completed, it also includes analysis results and quality scores. See Completing Interviews via API for more on the analysis payload.

Example Transcript Response

{
  "interview_id": "f8e7d6c5-b4a3-2109-8765-432109876543",
  "status": "active",
  "transcript": [
    {
      "role": "interviewer",
      "content": "Hi Jane! Thanks for taking the time to chat...",
      "timestamp": "2025-01-15T10:00:00Z"
    },
    {
      "role": "respondent",
      "content": "Happy to help! I've been using the product for about six months now...",
      "timestamp": "2025-01-15T10:00:45Z"
    }
  ],
  "messages_count": 2,
  "stats": {
    "duration_seconds": 45,
    "respondent_messages": 1,
    "interviewer_messages": 1
  }
}

Building Your Own Chat UI

When integrating the message flow into your application, here is a recommended approach:

  1. Display the initial_message from the start response as the first chat bubble.
  2. Collect respondent input via a text field or voice recording interface.
  3. Send the message to the messages endpoint.
  4. Display a loading state while waiting for the interviewer's response.
  5. Show the interviewer_response when it arrives.
  6. Check interview_status — if it indicates the interview is winding down, prepare to call the complete endpoint.

Keep the experience conversational. Avoid overwhelming respondents with too much UI chrome. The interview should feel like a natural conversation, not a form.


Message Content Guidelines

When sending respondent messages to the API:

  • Send the raw text. Do not pre-process, summarize, or modify what the respondent typed.
  • Preserve formatting. If the respondent uses line breaks, keep them.
  • Do not inject instructions. Sending hidden prompts or instructions alongside the respondent's message violates the terms of service and produces unreliable results.
  • Handle empty messages. Validate on your end that the message is not empty before sending.

Conversation Length

Koji's interview engine manages conversation length based on your interview guide configuration. The interviewer naturally wraps up the conversation when it has covered the topics defined in the guide. You can also end the interview at any time by calling the complete endpoint.

Typical interviews last 5 to 15 minutes for text and 8 to 20 minutes for voice, but this varies based on the guide complexity and respondent engagement.


Error Handling

Status CodeErrorMeaning
400invalid_messageMessage content is empty or malformed
401unauthorizedInvalid API key or session token
404not_foundInterview does not exist
409conflictInterview is already completed
429rate_limitedToo many messages in a short period

Implement retry logic with exponential backoff for transient errors like 429 and 5xx responses.


Next Steps