# 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.

Last updated: 2026-10-08 · HTML: https://console.kolwrite.com/use-cases/developers

## The production flow

1. Your backend calls `POST /upload` for the user's file and returns the signed form to the client, or uploads it itself (up to 100 MiB).
2. Start the job with the returned `fileUrl` as `audio_url` and your `webhook_url`.
3. Verify the webhook signature, deduplicate on `X-KolWrite-Delivery-Id`, then fetch `GET /transcribe/{jobId}` if `result_omitted` is true.
4. Store `segments` (text, times, speakers) and render them; export `srt` or `txt` on demand.

Job request:

```json
{
  "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

```javascript
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 `PENDING` and `IN_PROGRESS`.
- `channel_speaker_separation` cannot 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.

## Related pages

- [AI agents & vibe coding](https://console.kolwrite.com/use-cases/ai-agents)
- [Call centers & sales calls](https://console.kolwrite.com/use-cases/call-centers)
- [Enterprises](https://console.kolwrite.com/use-cases/enterprise)
- [Supported languages](https://console.kolwrite.com/languages/)

---
Get an API key: https://console.kolwrite.com/auth · API documentation: https://console.kolwrite.com/public_api_docs · llms.txt: https://console.kolwrite.com/llms.txt
