Transcription for apps and SaaS products
Add voice notes, meeting transcripts, captions or searchable audio to your product with an asynchronous REST API: upload, start a job, take the webhook, store the segments.
The production flow
- Your backend calls
POST /uploadfor the user's file and returns the signed form to the client, or uploads it itself (up to 100 MiB). - Start the job with the returned
fileUrlasaudio_urland yourwebhook_url. - Verify the webhook signature, deduplicate on
X-KolWrite-Delivery-Id, then fetchGET /transcribe/{jobId}ifresult_omittedis true. - Store
segments(text, times, speakers) and render them; exportsrtortxton demand.
Job request:
{
"audio_url": "<fileUrl from POST /upload>",
"model": "oracle",
"language": "auto",
"include_diarization": true,
"output_format": "json",
"webhook_url": "https://api.example.com/hooks/kolwrite",
"webhook_secret": "<random secret>"
}
Verify webhooks
import crypto from "node:crypto";
// Express-style handler; rawBody is the exact request body as received.
export function verifyKolWrite(req, rawBody, secret) {
const timestamp = req.headers["x-kolwrite-timestamp"];
const expected = "sha256=" + crypto.createHmac("sha256", secret)
.update(timestamp + "." + rawBody).digest("hex");
const given = req.headers["x-kolwrite-signature"] || "";
return given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}
Deliveries are at least once and retried on failure. Keep GET /transcribe/{jobId} as the source of truth.
Pick the model per job
| Product need | Setting | Why |
|---|---|---|
| Cheap, clean audio at volume | model: "base" | 0.40 credits (US$0.48) per hour |
| Default for user uploads | model: "oracle" | Production quality, translation available |
| Transcript in another language | Oracle model + translate_to | One job, translated text |
| Who said what | include_diarization: true | Included in the price |
| Captions | output_format: "srt" | Ready for video players |
Limits to design around
- Inline Base64 upload: 4 MiB of decoded media. Signed upload: 100 MiB.
- The signed upload form expires after 60 minutes; the read URL after 24 hours.
- Jobs are asynchronous: show progress from
PENDINGandIN_PROGRESS. channel_speaker_separationcannot be combined with diarization.
Questions and answers
Is there an SDK?
The API is plain HTTPS and JSON; use any HTTP client. The OpenAPI 3.1 file at /openapi.json works with code generators.
How do I avoid double-processing webhooks?
Deduplicate on X-KolWrite-Delivery-Id; retries reuse it.
How is usage billed?
Per second of audio at the model's credit rate, with no minimum. 1 credit is US$1.20.