New

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

Back to docs
API Reference

Building with AI: the Koji API for LLMs and coding assistants

A complete, copy-paste starting point for building Koji integrations with an AI coding assistant, plus the MCP server for agentic workflows.

Building with AI: the Koji API for LLMs and coding assistants

This page is written to be handed straight to an AI coding assistant. Paste its URL (or the context block near the bottom) into Claude, Cursor, or any LLM tool, and it has everything needed to build a working Koji integration. Humans get a fast one-page overview here too, with links to the deeper guides.


Machine-readable resources

Point your assistant at these first. Each is complete and always current:

  • Full docs as plain text: https://www.koji.so/llms-full.txt (every doc article, including this whole API reference, in one file)
  • Docs index for LLMs: https://www.koji.so/llms.txt (summary index plus the MCP tool list)
  • Docs as JSON: https://www.koji.so/api/content (filter with ?type=docs, ?category=api-reference, or ?slug=<slug>)

Two ways to integrate

There are two supported paths. Pick by what you are building.

PathBest forAuth
MCP serverNatural-language and agentic workflows: let Claude, Cursor, or another MCP client run studies, read interviews, and generate reports conversationallyOAuth 2.1 (PKCE)
REST Headless APICode that runs interviews inside your own product or backendBearer pk_live_... API key

Path 1: the MCP server

Koji ships a Model Context Protocol server, so an AI assistant can manage your research by talking to it. Add this connector URL to your MCP client:

https://www.koji.so/api/mcp/mcp
  • Transport: Streamable HTTP (SSE is a deprecated fallback)
  • Auth: Auth is OAuth 2.1 with PKCE (S256). The client is public (no client secret). Discovery is automatic: clients read https://www.koji.so/.well-known/oauth-authorization-server and https://www.koji.so/.well-known/oauth-protected-resource. Dynamic Client Registration (RFC 7591) is supported, so most clients just need the connector URL and will walk you through sign-in. Tokens are Bearer (Authorization header). Access tokens last 1 hour; refresh tokens 30 days.
  • Scopes: read-only (read:studies, read:interviews, read:account).

Add the URL above as a custom MCP server and approve the sign-in. Then ask in plain language, for example "create a churn study and show me the interviews as they come in." Client-specific setup guides: Claude, Claude Code, Cursor, VS Code, Windsurf.

Available tools (15):

ToolWhat it does
koji_list_studiesList your studies
koji_get_studyGet a study's configuration and status
koji_get_interviewsList interviews for a study
koji_get_transcriptGet a single interview transcript
koji_get_accountGet your account, plan, and credit balance
koji_create_studyCreate a new study from a research goal
koji_update_briefIteratively edit a study's research brief
koji_publish_studyPublish a study so it starts collecting interviews
koji_get_study_dataGet aggregated study data and insights
koji_generate_reportGenerate an analysis report for a study
koji_get_reportGet a study's current report
koji_publish_reportPublish a report to a public link
koji_configure_studyConfigure branding, lead form, interaction modes, URL slug, and Open Graph in one call
koji_export_dataExport interviews and transcripts as CSV or JSON
koji_import_respondentsImport contacts with personalized interview URLs

Path 2: the REST Headless API in one page

Everything below is enough to build a full text or voice interview flow.

Base URL

https://www.koji.so/api/v1

Authentication

Send a study-scoped API key as a Bearer token. You never send a project id; the key identifies the study.

Authorization: Bearer pk_live_wJalrXUtnFEMI4K7MDENGbPxRfiCYz2K

Create a key from the study's Integrate page. See API Authentication for the full model.

Endpoints

EndpointPurpose
POST /interviews/startStart an interview
POST /interviews/{interview_id}/messageSend a message
POST /interviews/{interview_id}/completeComplete an interview
GET /interviews/{interview_id}Get interview results

Credentials per endpoint

EndpointAPI keySession tokenPermission
POST /interviews/startYesNointerview:start
POST /interviews/{interview_id}/messageYesYesinterview:chat
POST /interviews/{interview_id}/completeYesYesinterview:complete
GET /interviews/{interview_id}YesNointerview:read
GET /embed/{project_id}OptionalNoNone

Lifecycle

Start an interview, stream the AI's replies as the respondent answers, complete it, then read the analysis:

// Full interview lifecycle: start → message → complete → read results.
// Node 18+ (built-in fetch). Replace the key with your own.
const API_KEY = 'pk_live_wJalrXUtnFEMI4K7MDENGbPxRfiCYz2K'
const BASE = 'https://www.koji.so/api/v1'
const HEADERS = { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }

async function main() {
  // 1. Start
  const started = await fetch(`${BASE}/interviews/start`, {
    method: 'POST',
    headers: HEADERS,
    body: JSON.stringify({ respondent: { external_id: 'user_8271' }, mode: 'text' }),
  }).then(r => r.json())
  console.log('AI:', started.initial_message)

  const session = { 'X-Session-Token': started.session_token }

  // 2. One message exchange (SSE)
  const res = await fetch(`${BASE}/interviews/${started.interview_id}/message`, {
    method: 'POST',
    headers: { ...HEADERS, ...session },
    body: JSON.stringify({ content: 'I found onboarding confusing.' }),
  })
  let reply = ''
  const reader = res.body.getReader()
  const decoder = new TextDecoder()
  let buffer = ''
  while (true) {
    const { value, done } = await reader.read()
    if (done) break
    buffer += decoder.decode(value, { stream: true })
    const lines = buffer.split('\n')
    buffer = lines.pop()
    for (const line of lines) {
      if (!line.startsWith('data: ')) continue
      const frame = JSON.parse(line.slice(6))
      if (frame.type === 'chunk') reply += frame.content
    }
  }
  console.log('AI:', reply)

  // 3. Complete
  await fetch(`${BASE}/interviews/${started.interview_id}/complete`, {
    method: 'POST',
    headers: { ...HEADERS, ...session },
    body: JSON.stringify({ reason: 'natural' }),
  })

  // 4. Read results (analysis lands asynchronously)
  const result = await fetch(`${BASE}/interviews/${started.interview_id}`, {
    headers: HEADERS,
  }).then(r => r.json())
  console.log('Transcript messages:', result.stats.message_count)
}

main()

The message endpoint streams Server-Sent Events. For the frame-by-frame parser and every field, see Sending Messages via API. For the embed widget instead of code, see Embed Widget Reference.


Copy-paste context block

Give this to your AI assistant as a system or context message. It is intentionally compact and complete:

You are integrating the Koji Headless API (AI-moderated research interviews).
Base URL: https://www.koji.so/api/v1
Auth: header "Authorization: Bearer pk_live_<key>" on every request. No project id in bodies; the key identifies the study.
Endpoints:
- POST /interviews/start -> { interview_id, session_token, initial_message?, mode }. Body optional: { respondent: { external_id?, display_name?, metadata? }, mode: "text"|"voice", locale }.
- POST /interviews/{id}/message -> Server-Sent Events. Headers add "X-Session-Token: <session_token>". Body { content }. Frames: {"type":"chunk","content":...} then {"type":"done","message_id":...,"interview_complete":bool}.
- POST /interviews/{id}/complete -> { status, stats, analysis, analysis_pending }. Header "X-Session-Token: <session_token>". Body { reason? }.
- GET /interviews/{id} -> transcript, stats, respondent, analysis. API key only.
Errors are JSON { "error": string } (plus retry_after / message / reason on some). No code/details field.
Rate limit: 60/min per key, headers X-RateLimit-*.
Full spec: https://www.koji.so/llms-full.txt

Where to go next

REST Headless API:

MCP server:


FAQ

What is the single best link to give an AI assistant?

https://www.koji.so/llms-full.txt for the full text of every doc, or this page's URL for a focused API-only starting point. Both are always current.

Do I need the MCP server and the REST API?

No. Use the MCP server for conversational and agentic control of your research, or the REST API to build interviews into your own product. They are independent and you can use either alone.

Does the API need a paid plan?

The headless API is available on all plans. Usage is governed by interview credits: each interview consumes credits from the study owner's balance (text and voice at different rates), and the start endpoint rejects new interviews when the study is out of quota or the owner has no credits.

Related Articles

Airtable + Koji: Launch AI Interviews from a Base and Sync Every Insight Back to the Record

Turn any Airtable base into a customer research engine: trigger Koji AI-moderated interviews from a view or automation, then write themes, sentiment, quality scores, and a transcript link straight back onto the matching record. No native connector required — Airtable speaks Zapier, Make, and webhooks, and Koji speaks all three.

Amplitude + Koji: Trigger AI Interviews from Product Analytics and Pipe Insights Back as Events

How to close the loop between Amplitude product analytics and Koji AI interviews — fire interview links to users in specific Amplitude cohorts, and pipe interview themes, sentiment, and quality scores back into Amplitude as user properties and custom events.

API Authentication

How Koji Headless API authentication works: pk_live_ API keys, permissions, session tokens, and origin allowlists.

Attio + Koji: Launch AI Interviews from Your CRM and Sync Every Insight Back to the Record

Connect Attio to Koji to trigger AI customer interviews from CRM records and lists, then write themes, quotes, and structured scores back onto each contact and company automatically.

Calendly + Koji: Auto-Run AI Interviews After Every Demo, Call, and No-Show

Connect Calendly to Koji to trigger AI voice and text interviews from meeting events — post-demo, post-onboarding, and no-show follow-ups — and capture structured feedback automatically.

Completing Interviews via API

Finalize interviews with POST /interviews/{id}/complete, then poll the read endpoint for the asynchronous AI analysis.