Voice API and MCP

Connect your writing profile to an agent or your own application. The API uses the same saved documents, checks and allowance as Voice.

Create a key

Open Voice settings and create an API key. Give each integration its own key, choose the scopes it needs, and copy it when it appears. You can’t reveal it again; revoke it and create another if you lose it.

  • voice:read reads documents, writing samples and profiles.
  • voice:write changes profiles, samples and feedback, and deletes documents.
  • voice:generate writes or rewrites text and rebuilds a profile. These actions use your plan allowance.

Keys expire after 90 days by default. You can choose between 1 and 365 days, with up to 20 active keys on an account. Revoking a key prevents its next request; work already accepted may finish.

Connect an MCP client

Add a Streamable HTTP server at https://voice.unwrite.co/api/public/voice/mcp and set its Authorization header to Bearer YOUR_VOICE_KEY. Store the key in your client’s secret or environment settings. This connection uses API keys, so a client that only supports OAuth sign-in won’t work.

For Codex CLI, set VOICE_API_KEY in your environment, then run:

codex mcp add unwrite-voice \
  --url https://voice.unwrite.co/api/public/voice/mcp \
  --bearer-token-env-var VOICE_API_KEY

The server supports protocol versions 2025-03-26, 2025-06-18 and 2025-11-25. It’s stateless: each POST carries your key, and GET or DELETE on the MCP endpoint returns 405.

After initialisation, call tools/list for the exact argument schemas. resources/read with {"uri":"voice://openapi"} returns the full REST reference. The tools cover generation, document history and deletion, profiles and rebuilds, writing samples, and feedback.

Call the REST API

Send the same bearer header to https://voice.unwrite.co/api/public/voice. Start with GET /profile; if you haven’t set up a profile, create one in Voice or use POST /profile/rebuild with questionnaire answers.

curl 'https://voice.unwrite.co/api/public/voice/documents' \
  -H "Authorization: Bearer $VOICE_API_KEY"

The OpenAPI reference includes every path, method, scope and request schema. REST requests send the JSON body directly. MCP tool calls wrap that body in body, with resource id and optional cursor beside it.

Document, version and sample lists return nextCursor. Send it as the next request’s cursor until it’s null. Resource IDs always belong to the account that created the key; a missing or foreign resource returns 404.

A gold writing sample needs an owned versionId and text you’ve edited from that version’s generated output. Saving it also records the learning event, so don’t send a separate gold-feedback request. Repeating the same gold save won’t duplicate it.

Generation and retries

Check GET /usage or voice_get_usage before starting work. Both return your Voice plan, quota used and quota limit.

POST /generate streams SSE events: meta, token, retry, final and error. Save the document and version IDs from meta. Tokens are provisional; display or save the checked text from final. Discard provisional text on retry or error.

If the connection drops, send the original request with both documentId and versionId to resume the saved result. Don’t blindly start another generation: a fresh request may use another allowance. The MCP generation tool returns the checked result and its IDs, or an error; it never returns unchecked partial text.

Profile rebuilds and generation share Voice’s plan and concurrency limits. Adding a key doesn’t create another allowance. The API also allows 60 requests per minute across all keys on one account.

A separate burst limit allows 120 requests per minute from one IP address, including unauthenticated attempts. A short-lived keyed hash identifies that network counter; it doesn’t store your raw IP address.

Errors and limits

REST errors contain {"error":{"code":"…","message":"…"}}. A 400 means the request needs changing; 401 means the key is missing, expired, revoked or unknown; 403 means a scope or origin check failed. A 413 means the body is too large, and 429 means a rate, concurrency or plan limit applies. Follow Retry-After when present, and inspect allowance details before retrying.

A 503 means the service couldn’t complete the request. Read-only requests can retry; check for an existing result before repeating a write. MCP protocol errors use JSON-RPC error codes, while a tool failure returns isError: true with the same actionable error envelope.

The public request ceiling is 4 MiB; each operation also keeps its smaller app limit and published schema bounds. Keys stay out of URLs and logs. Browser origins must belong to the configured Unwrite origin list; server and CLI clients can omit Origin.