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:
- You start the interview via
POST /api/v1/interviews/start. The response includes aninitial_message— this is the first thing the interviewer says to the respondent. - The respondent replies. Your application collects the respondent's input (text or voice) and sends it to Koji.
- Koji processes the response and generates the next interviewer message, including follow-up questions that adapt to what the respondent said.
- 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
| Header | Value | Required |
|---|---|---|
Authorization | Bearer your_api_key | Yes |
X-Session-Token | session_token_from_start | Yes |
Content-Type | application/json | Yes |
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 Type | Description |
|---|---|
transcript | Real-time transcription of the respondent's speech |
interviewer_start | The interviewer has begun speaking |
interviewer_end | The interviewer has finished speaking |
interview_status | Status update (active, completing, completed) |
error | An 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
| Header | Value | Required |
|---|---|---|
Authorization | Bearer your_api_key | Yes |
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:
- Display the initial_message from the start response as the first chat bubble.
- Collect respondent input via a text field or voice recording interface.
- Send the message to the messages endpoint.
- Display a loading state while waiting for the interviewer's response.
- Show the interviewer_response when it arrives.
- 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 Code | Error | Meaning |
|---|---|---|
| 400 | invalid_message | Message content is empty or malformed |
| 401 | unauthorized | Invalid API key or session token |
| 404 | not_found | Interview does not exist |
| 409 | conflict | Interview is already completed |
| 429 | rate_limited | Too many messages in a short period |
Implement retry logic with exponential backoff for transient errors like 429 and 5xx responses.
Next Steps
Related Articles
Starting Interviews via API
Use the POST /start endpoint to programmatically launch interviews from your application.
Completing Interviews via API
Use the POST /complete endpoint to finish an interview session and trigger automatic analysis.
API Authentication
Learn how to authenticate with the Koji API using API keys and Bearer tokens.
Webhook Setup
Receive real-time notifications when interviews complete and analysis finishes using webhooks.
Embed Widget Reference
Technical reference for the Koji embed widget including iframe parameters and PostMessage API.
Rate Limits and CORS
Understand how Koji's API rate limiting works and how to configure CORS origins for your integration.