connect hold your voice to your automations, ai agents, n8n workflows, and ci pipelines. pipe any document through voice analysis without opening a browser tab.
api keys live in the app under settings → api & mcp. you get up to 10 active keys per account — name them by integration so you can revoke individual ones without breaking everything else.
all keys start with hyv_live_ followed by 48 lowercase hex characters. never commit keys to git — use environment variables.
the full key is shown once at creation time. copy it immediately — we only store the hash. if you lose it, delete and recreate.
Authorization: Bearer hyv_live_<your-key>
curl https://holdyourvoice.com/v1/usage \ -H "Authorization: Bearer hyv_live_<your-key>"
base url: https://holdyourvoice.com — all v1 endpoints require the authorization header above. responses are json. errors return {"error": "...", "error_code": "..."} with the appropriate http status code.
| endpoint | what it does | quota? |
|---|---|---|
| POST /v1/analyze | analyze text against a voice profile — returns score, highlights, per-signal breakdown, and a cost block | unmetered |
| POST /v1/rewrite | rewrite a flagged passage to fix a specific drift signal | unmetered |
| GET /v1/profiles | list voice profiles you can access (own + team-shared) | free |
| GET /v1/usage | current month quota, limits, and per-brand 30-day token breakdown | free |
| GET /v1/context | slash-command context: plan, byok billing, profiles, recent documents, and team/byok user rollups | free |
| GET /v1/documents | list documents (paginated by limit and offset) | free |
| POST /v1/documents | create a new document | 1 slot |
| GET /v1/documents/:id | fetch a single document by id | free |
| PUT /v1/documents/:id | update title or content of an existing document (a substantial content swap charges 1 slot — see abuse guard below) | free* |
every endpoint that takes a voice profile accepts any of these identifier forms on a brand, profile, or profile_id field — matched in this precedence:
| form | example | matching |
|---|---|---|
| uuid | "550e8400-e29b-41d4-a716-446655440000" | exact — canonical id |
| slug | "acme-co" | exact, lowercase kebab-case |
| name | "Acme Co" | exact, case-insensitive, unicode-normalised |
| business | "Acme Corporation" | exact, case-insensitive |
| partial | "acme" | substring match (3+ chars) on name or business |
if a fuzzy query matches more than one profile, the api returns 400 with the candidate list so your tool can disambiguate instead of guessing.
{
"error": "Profile query 'Growth' is ambiguous — matched 2 profiles...",
"error_code": "profile_ambiguous",
"query": "Growth",
"candidates": [
{ "id": "...", "name": "Growth Inc", "business": "Growth Inc", "slug": "growth-a" },
{ "id": "...", "name": "Growth Labs", "business": "Growth Labs", "slug": "growth-b" }
]
}
pass any text and optionally a brand. get back a drift score, flagged highlights, remaining quota, and a cost block so you can see whether the call was billed to our platform or your own provider key (byok).
curl -X POST https://holdyourvoice.com/v1/analyze \
-H "Authorization: Bearer hyv_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"text": "our platform leverages best-in-class ai to deliver robust synergies",
"brand": "Acme Co"
}'
{
"highlights": [
{ "text": "leverages best-in-class ai", "signal": "corporate_filler", "severity": "high", "suggestion": "try: uses ai" },
{ "text": "robust synergies", "signal": "buzzword_stacking", "severity": "high", "suggestion": "try: real results" }
],
"signal_score_delta": { "corporate_filler": -28, "buzzword_stacking": -30 },
"trust_tools_present": [],
"trust_tools_missing": [ "specific_numbers", "attributed_quote" ],
"profile_id": "550e8400-e29b-41d4-a716-446655440000",
"remaining": { "documents": 47, "rewrites": null },
"cost": {
"paid_by": "hyv_platform",
"model": "claude-haiku-4-5-20251001",
"input_tokens": 420,
"output_tokens": 180
}
}
takes the original sentence and a specific drift signal. returns a rewrite that fixes that signal while preserving meaning. uses the same brand-resolution rules.
curl -X POST https://holdyourvoice.com/v1/rewrite \
-H "Authorization: Bearer hyv_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"text": "leverages best-in-class ai to deliver robust synergies",
"issue": "corporate_filler",
"brand": "acme-co"
}'
{
"rewritten": "uses ai to get real results",
"original": "leverages best-in-class ai to deliver robust synergies",
"profile_id": "550e8400-e29b-41d4-a716-446655440000",
"remaining": { "documents": 47, "rewrites": null },
"cost": {
"paid_by": "hyv_platform",
"model": "claude-haiku-4-5-20251001",
"input_tokens": 180,
"output_tokens": 95
}
}
returns every profile you can access — your own and any shared with your team. each row carries an ownership marker so your tool can distinguish them.
curl https://holdyourvoice.com/v1/profiles \ -H "Authorization: Bearer hyv_live_<your-key>"
{
"profiles": [
{
"id": "550e8400-...",
"name": "Acme Co",
"business": "Acme Corporation",
"slug": "acme-co",
"industry": "saas",
"voice_keywords": [ "direct", "specific", "first-person" ],
"voice_descriptions": { "direct": "...", "specific": "..." },
"is_default": true,
"ownership": "own",
"team_id": null,
"created_at": "2026-02-10T12:00:00Z",
"updated_at": "2026-04-18T09:14:22Z"
},
{
"id": "6ba7b810-...",
"name": "Shared Brand",
"business": "Shared Brand LLC",
"slug": "shared-brand",
"ownership": "team",
"team_id": "team-acme-0000",
"is_default": false
/* ... */
}
]
}
the by_brand_30d array lets agencies managing many brands see which one is consuming what. null-profile rows land in the unattributed bucket for legacy calls that predate brand tagging.
{
"plan": "team",
"plan_active": true,
"is_byok": false,
"token_billing": "hyv_platform",
"period": "2026-04",
"documents_used": 12,
"rewrites_used": 0,
"remaining": { "documents": 288, "rewrites": null },
"limits": {
"documents": 300,
"rewrites": null,
"rpm": 60
},
"quota_model": {
"document_create": "counted_monthly",
"analyze": "unmetered",
"rewrite": "unmetered_monthly_legacy"
},
"by_brand_30d": [
{ "profile_id": "550e...", "name": "Acme Co", "slug": "acme-co", "scope": "own", "analyses": 47, "rewrites": 18, "input_tokens": 32100, "output_tokens": 9200 },
{ "profile_id": "6ba7...", "name": "Shared Brand", "slug": "shared-brand", "scope": "team", "analyses": 22, "rewrites": 5, "input_tokens": 14500, "output_tokens": 3400 },
{ "profile_id": null, "name": null, "slug": null, "scope": "unattributed", "analyses": 3, "rewrites": 0, "input_tokens": 1900, "output_tokens": 400 }
]
}
documents created via the api appear in your hold your voice editor. create uses 1 monthly slot. updates are free unless the new content is substantially different from the last counted version — see the abuse guard note below.
curl -X POST https://holdyourvoice.com/v1/documents \
-H "Authorization: Bearer hyv_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{
"title": "q2 product announcement",
"content": "your draft text here...",
"brand": "Acme Co"
}'
curl -X PUT https://holdyourvoice.com/v1/documents/doc_abc123 \
-H "Authorization: Bearer hyv_live_<your-key>" \
-H "Content-Type: application/json" \
-d '{ "content": "updated draft..." }'
| status | error_code | meaning |
|---|---|---|
| 401 | — | missing or invalid api key |
| 402 | — | plan inactive (none / expired) — renew to resume api access |
| 403 | — | scope denied (e.g. key without rewrite scope calling /v1/rewrite) |
| 400 | profile_ambiguous | brand query matched more than one profile — see candidates array |
| 404 | profile_not_found | the brand query didn't resolve in your workspace |
| 429 | — | rpm exceeded (60 solo/team, 120 byok) or monthly document quota exhausted |
| 429 | submission_quota_exceeded | a put hit the abuse guard and you have no remaining document slots — upgrade plan or edit existing doc |
| 500 | — | server error — retry with exponential backoff |
v1 onboarding is intentionally simple: upgrade to a paid plan, create a scoped key under settings → api & mcp, then paste the prompt below into codex, cursor, or another local agent. the agent installs the mcp server and verifies the connection for you.
set up hold your voice mcp in this local agent environment. context: - hold your voice api and mcp access require an active paid plan. this key was created from a paid account. - the public setup contract is https://holdyourvoice.com/.well-known/agent-onboarding - the mcp server card is https://holdyourvoice.com/.well-known/mcp/server-card.json requirements: 1. use this api key as HYV_API_KEY: <paste-your-api-key-here> 2. do not print the key back to me, commit it, or store it in a shared project file. 3. download https://holdyourvoice.com/mcp_server.py or reuse an existing local copy if it is already present. 4. configure an mcp server named hyv with command "python3" and args pointing at the local mcp_server.py file. 5. set HYV_BASE_URL to https://holdyourvoice.com unless i explicitly gave you another endpoint. 6. install /hyv-status from https://holdyourvoice.com/.well-known/agent-commands/hyv-status.md if your agent supports custom slash commands. 7. verify the setup by calling hyv_get_context, hyv_get_usage, and hyv_list_profiles. 8. after verification, use /hyv-status for account context, use hyv_analyze before hyv_rewrite, use brand names or slugs when a brand is mentioned, and ask before overwriting documents. expected result: - tell me where you placed the mcp server file. - tell me which mcp config you updated. - confirm the usage and profile tools worked. - do not include the raw api key in your final response.
the mcp server lets any mcp-compatible ai assistant call hold your voice tools directly — mid-conversation, no copy-paste. same quota rules as the rest api.
curl -O https://holdyourvoice.com/mcp_server.py
{
"mcpServers": {
"hyv": {
"command": "python3",
"args": ["/path/to/mcp_server.py"],
"env": {
"HYV_API_KEY": "hyv_live_..."
}
}
}
}
replace /path/to/mcp_server.py with the actual path where you downloaded the file. restart claude desktop after saving.
| tool name | what it does |
|---|---|
| hyv_analyze | analyze text against a voice profile. takes brand (name/slug/uuid) |
| hyv_rewrite | rewrite a flagged passage to fix a specific drift signal |
| hyv_resolve_brand | preview which profile a brand query resolves to. returns {matched, via, candidates} — via is one of id / slug / name / business / partial / ambiguous_name / ambiguous_business / ambiguous_partial / not_found |
| hyv_list_profiles | list all voice profiles the key can access — own + team-shared, each marked with ownership |
| hyv_get_usage | quota, limits, and per-brand 30-day token breakdown |
| hyv_get_context | one-call context for /hyv-status: plan, byok billing, profiles, recent documents, per-profile counts, and team/byok per-user rollups |
| hyv_list_documents | list documents (paginated) |
| hyv_get_document | fetch a single document by id |
| hyv_create_document | create a new document. takes brand to tie the doc to a voice (uses 1 quota slot) |
| hyv_update_document | update title or content of an existing document |
agents that support custom commands can install the predefined context command:
/hyv-status source: https://holdyourvoice.com/.well-known/agent-commands/hyv-status.md what it does: - calls hyv_get_context - shows plan, byok billing, quota, profiles, and recent documents - shows per-user rollups for team and byok accounts - keeps raw api keys and secrets out of the response
once the mcp server is wired up, claude can run voice checks mid-conversation:
user: analyze this draft against our acme voice and fix anything off-brand
claude: → hyv_resolve_brand(brand="acme")
← { "matched": { "name": "Acme Co", "slug": "acme-co", "id": "550e..." }, "via": "partial" }
claude: → hyv_analyze(text="...", brand="acme-co")
← { "highlights": [...], "cost": { "paid_by": "hyv_platform", "input_tokens": 420, ... } }
claude: → hyv_rewrite(text="leverages best-in-class ai", issue="corporate_filler", brand="acme-co")
← { "rewritten": "uses ai", "cost": { ... } }
claude: here's your draft with 3 fixes for the acme voice: ...
what a complete agent loop looks like — draft, check, fix, ship:
# your agent in python, n8n, or any tool that supports http
import requests
key = "hyv_live_..."
headers = {"Authorization": f"Bearer {key}"}
base = "https://holdyourvoice.com"
# 1. agent writes a draft
draft = llm.generate(brief)
# 2. create the document, tagged with the right brand
doc = requests.post(f"{base}/v1/documents",
headers=headers,
json={"title": "q2 announcement", "content": draft, "brand": "Acme Co"}
).json()
# 3. analyze against the same brand
result = requests.post(f"{base}/v1/analyze",
headers=headers,
json={"text": draft, "brand": "Acme Co"}
).json()
print(f"tokens used: {result['cost']['input_tokens']} in / "
f"{result['cost']['output_tokens']} out — paid by {result['cost']['paid_by']}")
# 4. rewrite any high-severity flags against the same brand
for flag in result["highlights"]:
if flag["severity"] == "high":
fix = requests.post(f"{base}/v1/rewrite",
headers=headers,
json={"text": flag["text"], "issue": flag["signal"], "brand": "Acme Co"}
).json()
draft = draft.replace(flag["text"], fix["rewritten"])
# 5. update document with clean version
requests.put(f"{base}/v1/documents/{doc['id']}",
headers=headers,
json={"content": draft}
)
# 6. check per-brand spend at end of run
usage = requests.get(f"{base}/v1/usage", headers=headers).json()
for brand in usage["by_brand_30d"]:
print(f"{brand['name'] or 'unattributed'}: "
f"{brand['analyses']} analyses + {brand['rewrites']} rewrites "
f"({brand['input_tokens']}+{brand['output_tokens']} tokens)")
the api shares your monthly quota with the web app. documents is the only meter that caps your plan. analyses and rewrites done against a document are included in that document's slot.
every document created — via the web app or the api — counts against your monthly quota. solo 50, team 300, byok unlimited. resets on the 1st of each month (utc).
unmetered on every active plan. running POST /v1/analyze or POST /v1/rewrite on a document you've already created doesn't consume a second slot. a call against 5 different brands on the same draft = zero extra documents used.
60 req/min on solo and team. 120 req/min on byok. per-account across all your keys combined. exceeding returns 429 with a retry_after value — the window resets every 60 seconds.
on team plans, the 300-doc/month quota is shared across all members. api keys issued by any teammate draw from the same pool. voice profiles marked with team_id are visible to every team member — no need to re-create them per seat.
on solo / team / byok, analyze + rewrite calls route through our opencode zen platform account. on byok with byok enabled, your own provider key is used and you pay that provider directly — our token_usage table still logs the call for your reference but no charge routes through us. every response carries a cost.paid_by field so your automation knows which.
bring-your-own-key is exclusive to byok. solo and team users can't enable byok — the plan price includes typical platform ai usage. for high-volume workloads, upgrade to byok and configure byok under settings → api & mcp.
solo: 1 voice profile. team: 4 profiles shared across all team members (each doc can be tied to one). byok: unlimited. team-shared profiles carry a team_id so teammates see and can analyse against them without cloning.
hold your voice publishes standard discovery documents so ai agents and tooling can find and configure the api automatically.
| url | standard |
|---|---|
| /.well-known/api-catalog | ietf api catalog (rfc draft) |
| /.well-known/agent-onboarding | copy-prompt setup contract for paid users connecting local agents |
| /.well-known/agent-commands/hyv-status.md | predefined slash-command prompt for account context |
| /.well-known/mcp/server-card.json | mcp server card |
| /.well-known/oauth-protected-resource | rfc 9728 — protected resource metadata |
| /.well-known/oauth-authorization-server | rfc 8414 — authorization server metadata |
| /.well-known/agent-skills/index.json | agentskills.io skills index |
start a solo plan, generate an api key, and connect your first workflow. voice analysis and rewrites are unlimited — you only count document creation against your quota.
start free trial →