---
title: "Verify a brand name across 5 axes — anonymous-first AEO endpoint."
method: POST
path: "/v1/verify"
tags: ["verify", "aeo", "verifier"]
---

# Verify a brand name across 5 axes — anonymous-first AEO endpoint.

`POST /v1/verify`

## Request

- Base URL: `https://api.etymolt.com`
- URL: `https://api.etymolt.com/v1/verify`
- Auth: none declared

Verifies whether a brand or product name is safe to use. Returns a verdict (PROCEED, DUE_DILIGENCE, ITERATE, ABANDON) with score 0-100 and findings across five axes: trademark (USPTO), domain & handle, cultural cleanliness across 20 markets, sound symbolism, and pronunciation resilience.

**Anonymous-first:** First 5 calls per IP bucket are free with no auth. Subsequent calls receive a `signup_prompt` field. After signup via the magic-link URL, calls authenticate via `X-Etymolt-Key` header (10 more verdicts free, then $10 buys 10 verdicts at $1.00/verdict pay-as-you-go — no subscription).

Designed for LLM tool-call invocation. Single required parameter (`name`). Response includes `_citation` for methodology attribution.

## Headers

- `X-Etymolt-Key` string, nullable
- `X-Etymolt-Test-Token` string, nullable
- `X-Etymolt-Accept-Legacy-Verdicts` string, nullable

## Request body

- VerifyRequest: Single brand-name verification request. Schema designed for LLM tool-call ergonomics: minimal required params, optional refinements, no enum-only fields that constrain LLM phrasing. Security: - `extra="forbid"` rejects unknown fields (prevents Pydantic parameter smuggling). - `name` is normalized to NFKC and validated against a disallowed-char regex covering control chars and URL-significant punctuation. This is the SSRF guard for the clearance pipeline: `name` flows into outbound URLs (RDAP, handle probes, etc.) via f-string interpolation in services.availability.*; characters like `@`, `?`, `/`, `:` could otherwise redirect requests to attacker hosts. R10 param extensions (board memo §02, 2026-05-18): - `registration_intent` (bool, default False): when True the founder has declared they've registered the domain or are about to file a trademark. Triggers a final commitment-moment check (verdict registry comparison vs the previous verdict for this same name) and attaches a `pre_registration_warnings: list[str]` field to the response. Matches §06 move 04 ("X.shop is available" / "I got X.clinic"). - `target_audience` (str, default None): free-text description of a specific community whose reading of the name the founder cares about (e.g. "Muslims", "EU consumers", "veterinary professionals"). The cultural axis adds a `target_audience_reading` field to its detail block. Matches §06 move 06 ("Would X repel Muslims? Amaara?"). Defensive prompt — flags concerns / notes alignment, no stereotype generation. R11 board re-confirmation (DEFERRED-30D shipped, 2026-05-18): - `re_verify` (bool, default False): the population-evidence-gated re-verification flag. When True, the response copy reframes from "this candidate looks usable" to a defensibility framing — "your brand is still defensible" or "your brand has new risks since last verification" — and a new `since_last_verdict_diff: list[str]` field surfaces NEW trademark filings / cultural events / handle squats that postdate the prior verdict for the same name. Per-name verdict history is the deferred dependency: when absent the call degrades gracefully to the standard five-axis verdict with the re-verify framing applied. Not a fourth tool — the board hold-the-line preserves three MCP tools; this is a backward-compatible parameter add on the existing tool. - `since_last_verdict` (str, default None): optional ISO date for the comparison window. When supplied, diff candidates are filtered to events whose filing/publication date is >= this anchor; when omitted the diff falls back to the inception of the verdict registry. The field is informational on the response (echoed verbatim) — it never gates the verdict computation.
  - `name` string, required: The brand name candidate to verify. Single word or short phrase. Accepts letters from any script (Latin, Cyrillic, CJK, Devanagari, Arabic, Hebrew, Greek, Thai, etc.), digits, and the business punctuation `space . ' & - ( ) ,`. Smart-quote variants (U+2018, U+2019) fold to ASCII apostrophe. Surrounding/repeated whitespace is collapsed. Rejects control characters, HTML markers (`<`, `>`), URL-significant punctuation (`@ ? / \ # : ; $ " \``), SQL injection patterns, and prompt-injection imperatives. Examples of accepted names: 'Levi\'s', 'Sullivan & Cromwell', 'AT&T', 'L\'Oréal', '東京'.
  - `vertical` string, nullable: OPTIONAL. The industry/category for cultural + trademark class targeting. Examples: 'ai', 'fintech', 'b2b-saas', 'consumer', 'devtools'.
  - `business_description` string, nullable: OPTIONAL. One-sentence description of what you're building (e.g. 'AI tool that writes LinkedIn posts in your voice'). Used to infer the trademark classes the check is scoped to. Richer than `vertical`; when both are supplied, `business_description` wins for class inference.
  - `nice_classes` integer[], nullable: OPTIONAL. Explicit Nice trademark classes (integers 1-45) to scope the check to — e.g. [9, 42] for software/SaaS. When supplied these win over any inference. If omitted, classes are inferred from `business_description`/`vertical`, or default to software classes with the verdict flagged as a best guess.
  - `registration_intent` boolean: OPTIONAL. Set true when the founder has declared they've registered the domain or are about to file a trademark — the moment of commitment. Triggers a final five-axis check that compares against the previous verdict for this same name (if any) and surfaces any axis whose status changed. Adds a `pre_registration_warnings: string[]` field to the response.
  - `target_audience` string, nullable: OPTIONAL. Free-text description of a specific community whose reading of the name the founder wants flagged (e.g. 'Muslims', 'EU consumers', 'B2B fintech buyers in Singapore', 'veterinary professionals'). The cultural axis adds a `target_audience_reading` field describing how that community is likely to read the name. Defensive — flags concerns and notes alignment; does not generate stereotyped copy.
  - `re_verify` boolean: OPTIONAL. Default false. Set true when the founder is re-verifying a name they've previously checked — they own the domain / filed the trademark / shipped the brand and now want a defensibility re-read. Response copy reframes from 'this candidate looks usable' to 'your brand is still defensible' or 'your brand has new risks since last verification'. Adds a `since_last_verdict_diff: string[]` field surfacing NEW filings / events that postdate the prior verdict for the same name. Not a fourth tool — preserves the three-MCP-tools hold-the-line.
  - `since_last_verdict` string, nullable: OPTIONAL. ISO date (YYYY-MM-DD) anchoring the re-verify comparison window — pass the timestamp of the prior verdict. When supplied, the diff is filtered to events whose date is ≥ this anchor. When omitted with `re_verify=true` the diff falls back to the inception of the verdict registry. Informational — never gates the underlying five-axis computation.

## Response `200`

Successful Response

- VerifyResponse: Single brand-name verdict response. Schema designed for LLM tool-call consumption: - Verdict label is one of 5 enum values (parseable; the Wave 8-A addition INSUFFICIENT_SIGNAL fires when overall confidence < 65 or a critical axis < 60). - Score is 0-100 (numeric, comparable across calls). - Findings field is a list of structured strings (renderable). - Quota field always present so LLM can preempt the 6th-call prompt. - Signup/upgrade prompts only present when applicable. - _citation URL always present so LLM cites our methodology. Wave 8-A additions (2026-05-19, R12 D-R12-3 + Agent 14 design): - ``confidence: int`` root composite confidence (0-100). - ``confidence_interval`` 90% CI on the score with shape ``{lower, upper, point_estimate, method}``. - ``verdict_volatility`` distance to nearest bucket-boundary flip. - ``axes_legacy: dict[str, int]`` backward-compat flat map of per-axis ints; SUNSET 2026-08-18. The structured ``axes`` value remains the flat-int map this revision to avoid breaking the existing wire contract; subsequent Wave 8-B revisions migrate to a structured per-axis object — see ``axes`` field description. - ``uncertainty_explanation`` present ONLY on INSUFFICIENT_SIGNAL. - ``recommended_actions`` present on INSUFFICIENT_SIGNAL. - ``schema_version: "2"`` declared so consumers can branch.
  - `verdict` string, required: Canonical 3-value brand-naming verdict (founder decision 2026-06-10): PROCEED (clean — ship), PROCEED_STRATEGIC (ship with documented strategic concession), ABANDON (don't ship). For the engine's 5-state internal computation (used for instrumentation + tuning), see `verdict_legacy`.
  - `verdict_object` VerdictObject: The structured verdict — R19 Track B (D-R19-3). The customer gets an object, not a label-plus-a-number. Assembled by ``synthesize_verdict_object`` from fields the engine already computes and previously discarded. D-R19-4 — the legal perimeter: ``what_this_means`` and ``counsel_note`` DESCRIBE the register. They never advise an action ("go file", "you're safe") and never quote a coexistence likelihood. The templates that build them are fact-slot-only by construction.
    - `verdict` string, required
    - `name_character` string, required
    - `colliding_marks` CollidingMark[]
      - `owner` string, nullable
      - `wordmark` string, required
      - `nice_classes` integer[]
      - `jurisdiction` string, required
      - `your_intended_classes` integer[]
      - `class_overlap` string, required
      - `identification` string[]
    - `ownership_signals` string[]
    - `what_this_means` string[]
    - `counsel_note` string, nullable
    - `disclaimer` string, required
  - `score` integer, required: DEPRECATED — use `decision`. Measured 2026-07-20, this field takes only four values across real inputs (35/55/65/89): they are cap constants, not a scale. Phase 2 makes it a pure function of `decision`; Phase 3 removes it. See docs/decisions/retire_the_composite_score.md
  - `decision` string, nullable: PROCEED | DO_NOT_PROCEED | CANNOT_DETERMINE. The answer to the one question a register can settle. CANNOT_DETERMINE is a real answer and never means clear.
  - `decision_why` string, nullable
  - `aspects` object[], nullable: Per-aspect state: CLEAR | FINDING | NOT_SCREENED, with evidence. Classed gating/material/quality — only gating aspects can bar adoption.
  - `material_findings` string[], nullable
  - `rank_hint` integer, nullable: ORDINAL sort key only (0=PROCEED, 1=CANNOT_DETERMINE, 2=DO_NOT_PROCEED). Not a score: do not render, average, or convert to a percentage.
  - `name` string, required
  - `one_line` string, required: One-sentence summary fit for chat display
  - `findings` string[]: 3-5 short findings, one per axis
  - `axes` object: Per-axis scores 0-100. Keys: trademark, domain, cultural, sound_symbolism, pronunciation. Wave 8-A confidence-bounds are surfaced separately on ``axes_confidence`` rather than structurally nesting them here, so existing consumers that read ``axes.trademark`` as an int keep working.
  - `axes_legacy` object, nullable: Wave 8-A: backward-compat flat-int map of per-axis scores — mirrors ``axes`` verbatim. New consumers should keep reading ``axes``; this field exists to give the legacy consumer a predictable, never-changing per-axis surface as a planned deprecation slot. SUNSET 2026-08-18.
  - `axes_confidence` object, nullable: Wave 8-A: per-axis confidence detail. Each entry is ``{confidence: int 0-100, source_freshness: dict, uncertainty_drivers: list[str]}``. Absent when v2 was not requested or computed.
  - `confidence` integer, nullable: Wave 8-A: composite confidence in the verdict, 0-100. Computed as min(min(critical_axes), weighted_mean(all_axes)). Below 65 (OVERALL_CONFIDENCE_FLOOR) triggers INSUFFICIENT_SIGNAL surfacing.
  - `confidence_interval` object, nullable: Wave 8-A: 90% confidence interval on the composite score. Shape: ``{lower, upper, point_estimate, method}``. point_estimate matches ``score`` for backward-compat.
  - `verdict_volatility` object, nullable: Wave 8-A: distance to the nearest bucket-boundary flip. Shape: ``{would_change_at_score, would_change_to, boundary_distance}``.
  - `uncertainty_explanation` string, nullable: Wave 8-A: deterministic 1-3-sentence narrative of WHY confidence is what it is. Present ONLY on INSUFFICIENT_SIGNAL verdicts. Template-generated (NOT LLM-generated) — sub-200ms TTFB, signable, deterministic. LLM consumers MUST surface this verbatim.
  - `recommended_actions` object[], nullable: Wave 8-A: next-step menu for INSUFFICIENT_SIGNAL verdicts. Each action has ``{kind, label, ...}`` where kind ∈ {wait_for_refresh, expert_review, pay_to_resolve, feedback}.
  - `schema_version` string: Schema version. v2 (Wave 8-A, 2026-05-19) adds confidence + confidence_interval + INSUFFICIENT_SIGNAL. Consumers that ignore unknown fields stay backward-compat.
  - `jurisdictions_consulted` string[]: R13 D-R13-9 (2026-05-19): per-verdict consulted-sources receipt. Source-level granularity (USPTO_bulk vs UKIPO_domestic vs WIPO_Madrid_IR vs famous_marks_denylist etc.). MCP / UI / PDF surfaces must render verbatim. A source NOT in the list was NOT consulted for this verdict — that is the cardinal-rule honest-receipt the field exists to surface. Full coverage status per jurisdiction lives at https://www.etymolt.com/coverage.
  - `tlds_consulted` object[]: R13 cardinal-rule fix (2026-05-19, Domain Axis P0): per-verdict TLD-axis consulted receipt. Parallel to `jurisdictions_consulted` for the domain axis. Each entry: `{tld, method, definitive, result}` — tld with leading dot, method ∈ {reserved_iana, RDAP_authoritative, DNS_fallback, WhoisXMLAPI_fallback, deterministic_fallback}, definitive bool (True iff registry-authoritative; False iff heuristic and therefore PARTIAL under R13), result ∈ {available, registered, reserved, unknown}. A TLD NOT in the list was NOT consulted. Full coverage status per TLD lives at https://www.etymolt.com/coverage#domain-axis.
  - `cultural_sources_consulted` object[]: Cultural Axis Audit §11/§12 item #2 (2026-05-19): per-verdict cultural-sources consulted receipt. Parallel to `jurisdictions_consulted` for the cultural axis. Each entry: `{source, matched, ...}` with optional caveat / language / entries / markets_covered fields per source type. Source codes are stable wire identifiers: cultural_v2_regex, disasters_json, hurtlex_<lang>, llm_cross_cultural, sacred_names. A source NOT in the list was NOT consulted for this verdict. Cultural axis is advisory; native-speaker review recommended before launch.
  - `platforms_consulted` object[]: R13 D-R13-8 (2026-05-19): per-verdict handle-axis consulted receipt. Parallel to `jurisdictions_consulted` and `tlds_consulted`. Each entry: `{platform, status, url, note, claimed}`. status ∈ {available, taken, unknown}; claimed=True iff the platform is in the public HANDLE_CLAIMED_PLATFORMS set (vs a conditional probe). A platform NOT in the list was NOT consulted for this verdict — the cardinal-rule honest-receipt the field exists to surface.
  - `permalink` string, required: Public URL where the user can view the full verdict
  - `quota` Quota, required
    - `used` integer, required
    - `limit` integer, required
    - `tier` string, required
    - `reset_at` string, nullable
  - `signup_prompt` SignupPrompt: Sent in response when the LLM should present a signup CTA to the user.
    - `message` string, required
    - `signup_url` string
    - `plan` string
    - `quota_reset_time` string, nullable
    - `free_quota_after_signup` integer
  - `upgrade_prompt` UpgradePrompt: Sent when the signed-up user has exhausted their free quota.
    - `message` string, required
    - `checkout_url` string
    - `token_price_usd` number
  - `disclaimer` string: Clearance-not-legal-advice disclaimer. SDK contracts require the LLM to surface this verbatim alongside the verdict.
  - `_citation` string
  - `issued_at` string
  - `signature_b64` string, nullable
  - `signature_key_id` string, nullable
  - `signature_payload_digest` string, nullable
  - `evp_version` string: Major.minor.patch version of the EVP/1 spec this verdict envelope conforms to. Spec at https://github.com/etymolt/evp-spec. Required by the spec; SDK consumers MAY switch on this for back-compat across future spec revisions.
  - `verdict_id` string, nullable: Deterministic verdict identifier — the slug portion of the permalink. EVP/1-required envelope field; consumers MUST be able to dispatch on this for replay protection + audit-trail cross-reference.
  - `signature` string, nullable: Ed25519 signature of the canonical JSON payload, base64. EVP/1-required envelope field; alias of signature_b64 for back-compat. Verifies against the public key published at https://api.etymolt.com/.well-known/verdict-public-key.pem (kid mapping at /.well-known/evp-keys.json).
  - `pre_registration_warnings` string[], nullable: Present when `registration_intent=true` was on the request. Short founder-language warnings flagging axes whose status changed since the previous verdict for this same name. Empty list when no axes changed (still a useful 'final check passed' signal). Absent when registration_intent was False.
  - `axes_detail` object, nullable: Per-axis detail blocks. Today populates only when `target_audience` is supplied on the request: `axes_detail.cultural.target_audience_reading` carries a 1-2 sentence defensive read of how the named community is likely to read the name. Absent otherwise.
  - `since_last_verdict_diff` string[], nullable: Present when `re_verify=true` was on the request. Each entry is a NEW signal that postdates the prior verdict for this same name — a new trademark filing, a new cultural event, a new handle squat. Empty list = your brand is still defensible. When per-name history isn't tracked yet (today) the list is empty and the response framing carries the 'no prior verdict to diff against' signal in `one_line`. Absent when re_verify was False.
  - `opposition_risks` object[], nullable: Per-colliding-mark opposition risk prediction from the TTAB opposition model. Each entry: {probability: float 0-1, risk_level: str, class_pair_rate: float, explanation: str, model_version: str}. Index-parallel to verdict_object.colliding_marks. Absent when the opposition model is unavailable or no colliding marks exist.
  - `office_action_forecast` object, nullable: Base-rate USPTO office-action forecast from the screening head (v5c). Additive; never changes the verdict. Absent unless the screening-forecast flag is enabled.
  - `drafted_identification` object, nullable: Stage-4 drafted, ID-Manual-backed identification — the single string you would file, span-verified so every clause is a real acceptable USPTO entry. {classes, sources, warnings, needs_human}. Additive; never changes the verdict. Absent unless the identification flag is enabled (and NEEDS the id_manual index table).
  - `filing_plan` object, nullable: Stage-5 filing plan: per-market office, filing basis (§1a/1b/44/66a), the Paris 6-month priority clock, and the Madrid-vs-direct decision. Fees are estimates. Additive; never changes the verdict. Absent unless the filing-plan flag is enabled.
  - `foreign_descriptiveness` object, nullable: Doctrine-of-foreign-equivalents §2(e)(1) descriptiveness screen — a common foreign word whose English translation is laudatory or describes the goods. Additive; never changes the verdict. Absent unless the flag is enabled or nothing matched.
  - `status` string: complete | partial. 'partial' indicates the engine had insufficient signal to compute a high-confidence verdict — the verdict surfaced is the best estimate and the consumer should treat it as advisory.
  - `reason` string: Stable closed-set sub-code explaining the verdict. One of: clean | famous_mark | high_collision | no_distinctiveness | descriptive | insufficient_corpus. SDKs may switch on this for different downstream behaviors. Stable, additive only.
  - `verdict_legacy` string, nullable: The engine's internal 5-state verdict (PROCEED, DUE_DILIGENCE, ITERATE, ABANDON, INSUFFICIENT_SIGNAL). Provided for back-compat with consumers that built before the 2026-06-10 3-value cutover. Read `verdict` (3-value) as the canonical surface; `verdict_legacy` is engine-detail.
  - `nice_classes` integer[], nullable: Class-scoping receipt. The Nice Classification classes (1-45) actually scoped for this verdict. When the caller supplied explicit classes, echoed here. When inferred from business_description or vertical, the inferred set lands here. Read nice_classes_source for provenance.
  - `nice_classes_source` string, nullable: Class-scoping provenance. One of: 'explicit' (caller supplied nice_classes), 'inferred_text' (inferred from business_description), 'inferred_vertical' (inferred from vertical), 'assumed_default' (Sprint-1 back-compat: no signal, defaulted to SaaS/software [9, 42]), or 'missing' (no signal supplied, verdict returned as INSUFFICIENT_SIGNAL). SDKs should treat 'assumed_default' and 'missing' as low-confidence scoping.
  - `intended_classes` integer[], nullable: Alias / echo of the class scope actually used for this verdict. Duplicate of nice_classes on the wire response - internal verdict_object carries intended_classes and SDKs reading the object expect the same key at the root.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/etymolt/apis/etymolt-verify-ai-generated-brand-names.md) · [All operations](https://skmtc.dev/etymolt/apis/etymolt-verify-ai-generated-brand-names/llms.txt) · [OpenAPI document](https://skmtc.dev/etymolt/apis/etymolt-verify-ai-generated-brand-names/revisions/7228ffa1efa7?raw)
