API Reference
One API to search, register, and connect domain names. Built for AI agents, developers, and automation.
Authentication
All authenticated endpoints require a Bearer token in the Authorization header.
curl https://domani.run/api/domains/search?q=example.com \ -H "Authorization: Bearer domani_sk_xxx"
Get an API key by creating an account or from the dashboard. Some endpoints (search, TLDs, WHOIS) are public but have lower rate limits without auth.
Token Scopes
Tokens can be restricted to specific permission scopes. Use * for full access (default). Create scoped tokens via POST /api/tokens with a scopes array.
| Scope | Grants access to |
|---|---|
| domains:read | GET /api/domains, GET /api/domains/{domain}, GET /api/domains/{domain}/dns, /status, /email/check, /auth-code, /transfer-away, /transfer-status, /analytics |
| domains:write | PUT /api/domains/{domain}/dns, POST /connect, POST /verify, PUT /settings, PUT /parking, PUT/DELETE /api/domains/{domain}/for-sale, POST /api/domains/import, POST /import/verify |
| domains:transfer | POST /api/domains/buy, POST /transfer, POST /renew (involves payment, includes marketplace purchases) |
| tokens:read | GET /api/tokens |
| tokens:write | POST /api/tokens, DELETE /api/tokens/{id} |
| webhooks:read | GET /api/webhooks, GET /api/webhooks/{id}/deliveries |
| webhooks:write | POST /api/webhooks, PATCH /api/webhooks/{id}, DELETE /api/webhooks/{id} |
| email:read | GET /api/emails, /api/emails/{address}, /api/emails/{address}/messages, /api/domains/{domain}/email/status |
| email:write | POST /api/emails, POST /api/emails/{address}/send, POST /api/domains/{domain}/email/setup |
| account:read | GET /api/me |
| account:write | DELETE /api/me, POST /api/billing/setup, POST /api/me/resend-verification |
| billing:read | GET /api/billing/invoices |
| search | GET /api/domains/search, /suggest, /whois, /dns-check, GET /api/tlds |
| deals:read | GET /api/deals, GET /api/deals/{id} |
| deals:write | POST /api/domains/sell, PATCH /api/deals/{id} |
| notifications:read | GET /api/notifications, GET /api/notifications/count |
Scope attenuation: a token can only create child tokens with equal or fewer scopes — never more.
Missing scope returns 403 with code INSUFFICIENT_SCOPE and a hint naming the required scope.
Errors
All errors return a consistent JSON envelope. Use code for programmatic handling and hint for recovery guidance.
{
"error": "Missing API key",
"code": "MISSING_API_KEY",
"hint": "Include 'Authorization: Bearer domani_sk_xxx' header",
"documentation_url": "https://domani.run/llms.txt"
}| Code | HTTP |
|---|---|
| MISSING_API_KEY | 401 |
| INVALID_API_KEY | 401 |
| EXPIRED_API_KEY | 401 |
| MISSING_PARAMETER | 400 |
| INVALID_DOMAIN | 400 |
| PAYMENT_REQUIRED | 402 |
| DOMAIN_UNAVAILABLE | 409 |
| RATE_LIMIT_EXCEEDED | 429 |
| USER_EXISTS | 409 |
| NOT_FOUND | 404 |
| UNKNOWN_PROVIDER | 400 |
| UNKNOWN_METHOD | 400 |
| TARGET_REQUIRED | 400 |
| INVALID_PARAMETER | 400 |
| CONTACT_REQUIRED | 400 |
| INVALID_CONTACT | 400 |
| INSUFFICIENT_SCOPE | 403 |
| SCOPE_ESCALATION | 400 |
| DOMAIN_EXISTS | 400 |
| DOMAIN_NOT_ACTIVE | 400 |
| UNSUPPORTED_TLD | 400 |
| NOT_AVAILABLE | 400 |
| ALREADY_REGISTERED | 400 |
| TRANSFER_NOT_ELIGIBLE | 400 |
| ALREADY_LISTED | 400 |
| LISTING_SOLD | 400 |
| SELF_PURCHASE | 400 |
| DESCRIPTION_TOO_LONG | 400 |
| DEAL_NOT_FOUND | 400 |
| INVALID_DEAL_STATE | 400 |
| EPP_ATTEMPTS_EXCEEDED | 400 |
| ACTIVE_DEAL_EXISTS | 400 |
Rate Limits
Every response includes rate limit headers:
X-RateLimit-Limit— max requests per windowX-RateLimit-Remaining— requests leftX-RateLimit-Reset— unix timestamp when window resets
When rate limited (HTTP 429), check the Retry-After header for seconds to wait.
Authentication
/api/auth/registerPublicCreate a new account
Creates a new user account and returns an API key. Use this key as a Bearer token for all authenticated endpoints. Each account also receives a unique referral code. Pass a ref code to permanently link the new user to a referrer - the referrer earns a commission on all future domain purchases by this user.
Request body
| email* | string (email) | |
| ref | string | Referral code - permanently links this user to the referrer. The referrer earns a commission on every domain purchase. |
Response 201
| api_key | string | Your API key - save this securely |
| referral_code | string | Your unique referral code for earning commissions |
| has_payment_method | boolean | Whether the user has a card on file (always false for new accounts) |
| user | object |
Example response
{
"api_key": "domani_sk_a1B2c3D4e5F6g7H8i9J0kLmN",
"referral_code": "x7k9m2",
"has_payment_method": false,
"user": {
"id": "cm5abc123",
"email": "dev@example.com"
}
}Example
curl -X POST https://domani.run/api/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "dev@example.com"}'/api/auth/loginPublicSend a magic link sign-in email
Sends a sign-in link to the provided email. If the user doesn't exist, creates an account first. The magic link redirects to the dashboard with auth credentials. For programmatic API access, use POST /api/auth/register to get an API key directly.
Request body
| email* | string (email) |
Response 200
| ok | boolean | |
| message | string |
Example response
{
"ok": true,
"message": "Check your email for a sign-in link."
}Example
curl -X POST https://domani.run/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "dev@example.com"}'/api/auth/verifyPublicVerify a magic link token
Validates the magic link token and redirects to the dashboard with auth credentials. Used by the magic link email - not typically called by agents directly. Agents should use POST /api/auth/register instead.
Parameters
| token* | string | Magic link token from email (base64url-encoded) |
Account
/api/meGet current account details
Returns the authenticated user's account information including email, payment status, contact info status, referral code, and usage counts. Agents should call this after authentication to check has_payment_method and has_contact before attempting purchases.
Response 200
| id | string | |
| string (email) | ||
| has_payment_method | boolean | Whether the user has a card on file (required before buying domains) |
| has_contact | boolean | Whether the user has set WHOIS contact info (required before purchasing domains) |
| contact | RegistrantContact | Registrant contact info (null if not set) |
| referral_code | string | Your unique referral code |
| referral_rate | integer | Commission percentage on referred purchases |
| domain_count | integer | Number of domains owned |
| token_count | integer | Number of active API tokens |
| created_at | string (date-time) |
Example response
{
"id": "cm5abc123",
"email": "dev@example.com",
"has_payment_method": true,
"has_contact": true,
"referral_code": "x7k9m2",
"referral_rate": 20,
"domain_count": 3,
"token_count": 2,
"created_at": "2025-01-15T10:30:00.000Z"
}Example
curl https://domani.run/api/me \ -H "Authorization: Bearer domani_sk_xxx"
/api/meUpdate account preferences
Update the authenticated user's account preferences such as preferred payment method.
Request body
| preferred_payment_method* | string | null(card | usdc) | Default payment method for purchases (card, usdc, or null for auto-select) |
Response 200
| preferred_payment_method | string | null | |
| hint | string |
/api/meDelete account and all associated data
Permanently delete the authenticated user's account, all domains, tokens, webhooks, and other associated data. This action cannot be undone.
Response 200
| deleted | boolean | |
| hint | string |
/api/me/contactGet registrant contact info
Returns the authenticated user's WHOIS registrant contact information. Must be set before purchasing or transferring domains.
Response 200
| has_contact | boolean | |
| contact | RegistrantContact |
Example response
{
"has_contact": true,
"contact": {
"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"
}
}Example
curl https://domani.run/api/me/contact \ -H "Authorization: Bearer domani_sk_xxx"
/api/me/contactSet registrant contact info
Set or update your WHOIS registrant contact information. Required by ICANN before purchasing domains. Used as the domain registrant and billing contact. When updated, the new contact is automatically propagated to all active domains at registrars that support contact updates.
Response 200
| contact | RegistrantContact |
Example
curl -X PUT https://domani.run/api/me/contact \
-H "Authorization: Bearer domani_sk_xxx" \
-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"}'/api/me/resend-verificationResend contact email verification
Resend the verification email for the account's contact email address. Rate limited to once per 15 minutes per email.
Request body
| string (email) | Email address to verify (defaults to account contact email) |
Response 200
| sent | boolean | |
| already_verified | boolean | |
| hint | string |
TLDs
/api/tldsPublicList all available TLDs with pricing
Returns all 1014+ available TLDs with registration, renewal, and transfer pricing. Supports filtering by price range, searching by TLD name, sorting, and pagination via limit/offset. When paginated, includes a Link header with the next page URL. Public endpoint - no authentication required. Authenticated requests get higher rate limits (60/min vs 30/min).
Parameters
| max_price | number | Maximum registration price in USD |
| min_price | number | Minimum registration price in USD |
| sort | string(tld | price | renewal) | Sort field - 'tld' (alphabetical), 'price' (registration), or 'renewal' |
| order | string(asc | desc) | Sort order |
| search | string | Filter TLD names containing this string |
| limit | integer | Max results per page |
| offset | integer | Number of results to skip |
Response 200
| tlds | TldInfo[] | |
| total | integer | Total matching TLDs (before pagination) |
| offset | integer | |
| limit | integer | null |
Example response
{
"tlds": [
{
"tld": "xyz",
"registration": 4.04,
"renewal": 4.04,
"transfer": 4.04
},
{
"tld": "com",
"registration": 10.88,
"renewal": 13.08,
"transfer": 13.08
},
{
"tld": "dev",
"registration": 15.85,
"renewal": 15.85,
"transfer": 15.85
}
],
"total": 1014,
"offset": 0,
"limit": null
}Example
curl https://domani.run/api/tlds?max_price=10&sort=price
Domain Search
/api/domains/searchPublicCheck availability and pricing for domains
Checks availability and pricing for one or more domains via RDAP + retail pricing. Use q for a single domain (returns flat object) or domains for multiple (returns array). Supports SSE streaming via Accept: text/event-stream header - closing the connection cancels all in-flight RDAP lookups server-side. Max 200 domains per request. Public endpoint - no authentication required. Authenticated requests get higher rate limits (60/min vs 20/min).
Parameters
| q | string | Single domain including TLD (shorthand - returns flat object). Mutually exclusive with `domains`. |
| domains | string | Comma-separated list of full domain names to check (max 200). Returns array result. |
| max_price | number | Only include domains under this price (USD) |
| sort | string(price | tld) | Sort results by price or TLD name |
| source | string(all | primary | secondary) | Filter results by source: 'primary' (registrar availability only), 'secondary' (Domani marketplace listings only), or 'all' (both) |
| order | string(asc | desc) |
Response 200
| available | boolean | Whether the domain is available for registration |
| domain | string | Full domain name including TLD |
| price | number | Registration price in the specified currency |
| currency | string | ISO 4217 currency code (always USD) |
| error | boolean | True if RDAP lookup failed (availability unknown) |
| for_sale | object | Present when the domain is listed for sale (either on Domani marketplace or detected on an external aftermarket) |
Example
curl "https://domani.run/api/domains/search?q=mysite.com"
/api/domains/suggestPublicAI-powered domain suggestions with availability
Given a project description, generates full domain names with creative TLDs (e.g. codebuddy.dev, wavify.fm), checks availability, and returns only available domains with pricing. Uses an agentic loop that retries until enough available domains are found. Supports SSE streaming via Accept: text/event-stream - closing the connection cancels in-flight LLM generation and RDAP lookups server-side. Public endpoint - no authentication required.
Parameters
| prompt* | string | Project description or keywords to generate domain suggestions from (min 3 characters) |
| count | integer | Number of available domains to return (default 10) |
| exclude | string | Comma-separated list of domains to exclude from suggestions (e.g. from a previous batch) |
| tlds | string | Comma-separated preferred TLDs (without dots). Results will strongly favor these TLDs. |
| style | string(single | creative | short | brandable | keyword) | Name style preference: 'single' (one word), 'creative' (evocative/metaphorical), 'short' (3-6 chars), 'brandable' (invented), 'keyword' (descriptive). |
| lang | string(japanese | spanish | french | italian | latin | nordic | arabic | sanskrit) | Language/cultural inspiration for names. Names will draw from this language's words, aesthetics, and culture. |
Response 200
| suggestions | object[] | Available domain names with pricing |
| checked | integer | Total domains checked |
| iterations | integer | Number of LLM rounds |
Example response
{
"suggestions": [
{
"domain": "codebuddy.dev",
"price": 13,
"currency": "USD"
},
{
"domain": "aiforge.sh",
"price": 33,
"currency": "USD"
},
{
"domain": "devcraft.app",
"price": 13,
"currency": "USD"
}
],
"checked": 42,
"iterations": 2
}Example
curl "https://domani.run/api/domains/suggest?prompt=AI+coding+assistant"
/api/domains/dns-checkPublicFast DNS-based domain existence check
Tests if a domain name is taken across many TLDs at once using DNS queries (faster than search, no pricing). Returns 'taken' (definitely registered) and 'candidates' (potentially available) arrays. Use this to narrow down before calling search for pricing. Public endpoint - no authentication required.
Parameters
| name* | string | Domain name without TLD, e.g. 'myapp' |
| tlds* | string | Comma-separated TLDs to check, e.g. 'com,dev,ai,io' |
Response 200
| name | string | |
| taken | string[] | TLDs that are definitely registered |
| candidates | string[] | TLDs that are potentially available |
Domains
/api/domainsList all your registered domains
Returns all domains owned by the authenticated user, ordered by purchase date (newest first). Includes domain name, status, purchase date, and expiration date.
Response 200
| domains | DomainRecord[] |
Example response
{
"domains": [
{
"domain": "mysite.com",
"status": "active",
"purchasedAt": "2025-01-15T10:30:00.000Z",
"expiresAt": "2026-01-15T10:30:00.000Z",
"emailEnabled": true,
"emailVerified": true,
"emailVerifiedAt": "2025-01-15T10:35:00.000Z",
"mailboxCount": 2
},
{
"domain": "myapp.dev",
"status": "active",
"purchasedAt": "2025-02-01T14:00:00.000Z",
"expiresAt": "2026-02-01T14:00:00.000Z",
"emailEnabled": false,
"emailVerified": false,
"emailVerifiedAt": null,
"mailboxCount": 0
}
]
}Example
curl https://domani.run/api/domains \ -H "Authorization: Bearer domani_sk_xxx"
/api/domains/buyPurchase one or more domains
Purchases one or more domains and registers them immediately. Send {"domain": "..."} for single or {"domains": ["...", "..."]} for bulk (max 10). If the domain is listed for sale on the Domani marketplace, this endpoint automatically routes to a marketplace purchase instead of a fresh registration. The response includes years: 0 and marketplace_purchase: true to distinguish it from a registration. Marketplace purchases are single-domain only. The buyer pays the full listed price; the seller receives the price minus a platform commission (default 10%). Bulk purchases process each domain independently - failures don't block other purchases. The response includes results (succeeded), errors (failed), and summary with counts. Accepts three payment methods: 1. Card (default): Requires a card on file via POST /api/billing/setup. Charged per domain. 2. USDC (two-step): First call without payment to get a 402 with payment_options.crypto (wallet address, amount, chains). Send USDC on-chain, then retry with payment_tx and payment_chain in the request body. 3. x402 USDC (atomic): Include a PAYMENT-SIGNATURE header with a signed USDC payment on Base. Bulk purchases require a card on file (USDC not supported for multi-domain). If no payment is provided for single domain, returns HTTP 402 with payment instructions.
Request body
| domain | string | Single domain to purchase (e.g. mysite.com) |
| domains | string[] | Array of domains to purchase (max 10, requires card on file) |
| payment_method | string(card | usdc) | Payment method: 'card' (default) or 'usdc'. When 'usdc', first call returns 402 with payment instructions. |
| payment_tx | string | Transaction hash of a USDC payment already sent on-chain. Required on the second call of the two-step USDC flow. |
| payment_chain | string(base | ethereum) | Chain the USDC payment was sent on (e.g. 'base' or 'ethereum'). |
| years | integer | Number of years to register (1-10, default 1). Price is multiplied by years. |
| ref | string | Referral code (optional) - the referrer earns a commission |
Response 201
| domain | string | |
| status | string | |
| expires | string (date-time) | |
| price | number | |
| currency | string | |
| years | integer | Number of years registered |
| payment_method | string | How the domain was paid for |
| payment_tx | string | On-chain transaction hash (present for USDC payments) |
| marketplace_purchase | boolean | True when the domain was bought from the Domani marketplace (not a fresh registration) |
Example
curl -X POST https://domani.run/api/domains/buy \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"domain": "mysite.com"}'/api/domains/{domain}Get detailed information about a domain you own
Returns comprehensive information about a domain: status, purchase date, expiry, payment method, security lock, WHOIS privacy, auto-renew status, and registration dates. The registrar object may be null if extended data is unavailable.
Parameters
| domain* | string | Domain name you own |
Response 200
| domain | string | |
| status | string | |
| auto_renew | boolean | Whether auto-renew is enabled |
| purchased_at | string (date-time) | |
| expires_at | string (date-time) | |
| days_until_expiry | integer | Days until domain expiry |
| payment_method | string | How the domain was paid for |
| payment_tx | string | null | On-chain tx hash (USDC payments) |
| registrar | object | null | Domain registration details (null if unavailable) |
Example response
{
"domain": "mysite.com",
"status": "active",
"auto_renew": true,
"purchased_at": "2025-06-01T12:00:00.000Z",
"expires_at": "2026-06-01T12:00:00.000Z",
"days_until_expiry": 365,
"payment_method": "card",
"registrar": {
"security_lock": true,
"whois_privacy": true,
"auto_renew": true,
"create_date": "2025-06-01T12:00:00Z",
"expire_date": "2026-06-01T12:00:00Z"
}
}Example
curl https://domani.run/api/domains/mysite.com \ -H "Authorization: Bearer domani_sk_xxx"
/api/domains/{domain}/settingsUpdate domain settings
Update settings for a domain you own. Supports auto_renew, whois_privacy, and security_lock. At least one field must be provided.
Parameters
| domain* | string | Domain name you own |
Request body
| auto_renew | boolean | Whether to enable auto-renew for this domain |
| whois_privacy | boolean | Whether to enable WHOIS privacy protection |
| security_lock | boolean | Whether to lock the domain against transfers |
Response 200
| domain | string | |
| auto_renew | boolean | |
| whois_privacy | boolean | |
| security_lock | boolean | |
| hint | string |
Example
curl -X PUT https://domani.run/api/domains/mysite.com/settings \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"auto_renew": false}'/api/domains/{domain}/parkingUpdate parking settings
Update parking and listing settings for a domain you own. Enable/disable the parking page and set a 'For Sale' listing price. When parking is enabled, visitors to the domain see a branded page. When a listing price is set, visitors see a 'For Sale' page with a contact form.
Parameters
| domain* | string | Domain name |
Request body
| enabled | boolean | Whether to enable the parking page |
| listing_price | number | null | Sale price in USD, or null to remove listing |
Response 200
| domain | string | |
| parking_enabled | boolean | |
| listing_price | number | null | |
| hint | string |
/api/domains/{domain}/analyticsGet parking analytics
Get visitor analytics for a parked domain - page views, inquiries, conversion rate, 30-day daily breakdown, and recent inquiries.
Parameters
| domain* | string | Domain name |
Response 200
| domain | string | |
| views_7d | integer | Total page views in the last 7 days |
| views_30d | integer | Total page views in the last 30 days |
| inquiries_30d | integer | Total inquiries in the last 30 days |
| conversion_rate | number | Inquiry-to-view ratio as percentage |
| daily_views | object[] | Daily view counts for the last 30 days |
| recent_inquiries | object[] | 5 most recent inquiries |
| hint | string |
Example response
{
"domain": "example.com",
"views_7d": 42,
"views_30d": 156,
"inquiries_30d": 3,
"conversion_rate": 1.92,
"daily_views": [
{
"date": "2026-02-25",
"views": 7
}
],
"recent_inquiries": [
{
"email": "buyer@example.com",
"offer": 500,
"date": "2026-02-25T10:00:00Z"
}
],
"hint": "example.com received 156 views and 3 inquiries in the last 30 days (1.92% conversion)."
}/api/domains/{domain}/auth-codeGet EPP auth code
Get the EPP/auth code needed to transfer a domain to another registrar. Automatically unlocks the domain if it's locked.
Parameters
| domain* | string | Domain name you own |
Response 200
| domain | string | |
| auth_code | string | EPP auth code to give to the new registrar |
| was_unlocked | boolean | Whether the domain was auto-unlocked |
| hint | string | |
| next_steps | string[] |
/api/domains/{domain}/transfer-awayGet outbound transfer status
Check the status of an outbound domain transfer. Use after getting an auth code and initiating the transfer at the new registrar.
Parameters
| domain* | string | Domain name you own |
Response 200
| domain | string | |
| status | string | |
| gaining_registrar | string | |
| request_date | string (date-time) | |
| hint | string |
/api/domains/{domain}/transfer-statusCheck inbound transfer status
Check the status of an inbound domain transfer. Returns detailed status with actionable hints. Use after initiating a transfer with POST /api/domains/transfer.
Parameters
| domain* | string | Domain name you own |
Response 200
| domain | string | |
| status | string | |
| timestamp | string | Timestamp of last status update |
| hint | string | Actionable guidance based on the current status |
/api/domains/importImport an external domain
Import a domain you own at another registrar (GoDaddy, Namecheap, Cloudflare, etc.) to manage through domani.run. Free, no transfer needed. Returns a TXT record for DNS-based ownership verification.
Request body
| domain* | string | Domain to import |
Response 200
| domain | string | |
| status | string | |
| token | string | |
| txt_record | object | |
| hint | string |
/api/domains/import/verifyVerify and complete domain import
Verify DNS TXT record and complete domain import. Call after adding the TXT record returned by POST /api/domains/import. Uses Cloudflare DNS-over-HTTPS to check propagation.
Request body
| domain* | string | Domain to verify |
Response 201
| domain | string | |
| status | string | |
| expires | string (date-time) | |
| registrar | string | |
| payment_method | string | |
| hint | string |
/api/domains/transfer-checkPre-check transfer eligibility
Check whether a domain is eligible for transfer before initiating. Returns transfer price, eligibility status, and any blockers (unsupported TLD, ICANN waiting period, domain locked). Call this before POST /api/domains/transfer.
Parameters
| domain* | string | Domain to check, e.g. mysite.com |
Response 200
| domain | string | |
| tld | string | |
| eligible | boolean | |
| price | number | null | Transfer price in USD (null if TLD unsupported) |
| currency | string | |
| reason | string | Human-readable reason if not eligible |
| code | string | |
| eligible_at | string (date) | ISO date when domain becomes eligible (ICANN waiting period) |
| hint | string |
/api/domains/transfer-watchWatch a domain for transfer eligibility
Watch a domain and get notified (email + transfer.eligible webhook) when it becomes eligible for transfer. Uses RDAP to check EPP status codes and ICANN 60-day lock windows. If already eligible, returns immediately without creating a watch.
Request body
| domain* | string | Domain to watch, e.g. mysite.com |
Response 200
| domain | string | |
| eligible | boolean | Whether the domain is currently eligible for transfer |
| eligible_at | string (date) | ISO date when domain becomes eligible (if not yet eligible) |
| hint | string | Actionable guidance |
| watching | boolean | Whether a notification has been set up |
/api/domains/transferTransfer domain from another provider
Initiate a domain transfer-in. Requires the authorization/EPP code from the current provider. Transfer typically takes 1-5 days to complete. Payment is charged upfront via card or USDC (two-step: get 402, send USDC, retry with payment_tx + payment_chain).
Request body
| domain* | string | Domain to transfer, e.g. mysite.com |
| auth_code* | string | Authorization/EPP code from the current provider |
| payment_method | string(card | usdc) | Payment method: 'card' (default) or 'usdc'. |
| payment_tx | string | Transaction hash of a USDC payment already sent on-chain. |
| payment_chain | string(base | ethereum) | Chain the USDC payment was sent on. |
| extra_subdomains | string[] | Additional subdomains to include in the pre-transfer DNS snapshot. We auto-discover subdomains via CT logs, SPF, MX/DKIM inference, and a common wordlist - use this for any custom subdomains we might miss. |
Response 202
| domain | string | |
| status | string | |
| expires | string (date-time) | |
| price | number | |
| currency | string | |
| payment_method | string | |
| payment_tx | string | |
| auto_renew | boolean | |
| hint | string | |
| dns_snapshot | object | Summary of DNS records captured before the transfer. Records are auto-restored when the transfer completes. |
Example
curl -X POST https://domani.run/api/domains/transfer \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"domain": "mysite.com", "auth_code": "EPP-AUTH-CODE"}'/api/domains/{domain}/renewRenew a domain
Renew a domain you own for additional years (1-10). Payment is charged upfront. The new expiry date is returned in the response.
Parameters
| domain* | string | Domain name you own |
Request body
| years | integer | Number of years to renew (default 1) |
| payment_method | string(card | usdc) | Payment method: 'card' (default) or 'usdc'. |
| payment_tx | string | Transaction hash of a USDC payment already sent on-chain. |
| payment_chain | string(base | ethereum) | Chain the USDC payment was sent on. |
Response 200
| domain | string | |
| renewed_years | integer | |
| new_expiry | string (date-time) | |
| price | number | |
| currency | string | |
| payment_method | string | |
| payment_tx | string |
Example
curl -X POST https://domani.run/api/domains/mysite.com/renew \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"years": 1}'DNS
/api/domains/{domain}/dnsGet DNS records for a domain you own
Returns all DNS records configured for a domain you own. Includes A, AAAA, CNAME, MX, TXT, NS, and other record types.
Parameters
| domain* | string | Domain name you own |
Response 200
| domain | string | |
| records | DnsRecord[] |
Example response
{
"domain": "mysite.com",
"records": [
{
"type": "A",
"name": "@",
"value": "76.76.21.21",
"ttl": 3600
},
{
"type": "CNAME",
"name": "www",
"value": "mysite.com",
"ttl": 3600
},
{
"type": "MX",
"name": "@",
"value": "mail.example.com",
"ttl": 3600,
"priority": 10
}
]
}Example
curl https://domani.run/api/domains/mysite.com/dns \ -H "Authorization: Bearer domani_sk_xxx"
/api/domains/{domain}/dnsSet DNS records for a domain you own
Replaces all DNS records for a domain you own. Send the complete set of records - any existing records not included will be removed.
Parameters
| domain* | string | Domain name you own |
Request body
| records* | DnsRecord[] |
Response 200
| success | boolean | |
| domain | string | |
| records | DnsRecord[] |
Example response
{
"success": true,
"domain": "mysite.com",
"records": [
{
"type": "A",
"name": "@",
"value": "76.76.21.21",
"ttl": 3600
},
{
"type": "CNAME",
"name": "www",
"value": "cname.vercel-dns.com",
"ttl": 3600
}
]
}Example
curl -X PUT https://domani.run/api/domains/mysite.com/dns \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"records": [
{"type": "A", "name": "@", "value": "76.76.21.21", "ttl": 3600},
{"type": "CNAME", "name": "www", "value": "cname.vercel-dns.com", "ttl": 3600}
]}'/api/domains/{domain}/dns/snapshotCapture a DNS snapshot
Captures all DNS records for a domain via public DNS lookups (Cloudflare DoH). Discovers subdomains from Certificate Transparency logs, SPF records, and common names. The snapshot is also stored server-side as a backup.
Parameters
| domain* | string | Domain name you own |
Request body
| extra_subdomains | string[] | Additional subdomains to include in the snapshot |
Response 200
| domain | string | |
| records | DnsRecord[] | |
| subdomains | string[] | |
| sources | string[] | |
| capturedAt | string (date-time) |
Example response
{
"domain": "mysite.com",
"records": [
{
"type": "A",
"name": "@",
"value": "76.76.21.21",
"ttl": 3600
},
{
"type": "CNAME",
"name": "www",
"value": "mysite.com",
"ttl": 3600
},
{
"type": "MX",
"name": "@",
"value": "aspmx.l.google.com",
"ttl": 3600,
"priority": 1
}
],
"subdomains": [
"www",
"mail",
"api"
],
"sources": [
"ct",
"wordlist"
],
"capturedAt": "2026-03-06T12:00:00.000Z"
}Example
curl -X POST https://domani.run/api/domains/mysite.com/dns/snapshot \ -H "Authorization: Bearer domani_sk_xxx"
/api/domains/{domain}/dns/restoreRestore DNS from a snapshot
Applies DNS records from a snapshot to the domain's current registrar. Uses a diff algorithm to avoid duplicating records that already exist. If no snapshot is provided in the body, restores from the server-side backup (created by snapshot or transfer).
Parameters
| domain* | string | Domain name you own |
Request body
| snapshot | object | A DNS snapshot object (as returned by the snapshot endpoint). If omitted, the stored server-side backup is used. |
Response 200
| domain | string | |
| applied | integer | Number of records created |
| skipped | integer | Number of records already present |
| recordCount | integer | Total records in the snapshot |
| errors | string[] |
Example response
{
"domain": "mysite.com",
"applied": 5,
"skipped": 2,
"recordCount": 7,
"errors": []
}Example
curl -X POST https://domani.run/api/domains/mysite.com/dns/restore \ -H "Authorization: Bearer domani_sk_xxx"
/api/domains/{domain}/nameserversGet nameservers for a domain
Returns the authoritative nameservers configured for a domain you own. If empty, DNS operations (parking, email, connect) will fail until nameservers are assigned.
Parameters
| domain* | string | Domain name you own |
Response 200
| domain | string | |
| nameservers | string[] | |
| default_nameservers | string[] | Registrar default nameservers (null if unknown) |
| hint | string |
Example response
{
"domain": "mysite.com",
"nameservers": [
"ns1.systemdns.com",
"ns2.systemdns.com",
"ns3.systemdns.com"
],
"default_nameservers": [
"ns1.systemdns.com",
"ns2.systemdns.com",
"ns3.systemdns.com"
],
"hint": "3 nameservers configured for mysite.com."
}Example
curl https://domani.run/api/domains/mysite.com/nameservers \ -H "Authorization: Bearer domani_sk_xxx"
/api/domains/{domain}/nameserversSet nameservers for a domain
Replace the nameservers for a domain you own. Requires 2–13 valid hostnames. Common values: OpenSRS DNS (ns1.systemdns.com, ns2.systemdns.com, ns3.systemdns.com).
Parameters
| domain* | string | Domain name you own |
Request body
| nameservers* | string[] | Nameserver hostnames (2–13 required) |
Response 200
| success | boolean | |
| domain | string | |
| nameservers | string[] | |
| hint | string |
Example response
{
"success": true,
"domain": "mysite.com",
"nameservers": [
"ns1.systemdns.com",
"ns2.systemdns.com",
"ns3.systemdns.com"
],
"hint": "Nameservers for mysite.com updated to ns1.systemdns.com, ns2.systemdns.com, ns3.systemdns.com. Changes may take up to 48 hours to propagate."
}Example
curl -X PUT https://domani.run/api/domains/mysite.com/nameservers \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"nameservers": ["ns1.systemdns.com", "ns2.systemdns.com", "ns3.systemdns.com"]}'Connect
/api/domains/{domain}/connectConnect a domain to a hosting or email provider
Connects a domain to a hosting or email provider by auto-detecting the provider from a target URL or accepting an explicit provider name. For domains managed through the platform, DNS records are set automatically (status: dns_set). For imported domains (external registrar), returns the records as instructions to add manually at your registrar (status: manual_setup_required, records have status pending). Supported hosting providers: vercel, netlify, cloudflare-pages, github-pages, railway, fly. Supported email providers: google-workspace, fastmail, proton. Use GET /api/domains/{domain}/connect to list all available providers and methods.
Parameters
| domain* | string | Domain name you own |
Request body
| target | string | Target URL for auto-detection (e.g. my-app.vercel.app) |
| provider | string | Explicit provider name (e.g. vercel, google-workspace) |
| method | string | Connection method if provider has multiple (e.g. cname-only) |
Response 200
| domain | string | |
| provider | string | |
| category | string | |
| method | string | |
| records | DnsRecord[] | |
| docs | string (uri) | |
| status | string | dns_set = records written automatically. manual_setup_required = imported domain, add records at your registrar. |
| hint | string | |
| next_steps | string[] | Provider-specific actions to complete after DNS records are set |
Example
curl -X POST https://domani.run/api/domains/mysite.com/connect \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"target": "my-app.vercel.app"}'/api/domains/{domain}/connectList available providers for connecting a domain
Returns all supported hosting and email providers with their connection methods. Use this to discover what providers are available before calling POST.
Parameters
| domain* | string | Domain name you own |
Response 200
| domain | string | |
| providers | object |
/api/domains/{domain}/verifyVerify that a provider connection is working
Runs the provider's verification check to confirm DNS records have propagated and the connection is active. Use after POST /api/domains/{domain}/connect to verify.
Parameters
| domain* | string | Domain name you own |
Request body
| target | string | Target for auto-detection |
| provider | string | Provider name |
| method | string | Method name |
Response 200
| domain | string | |
| provider | string | |
| method | string | |
| verified | boolean | |
| message | string | |
| hint | string | |
| next_steps | string[] | Provider-specific troubleshooting steps (only when verified is false) |
Example
curl -X POST https://domani.run/api/domains/mysite.com/verify \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"target": "my-app.vercel.app"}'Status
/api/domains/{domain}/statusCheck domain health: DNS, SSL, email, expiry
Checks the health of a domain via DNS-over-HTTPS: A/AAAA/CNAME/MX/TXT/NS records, SSL certificate status, email configuration, and expiry date. Supports SSE streaming via Accept: text/event-stream - closing the connection cancels in-flight DNS/SSL checks server-side. Use this after connecting to verify DNS propagation.
Parameters
| domain* | string | Domain name you own |
Response 200
| domain | string | |
| status | string | |
| expires | string (date-time) | |
| days_until_expiry | integer | |
| dns | object | |
| ssl | object | |
| object |
Example response
{
"domain": "mysite.com",
"status": "active",
"expires": "2027-02-18T00:00:00.000Z",
"days_until_expiry": 365,
"dns": {
"propagated": true,
"a": [
"76.76.21.21"
],
"aaaa": [],
"cname": [],
"mx": [],
"txt": [],
"ns": [
"ns1.porkbun.com",
"ns2.porkbun.com"
],
"registrar_records": [
{
"type": "A",
"name": "@",
"value": "76.76.21.21",
"ttl": 3600
}
]
},
"ssl": {
"active": true
},
"email": {
"configured": false,
"mx": []
}
}Example
curl https://domani.run/api/domains/mysite.com/status \ -H "Authorization: Bearer domani_sk_xxx"
/api/domains/{domain}/email/checkCheck email DNS health (MX, SPF, DMARC, DKIM)
Checks email DNS configuration for a domain: MX records, SPF, DMARC, and DKIM. Auto-detects the email provider from MX records (Google Workspace, Proton, Fastmail).
Parameters
| domain* | string | Domain name you own |
Response 200
| domain | string | |
| provider | string | null | Auto-detected email provider (google-workspace, proton, fastmail) or null |
| mx | object | |
| spf | object | |
| dmarc | object | |
| dkim | object |
Example response
{
"domain": "mysite.com",
"provider": "google-workspace",
"mx": {
"configured": true,
"records": [
"1 aspmx.l.google.com",
"5 alt1.aspmx.l.google.com"
]
},
"spf": {
"configured": true,
"value": "v=spf1 include:_spf.google.com ~all"
},
"dmarc": {
"configured": true,
"value": "v=DMARC1; p=quarantine; rua=mailto:dmarc@mysite.com"
},
"dkim": {
"configured": true,
"selectors": [
"google"
]
}
}Example
curl https://domani.run/api/domains/mysite.com/email/check \ -H "Authorization: Bearer domani_sk_xxx"
/api/domains/{domain}/email/setupSet up email on a domain
Pre-configure email DNS on a domain (optional). This is called automatically by create_mailbox when needed. Pass force=true to override existing email provider (Google Workspace, Fastmail, Proton).
Parameters
| domain* | string | Domain name |
Request body
| force | boolean | Override existing email provider (Google Workspace, Fastmail, Proton). Default: false |
Response 200
| domain | string | |
| status | string | |
| verified_at | string | null | |
| records | DnsRecord[] | |
| hint | string |
/api/domains/{domain}/email/setupRemove email from a domain
Remove email from a domain, delete all mailboxes and messages, and set emailEnabled to false.
Parameters
| domain* | string | Domain name |
/api/domains/{domain}/email/statusCheck email DNS status
Check email DNS verification status for a domain. Returns whether the domain is verified and ready, DNS record details, and mailbox count.
Parameters
| domain* | string | Domain name |
Response 200
| domain | string | |
| verified | boolean | |
| records | DnsRecord[] | |
| mailbox_count | integer |
/api/emailCreate a mailbox on domani.run
Create a free email address on domani.run (e.g. hello@domani.run). No domain ownership required. Limited to 1 free @domani.run mailbox per user. Many common slugs are reserved.
Request body
| slug* | string | Local part of the email address (e.g. 'hello' for hello@domani.run) |
| name | string | Display name (defaults to capitalized slug) |
Response 200
| id | string | |
| address | string | |
| slug | string | |
| domain | string |
/api/emailList all mailboxes
List all email addresses (mailboxes) owned by the user across all domains.
Parameters
| domain | string | Filter by domain |
Response 200
| mailboxes | object[] |
/api/emailsCreate a mailbox
Create an email address (mailbox). Provide a full address like "hello@mysite.com". Email DNS is auto-configured on first use. Pass force=true to override existing email provider. Max 5 mailboxes per user. For free @domani.run mailboxes use POST /api/email.
Request body
| address* | string | Full email address, e.g. hello@mysite.com |
| name | string | Display name for outbound emails (e.g. "John Doe") |
| force | boolean | Override existing MX records. Default: false |
Response 200
| id | string | |
| address | string | |
| name | string | null | |
| domain | string | |
| slug | string | |
| webhook_url | string | null | |
| forward_to | string | null | |
| signing_secret | string | HMAC secret for verifying inbound webhook payloads (shown once at creation) |
/api/emailsList all mailboxes
List all email addresses (mailboxes) owned by the user across all domains. Same as GET /api/email.
Parameters
| domain | string | Filter by domain |
Response 200
| mailboxes | object[] |
/api/emails/{address}Get mailbox details
Get details for a specific mailbox by its full email address (URL-encoded, e.g. hello%40mysite.com).
Parameters
| address* | string | Full email address (URL-encoded, e.g. hello%40mysite.com) |
Response 200
| id | string | |
| address | string | |
| slug | string | |
| domain | string | |
| webhook_url | string | null | |
| forward_to | string | null | |
| signing_secret | string | HMAC secret for verifying inbound webhook payloads |
| message_count | integer | |
| created_at | string (date-time) |
/api/emails/{address}Update mailbox
Update a mailbox's display name, webhook URL, or forward-to email address by its full email address. Set to null to disable/clear.
Parameters
| address* | string | Full email address (URL-encoded) |
Request body
| name | string | null | Display name for the mailbox. Set to null to clear. |
| webhook_url | string | null | HTTPS URL for inbound email webhook, or null to clear |
| forward_to | string | null | Email address to forward inbound emails to, or null to clear |
Response 200
| id | string | |
| address | string | |
| slug | string | |
| domain | string | |
| name | string | null | |
| webhook_url | string | null | |
| forward_to | string | null | |
| signing_secret | string | HMAC secret for verifying inbound webhook payloads |
/api/emails/{address}Delete a mailbox
Delete a mailbox by its full email address. Requires confirm=true in body. Without confirm, returns a preview with message_count.
Parameters
| address* | string | Full email address (URL-encoded) |
Request body
| confirm | boolean | Must be true to actually delete. Omit to preview. |
/api/emails/{address}/sendSend an email
Send an email from a mailbox identified by its full email address. Rate limit: 100 emails per hour per mailbox.
Parameters
| address* | string | Sender email address (URL-encoded) |
Request body
| to* | string | Recipient email address(es) |
| cc | any | CC recipient(s) |
| bcc | any | BCC recipient(s) |
| subject | string | |
| text | string | Plain text body |
| html | string | HTML body |
| reply_to | string | Reply-To address |
| in_reply_to | string | Message-ID of the email being replied to (for threading) |
| references | string | Space-separated Message-ID chain (for threading) |
| attachments | object[] | File attachments (max 10, max 40MB total). Content must be base64-encoded. |
Response 200
| id | string | |
| message_id | string | RFC 5322 Message-ID |
| from | string | |
| to | string | |
| status | string | |
| attachments_count | integer |
/api/emails/{address}/messagesList emails
List emails sent and received by a mailbox identified by its full email address. Supports cursor-based pagination and direction filtering.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
| cursor | string | Pagination cursor from previous response |
| limit | integer | Max messages to return |
| direction | string(in | out | all) | Filter by direction (default: all) |
| since | string | ISO timestamp - return only messages created after this date |
| from | string | Filter by sender address |
| to | string | Filter by recipient address |
| subject | string | Filter by subject |
Response 200
| messages | object[] | |
| next_cursor | string | null | |
| has_more | boolean |
/api/emails/{address}/messages/readMark messages as read or unread
Mark one or more messages as read or unread in a mailbox identified by its full email address.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
Request body
| message_ids* | string[] | IDs of messages to mark |
| read* | boolean | true to mark as read, false to mark as unread |
Response 200
| count | integer |
/api/emails/{address}/messages/deleteBulk delete messages
Soft-delete multiple messages at once in a mailbox identified by its full email address.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
Request body
| message_ids* | string[] | IDs of messages to delete |
Response 200
| count | integer |
/api/emails/{address}/messages/{id}Get a message
Fetch a single message by ID from a mailbox identified by its full email address.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
| id* | string | Message ID |
Response 200
| id | string | |
| message_id | string | null | |
| direction | string | |
| from | string | |
| to | string | |
| cc | string | null | |
| reply_to | string | null | |
| subject | string | |
| text | string | null | |
| html | string | null | |
| in_reply_to | string | null | |
| references | string | null | |
| attachments | object[] | |
| status | string | |
| is_read | boolean | |
| read_at | string | null | |
| events | object[] | |
| created_at | string (date-time) |
/api/emails/{address}/messages/{id}Delete a message
Soft-delete a message from a mailbox identified by its full email address.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
| id* | string | Message ID |
Response 200
| id | string | |
| deleted | boolean |
/api/emails/{address}/messages/{id}/replyReply to a message
Reply to a message in a mailbox identified by its full email address. Threading headers are auto-populated.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
| id* | string | Message ID to reply to |
Request body
| text | string | Plain text reply body |
| html | string | HTML reply body |
| cc | any | Additional CC recipient(s) |
| all | boolean | Reply-all: auto-populate CC from original To/CC |
Response 200
| id | string | |
| message_id | string | |
| from | string | |
| to | string | |
| status | string |
/api/emails/{address}/messages/{id}/forwardForward a message
Forward a message from a mailbox identified by its full email address to another recipient.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
| id* | string | Message ID to forward |
Request body
| to* | string | Recipient email address |
| text | string | Optional note to prepend |
Response 200
| id | string | |
| message_id | string | |
| from | string | |
| to | string | |
| status | string |
/api/emails/{address}/avatarGet mailbox avatar info
Returns the Gravatar URL (if set) and signup link for a mailbox identified by its full email address.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
Response 200
| address | string | |
| gravatar_url | string | null | |
| gravatar_signup | string | |
| has_avatar | boolean | |
| hint | string |
/api/emails/{address}/webhookGet mailbox webhook config
Returns the webhook URL, signing secret, and status for a mailbox.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
Response 200
| address | string | |
| webhook_url | string | null | |
| signing_secret | string | null | |
| has_webhook | boolean | |
| hint | string |
/api/emails/{address}/webhookSet mailbox webhook
Set or update the inbound webhook URL for a mailbox. Incoming emails will be POSTed to this URL with HMAC-SHA256 signing.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
Request body
| url* | string | HTTPS webhook URL |
Response 200
| address | string | |
| webhook_url | string | |
| signing_secret | string | |
| has_webhook | boolean | |
| hint | string |
/api/emails/{address}/webhookRemove mailbox webhook
Remove the webhook URL and signing secret from a mailbox. Inbound emails will only be stored.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
Response 200
| address | string | |
| webhook_url | string | null | |
| signing_secret | string | null | |
| has_webhook | boolean | |
| hint | string |
/api/emails/{address}/webhook/rotateRotate webhook signing secret
Regenerate the HMAC-SHA256 signing secret for a mailbox webhook. The old secret is immediately invalidated.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
Response 200
| address | string | |
| webhook_url | string | |
| signing_secret | string | |
| has_webhook | boolean | |
| hint | string |
/api/emails/{address}/webhook/testTest mailbox webhook
Send a signed test payload to the mailbox webhook URL. Returns HTTP status and whether delivery succeeded.
Parameters
| address* | string | Mailbox email address (URL-encoded) |
Response 200
| address | string | |
| webhook_url | string | |
| status | integer | |
| success | boolean | |
| hint | string |
Verify Service
/api/domains/{domain}/verify-serviceAdd DNS records to verify domain ownership for a third-party service
Adds the correct DNS records (TXT, CNAME) to prove domain ownership to a third-party service. Supports Stripe, Google Search Console, AWS SES, Postmark, Resend, Facebook, HubSpot, and Microsoft 365. Unknown service names fall back to a generic TXT record. Records are merged with existing DNS - existing records are preserved.
Parameters
| domain* | string | Domain name you own |
Request body
| service* | string | Service name (e.g. stripe, google-search-console, aws-ses, postmark, resend, facebook, hubspot, microsoft-365) |
| token* | string | Verification token provided by the service |
Response 200
| domain | string | |
| service | string | |
| service_display_name | string | |
| known_service | boolean | |
| records_added | DnsRecord[] | |
| total_records | integer | |
| docs | string (uri) | null | |
| hint | string |
Example response
{
"domain": "mysite.com",
"service": "stripe",
"service_display_name": "Stripe",
"known_service": true,
"records_added": [
{
"type": "TXT",
"name": "@",
"value": "stripe-verification=abc123def456",
"ttl": 3600
}
],
"total_records": 3,
"docs": "https://docs.stripe.com/identity/verification-checks/selfie#domain-verification",
"hint": "Stripe verification records added. Return to Stripe to complete verification."
}Example
curl -X POST https://domani.run/api/domains/mysite.com/verify-service \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"service": "stripe", "token": "abc123def456"}'/api/domains/{domain}/verify-serviceList supported services for domain verification
Returns all known services with their names, descriptions, and documentation URLs. Unknown services fall back to a generic TXT record.
Parameters
| domain* | string | Domain name you own |
Response 200
| domain | string | |
| services | object[] | |
| hint | string |
Discovery
/api/domains/whoisPublicLook up domain registration data via RDAP
Queries the RDAP (Registration Data Access Protocol) network for domain registration data. Returns registrar, creation/expiry/update dates, days until expiry, status codes, nameservers, DNSSEC status, and contact information (registrant, admin, tech, billing, abuse). Works for any domain - no ownership required. RDAP is the modern, JSON-native replacement for WHOIS. Falls back to WHOIS (port 43) for TLDs without RDAP, and supplements RDAP with WHOIS referral data when contacts are missing (common post-GDPR). Contact information may be partially or fully redacted due to GDPR or privacy protection services. Public endpoint - no authentication required. Authenticated requests get higher rate limits (30/min vs 10/min).
Parameters
| q* | string | Domain to look up (e.g. example.com) |
Response 200
| domain | string | |
| registered | boolean | Whether the domain is currently registered |
| registrar | string | null | Current registrar name |
| registrar_url | string | null | Registrar website URL |
| registrar_iana_id | string | null | Registrar IANA ID (from RDAP publicIds) |
| created | string | null | Registration date (YYYY-MM-DD) |
| expires | string | null | Expiration date (YYYY-MM-DD) |
| updated | string | null | Last updated date (YYYY-MM-DD) |
| days_until_expiry | integer | null | Days until expiry (null if not registered) |
| status | string[] | EPP status codes |
| nameservers | string[] | Authoritative nameservers |
| dnssec | boolean | Whether DNSSEC is enabled |
| redacted | boolean | Whether WHOIS/RDAP data is redacted for privacy (GDPR) |
| contacts | object | Contact information by role (null when no data available, fields may be null when redacted) |
Example
curl "https://domani.run/api/domains/whois?q=google.com"
/api/domains/{domain}/ogPublicGet website preview metadata for a domain
Returns Open Graph metadata (title, description, image, favicon) for a domain. Data is lazily fetched and cached for 7 days. First request for a domain may take 1-5s (live fetch); subsequent requests are instant (cache hit). Returns null fields if the site is unreachable or has no OG tags. Public endpoint - no authentication required.
Parameters
| domain* | string | Domain to fetch metadata for (e.g. google.com) |
Response 200
| title | string | null | Page title (og:title > twitter:title > <title>) |
| description | string | null | Page description (og:description > meta description) |
| image | string | null | OG image URL (absolute) |
| favicon | string | null | Favicon URL (absolute) |
Example
curl "https://domani.run/api/domains/google.com/og"
API Tokens
/api/tokensList your API tokens
Returns all API tokens for the authenticated user. Keys are masked for security (first 12 + last 4 characters visible). Use this to audit active tokens.
Response 200
| tokens | TokenInfo[] |
Example response
{
"tokens": [
{
"id": "cm5abc123",
"name": "Default",
"key": "domani_sk_a...wxyz",
"lastUsed": "2025-02-18T10:30:00.000Z",
"expiresAt": null,
"expired": false,
"scopes": [
"*"
],
"createdAt": "2025-01-15T10:30:00.000Z"
},
{
"id": "cm5def456",
"name": "CI/CD",
"key": "domani_sk_b...vwxy",
"lastUsed": null,
"expiresAt": "2025-05-01T14:00:00.000Z",
"expired": false,
"scopes": [
"domains:read",
"domains:write"
],
"createdAt": "2025-02-01T14:00:00.000Z"
}
]
}Example
curl https://domani.run/api/tokens \ -H "Authorization: Bearer domani_sk_xxx"
/api/tokensCreate a new API token
Creates a new API token. The full key is returned only in this response - store it securely. You can create multiple tokens to separate access for different applications.
Request body
| name | string | Human-readable name to identify this token |
| expires_in | integer | Token lifetime in seconds (min 3600 = 1h, max 31536000 = 1y). Omit for no expiration. |
| expires_at | string (date-time) | Absolute expiration date (ISO 8601). Alternative to expires_in. |
| scopes | string[] | Permission scopes for this token. Use ['*'] for full access (default). A token can only grant scopes it already has (scope attenuation). Scopes: domains:read (GET /api/domains, GET /api/domains/{domain}, GET /api/domains/{domain}/dns, /status, /email/check, /auth-code, /transfer-away, /transfer-status, /analytics), domains:write (PUT /api/domains/{domain}/dns, POST /connect, POST /verify, PUT /settings, PUT /parking, PUT/DELETE /api/domains/{domain}/for-sale, POST /api/domains/import, POST /import/verify), domains:transfer (POST /api/domains/buy, POST /transfer, POST /renew (involves payment, includes marketplace purchases)), tokens:read (GET /api/tokens), tokens:write (POST /api/tokens, DELETE /api/tokens/{id}), webhooks:read (GET /api/webhooks, GET /api/webhooks/{id}/deliveries), webhooks:write (POST /api/webhooks, PATCH /api/webhooks/{id}, DELETE /api/webhooks/{id}), email:read (GET /api/emails, /api/emails/{address}, /api/emails/{address}/messages, /api/domains/{domain}/email/status), email:write (POST /api/emails, POST /api/emails/{address}/send, POST /api/domains/{domain}/email/setup), account:read (GET /api/me), account:write (DELETE /api/me, POST /api/billing/setup, POST /api/me/resend-verification), billing:read (GET /api/billing/invoices), search (GET /api/domains/search, /suggest, /whois, /dns-check, GET /api/tlds), deals:read (GET /api/deals, GET /api/deals/{id}), deals:write (POST /api/domains/sell, PATCH /api/deals/{id}), notifications:read (GET /api/notifications, GET /api/notifications/count). |
Response 201
| id | string | |
| name | string | |
| key | string | Full API key - save this, it won't be shown again |
| expiresAt | string (date-time) | null | When this token expires (null = never) |
| scopes | string[] | Permission scopes granted to this token |
| createdAt | string (date-time) |
Example response
{
"id": "cm5ghi789",
"name": "CI/CD Pipeline",
"key": "domani_sk_x1Y2z3A4b5C6d7E8f9G0hIjK",
"expiresAt": "2025-03-20T10:30:00.000Z",
"scopes": [
"domains:read",
"domains:write"
],
"createdAt": "2025-02-18T10:30:00.000Z"
}Example
curl -X POST https://domani.run/api/tokens \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"name": "CI/CD Pipeline"}'/api/tokens/{id}Revoke an API token
Permanently revokes an API token. Any applications using this token will immediately lose access. This action cannot be undone.
Parameters
| id* | string | Token ID (from GET /api/tokens response) |
Response 200
| success | boolean |
Example response
{
"success": true
}Example
curl -X DELETE https://domani.run/api/tokens/cm5abc123 \ -H "Authorization: Bearer domani_sk_xxx"
Billing
/api/billing/setupGet checkout URL for adding a payment method
Creates a checkout session for adding a credit card. Redirect the user to the returned URL. This is required before purchasing domains. After the user completes checkout, they are redirected back to the dashboard.
Request body
| mode | string(setup_intent | checkout) | setup_intent returns a client_secret for frontend Stripe.js. checkout returns a URL to redirect the user to Stripe Checkout. |
Response 200
| url | string (uri) | Checkout URL (checkout mode) |
| client_secret | string | Stripe SetupIntent client secret (setup_intent mode) |
| hint | string | Next steps guidance |
Example response
{
"url": "https://checkout.domani.run/session/cs_abc123...",
"hint": "Open this URL in a browser to add your payment card."
}Example
curl -X POST https://domani.run/api/billing/setup \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"mode": "checkout"}'/api/billing/cardRemove saved payment method
Detaches the user's saved credit card from their account. After removal, a new card must be added before purchasing domains.
Response 200
| removed | boolean |
Example response
{
"removed": true
}Example
curl -X DELETE https://domani.run/api/billing/card \ -H "Authorization: Bearer domani_sk_xxx"
/api/billing/invoicesList payment invoices
Returns a list of paid invoices with invoice number, amount, date, and links to view or download the PDF. Invoices are generated automatically for every domain purchase, renewal, and transfer.
Parameters
| limit | integer | Max invoices to return |
Response 200
| invoices | object[] | |
| has_more | boolean | |
| hint | string |
Example
curl https://domani.run/api/billing/invoices \ -H "Authorization: Bearer domani_sk_xxx"
Referrals
/api/referralsGet referral earnings and history
Returns your unique referral code, commission rate (20%), earnings breakdown (total, paid, pending), and a history of all referred purchases. Share your referral code with others - when they register with your code (via the ref parameter), you earn a commission on all their future domain purchases automatically.
Response 200
| referral_code | string | Your unique referral code to share |
| referral_rate | integer | Commission percentage (currently 20%) |
| total_earned_cents | integer | Total earnings in cents |
| total_paid_cents | integer | Total paid out in cents |
| total_pending_cents | integer | Pending payout in cents |
| referrals | object[] |
Example response
{
"referral_code": "x7k9m2",
"referral_rate": 20,
"total_earned_cents": 400,
"total_paid_cents": 200,
"total_pending_cents": 200,
"referrals": [
{
"id": "cm5ref001",
"domains": [
"friend-site.com"
],
"earned_cents": 200,
"paid_out": true,
"created_at": "2025-01-20T10:00:00.000Z"
},
{
"id": "cm5ref002",
"domains": [
"another.dev"
],
"earned_cents": 200,
"paid_out": false,
"created_at": "2025-02-10T15:30:00.000Z"
}
]
}Example
curl https://domani.run/api/referrals \ -H "Authorization: Bearer domani_sk_xxx"
Notifications
/api/notificationsList notifications
List your in-app notifications with cursor-based pagination. Includes deal events, listing updates, and system alerts. Requires notifications:read scope.
Parameters
| limit | integer | Max notifications to return |
| cursor | string | Cursor for pagination (from previous response's next_cursor) |
Response 200
| notifications | object[] | |
| has_more | boolean | |
| next_cursor | string | null |
/api/notifications/countGet unread notification count
Lightweight endpoint for polling unread notification count. Requires notifications:read scope.
Response 200
| unread_count | integer |
Example response
{
"unread_count": 3
}/api/notifications/{id}Mark notification as read
Mark a single notification as read. Requires notifications:read scope.
Parameters
| id* | string | Notification ID |
Request body
| read* | boolean | Set to true to mark as read |
Response 200
| ok | boolean |
/api/notifications/read-allMark all notifications as read
Mark all unread notifications as read. Requires notifications:read scope.
Response 200
| ok | boolean | |
| count | integer | Number of notifications marked as read |
/api/notifications/preferencesGet notification preferences
Returns the user's notification preferences for each category and channel (in_app, email). Missing preferences default to enabled. Requires notifications:read scope.
Response 200
| preferences | object[] |
/api/notifications/preferencesUpdate notification preferences
Update notification preferences for specific categories and channels. Requires notifications:read scope.
Request body
| preferences* | object[] |
Response 200
| ok | boolean |
Webhooks
/api/webhooksList webhooks
List all webhook endpoints configured for your account. Returns URL, subscribed events, and active status for each webhook. Secrets are never returned after creation.
Response 200
| webhooks | object[] | |
| hint | string |
Example response
{
"webhooks": [
{
"id": "cm5wh001",
"url": "https://example.com/webhooks/domani",
"events": [
"domain.purchased",
"dns.updated"
],
"active": true,
"created_at": "2026-02-25T10:00:00.000Z"
}
],
"hint": "1 webhook(s)."
}Example
curl https://domani.run/api/webhooks \ -H "Authorization: Bearer domani_sk_xxx"
/api/webhooksCreate webhook
Register a new webhook endpoint. The URL must use HTTPS. Choose which event types to subscribe to. The webhook secret is returned only once at creation - save it to verify incoming payloads with HMAC-SHA256. Maximum 5 webhooks per account.
Request body
| url* | string (uri) | HTTPS URL that will receive webhook POST requests |
| events* | string[] | Event types to subscribe to. Use GET /api/webhooks/events to list available types. |
Response 200
| id | string | |
| url | string (uri) | |
| events | string[] | |
| secret | string | HMAC-SHA256 signing secret (shown once) |
| active | boolean | |
| created_at | string (date-time) | |
| hint | string |
Example response
{
"id": "cm5wh001",
"url": "https://example.com/webhooks/domani",
"events": [
"domain.purchased",
"dns.updated"
],
"secret": "whsec_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345",
"active": true,
"created_at": "2026-02-25T10:00:00.000Z",
"hint": "Webhook created. Save the secret - it won't be shown again. Use it to verify incoming payloads with HMAC-SHA256."
}Example
curl -X POST https://domani.run/api/webhooks \
-H "Authorization: Bearer domani_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/hook","events":["domain.purchased","dns.updated"]}'/api/webhooks/{id}Update webhook
Update a webhook's URL, subscribed events, or active status. Send only the fields you want to change.
Parameters
| id* | string | Webhook ID |
Request body
| url | string (uri) | New HTTPS URL |
| events | string[] | New list of event types |
| active | boolean | Set false to pause, true to resume |
Response 200
| id | string | |
| url | string (uri) | |
| events | string[] | |
| active | boolean | |
| hint | string |
/api/webhooks/{id}Delete webhook
Delete a webhook endpoint. All pending deliveries will be cancelled. This cannot be undone.
Parameters
| id* | string | Webhook ID |
Response 200
| success | boolean | |
| hint | string |
/api/webhooks/{id}/deliveriesList webhook deliveries
Get recent delivery attempts for a webhook. Shows event type, HTTP status, number of attempts, and any errors. Useful for debugging failed deliveries.
Parameters
| id* | string | Webhook ID |
| limit | integer | Maximum number of deliveries to return |
Response 200
| deliveries | object[] | |
| hint | string |
Example response
{
"deliveries": [
{
"id": "cm5del001",
"event_type": "domain.purchased",
"status": "delivered",
"http_status": 200,
"attempts": 1,
"last_error": null,
"created_at": "2026-02-25T10:05:00.000Z"
}
],
"hint": "1 delivery(ies) for this webhook."
}/api/webhooks/eventsList webhook event types
List all available webhook event types with descriptions. No authentication required.
Response 200
| events | object[] |
Example response
{
"events": [
{
"type": "domain.purchased",
"description": "A new domain was successfully registered"
},
{
"type": "dns.updated",
"description": "DNS records were changed for a domain"
},
{
"type": "transfer.completed",
"description": "A domain transfer completed successfully"
}
]
}Example
curl https://domani.run/api/webhooks/events