A standalone service that drops alongside any existing webshop. Each brand gets its own server-side config — TLD catalog, market bias, premium on/off. The AI generates concept-aware candidates, checks live availability, and streams results progressively.
Two rounds produce 20–40 candidates: creative brandable names unique to your concept, and short keyword names for the core business — all scoped to your brand's TLD catalog.
Batches of 32 are checked in parallel. First result appears within ~4 seconds — each batch streams as it resolves so your users see domains load progressively, not all at once.
Premium detection is enabled per brand. When on, registry pricing surfaces inline from the availability check — no second lookup. When off, only standard available domains are returned.
No available domains found
Try a different description, or check your brand's TLD config
Add this block to your Claude Desktop claude_desktop_config.json to expose suggest_domains as a tool your AI agent can call.
The MCP server runs as a separate container per brand. See CONFIGURATION.md for setup.
Native integrations for the platforms your brands run on. No custom code required to go live.
Via the WordPress plugin.
The WordPress plugin includes a Basket API integration that works with OneWebshop-compatible checkouts (GravityVault / Uniweb). Install the plugin, enter your shop's basket API URL in settings, and the "Add to cart" button on each domain result will fetch the current TLD plan and price, add the domain to the customer's basket, and redirect them to checkout — all from within the WordPress widget. No custom backend code required.
Drop-in shortcode for any WordPress site
Place [ai_domain_search] on any page or post. The admin panel covers everything: brand colours with live preview, five card border styles, 1–4 column grid, max-results snapped to column count, and localisation in EN / NO / FI / DE.
SSE delivers domains in CNR batch-arrival order (random). When the stream ends, results are re-sorted client-side by relevance score — best match surfaces first. Scoring per domain name:
Example: query "onkel edvins bodega" (locale=no) — edvinsbodega.no scores 78 (two word matches + compound + efficiency + .no nudge) and ranks first over onkeledvins.com at 69.
{{domain}} placeholderaisds:domain_selected on documentAny stack — REST or script embed
For teams with their own frontend: a single POST /suggest returns an SSE stream — merge results into your existing domain list as they arrive, dedup by name, and your existing result wins on conflict. No npm, no build step. Or embed the lightweight JS snippet from the webshop integration guide with three config values.
suggest_domains collects into a listA standalone service you drop alongside any existing stack — no schema changes, no shared database, no framework lock-in.
Each brand gets its own YAML file: TLD catalog, market bias via primary_tlds, and a premium_enabled toggle to turn premium detection on or off. ccTLD entries in primary_tlds also drive language bias — set .no and the AI generates Norwegian names; set .de for German, .nl for Dutch, and so on. Changes take effect within 60 seconds — no image rebuild, no redeploy. Unicode (IDN) domain names are returned as readable display names like bjørn.no.
The LLM generates two types of candidates from the user's natural-language description: creative brandable names unique to the concept, and short keyword names that describe the core business. Two rounds per search produce 20–40 candidates scoped to the brand's TLD catalog. Progressive streaming means the first result appears within ~4 seconds — not after the full search completes — so users see suggestions loading in real time.
A single POST /suggest opens a server-sent event stream. Results arrive domain by domain as each availability batch completes. The same service exposes an MCP tool for AI agents — same logic, same brand config, no duplication. For webshop teams: embed a script tag with three config values (no npm, no build step), or proxy through your backend in a few lines of Node.js or PHP to keep the API key server-side.
Per-brand rate limiting prevents abuse across shared infrastructure — each brand's key has its own request budget with no shared ceiling. The prompt pipeline validates and sanitizes input to block prompt injection attempts before they reach the model. Runs as a single Docker container; brand YAML configs mount as a read-only volume — no image rebuild to onboard a new brand. API keys never appear in logs or error responses.
Identical queries within the cache window replay instantly — no LLM call, no CNR round-trip. Set REDIS_URL to use a shared Redis instance across multiple app containers; omit it and the service falls back to in-process memory automatically. The Docker Compose stack ships a pre-configured redis:7-alpine service with AOF persistence, so cache entries survive container restarts. TTL is configurable via QUERY_CACHE_TTL_SECONDS.
Circuit breakers are scoped per brand — one brand's failures cannot open the breaker for others. Both LLM and CNR breakers open independently after a configurable threshold and recover via a timed half-open probe. All logs emit as single-line JSON with ts, level, latency_ms, and cache_hit fields, ready to pipe into any log aggregator. GET /health returns brand count and uptime; GET /metrics exposes per-brand circuit states (admin-gated).
One POST request. Results stream back as Server-Sent Events — one JSON object per line as each domain resolves.
# cURL curl -N -X POST https://ai-domain-search.digitalfoundation.one/suggest \ -H "Content-Type: application/json" \ -H "X-Brand-Api-Key: <your-key>" \ -d '{"query": "indie coffee roastery"}' # Request body { "query": "indie coffee roastery" }
The -N flag keeps curl open to receive the stream. Replace <your-key> with the brand API key from your YAML config.
# Standard domain — available, not premium { "domain": "beanroasters.no", "is_premium": false } # Premium domain — includes reg + renewal price { "domain": "coffee.shop", "is_premium": true, "premium_price": "299.00", "premium_renewal_price": "95.00", "premium_currency": "USD" } # Stream always ends with a done sentinel { "done": true }
Domains stream as they resolve — no waiting for all 20. On your side: push each result into your existing domain list as it arrives. Dedup by domain name — your own check wins on conflict.
| Field | Type | Always present | Description |
|---|---|---|---|
| domain | string | Yes | Fully qualified domain name, e.g. coffee.shop |
| is_premium | boolean | Yes | True if the registry classifies this as a premium domain |
| premium_price | string | When premium | Registration price (decimal string). Includes brand markup if configured. |
| premium_renewal_price | string | When premium | Annual renewal price at registry rate (no markup applied) |
| premium_currency | string | When premium | ISO 4217 currency code for both price fields, e.g. USD |
| done | boolean | Last event only | Stream terminator — close the connection when you receive this |