WordPress Plugin · v1.4.0

AI domain search, drop-in for WordPress

One shortcode. Full brand control. Streams AI-powered domain suggestions directly into any page — API key stays server-side, results sorted by relevance, basket connected.

WordPress 6.0+
PHP 7.4+ with cURL
WooCommerce optional
[ai_domain_search] — paste on any page or post
Quick install
  1. 1
    Upload the plugin
    Upload ai-domain-search/ to /wp-content/plugins/ and activate.
  2. 2
    Enter your API details
    Go to Settings → AI Domain Search and enter the service URL and API key.
  3. 3
    Configure the widget
    Set brand colours, layout, language, and button action. Live preview updates instantly.
  4. 4
    Place the shortcode
    Add [ai_domain_search] to any page or post. Done.
Architecture

How it works

Three PHP classes, no external dependencies. The plugin proxies all AI calls server-side — the API key never touches the browser.

AISDS_Admin
Settings page with live preview. Colour pickers, layout controls, language selector, button action config. All saved to WordPress options.
AISDS_Proxy
WordPress admin-ajax handler. SSE proxy to the AI service, TLD list cache, basket add handler, per-IP rate limiter. API key injected server-side only.
AISDS_Shortcode
Renders the widget HTML and outputs the aisdsConfig JS object with all settings. CSS custom properties written inline from saved values — correct before JS runs.
Request flow
Browser
EventSource
WP admin-ajax
AISDS_Proxy
AI Service
Bearer key injected

The proxy streams the AI service response straight through to the browser. No buffering. First result appears in ~4 seconds.

Widget

What the widget does

AI-powered suggestions with live streaming

User types a business description — the widget sends it to the AI service and streams results back via Server-Sent Events. Domains appear one by one as they resolve. First result in ~4 seconds, not after the full search completes.

Results sorted by relevance

SSE delivers domains in CNR batch-arrival order (random). When the stream ends, results are re-sorted client-side so the best match is always at the top. See scoring rules below.

Full styling control

Brand colour pickers (primary, accent, text, background) output as CSS custom properties. Five card border presets: Flat, Soft, Medium, Rounded, Shadow. Custom CSS textarea for complete override control. Live preview updates in real time before saving.

1–4 column grid with max-results

Choose 1, 2, 3, or 4 columns. Set a max-results cap — the plugin snaps it to the nearest multiple of your column count so you never get orphaned half-rows. Premium domain badge shows registry price inline.

Campaign / sale price highlight

When enabled, domains with a discounted TLD price get a coloured outline and a pill badge (e.g. "Sale"). Detects discounts from the basket API TLD price map automatically — no manual tagging.

Localisation — EN, NO, FI, DE

Language setting controls price period strings (/yr/årJahr) and all customer-facing labels. Every widget text label is also individually overridable in settings.

Ranking

How results are ranked

Domains stream in random CNR batch-arrival order. When the stream ends the widget re-sorts all results client-side using this scoring formula.

Scoring signals
Signal Points
Query word (≥3 chars) found in domain name word.length × 4 per word
2 or more query words matched (compound bonus) +15
Matched characters ÷ name length (efficiency — rewards tight exact matches) up to +10
TLD matches locale preference (.no → no, .fi → fi, .de → de, .com → en) +5

Query words are lowercased, non-alphanumeric stripped, and words shorter than 3 characters dropped before scoring. Only the domain name part (before the first .) is scored against.

Worked example
Query: "onkel edvins bodega" — words: onkel edvins bodega — locale: no
#1
edvinsbodega.no
edvins(24) + bodega(24) + compound(15) + efficiency(10) + .no(5) = 78
#2
onkeledvins.com
onkel(20) + edvins(24) + compound(15) + efficiency(10) = 69
#3
norskbodega.no
bodega(24) + efficiency(~5) + .no(5) = ~34
Stale result protection

Each search increments an internal generation counter. Events queued from a previous EventSource are silently discarded if the counter has advanced — domains from an old search can never bleed into new results.

Configuration

Button actions

Choose one action per widget. Configure in Settings → AI Domain Search → Button action.

Open URL

Redirect to a URL template when the button is clicked. Use {{domain}} as a placeholder for the selected domain name.

https://example.com/register?domain={{domain}}

WooCommerce

Adds a configurable product to the WooCommerce cart with the selected domain name attached as order metadata. WooCommerce must be active. The cart fragment is refreshed automatically after adding.

Requires WooCommerce · Set product ID in settings

JavaScript event

Dispatches a custom DOM event on document for your theme or app to handle — no redirect, no WooCommerce dependency.

document.addEventListener('aisds:domain_selected', e => {'{'} e.detail.domain // "example.no" e.detail.is_premium // true/false e.detail.premium_price // "299.00" e.detail.premium_currency// "USD" {'}'});

Basket API GravityVault / Uniweb

Resolves the TLD plan UUID from the price map, builds a full basket payload (merging any previously added items from the client-side cache), and POSTs to your configured basket endpoint. On success redirects to the cart page.

  • Optimistic UI — button shows ✓ Added instantly; POST runs in background
  • Reverts button if POST fails, shows error message
  • In-memory basket item cache eliminates GET round-trip on subsequent adds
  • x-tenant / x-brand headers sent on all basket API calls
Reference

All settings

Setting
API
Service URL
API Key
Styling
Primary colour
Accent colour
Text colour
Background colour
Card border style
Custom CSS
Layout
Columns
Max results
Campaign highlight
Language & text
Language
Search label
Placeholder
Button label
Added state label
Basket API (GravityVault / Uniweb)
Shop base URL
TLD list path
Basket path
Cart redirect URL
Market region ID
Tenant slug
Security & Performance

Built secure by default

API key — server-side only

The Bearer token is stored in WordPress options and injected by the PHP proxy. It never appears in page source, JS, or network requests from the browser.

Rate limiting — 20 req / 60 s per IP

WP transient-based per-IP counter on the SSE proxy. Returns HTTP 429 when exceeded. Prevents abuse without requiring a session or login.

Input validation

Query validated: minimum 2 chars, maximum 500. Nonce checked on every AJAX action. Basket payload whitelisted field-by-field before forwarding.

TLD list cache — 1 hour

Server-side WP transient cache on the TLD list endpoint. Client-side localStorage cache with 1-hour TTL. TLD data rarely changes intra-day — no repeated fetches.

In-memory basket cache

Added items cached client-side for the page session. Eliminates the GET round-trip on subsequent adds — the browser sends existing items directly, cutting one HTTP call from the critical path.

cURL availability check

Proxy checks for curl_init before opening the stream. If cURL is missing it emits a structured SSE error event rather than a PHP fatal error.