Build voice profiles from writing samples and generate messages that sound like you, not like AI.
Go from zero to drafting in your voice in three steps.
Sign up at tonos.fyi/register, then create a key at Settings. Your key starts with tnos_live_.
Submit at least 3 writing samples. More samples = higher confidence.
curl -X POST https://tonos.fyi/profile \
-H "Authorization: Bearer tnos_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"samples": [
{"text": "hey just checking in on that proposal, lmk if you need anything"},
{"text": "sounds good, i can hop on a call tomorrow if easier"},
{"text": "perfect, will send over the updated deck by end of day"}
],
"platform": "slack"
}'Use the profile ID from step 2 to generate messages in your voice.
curl -X POST "https://tonos.fyi/generate/draft?stream=false" \
-H "Authorization: Bearer tnos_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"profile_id": "PROFILE_ID_FROM_STEP_2",
"recipient": "Sarah",
"purpose": "Follow up on the partnership proposal",
"key_points": ["Check if she reviewed it", "Offer to hop on a call"]
}'All API requests require a Bearer token in the Authorization header.
Authorization: Bearer tnos_live_YOUR_KEYCreate an API key from the Settings page in the web UI, or use the POST /keys endpoint with an existing key. Keys use the tnos_live_ prefix. The full key is only shown once at creation -- store it securely.
MCP clients (Claude Code, Cursor) authenticate via OAuth and receive tnos_oauth_ tokens automatically. The OAuth flow is handled by the MCP client -- no manual setup needed.
429 with a RATE_LIMITED error.Each operation deducts credits based on the model tier used. Trial accounts start with 45 free credits.
| Operation | Haiku | Sonnet | Opus |
|---|---|---|---|
| Profile build | 35 | 100 | 175 |
| Draft / Rewrite | 1 | 3 | 5 |
| Plan | Credits / month | Models available | Default model |
|---|---|---|---|
| Trial | 45 | Haiku | Haiku |
| Starter ($9/mo) | 500 | Haiku, Sonnet | Haiku |
| Pro ($24/mo) | 1,500 | Haiku, Sonnet | Sonnet |
| Business ($49/mo) | 4,000 | Haiku, Sonnet, Opus | Sonnet |
Manage API keys for programmatic access.
/keysCreate a new API key. The plaintext key is only returned once.
| Field | Type | Description |
|---|---|---|
label required | string | Human-readable label (max 100 chars) |
curl -X POST https://tonos.fyi/keys \
-H "Authorization: Bearer tnos_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"label": "production-server"}'{
"id": "key_abc123",
"plaintext": "tnos_live_abc123def456...",
"prefix": "tnos_live_abc1"
}/keysList all API keys for your account. Keys are returned without the plaintext value.
curl https://tonos.fyi/keys \
-H "Authorization: Bearer tnos_live_YOUR_KEY"{
"keys": [
{
"id": "key_abc123",
"key_prefix": "tnos_live_abc1",
"label": "production-server",
"last_used_at": "2026-04-01T12:00:00Z",
"revoked": false,
"created_at": "2026-03-15T08:30:00Z"
}
]
}/keys/:idPermanently revoke an API key. The key stops working immediately.
curl -X DELETE https://tonos.fyi/keys/key_abc123 \
-H "Authorization: Bearer tnos_live_YOUR_KEY"{"revoked": true}/keys/:id/usageGet usage summary for a specific API key.
curl https://tonos.fyi/keys/key_abc123/usage \
-H "Authorization: Bearer tnos_live_YOUR_KEY"{
"total_calls": 142,
"endpoints": [
{"endpoint": "generation", "call_count": 98},
{"endpoint": "extraction:profile", "call_count": 44}
]
}Voice profiles are built from writing samples. More samples and more variety produce higher confidence.
/profileCreate a new voice profile from writing samples. Requires at least 3 samples.
| Field | Type | Description |
|---|---|---|
samples required | array | Array of writing samples (min 3). Each has text (required), platform, sample_date, context. |
platform | string | Default platform context for all samples (e.g., "slack", "email") |
curl -X POST https://tonos.fyi/profile \
-H "Authorization: Bearer tnos_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"samples": [
{"text": "hey just checking in, lmk if you need anything from my end"},
{"text": "sounds good, i can hop on a call tomorrow if easier"},
{"text": "perfect, will send over the updated deck by eod. no rush"}
]
}'{
"profile_id": "prof_abc123",
"voice": {
"formality": {"value": 2.1, "confidence": 0.85},
"directness": {"value": 8.6, "confidence": 0.90},
"warmth": {"value": 7.4, "confidence": 0.82},
"summary": "Casual, direct communicator who defaults to lowercase...",
...
},
"confidence": 0.78,
"confidence_label": "high",
"tokens_used": 1847
}/profile/:idRetrieve the active version of a voice profile.
curl https://tonos.fyi/profile/prof_abc123 \
-H "Authorization: Bearer tnos_live_YOUR_KEY"{
"profile_id": "prof_abc123",
"voice": { ... },
"confidence": 0.78,
"confidence_label": "high",
"version": 2,
"extracted_at": "2026-04-01T12:00:00Z"
}/profile/:idDelete a voice profile and all associated data. This is permanent.
curl -X DELETE https://tonos.fyi/profile/prof_abc123 \
-H "Authorization: Bearer tnos_live_YOUR_KEY"{"deleted": true}/profile/:id/samplesAdd new writing samples to an existing profile and re-extract. Requires at least 3 total samples.
| Field | Type | Description |
|---|---|---|
samples required | array | New samples to add (min 1). Each has text (required), platform, sample_date, context. |
platform | string | Default platform context for new samples |
curl -X POST https://tonos.fyi/profile/prof_abc123/samples \
-H "Authorization: Bearer tnos_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"samples": [
{"text": "thanks for the quick turnaround on this, really appreciate it"},
{"text": "just a heads up, i might be a few minutes late to the standup"}
]
}'{
"profile_id": "prof_abc123",
"voice": { ... },
"confidence": 0.84,
"confidence_label": "high",
"tokens_used": 2105
}Draft new messages or rewrite existing text in your voice. Both endpoints support SSE streaming (default) or non-streaming mode.
data: events, followed by an event: metadata event with profile_id and tokens_used, then data: [DONE]. To get a single JSON response instead, add ?stream=false./generate/draftGenerate a new message in the voice of the specified profile.
| Field | Type | Description |
|---|---|---|
profile_id required | string | Voice profile ID |
recipient required | string | Who the message is for (max 500 chars) |
purpose required | string | What the message should accomplish (max 5,000 chars) |
key_points required | string[] | Key points to include (1-10 items, each max 5,000 chars) |
platform | string | Platform voice mode: imessage, slack, linkedin, email, etc. |
curl -X POST "https://tonos.fyi/generate/draft?stream=false" \
-H "Authorization: Bearer tnos_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"profile_id": "prof_abc123",
"recipient": "Sarah",
"purpose": "Follow up on partnership proposal",
"key_points": [
"Check if she reviewed the proposal",
"Offer to hop on a call to discuss"
],
"platform": "email"
}'{
"text": "hey sarah, wanted to circle back on the proposal i sent over last week. no pressure at all, just wondering if you've had a chance to look through it. happy to jump on a quick call if that's easier. let me know",
"profile_id": "prof_abc123",
"tokens_used": 312
}/generate/rewriteRewrite source text to match the voice of the specified profile. Preserves meaning, transforms style.
| Field | Type | Description |
|---|---|---|
profile_id required | string | Voice profile ID |
source_text required | string | Text to rewrite (max 10,000 chars) |
platform | string | Platform voice mode: imessage, slack, linkedin, email, etc. |
curl -X POST "https://tonos.fyi/generate/rewrite?stream=false" \
-H "Authorization: Bearer tnos_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"profile_id": "prof_abc123",
"source_text": "Dear Sarah, I hope this email finds you well. I wanted to follow up regarding our previous conversation about the partnership opportunity. Please let me know your thoughts at your earliest convenience.",
"platform": "email"
}'{
"text": "hey sarah, just circling back on the partnership thing we talked about. would love to hear what you're thinking whenever you get a chance. no rush",
"profile_id": "prof_abc123",
"tokens_used": 287
}Tonos exposes all tools via the Model Context Protocol (MCP) over Streamable HTTP at /mcp. Connect from any MCP-compatible client.
claude mcp add tonos \
--transport http \
--url https://tonos.fyi/mcpClaude Code handles OAuth automatically. On first use, it opens a browser for login, then caches the token.
Add to your .cursor/mcp.json:
{
"mcpServers": {
"tonos": {
"url": "https://tonos.fyi/mcp"
}
}
}| Tool | Description | Credits |
|---|---|---|
tonos_submit_samples | Store writing samples for extraction | Free |
tonos_generate_profile | Extract voice profile from stored samples | 35-175 |
tonos_clear_samples | Delete all stored samples | Free |
tonos_get_profile | Get current voice profile | Free |
tonos_export_profile | Export profile as JSON or Markdown | Free |
tonos_delete_profile | Permanently delete profile and all data | Free |
tonos_draft_message | Generate a new message in your voice | 1-5 |
tonos_rewrite_text | Rewrite text to match your voice | 1-5 |
tonos_submit_feedback | Rate or correct a generated draft | Free |
All errors return a consistent JSON structure with code, message, and optional hint.
{
"code": "VALIDATION_ERROR",
"message": "At least 3 samples are required",
"hint": "Provide an array of at least 3 objects, each with a non-empty \"text\" field"
}| Code | HTTP | Meaning |
|---|---|---|
AUTH_REQUIRED | 401 | No Authorization header provided |
AUTH_INVALID | 401 | API key is invalid, revoked, or expired |
AUTH_FAILED | 401 | Authentication check failed |
RATE_LIMITED | 429 | Too many requests (IP or key limit) |
CREDITS_INSUFFICIENT | 402 | Not enough credits for this operation |
MODEL_NOT_AVAILABLE | 403 | Requested model not available on your plan |
VALIDATION_ERROR | 400 | Invalid request body or parameters |
NOT_FOUND | 404 | Resource does not exist or is not owned by your account |
EXTRACTION_ERROR | 502 | Voice extraction failed (upstream LLM error) |
GENERATION_ERROR | 502 | Text generation failed (upstream LLM error) |
INTERNAL_ERROR | 500 | Unexpected server error |
| Environment | URL |
|---|---|
| Production | https://tonos.fyi |
| Local dev | http://localhost:3003 |