KolWrite API for AI agents
How to let an AI agent (Claude Code, Codex, Cursor, or a script of your own) turn recordings into transcripts, SRT subtitles and word timings with the KolWrite platform API at https://api.kolwrite.com. A person creates a token once; the agent reads it from an environment variable and calls plain HTTPS. No SDK is needed.
This is the platform API with kol_ tokens. The credits API at https://app.kolwrite.com (header x-api-key) is a different API, described at https://console.kolwrite.com/llms.txt.
1. Give the agent a token
- API access is enabled per account; without it, creating or using a token is refused.
- A person creates the token in the KolWrite app (Settings → Account → API access → Create token) and picks the smallest set of scopes. The secret (
kol_...) is shown once. - Put it in an environment variable and keep it out of prompts, logs and git.
| Task | Scopes |
|---|---|
| Transcribe and export | files:write, jobs:write, jobs:read, transcripts:read |
| Also check the credit balance | add billing:read |
| OpenAI-compatible endpoints | ai:use (spends credits; not part of the app's Automation preset) |
| Create share links | add shares:write |
Paste this into AGENTS.md, CLAUDE.md or .cursorrules:
## KolWrite platform API
- Base URL https://api.kolwrite.com. The token is in the environment variable KOLWRITE_TOKEN: send it as "Authorization: Bearer $KOLWRITE_TOKEN" and never print it.
- Flow: POST /api/v1/media/upload-session, PUT the bytes to data.upload_url, POST /api/v1/media/complete, POST /api/v1/jobs, poll GET /api/v1/jobs/{job_id} until status is succeeded, then GET /api/v1/jobs/{job_id}/export?format=json
- Spec: https://kolwrite-docs.pages.dev/openapi.json. Guide: https://kolwrite-docs.pages.dev/agents.md
2. When to use which call
| You want | Call |
|---|---|
| A transcript of a file, with word timings and speakers | upload session, then POST /api/v1/jobs (type: transcription), then GET /api/v1/jobs/{job_id}/export?format=json |
| SRT or VTT subtitles | GET /api/v1/jobs/{job_id}/export?format=srt or format=vtt (Plus plan or higher) |
| Subtitles in another language | options.target_language on the job, then export |
| Plain text | export?format=text |
| Text for a short voice clip, no job | POST /api/v1/ai/openai/audio/transcriptions |
| Chat, responses, embeddings | /api/v1/ai/openai/* (OpenAI-compatible endpoints) |
| The saved, versioned transcript | GET /api/v1/jobs/{job_id}/artifacts |
| The credit balance | GET /api/v1/billing/balance |
| A public link to a transcript | POST /api/v1/shares |
| Which model profiles exist | GET /api/v1/models |
3. Transcribe a file in Python
import json, mimetypes, os, time
import requests
BASE = "https://api.kolwrite.com"
TOKEN = os.environ["KOLWRITE_TOKEN"]
def call(method, path, **kw):
headers = {"Authorization": f"Bearer {TOKEN}", **kw.pop("headers", {})}
r = requests.request(method, BASE + path, headers=headers, timeout=120, **kw)
r.raise_for_status()
return r
def transcribe(path, profile="oracle", language="auto"):
name, size = os.path.basename(path), os.path.getsize(path)
ctype = mimetypes.guess_type(name)[0] or "application/octet-stream"
session = call("POST", "/api/v1/media/upload-session",
json={"filename": name, "file_size_bytes": size,
"content_type": ctype}).json()["data"]
with open(path, "rb") as f:
requests.put(session["upload_url"], data=f, headers=session["headers"],
timeout=3600).raise_for_status()
media = call("POST", "/api/v1/media/complete",
json={"finalize_token": session["finalize_token"]}).json()["data"]
job = call("POST", "/api/v1/jobs",
headers={"Idempotency-Key": f"{name}-{size}-{profile}-{language}"},
json={"type": "transcription",
"input": {"type": "media_ref", "media_id": media["media_id"]},
"profile": profile,
"options": {"language": language,
"diarization": {"mode": "regular"}}}).json()["data"]
while True:
status = call("GET", f"/api/v1/jobs/{job['job_id']}").json()["data"]["status"]
if status in ("succeeded", "failed", "cancelled"):
break
time.sleep(5)
if status != "succeeded":
raise RuntimeError(f"job {job['job_id']} {status}")
export = call("GET", f"/api/v1/jobs/{job['job_id']}/export",
params={"format": "json"}).json()
return job["job_id"], json.loads(export["data"]) # data is a JSON string
job_id, transcript = transcribe("interview.wav")
for seg in transcript["segments"]:
print(f"{seg['start']:.2f}-{seg['end']:.2f} {seg.get('speaker', '')} {seg['text']}")
Large files are uploaded in parts: the session then returns data.upload_url as null and a URL for each part in data.multipart_part_urls (see the quickstart).
4. Use the timings
The JSON export has segments, each with start and end in seconds, text, a speaker when diarization ran, and words (text, start, end). Let the agent choose segments by index and read the times from the transcript; never let a model type timestamps.
import subprocess
keep = [s for s in transcript["segments"] if "pricing" in s["text"].lower()]
for i, seg in enumerate(keep):
subprocess.run(["ffmpeg", "-y", "-i", "talk.mp4", "-ss", str(seg["start"]),
"-to", str(seg["end"]), f"clip{i}.mp4"], check=True)
Subtitles come straight from the API. include_speakers=false leaves the speaker names out, and max_chars_per_entry sets the length of each subtitle entry:
curl -H "Authorization: Bearer $KOLWRITE_TOKEN" -o talk.srt \
"https://api.kolwrite.com/api/v1/jobs/JOB_ID/export?format=srt&include_speakers=false&max_chars_per_entry=42"
SRT and VTT downloads need a Plus plan or higher. Other plans get HTTP 403 with the code subtitle_export_requires_plus; JSON and text exports are not affected.
5. Rules for agents
- Send an
Idempotency-Keywhen creating a job, so a retry never starts a second one. - Retry
429,502,503and504with exponential backoff; on429wait forRetry-After. Do not retry401,403or validation errors. See errors. - Read
detail.code, not the message text. - A job reserves credits when it is created and settles when it finishes; check
GET /api/v1/billing/balancebefore a large batch. - The OpenAPI file is the contract: every operation lists its required scope in
x-required-scope.
Questions
How do I make SRT subtitles from a video with an AI agent?
Upload the file, create a transcription job, wait for succeeded, then call the export endpoint with format=srt. Subtitles need a Plus plan or higher.
Can Claude Code or Codex call the KolWrite API?
Yes. They call it over HTTPS with a personal access token from an environment variable. There is no SDK or MCP server to install.
How does an agent get word timestamps?
Export the job as JSON. Each segment carries words with start and end in seconds.