domani
npx skills add https://domani.run
Agent 安装分布
Skill 文档
domani.run – Domain Infrastructure for AI Agents
One API to search, register, and connect domain names.
Skill version 0.1.0 (2026-02-24). If the user asks to check for updates, fetch
https://domani.run/SKILL.mdand compare theupdateddate in the frontmatter with the one above. If newer, run:npx skills add domani.run
Setup
Step 1: Find or get an API key
Check for existing authentication first â the user may already be logged in:
- Check environment variable:
$DOMANI_API_KEY - Check the CLI config file:
~/.domani/config.jsonâ if it exists, read thetokenfield:cat ~/.domani/config.json 2>/dev/null # Returns: {"token":"domani_sk_xxx","email":"user@example.com"} - Validate the token still works:
If this returns account info, you’re authenticated. Skip to Step 2.curl -s https://domani.run/api/me -H "Authorization: Bearer TOKEN_FROM_CONFIG"
If no existing token found, create an account or log in:
New user:
curl -X POST https://domani.run/api/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "USER_EMAIL"}'
Returns api_key (format: domani_sk_...). If the user already has an account (409 response), use the login flow below.
Existing user (409 on register, or user says they have an account):
# Step A: Start device login â get a code
curl -s -X POST https://domani.run/api/auth/cli
# Returns: {"code":"XXXX","auth_url":"https://domani.run/auth/callback?code=XXXX","expires_in":600}
# Step B: Tell the user to open the auth_url in their browser and approve
# Step C: Poll until approved (every 5 seconds, up to 10 minutes)
curl -s "https://domani.run/api/auth/cli/poll?code=XXXX"
# Pending: {"status":"pending"}
# Approved: {"status":"complete","token":"domani_sk_xxx","email":"user@example.com"}
Store the token so the CLI and future agent sessions share the same auth:
mkdir -p ~/.domani && chmod 700 ~/.domani
echo '{"token":"domani_sk_xxx","email":"user@example.com"}' > ~/.domani/config.json
chmod 600 ~/.domani/config.json
Use the token for all authenticated API calls â add this header to every request:
Authorization: Bearer domani_sk_xxx
All the curl examples below use $DOMANI_API_KEY as a placeholder â replace it with the actual token you found or received.
Step 2: Check account readiness
curl -s https://domani.run/api/me \
-H "Authorization: Bearer $DOMANI_API_KEY"
This single call tells you everything about the account: email, has_payment_method, has_contact, setup_required (array of what’s missing), domain_count, referral_code. A payment method is required before purchasing. WHOIS contact is optional for purchases (a default is used) but recommended for ICANN compliance and required before transfers.
Step 3: Add a payment method (if has_payment_method is false)
Option A: Credit card (required for bulk purchases)
curl -s -X POST https://domani.run/api/billing/setup \
-H "Authorization: Bearer $DOMANI_API_KEY"
Returns { "url": "https://checkout.stripe.com/..." }. Tell the user to open this URL in their browser to add their card. After they complete the form, verify the card was added:
curl -s https://domani.run/api/me \
-H "Authorization: Bearer $DOMANI_API_KEY"
Check that has_payment_method is now true. If not, the user hasn’t completed the Stripe checkout yet.
Set a default payment preference (optional):
curl -s -X PUT https://domani.run/api/me \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"preferred_payment_method": "usdc"}'
Set to "card", "usdc", or null (auto). This sets the default for all purchases. You can override per-request with payment_method in the buy/transfer/renew body.
Option B: USDC (single domain only)
No setup needed. Two-step flow:
Step 1: Get payment instructions â call buy/transfer/renew with "payment_method": "usdc":
curl -s -X POST https://domani.run/api/domains/buy \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "mysite.com", "payment_method": "usdc"}'
Returns 402 with payment_options.crypto containing the wallet address, amount, and supported chains:
{
"code": "PAYMENT_REQUIRED",
"price": 9.99,
"payment_options": {
"crypto": {
"token": "USDC",
"amount": "9990000",
"chains": [
{ "chain": "base", "chain_id": 8453, "pay_to": "0x...", "token_address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
{ "chain": "ethereum", "chain_id": 1, "pay_to": "0x...", "token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" }
]
},
"card": { "setup_url": "https://domani.run/api/billing/setup" }
}
}
Step 2: Send USDC and retry with tx hash:
curl -s -X POST https://domani.run/api/domains/buy \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "mysite.com", "payment_tx": "0xabc123...", "payment_chain": "base"}'
The server verifies the transaction on-chain (recipient, amount, token, confirmations) and proceeds with registration. Each tx hash can only be used once. Only works for single domain purchases.
Option C: x402 Protocol (atomic, single domain only)
For x402-compatible agents, the API supports the x402 payment protocol for atomic USDC payments on Base. This is a single-request flow â no separate payment step needed.
- Make a normal API call (e.g.
POST /api/domains/buy). If payment is needed, the server returns HTTP 402 with aPAYMENT-REQUIREDheader containing the x402 payment requirements (amount, asset, network, payTo address). - Your x402 client signs an EIP-3009
transferWithAuthorizationfor the exact USDC amount to thepayToaddress on Base. - Retry the same request with the signed payload in the
PAYMENT-SIGNATUREheader (base64-encoded). - The server verifies the signature, settles the payment on-chain via a facilitator, and completes the operation. The response includes a
PAYMENT-RESPONSEheader with the settlement result.
If you use @x402/fetch, this entire flow is handled automatically â just wrap your fetch call:
import { wrapFetch } from "@x402/fetch";
const x402Fetch = wrapFetch(fetch, walletClient);
const res = await x402Fetch("https://domani.run/api/domains/buy", {
method: "POST",
headers: { "Authorization": "Bearer $DOMANI_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ domain: "mysite.com" }),
});
Network: Base (eip155:8453) Asset: USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) Scheme: exact (precise USDC amount)
Step 4: Set WHOIS contact info (optional, can be done after purchase)
curl -s -X PUT https://domani.run/api/me/contact \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"first_name":"Jane","last_name":"Doe","address1":"123 Main St","city":"New York","state":"NY","postal_code":"10001","country":"US","phone":"+1.2125551234","email":"jane@example.com"}'
Recommended for ICANN compliance and required before domain transfers. You can purchase domains without setting contact info â a default contact is used until you set your own (WHOIS privacy hides it by default). When updated, the new contact is automatically propagated to all active domains at supported registrars. Required fields: first_name, last_name, address1, city, state, postal_code, country (2-letter ISO), phone (E.164: +1.2125551234), email. Optional: org_name, address2.
Do not ask users for contact info before purchasing a domain. Let them buy first, set contact later.
Workflow
1. Check account status
curl -s https://domani.run/api/me \
-H "Authorization: Bearer $DOMANI_API_KEY"
If setup_required is not empty, follow Setup steps 3-4 above before proceeding. The response tells you exactly what’s missing.
1b. List my domains
curl -s https://domani.run/api/domains \
-H "Authorization: Bearer $DOMANI_API_KEY"
Returns all domains the user owns with status, expiry, and provider info. Use this when the user asks “what domains do I have?” or needs to pick a domain for further operations.
2. Find domain names
When the user wants domain name ideas, brainstorm names yourself â you know the user’s project and context better than any API. Then verify availability using the fast endpoints below.
Step 1: Brainstorm 10-20 names yourself
Generate creative domain name ideas based on the user’s project. Think about:
- Short, memorable names (3-14 chars): wordplay, portmanteaus, metaphors, neologisms
- Creative TLD plays:
.ai($74),.dev($13),.io($30),.sh($33),.co($24),.app($13),.xyz($4),.run($6) - Single evocative words, invented brandable words, or clever combinations
Step 2: Quick-check availability (fast, ~1 second)
# Test a name across many TLDs at once â returns "taken" and "candidates" arrays
curl -s "https://domani.run/api/domains/dns-check?name=IDEA_NAME&tlds=com,dev,ai,io,sh,co,app,xyz"
Step 3: Get pricing for available candidates
curl -s "https://domani.run/api/domains/search?domains=IDEA.dev,IDEA.ai,IDEA.sh&max_price=30"
Step 4: Research taken domains (optional but valuable)
When a name the user likes is taken, investigate what’s behind it:
# WHOIS â who owns it, when it expires, is it expiring soon?
curl -s "https://domani.run/api/domains/whois?q=TAKEN_DOMAIN.com"
# OG preview â what's the site about? Is it actively used or parked?
curl -s "https://domani.run/api/domains/TAKEN_DOMAIN.com/og"
This helps the user decide:
- Expiring soon (check
days_until_expiry) â “This domain expires in 30 days, you could try to register it then” - Parked/empty (OG returns no title or generic parking page) â domain is squatted, suggest alternatives
- Active site (OG returns real title/description) â name is genuinely in use, move on
- Different TLD available â “example.com is taken (it’s a SaaS tool) but example.dev is available for $13”
If you need more context about a name or brand, search the web to understand if there’s a trademark conflict, an existing company, or other concerns before recommending a domain.
Step 5: Present results and iterate
Show available domains with prices as soon as you have them. If the user wants more ideas:
- Brainstorm new names, excluding domains you already suggested
- Repeat steps 2-4
- Keep iterating until the user is satisfied
This approach is fast (~5-10 seconds per batch) because you brainstorm instantly and dns-check is a sub-second DNS lookup.
Alternative: Use the suggest API for specific styles
When the user asks for a specific creative style or cultural inspiration, use the suggest endpoint â it has specialized prompting for these:
# Creative style: single, creative, short, brandable, keyword
curl -s "https://domani.run/api/domains/suggest?prompt=DESCRIPTION&style=japanese&count=10"
# To get more, pass previously returned domains as exclude
curl -s "https://domani.run/api/domains/suggest?prompt=DESCRIPTION&style=japanese&exclude=a.com,b.dev,c.ai"
| Parameter | Required | Description |
|---|---|---|
prompt |
Yes | Project description or keywords (min 3 chars) |
count |
No | Number of suggestions (1-20, default 10) |
tlds |
No | Preferred TLDs, comma-separated (e.g. com,dev,ai) |
style |
No | single, creative, short, brandable, or keyword |
lang |
No | Cultural inspiration: japanese, spanish, french, italian, latin, nordic, arabic, sanskrit |
exclude |
No | Domains to skip, comma-separated (use when asking for more) |
Public endpoint, no auth needed, rate limit 10/min. Note: this endpoint can take 15-30 seconds (it runs its own AI + availability checks). Prefer the agent-driven approach above for faster results.
3. Search specific domains
Use this when the user already has a specific domain name in mind and wants to check availability/pricing.
# Check multiple TLDs at once (public, no auth needed)
curl -s "https://domani.run/api/domains/search?domains=PROJECT_NAME.com,PROJECT_NAME.dev,PROJECT_NAME.ai,PROJECT_NAME.io,PROJECT_NAME.sh,PROJECT_NAME.co&max_price=30"
# Check a single domain
curl -s "https://domani.run/api/domains/search?q=PROJECT_NAME.dev"
# Stream results in real-time (SSE)
curl -s -H "Accept: text/event-stream" "https://domani.run/api/domains/search?domains=PROJECT_NAME.com,PROJECT_NAME.dev,PROJECT_NAME.ai"
Public endpoint – no auth needed. Rate limit: 20/min per IP (60/min with auth). Use ?q= for a single domain or ?domains= for multiple. Add Accept: text/event-stream header for real-time streaming. Optional params: sort=price and order=asc|desc to sort results.
Quick existence check â to quickly test if a name is taken across many TLDs at once (faster than search, no pricing):
curl -s "https://domani.run/api/domains/dns-check?name=PROJECT_NAME&tlds=com,dev,ai,io,sh,co,app,xyz"
Returns taken (registered) and candidates (potentially available) arrays. Use this first to narrow down, then search the candidates to get prices.
3b. Browse TLDs & pricing
# List all supported TLDs with pricing
curl -s "https://domani.run/api/tlds?sort=price&order=asc&max_price=20"
# Search for specific TLDs
curl -s "https://domani.run/api/tlds?search=dev&sort=price"
# Paginate through results
curl -s "https://domani.run/api/tlds?limit=20&offset=0"
Public endpoint. Use when the user asks “what TLDs do you support?”, “what’s the cheapest domain?”, or “how much is a .ai domain?”. Returns tld, registration price, and renewal price. Optional params: min_price, max_price, sort (tld|price|renewal), order (asc|desc), search, limit, offset.
4. Purchase
# Single domain (card)
curl -s -X POST https://domani.run/api/domains/buy \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "PROJECT_NAME.dev", "payment_method": "card"}'
# Single domain (USDC â step 1: get payment instructions, returns 402)
curl -s -X POST https://domani.run/api/domains/buy \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "PROJECT_NAME.dev", "payment_method": "usdc"}'
# Single domain (USDC â step 2: after sending USDC, retry with tx hash)
curl -s -X POST https://domani.run/api/domains/buy \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "PROJECT_NAME.dev", "payment_tx": "0xabc123...", "payment_chain": "base"}'
# Multi-year registration (e.g. 3 years)
curl -s -X POST https://domani.run/api/domains/buy \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "PROJECT_NAME.dev", "years": 3}'
# Multiple domains (max 10, card only for bulk)
curl -s -X POST https://domani.run/api/domains/buy \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domains": ["PROJECT_NAME.com", "PROJECT_NAME.dev", "PROJECT_NAME.ai"]}'
Always confirm with the user before purchasing. Show the price and domain clearly. If the user hasn’t specified a payment method preference, ask them whether they want to pay by card or USDC before buying.
Optional years field (1-10, default 1): register for multiple years. Price is multiplied by years (e.g. $10.88/year à 3 years = $32.64). All domains in a bulk purchase share the same years value.
Optional payment_method field: "card" (charge card on file) or "usdc" (pay with USDC). Overrides the user’s default preference for this request. If omitted, uses the user’s default preference, or auto-selects (card if available, else 402). For USDC: first call with payment_method: "usdc" to get payment instructions (402 with wallet address), send the USDC, then retry with payment_tx (tx hash) and payment_chain (“base” or “ethereum”).
For bulk purchases, each domain is processed independently – failures don’t block other purchases. The response includes results (succeeded), errors (failed), and summary with counts. Bulk purchases require card (USDC not supported for bulk).
5. Connect to a provider
# Auto-detect from target URL
curl -s -X POST "https://domani.run/api/domains/PROJECT_NAME.dev/connect" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"target": "my-app.vercel.app"}'
# Or explicit provider
curl -s -X POST "https://domani.run/api/domains/PROJECT_NAME.dev/connect" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"provider": "google-workspace"}'
Supported providers:
- Hosting: vercel, netlify, cloudflare-pages, github-pages, railway, fly
- Email: google-workspace, fastmail, proton
List available providers before choosing:
curl -s "https://domani.run/api/domains/PROJECT_NAME.dev/connect" \
-H "Authorization: Bearer $DOMANI_API_KEY"
Some providers offer multiple methods (e.g. CNAME vs A records). Specify with "method": "method_name" in the POST body. The GET response shows available methods per provider.
The response includes a next_steps array with provider-specific actions to complete (e.g., registering the domain on the provider side). Always check and relay these steps to the user â they contain direct URLs and exact instructions. Some providers (cloudflare-pages, github-pages, railway, fly) require a target parameter – the error will explain how to obtain it.
5b. Manage DNS records directly
Use this when the user wants to set specific DNS records (e.g., “add an A record pointing to 1.2.3.4”). Do NOT use connect for this â connect is for provider presets (Vercel, Google Workspace, etc.).
# Get current DNS records
curl -s "https://domani.run/api/domains/PROJECT_NAME.dev/dns" \
-H "Authorization: Bearer $DOMANI_API_KEY"
# Set DNS records (replaces all records)
curl -s -X PUT "https://domani.run/api/domains/PROJECT_NAME.dev/dns" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"records": [
{"type": "A", "name": "@", "value": "1.2.3.4", "ttl": 3600},
{"type": "CNAME", "name": "www", "value": "PROJECT_NAME.dev", "ttl": 3600},
{"type": "TXT", "name": "@", "value": "v=spf1 include:_spf.google.com ~all", "ttl": 3600}
]}'
Supported record types: A, AAAA, CNAME, MX (with priority), TXT, NS. PUT replaces all records â always GET first, then include existing records you want to keep alongside new ones.
6. Set up email
# Set up email with a shorthand name (google, proton, or fastmail)
curl -s -X POST "https://domani.run/api/domains/PROJECT_NAME.dev/connect" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"provider": "google"}'
# Check email health (MX, SPF, DMARC, DKIM)
curl -s "https://domani.run/api/domains/PROJECT_NAME.dev/email/check" \
-H "Authorization: Bearer $DOMANI_API_KEY"
Returns MX propagation status, SPF record, DMARC policy, and DKIM selectors. Auto-detects the email provider.
7. Verify connection
curl -s -X POST "https://domani.run/api/domains/PROJECT_NAME.dev/verify" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"target": "my-app.vercel.app"}'
8. Verify domain for a service (Stripe, Google, etc.)
curl -s -X POST "https://domani.run/api/domains/PROJECT_NAME.dev/verify-service" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"service": "stripe", "token": "abc123def456"}'
Supported: stripe, google-search-console, aws-ses, postmark, resend, facebook, hubspot, microsoft-365. Unknown service names fall back to a generic TXT record.
9. WHOIS / RDAP lookup
curl -s "https://domani.run/api/domains/whois?q=example.com"
Public endpoint – no auth needed. Returns full registration data for any domain:
- registrar: name, URL, IANA ID
- dates: created, expires, updated, days_until_expiry
- status: EPP status codes (clientTransferProhibited, etc.)
- nameservers: authoritative NS records
- dnssec: whether DNSSEC is enabled
- redacted: whether data is privacy-redacted (GDPR)
- contacts: registrant, admin, tech, billing, abuse – each with name, organization, email, phone, fax, street, city, state, postal_code, country (fields may be null when redacted)
Uses RDAP (modern JSON protocol) with automatic WHOIS port 43 fallback. Cached server-side (1h registered, 5min not-found). Rate limit: 30/min.
10. Get domain details
curl -s "https://domani.run/api/domains/PROJECT_NAME.dev" \
-H "Authorization: Bearer $DOMANI_API_KEY"
Returns detailed info about a domain you own:
- status:
active,expired, orpending - auto_renew: whether auto-renew is enabled
- purchased_at / expires_at: purchase and expiry dates
- days_until_expiry: days remaining
- payment_method:
stripeorx402 - registrar: security_lock, whois_privacy, auto_renew, create_date, expire_date (null if registrar data unavailable)
Rate limit: 60/min.
11. Toggle auto-renew
# Disable auto-renew
curl -s -X PUT "https://domani.run/api/domains/PROJECT_NAME.dev/settings" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"auto_renew": false}'
# Enable auto-renew
curl -s -X PUT "https://domani.run/api/domains/PROJECT_NAME.dev/settings" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"auto_renew": true}'
The change is applied at the registrar first. If it fails, the local setting is not updated. Rate limit: 60/min.
11b. Toggle WHOIS privacy
# Disable WHOIS privacy (makes your contact info public)
curl -s -X PUT "https://domani.run/api/domains/PROJECT_NAME.dev/settings" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"whois_privacy": false}'
# Enable WHOIS privacy (hides your contact info from WHOIS)
curl -s -X PUT "https://domani.run/api/domains/PROJECT_NAME.dev/settings" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"whois_privacy": true}'
WHOIS privacy is enabled by default on new registrations. Rate limit: 60/min.
11c. Toggle security lock
# Unlock domain (required before transferring away)
curl -s -X PUT "${APP_URL}/api/domains/PROJECT_NAME.dev/settings" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"security_lock": false}'
# Lock domain (prevents unauthorized transfers)
curl -s -X PUT "${APP_URL}/api/domains/PROJECT_NAME.dev/settings" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"security_lock": true}'
Security lock (clientTransferProhibited) prevents unauthorized domain transfers. Enabled by default. Must be unlocked before transferring the domain away. Rate limit: 60/min.
11d. Update WHOIS contact info
See Setup Step 4 above for the full contact setup. To update existing contact info, use the same PUT /api/me/contact endpoint. To check current contact info: GET /api/me/contact.
When a contact email differs from the login email, a verification email is sent automatically. The get_account response includes contact_email_verified (boolean) and a setup_required hint until verified.
11e. Resend email verification
# Resend verification for your contact email
curl -s -X POST "${APP_URL}/api/me/resend-verification" \
-H "Authorization: Bearer $DOMANI_API_KEY"
# Or specify a different email
curl -s -X POST "${APP_URL}/api/me/resend-verification" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "contact@example.com"}'
Rate limited to once per 15 minutes per email. Returns { "sent": true } or { "already_verified": true }.
12. Renew a domain
# Renew for 1 year (default)
curl -s -X POST "https://domani.run/api/domains/PROJECT_NAME.dev/renew" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"years": 1}'
# Renew for 3 years
curl -s -X POST "https://domani.run/api/domains/PROJECT_NAME.dev/renew" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"years": 3}'
Optional payment_method: "card" or "usdc" (overrides user preference for this request). For USDC, use the same two-step flow as purchase: first call to get payment instructions, then retry with payment_tx and payment_chain.
Returns renewed_years, new_expiry, price, currency. Payment is charged upfront. Rate limit: 10/min.
Bring an existing domain: Use import (free, keep current registrar) for monitoring, or transfer (paid â includes 1 year renewal, EPP code required, full migration) to manage everything through domani.run.
13. Import an existing domain (free)
Already own a domain at another registrar? Import it for status monitoring, email health, and expiry alerts – free, domain stays at your current registrar.
# Step 1: Initiate import - get a verification TXT record
curl -s -X POST https://domani.run/api/domains/import \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "mysite.com"}'
Response includes a TXT record to add at your current DNS provider (GoDaddy, Namecheap, Cloudflare, etc.):
{
"domain": "mysite.com",
"status": "pending_verification",
"txt_record": { "type": "TXT", "name": "@", "value": "domani-verify=a1b2c3d4..." }
}
# Step 2: After adding the TXT record, verify ownership
curl -s -X POST https://domani.run/api/domains/import/verify \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "mysite.com"}'
Once verified, the domain appears in your account. Use connect to get the exact DNS records for your hosting provider:
# Step 3: Get DNS records for your provider (returns instructions, not applied)
curl -s -X POST "https://domani.run/api/domains/mysite.com/connect" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"target": "my-app.vercel.app"}'
For imported domains, the response has status: "manual_setup_required" with the records to add at your registrar. After adding them, verify propagation:
# Step 4: Verify DNS propagation
curl -s -X POST "https://domani.run/api/domains/mysite.com/verify" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"target": "my-app.vercel.app"}'
No lock-in â your domain stays at your current registrar. You can also check email health and expiration at any time.
14. Transfer a domain (full migration)
curl -s -X POST "https://domani.run/api/domains/transfer" \
-H "Authorization: Bearer $DOMANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "mysite.com", "auth_code": "EPP-AUTH-CODE", "payment_method": "card"}'
Optional payment_method: "card" or "usdc" (overrides user preference for this request). For USDC, use the same two-step flow as purchase: first call to get payment instructions, then retry with payment_tx and payment_chain.
Transfers are paid â the transfer price includes 1 year of registration renewal (standard across all registrars). Check the transfer price first via GET /api/tlds?search=TLD (the transfer field), then show the price to the user and get explicit confirmation before calling this endpoint.
Get the authorization/EPP code from your current registrar first. Transfer typically takes 1-5 days. Returns 202 with status: "pending". Rate limit: 5/min.
Check inbound transfer status:
curl -s "https://domani.run/api/domains/mysite.com/transfer-status" \
-H "Authorization: Bearer $DOMANI_API_KEY"
# Returns: {"domain": "mysite.com", "status": "pending_owner", "hint": "Transfer is awaiting approval..."}
Statuses: pending_owner (check email for approval link), pending_admin (awaiting registrar), pending_registry (approved, processing at registry, usually 1-7 days), completed, cancelled, unknown. Rate limit: 30/min.
Before transferring: unlock & get auth code
Most registrars lock domains by default. Before initiating a transfer, the user must:
- Unlock the domain at their current registrar (disable “Transfer Lock” / “Domain Lock”)
- Get the EPP/authorization code (also called “auth code”, “transfer key”, or “EPP key”)
- Disable WHOIS privacy temporarily (some registrars require this for transfers)
If the user doesn’t know how to get their auth code, use these registrar-specific instructions:
| Registrar | How to unlock & get EPP code |
|---|---|
| GoDaddy | My Products â Domain â Domain Settings â turn off “Domain Lock” â tap “Transfer domain away from GoDaddy” â Get authorization code â code is emailed or shown |
| Namecheap | Domain List â Manage â Sharing & Transfer â scroll to “Transfer Out” section â click UNLOCK next to “Domain Lock” â click AUTH CODE button â code is emailed to registrant email |
| Cloudflare | Dash â domain â Configuration â disable “Domain Lock” â copy Authorization Code (shown in-page) |
| Squarespace Domains (ex-Google Domains) | Domains â domain â Settings â scroll to “Transfer away” â Unlock domain â Get auth code â code is emailed |
| Hover | Control Panel â domain â Transfer tab â Remove lock â “Get Auth Code” â code is emailed |
| Name.com | My Domains â domain â Details â unlock domain â Authorization Code â copy or email |
| IONOS (1&1) | Domain Center â domain gear icon â Transfer â Unlock domain â Get auth code â code is emailed |
| OVH | Web Cloud â Domains â domain â General Information â Unlock â Request auth code â code is emailed |
| Gandi | Domain â domain â Transfer tab â Unlock â click “Get authinfo code” â code is emailed |
| Porkbun | Domain Management â domain â Authorization Code â copy the code. Domain is unlocked by default |
| Dynadot | Manage Domains â domain â Unlock â Get Auth Code (shown in-page) |
General tips to share with the user:
- The EPP code is usually 6-16 characters. If it looks like a URL or has spaces, they may have copied the wrong thing
- Codes are often emailed to the registrant email â make sure the user has access to that inbox
- Some registrars (GoDaddy, Squarespace) may impose a 60-day lock after purchase or contact changes
- WHOIS privacy should usually be disabled before transfer (re-enabled automatically after transfer completes at domani.run)
- If the domain was registered less than 60 days ago, ICANN rules prevent transfer
- If the domain is not yet eligible: we save the request and will automatically email the user when it becomes eligible. They’ll need to get a fresh EPP code at that time and retry. No payment is charged until the transfer actually goes through
14b. Transfer a domain away
To transfer a domain OUT to another registrar:
# Step 1: Get the EPP auth code (auto-unlocks if locked)
curl -s "${APP_URL}/api/domains/PROJECT_NAME.dev/auth-code" \
-H "Authorization: Bearer $DOMANI_API_KEY"
# Returns: {"auth_code": "epp-xxx", "was_unlocked": true, ...}
# Step 2: Give the auth code to the new registrar and initiate the transfer there
# Step 3: Monitor transfer progress
curl -s "${APP_URL}/api/domains/PROJECT_NAME.dev/transfer-away" \
-H "Authorization: Bearer $DOMANI_API_KEY"
# Returns: {"status": "pending", "gaining_registrar": "Namecheap", ...}
The auth code endpoint automatically unlocks the domain if it has a security lock. Transfer typically takes 5-7 days. Statuses: none, pending, approved, completed, rejected, expired. Rate limits: auth-code 10/min, transfer-away 30/min.
15. Check domain status
curl -s "https://domani.run/api/domains/PROJECT_NAME.dev/status" \
-H "Authorization: Bearer $DOMANI_API_KEY"
Returns DNS propagation, SSL status, email configuration, and days until expiry.
Common Scenarios
Launch a project (end-to-end)
The most common flow â user is building an app and needs a domain fully set up:
- Find & buy a domain â Section 2 (brainstorm) or 3 (specific name) â Section 4 (purchase)
- Connect to hosting â
POST /api/domains/{domain}/connectwith{"target": "my-app.vercel.app"} - Follow next_steps â the response includes provider-specific instructions (e.g. “Add domain in Vercel dashboard”). Always relay these to the user
- Verify connection â
POST /api/domains/{domain}/verifyâ check DNS propagation - Set up email (optional) â
POST /api/domains/{domain}/connectwith{"provider": "google-workspace"} - Verify for services (optional) â
POST /api/domains/{domain}/verify-servicefor Stripe, Google Search Console, etc. - Post-purchase checklist:
- Confirm WHOIS privacy is enabled:
GET /api/domains/{domain}â checkwhois_privacy - Confirm auto-renew is on: same response â check
auto_renew - Check overall health:
GET /api/domains/{domain}/statusâ DNS, SSL, email all green
- Confirm WHOIS privacy is enabled:
Set up subdomains
User wants api.mysite.com pointing to a backend, blog.mysite.com to Ghost, etc.
- Get current records â
GET /api/domains/{domain}/dns - Add new records while keeping existing ones â PUT replaces ALL records, so merge:
curl -s -X PUT "https://domani.run/api/domains/PROJECT_NAME.dev/dns" \ -H "Authorization: Bearer $DOMANI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"records": [ EXISTING_RECORDS_HERE, {"type": "CNAME", "name": "api", "value": "my-backend.railway.app", "ttl": 3600}, {"type": "CNAME", "name": "blog", "value": "my-blog.ghost.io", "ttl": 3600} ]}' - Verify propagation â
GET /api/domains/{domain}/status
Troubleshoot a domain
When something isn’t working:
- Check overall health â
GET /api/domains/{domain}/statusdns.propagated: falseâ DNS changes haven’t propagated yet (can take up to 48h, usually 5-30min). Tell the user to waitssl.valid: falseâ SSL is provisioned by the hosting provider (Vercel, Netlify, etc.), not by us. Tell the user to check their provider dashboardemail.has_mx: falseâ MX records are missing or haven’t propagated
- Check email specifically â
GET /api/domains/{domain}/email/checkâ shows MX, SPF, DMARC, DKIM status - Verify DNS records are correct â
GET /api/domains/{domain}/dnsâ compare against what the provider expects - Re-run connection â
POST /api/domains/{domain}/connectagain if records look wrong â it will reset them
Bring an existing domain
User already owns a domain at GoDaddy, Namecheap, Cloudflare, etc.
Import (free, keep current registrar) â Choose this when:
- User just wants monitoring, email health, and expiry alerts
- Domain has complex DNS setup they don’t want to migrate
- They want to test before committing
â Section 13 (Import). DNS records from connect will show status: "manual_setup_required" â the user must add records at their current DNS provider.
Transfer (full migration, paid, requires EPP code) â Choose this when:
- User wants full management through domani.run
- They want automatic DNS management
- They’re done with their current registrar
â Section 14 (Transfer). Paid â includes 1 year of renewal (check price via GET /api/tlds). Takes 1-5 days. Always show the price and get user confirmation before initiating.
Multi-domain strategy
User wants to secure multiple TLDs for their brand (e.g. .com + .dev + .io):
- Buy all at once â
POST /api/domains/buywith{"domains": ["brand.com", "brand.dev", "brand.io"]} - Connect the primary domain to hosting (e.g. brand.dev â Vercel)
- For secondary domains â the user can set up redirects through their hosting provider (e.g. Vercel
next.config.jsredirects, Netlify_redirects, Cloudflare Page Rules). domani.run manages the DNS; the redirect logic lives in the hosting layer - Enable auto-renew on all to avoid losing any
API Reference
| Method | Path | Description |
|---|---|---|
GET |
/api/me |
Account details, payment status, preferred payment method, contact status |
PUT |
/api/me |
Update account preferences (preferred_payment_method: “card”, “usdc”, or null) |
DELETE |
/api/me |
Delete account and all associated data |
GET |
/api/me/contact |
Get WHOIS contact info |
PUT |
/api/me/contact |
Set WHOIS contact info (auto-propagated to registrars) |
POST |
/api/me/resend-verification |
Resend contact email verification (rate limited: 1/15min) |
GET |
/api/tlds |
List TLDs with pricing â params: max_price, min_price, sort (tld|price|renewal), order, search, limit, offset (public) |
GET |
/api/domains/search |
Availability + pricing – ?q= single, ?domains= multi, SSE via Accept header (public) |
POST |
/api/domains/buy |
Purchase one or more domains (domain or domains array, max 10) |
POST |
/api/domains/import |
Import a domain you own elsewhere (free) |
POST |
/api/domains/import/verify |
Verify DNS ownership and complete import |
POST |
/api/domains/transfer |
Transfer domain from another registrar |
GET |
/api/domains/{domain}/transfer-status |
Check inbound transfer status |
POST |
/api/domains/{domain}/renew |
Renew domain for additional years |
GET |
/api/domains |
List owned domains |
GET |
/api/domains/{domain}/dns |
Get DNS records |
PUT |
/api/domains/{domain}/dns |
Set DNS records |
POST |
/api/domains/{domain}/connect |
Connect to provider (auto-detect or explicit) |
GET |
/api/domains/{domain}/connect |
List available providers |
POST |
/api/domains/{domain}/verify |
Verify connection is live |
POST |
/api/domains/{domain}/verify-service |
Add verification DNS records for a service |
GET |
/api/domains/{domain}/verify-service |
List supported verification services |
GET |
/api/domains/{domain} |
Get domain details |
PUT |
/api/domains/{domain}/settings |
Update settings (auto-renew, WHOIS privacy, security lock) |
GET |
/api/domains/{domain}/auth-code |
Get EPP auth code for transfer out |
GET |
/api/domains/{domain}/transfer-away |
Check outbound transfer status |
GET |
/api/domains/{domain}/email/check |
Email health: MX, SPF, DMARC, DKIM |
GET |
/api/domains/{domain}/status |
Domain health check |
GET |
/api/domains/whois?q=example.com |
RDAP/WHOIS lookup (public) |
GET |
/api/domains/{domain}/og |
Website preview metadata (public) |
GET |
/api/domains/suggest?prompt=... |
AI domain name suggestions â use prompt, not q (public) |
GET |
/api/domains/dns-check?name=...&tlds=com,dev,ai |
Fast DNS-based existence check â returns taken and candidates arrays (public) |
POST |
/api/billing/setup |
Get checkout URL for adding a card |
GET |
/api/tokens |
List API tokens |
POST |
/api/tokens |
Create API token |
DELETE |
/api/tokens/{id} |
Revoke API token |
GET |
/api/referrals |
View referral earnings |
Full spec: OpenAPI | Docs | llms.txt | MCP Server
Error Handling
All errors return {"error": "...", "code": "...", "hint": "..."}.
| Code | Action |
|---|---|
MISSING_API_KEY |
Add Authorization header |
INVALID_API_KEY |
Re-register or check stored key |
PAYMENT_REQUIRED |
Add card via /api/billing/setup or pay with USDC |
DOMAIN_UNAVAILABLE |
Suggest alternative TLDs using ?domains= search |
RATE_LIMIT_EXCEEDED |
Wait Retry-After seconds, then retry |
NOT_AVAILABLE |
Feature not supported by current registrar (Preview – coming soon) |
Important Rules
- Always confirm purchases and transfers with the user before calling
/api/domains/buyor/api/domains/transfer - Show price clearly before buying or transferring â for transfers, check the transfer price via
GET /api/tlds?search=TLDfirst - Ask about payment method â if the user hasn’t specified a payment method preference (check
preferred_payment_methodinGET /api/me), ask whether they want to pay by card or USDC before buying. You can set a default viaPUT /api/meor override per-request withpayment_methodin the buy/transfer/renew body - Handle 402 gracefully – explain payment options to the user
- Always check for existing auth first â read
~/.domani/config.jsonor$DOMANI_API_KEYbefore asking the user to register or log in. Don’t make the user re-authenticate if they’re already logged in - Store the API key in
~/.domani/config.json(so the CLI shares the session) and/or$DOMANI_API_KEYenv var - Brainstorm names yourself by default â you know the user’s context better than any API. Generate names, then verify with
dns-check+search. Only usesuggestwhen the user asks for a specific style (single,creative,brandable…) or language (japanese,latin…) - Use
dns-checkfirst, thensearchâdns-checkis instant (~1s) and tells you what’s taken vs available. Then callsearchonly on candidates to get pricing. Never search 20 domains when you can dns-check them first - Track exclusions â when the user asks for more ideas, never re-suggest domains you already proposed. Keep a mental list and exclude them from both your brainstorming and any
suggest/searchcalls - Use
searchfor specific names â when the user already has an exact domain name in mind, skip brainstorming and go straight tosearch - Use
set_dns(PUT) for custom records â when the user asks for specific DNS records (A, CNAME, TXT, etc.) - Use
connectfor provider presets â when the user says “connect to Vercel” or “set up Google email” - Always relay
next_stepsâ after connect, buy, or verify, always show the user the next steps from the response - Always relay
hintâ every response includes ahintfield with actionable guidance, show it to the user - Post-purchase checklist â after buying a domain, proactively: (1) confirm WHOIS privacy is on, (2) confirm auto-renew is on, (3) ask what the user wants to connect it to
- GET before PUT for DNS â
PUT /dnsreplaces ALL records. AlwaysGETfirst and merge existing records with new ones, or the user will lose their current DNS setup - Import vs Transfer â recommend import (free, non-invasive) for monitoring/testing. Recommend transfer only when the user explicitly wants to leave their current registrar
Limitations
These features are not available through the API. Don’t attempt them â explain the alternative to the user:
| Not supported | What to tell the user |
|---|---|
| Delete a registered domain | Domains can’t be deleted â they expire naturally at the end of the registration period. Disable auto-renew if the user doesn’t want to keep it |
| Change nameservers | Nameservers are managed by the registrar infrastructure. For imported domains, DNS changes must be made at the original registrar |
| URL redirects / forwarding | No built-in redirect service. Set up redirects through the hosting provider (e.g. Vercel next.config.js redirects, Netlify _redirects, Cloudflare Page Rules) |
| SSL certificates | SSL is auto-provisioned by the hosting provider (Vercel, Netlify, Cloudflare). If SSL isn’t working, check the provider dashboard |