now in beta

api & mcp server

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.

rest api mcp server n8n · claude desktop · zapier · cursor

generate your api key

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.

key format

all keys start with hyv_live_ followed by 48 lowercase hex characters. never commit keys to git — use environment variables.

one-time reveal

the full key is shown once at creation time. copy it immediately — we only store the hash. if you lose it, delete and recreate.

authenticate every request
Authorization: Bearer hyv_live_<your-key>
quick test
curl https://holdyourvoice.com/v1/usage \
  -H "Authorization: Bearer hyv_live_<your-key>"
the api is available on individual and multiple plans. paid accounts get unlimited scans, rewrites, and documents. trials can view the docs but cannot generate keys until upgraded.

endpoints

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/analyzeanalyze text against a voice profile — returns score, highlights, per-signal breakdown, and a cost blockunmetered
POST /v1/rewriterewrite a flagged passage to fix a specific drift signalunmetered
GET /v1/profileslist voice profiles you can access (own + team-shared)free
GET /v1/usagecurrent month quota, limits, and per-brand 30-day token breakdownfree
GET /v1/contextslash-command context: plan, byok billing, profiles, recent documents, and team/byok user rollupsfree
GET /v1/documentslist documents (paginated by limit and offset)free
POST /v1/documentscreate a new document1 slot
GET /v1/documents/:idfetch a single document by idfree
PUT /v1/documents/:idupdate title or content of an existing document (a substantial content swap charges 1 slot — see abuse guard below)free*

summon a specific brand voice

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:

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

ambiguous match → 400
{
  "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" }
  ]
}
omit the brand field entirely to use your default profile. on multiple plans, the default can be a team-shared profile so teammates inherit the same voice without cloning it.

analyze text for voice drift

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

request — summon a brand by name
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"
  }'
response
{
  "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
  }
}
paid_by is "hyv_platform" when we route through opencode zen with our key, or "your_anthropic_key" / "your_openrouter_key" / "your_opencode_key" when you've enabled byok. token counts come straight from the provider response so you can estimate exact spend.

rewrite a flagged passage

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.

request
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"
  }'
response
{
  "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
  }
}

list voice profiles

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.

request
curl https://holdyourvoice.com/v1/profiles \
  -H "Authorization: Bearer hyv_live_<your-key>"
response
{
  "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
      /* ... */
    }
  ]
}

usage — with per-brand breakdown

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 }
  ]
}
with byok enabled, the response shifts: "is_byok": true, "token_billing": "your_<provider>_account", and quota_model.rewrite becomes "unmetered" (dropping the legacy monthly counter).

create and update documents

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.

create a document against a brand
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"
  }'
update content
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..." }'
abuse guard (content-swap detection): a put that replaces the body with substantially different content is treated as a new document and charges 1 slot. the test is strict: if the new content keeps under 50% of the baseline's words AND both sides are at least 25 words AND the trigram overlap with the baseline is under 15%, the guard fires. anything short of that — typing, editing, restructuring, rephrasing, or keeping at least half the vocabulary — stays free. the response includes _quota_charged: true whenever a slot was consumed so your automation knows.

error codes

statuserror_codemeaning
401missing or invalid api key
402plan inactive (none / expired) — renew to resume api access
403scope denied (e.g. key without rewrite scope calling /v1/rewrite)
400profile_ambiguousbrand query matched more than one profile — see candidates array
404profile_not_foundthe brand query didn't resolve in your workspace
429rpm exceeded (60 req/min on paid plans)
429submission_quota_exceededa put hit the abuse guard and you have no remaining document slots — upgrade plan or edit existing doc
500server error — retry with exponential backoff

copy this prompt to your agent

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.

api and mcp keys require an active paid plan. trials can read this page and preview the tools, but they cannot create keys until upgraded.
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. prefer remote MCP at https://holdyourvoice.com/mcp and complete OAuth when this client supports remote MCP.
2. otherwise configure a local server named hyv with command "npx" and args ["@holdyourvoice/hyv", "mcp"].
3. use HYV_API_KEY only for the local transport; do not print, commit, or share it.
4. set HYV_BASE_URL to https://holdyourvoice.com unless i explicitly gave you another endpoint.
5. install /hyv-status from https://holdyourvoice.com/.well-known/agent-commands/hyv-status.md if your agent supports custom slash commands.
6. verify the setup by calling hyv_get_context, hyv_get_usage, and hyv_profiles.
7. after verification, use /hyv-status for account context and use hyv_analyze before hyv_rewrite.

expected result:
- tell me which MCP transport and config you updated.
- confirm the usage and profile tools worked.
- do not include the raw api key in your final response.

use hyv inside claude desktop, cursor & more

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.

recommended — connect remote MCP
https://holdyourvoice.com/mcp

add this URL in a remote-MCP client and complete OAuth in the browser.

local fallback — add to claude_desktop_config.json
{
  "mcpServers": {
    "hyv": {
      "command": "npx",
      "args": ["@holdyourvoice/hyv", "mcp"],
      "env": {
        "HYV_API_KEY": "hyv_live_..."
      }
    }
  }
}

the npm CLI is the supported local MCP transport. restart the client after saving.

available tools
tool namewhat it does
hyv_analyzeanalyze a draft against a voice profile
hyv_scanscan text for AI writing patterns and voice drift
hyv_rewritereturn voice-aware rewrite context for a draft
hyv_validatevalidate text against a voice profile
hyv_profileslist available voice profiles
hyv_get_usageshow current quota usage and limits
hyv_get_contextshow account plan, profile, document, and quota context for /hyv-status
hyv_onboard_statusmcp-first get started: onboarding status (mcp verified, profile, next step)
hyv_onboard_save_stepsave one voice-onboarding answer at a time
hyv_onboard_from_urlseed a voice profile from a website (prefer host-fetched content)
hyv_onboard_finalizecreate/update voice profile and complete setup after mcp verify
use hyv_profiles to find the available voice profile names or slugs, then pass the selected value on a tool’s optional profile argument.

slash command

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

claude conversation example

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_profiles()
        ← available profiles: acme-co, founder voice

claude: → hyv_analyze(text="...", profile="acme-co")
        ← highlights voice drift and AI patterns

claude: → hyv_rewrite(text="leverages best-in-class ai", profile="acme-co")
        ← voice context for a precise rewrite

claude: → hyv_validate(text="uses AI to get real results", profile="acme-co")
        ← score, cadence metrics, and pass/fail result

example automation workflow

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)")

connect chatgpt desktop

recommended: path a (remote oauth) for all paid hyv users. turn developer mode on, add a new app with server url https://holdyourvoice.com/mcp and auth oauth. no local install — oauth runs in chatgpt's browser (no windows firewall issues). path b is free fallback only.

path a — remote oauth (recommended)

recommended for paid users. no local install needed.

  1. in chatgpt desktop: settings → turn developer mode on (accept the elevated risk warning — expected, not a hyv bug)
  2. settings → apps → new app
  3. fill in: name hold your voice, description scans writing for ai patterns and voice drift
  4. connection: server url → https://holdyourvoice.com/mcp
  5. authentication: oauth
  6. check "i understand and want to continue"
  7. save → complete oauth sign-in to holdyourvoice.com
  8. test: "scan this with hold your voice"
do not paste https://example.com/sse — that is a chatgpt placeholder, not a real url. cloud app setup: holdyourvoice.com/app/api/mcp

path b — local connector (free fallback only)

only if you do not have a paid hyv plan. requires node.js 18+ and npm i -g @holdyourvoice/hyv.

  1. settings → connectors (or chatgpt.com/#settings/Connectors)
  2. add connector: name hold your voice, command hyv, arguments mcp
  3. save, restart chatgpt desktop
  4. test: "scan this with hold your voice"

if the connector fails to start, use full paths from ~/.chatgpt/hyv-mcp-connector.txt. run hyv mcp --setup-chatgpt in terminal for detailed steps.

problemfix
i don't see "new app"turn developer mode on in chatgpt settings
i see connectors, not new appturn developer mode on for path a; path b is free fallback only
what goes in connection?https://holdyourvoice.com/mcp — not the example url
oauth failedensure paid hyv account; try logging into holdyourvoice.com first
elevated risk scary?normal for custom mcp — chatgpt warning, not a hyv error
free user?use path b (local connector); remote oauth needs paid
browser chatgpt?neither path works — desktop app only

wiki guide: holdyourvoice.com/wiki/use-hyv-in-chatgpt


how usage is counted

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.

documents

individual and multiple plans include unlimited documents, scans, and rewrites via the web app and api. free local scan stays free without an account.

analysis & rewrites

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.

rate limit (rpm)

60 req/min on individual and multiple plans, per-account across all your keys combined. exceeding returns 429 with a retry_after value — the window resets every 60 seconds.

multiple plan sharing

on multiple plans, voice profiles and api keys are shared across teammates. profiles marked with team_id are visible to every team member — no need to re-create them per seat.

who pays for tokens

on individual and multiple plans, analyze + rewrite calls route through our platform account. every response carries a cost.paid_by field so your automation knows billing context.

voice profiles per plan

individual: 1 voice profile. multiple: unlimited profiles with team sharing. shared profiles carry a team_id so teammates see and can analyse against them without cloning.

abuse guard (content-swap detection): updating an existing document with substantially different content — less than 50% word retention from the last counted version on a draft ≥25 words — is treated as a new piece of writing and uses 1 quota slot. typing, drafting, minor edits, rephrasing, short-form writing under 25 words, and identical re-pastes are always free. the response includes _quota_charged: true whenever a slot was consumed.

well-known endpoints

hold your voice publishes standard discovery documents so ai agents and tooling can find and configure the api automatically.

urlstandard
/.well-known/api-catalogietf api catalog (rfc draft)
/.well-known/agent-onboardingcopy-prompt setup contract for paid users connecting local agents
/.well-known/agent-commands/hyv-status.mdpredefined slash-command prompt for account context
/.well-known/mcp/server-card.jsonmcp server card
/.well-known/oauth-protected-resourcerfc 9728 — protected resource metadata
/.well-known/oauth-authorization-serverrfc 8414 — authorization server metadata
/.well-known/agent-skills/index.jsonagentskills.io skills index

get started for $1 — create your account and scan your first draft in minutes.

get started for $1 →
works in the ai apps you already use
claudeclaude codechatgptcodexcursorwindsurfantigravityopencodecommand code

set up your first integration in 5 minutes

install hyv, upgrade to individual ($1 first month, then $9/mo) or multiple ($29/mo), generate an api key, and connect your first workflow. scans and rewrites are unlimited on paid plans.

get started for $1

get started for $1 — create your account and scan your first draft in minutes.

get started for $1 →
works in the ai apps you already use
claudeclaude codechatgptcodexcursorwindsurfantigravityopencodecommand code