# A API do KolWrite para agentes de IA

Como deixar um agente de IA (Claude Code, Codex, Cursor ou um script seu)
transformar gravações em transcrições, legendas SRT e tempos por palavra com a
API da plataforma KolWrite em `https://api.kolwrite.com`. Uma pessoa cria um
token uma única vez; o agente o lê de uma variável de ambiente e chama HTTPS
puro. Não é preciso nenhum SDK.

Esta é a API da plataforma, com tokens `kol_`. A API de créditos em
`https://app.kolwrite.com` (cabeçalho `x-api-key`) é outra API, descrita (em inglês) em
https://console.kolwrite.com/llms.txt para quem a procura.

## 1. Dê um token ao agente

- O acesso à API é ativado por conta; sem ele, criar ou usar um token é recusado.
- Uma pessoa cria o token no app KolWrite (**Configurações → Conta → Acesso à
  API → Criar token**) e escolhe o menor conjunto de escopos. O segredo
  (`kol_...`) aparece uma única vez.
- Guarde-o em uma variável de ambiente, longe de prompts, logs e git.

| Tarefa | Escopos |
|---|---|
| Transcrever e exportar | `files:write`, `jobs:write`, `jobs:read`, `transcripts:read` |
| Consultar também o saldo de créditos | acrescente `billing:read` |
| Endpoints compatíveis com OpenAI | `ai:use` (gasta créditos; não faz parte da predefinição Automação do app) |
| Criar links de compartilhamento | acrescente `shares:write` |

Cole isto em `AGENTS.md`, `CLAUDE.md` ou `.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. Qual chamada usar

| Você quer | Chamada |
|---|---|
| A transcrição de um arquivo, com tempos por palavra e falantes | sessão de upload, depois `POST /api/v1/jobs` (`type: transcription`), depois `GET /api/v1/jobs/{job_id}/export?format=json` |
| Legendas SRT ou VTT | `GET /api/v1/jobs/{job_id}/export?format=srt` ou `format=vtt` (plano Plus ou superior) |
| Legendas em outro idioma | `options.target_language` na tarefa e depois exportar |
| Texto simples | `export?format=text` |
| O texto de um clipe de voz curto, sem tarefa | `POST /api/v1/ai/openai/audio/transcriptions` |
| Chat, respostas, embeddings | `/api/v1/ai/openai/*` ([endpoints compatíveis com OpenAI](/locales/pt/openai-compatible.md)) |
| A transcrição salva, com versões | `GET /api/v1/jobs/{job_id}/artifacts` |
| O saldo de créditos | `GET /api/v1/billing/balance` |
| Um link público para uma transcrição | `POST /api/v1/shares` |
| Quais perfis de modelo existem | `GET /api/v1/models` |

## 3. Transcrever um arquivo em Python

```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']}")
```

Arquivos grandes são enviados em partes: a sessão devolve então `data.upload_url`
como `null` e uma URL para cada parte em `data.multipart_part_urls` (veja o
[início rápido](/locales/pt/quickstart.md)).

## 4. Usar os tempos

A exportação JSON traz `segments`, cada um com `start` e `end` em segundos, `text`,
um `speaker` quando a separação de falantes foi executada e `words` (`text`, `start`,
`end`). Deixe o agente escolher segmentos pelo índice e ler os tempos na transcrição;
nunca deixe um modelo digitar marcas de tempo por conta própria.

```python
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)
```

As legendas vêm direto da API. `include_speakers=false` omite os nomes dos falantes
e `max_chars_per_entry` define o tamanho de cada entrada de legenda:

```bash
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"
```

Baixar SRT e VTT exige o plano Plus ou superior. Os outros planos recebem HTTP 403
com o código `subtitle_export_requires_plus`; as exportações em JSON e em texto não
são afetadas.

## 5. Regras para agentes

- Envie um `Idempotency-Key` ao criar uma tarefa, para que uma nova tentativa não inicie uma segunda.
- Tente de novo após `429`, `502`, `503` e `504` com espera exponencial; em `429`, aguarde
  `Retry-After`. Não tente de novo após `401`, `403` nem erros de validação. Veja
  [erros](/locales/pt/errors.md).
- Leia `detail.code`, não o texto da mensagem.
- Uma tarefa reserva créditos ao ser criada e acerta o valor ao terminar; consulte
  `GET /api/v1/billing/balance` antes de um lote grande.
- O arquivo OpenAPI é o contrato: cada operação indica o escopo exigido em
  `x-required-scope`.

## Perguntas

### Como faço legendas SRT de um vídeo com um agente de IA?

Envie o arquivo, crie uma tarefa de transcrição, espere `succeeded` e chame o endpoint
de exportação com `format=srt`. Legendas exigem o plano Plus ou superior.

### O Claude Code ou o Codex podem chamar a API do KolWrite?

Podem. Eles a chamam por HTTPS com um token de acesso pessoal lido de uma variável de ambiente.
Não há SDK nem servidor MCP para instalar.

### Como um agente obtém o carimbo de tempo de cada palavra?

Exporte a tarefa como JSON. Cada segmento traz `words` com `start` e `end` em segundos.
