Domains are the foundation of your agent’s email identity. A verified domain enables branded email addresses — whether that’s the auto-provisioned system domain (Documentation Index
Fetch the complete documentation index at: https://usenaive.ai/docs/llms.txt
Use this file to discover all available pages before exploring further.
{slug}.usenaive.ai), your own custom domain (agent@acme.com), or a domain purchased directly through the API.
CLI First
Tools
| Tool | Type | Description |
|---|---|---|
list_domains | Core | List all domains for the company with status (includes app_connect_status) |
connect_domain | Core | Register a custom domain (BYOD) for email |
resend_setup_records | Core | View the Resend email-provider setup records required for verification (read-only) |
verify_domain | Core | Trigger DNS verification check |
search_domain | Core | Check domain availability and price |
purchase_domain | Core | Purchase a domain via checkout |
list_dns_records | DNS edit | List every DNS record on the live Vercel zone (with provider IDs) |
set_dns_record | DNS edit | Create or replace a DNS record (A/AAAA/CNAME/MX/TXT/CAA). Apex A/AAAA writes flip the domain to agent_managed. |
delete_dns_record | DNS edit | Delete a DNS record by its provider record ID |
The DNS edit tools are enabled by default. To disable, set
AGENT_DNS_EDIT_ENABLED=false on the API — the endpoints will then return 501 feature_not_configured. They also require either VERCEL_REGISTRAR_TOKEN or DOMAIN_MOCK=true (for local development); without either they return MOCK_MODE_REJECTED.Every DNS edit (success or rejection) appends an activity_log row and emits a domain.updated live event on GET /v1/events.Domain Types
| Type | Auto-provisioned? | Example | Use case |
|---|---|---|---|
| System | Yes (on registration) | acme-corp.usenaive.ai | Quick start, testing, default sending |
| Custom (BYOD) | No (manual connect) | acme.com | Branded emails, own registrar |
| Purchased | No (buy via API) | acme.com | Branded emails, no existing registrar needed |
System Domains
Every company gets a system domain automatically when you register. It follows the pattern{company-slug}.usenaive.ai.
System domains may start with status
pending_dns until the email provider verifies the DNS records. This usually resolves automatically, but you can trigger a re-check with the verify endpoint.Check your system domain
status is pending_dns, trigger verification:
Custom Domain Setup (BYOD)
To send emails from your own domain instead of*.usenaive.ai:
Connect your domain
Tell Naive about a domain you already own at a registrar (Cloudflare, Namecheap, Route53, etc.). If you don’t own a domain yet, use Domain Purchasing instead.Response:
Configure DNS records
Go to your domain registrar’s DNS management panel and add each record from the response:
| Type | Name | Value |
|---|---|---|
| MX | acme.com | feedback-smtp.us-east-1.amazonses.com (priority 10) |
| TXT | acme.com | v=spf1 include:amazonses.com ~all |
| CNAME | resend._domainkey.acme.com | resend.domainkey.us-east-1.amazonses.com |
DNS propagation typically takes 5-30 minutes, but can take up to 48 hours in some cases.
Verify the domain
Once you’ve added records and waited for propagation, trigger verification:Response (success):If verification fails, wait a few more minutes for DNS propagation and try again.
Domain Purchasing
Don’t already own a domain? You can search for availability, check pricing, and purchase one directly through the API. Domains are registered via the domain registrar and payment is handled through checkout.Each company can purchase up to 3 domains. Purchased domains are automatically configured for email — no manual DNS setup required.
Check availability and price
available is false, try a different domain name.
Purchase a domain
Search for a domain
Use
GET /v1/domains/search?domain=yourdomain.com to check if the domain is available and see its price.Start purchase
Call
POST /v1/domains/purchase with the domain name. This creates a checkout session and returns a checkout_url.Complete payment
Open the
checkout_url in a browser to complete payment. The checkout session expires after 30 minutes.Agent DNS Editing
Once a domain is connected (custom or purchased) and the zone is delegated to Vercel, agents can read and modify the DNS records directly. This is a separate surface from the verification workflow above:| Endpoint | Returns | Mutable? | MCP tool |
|---|---|---|---|
GET /v1/domains/:id/dns-records | Resend email-provider setup records the user must add at their registrar to pass verification | No | naive_resend_setup_records |
GET /v1/domains/:id/zone-records | Every record currently in the live Vercel zone, with provider record IDs | Read | naive_list_dns_records |
POST /v1/domains/:id/zone-records | Create or replace a record on the live zone | Write | naive_set_dns_record |
DELETE /v1/domains/:id/zone-records/:recordId | Delete a record from the live zone | Write | naive_delete_dns_record |
List every record in the zone
Create or replace a record
mode is replace: if a single matching record exists at the same (type, name) it is PATCHed in place; if multiple exist, the new authoritative record is added then the old ones are best-effort deleted; if none exist, a new record is added. Set mode: "append" to always create a new row instead.
Delete a record
Safety rules
These rules are enforced by the API and surface viaerror.reason codes:
| Rule | Reason code |
|---|---|
| Allowed types only: A, AAAA, CNAME, MX, TXT, CAA | DISALLOWED_RECORD_TYPE |
Wildcards (* or *.foo) are rejected | DISALLOWED_RECORD_TYPE |
| TTL must be 60-86400 seconds | INVALID_TTL |
| MX records require a 0-65535 priority | INVALID_MX_PRIORITY |
| CNAME at apex is not permitted | CNAME_AT_APEX |
| CNAME cannot coexist with A/AAAA/MX/TXT at the same name (RFC 1912) | CNAME_COEXISTENCE |
| CAA values must use an allowlisted CA | CA_NOT_ALLOWED |
System domains (registrar=system) are read-only | SYSTEM_DOMAIN |
| DMARC and DKIM TXT records are protected | PROTECTED_RECORD |
Inbound subdomain MX/TXT (e.g. agents.acme.com) is protected | PROTECTED_RECORD |
| Per-company rate limit: 5 mutations/min, 20/hr | RATE_LIMITED (sets Retry-After) |
Overwriting a record not created by Naive requires acknowledge_unowned: true | UNOWNED_RECORD_REQUIRES_ACK |
Allowed CAs
ssl.com, letsencrypt.org, digicert.com, sectigo.com, globalsign.com, amazon.com, pki.goog, google.com.
Ownership marker
Records written by Naive carry acomment like naive:owned;company=<id>;agent=<id>;ts=<unix>. The legacy paperclip:owned prefix is also recognized for cross-product compatibility on a shared customer zone. To overwrite or delete a record without this marker (e.g. legacy records left at the registrar) pass acknowledge_unowned: true in the body or query string. The response’s ownership field will be naive for clean writes and unowned-acknowledged when the agent overrode an unowned record.
Apex A/AAAA flips app_connect_status
When a write creates or replaces an apex A or AAAA record (name is empty, @, or the zone domain), the API atomically transitions the domain’s app_connect_status to agent_managed_pending, writes to Vercel, and on success commits to agent_managed. On Vercel failure it rolls back to the previous status. Reapers and connect sweeps in either Naive product (this SDK or naive-paperclip) skip agent_managed/agent_managed_pending rows so the agent’s record won’t be reverted.
Listen for the apex flip on the live event stream (GET /v1/events) — domain.updated events with payload.action = "agent_managed" fire on commit.
Audit log + live events
Every DNS edit (success or rejection) appends a row toactivity_log with one of these actions: dns.record.set, dns.record.delete, dns.record.rejected. The same event also fans out on the company SSE stream:
domain.updated and activity.logged.
Mock mode
IfVERCEL_REGISTRAR_TOKEN is unset and DOMAIN_MOCK=true, all writes are simulated against an in-memory store and responses include mock: true. Useful for local development and CI; the operation does not touch real DNS.
Why Custom Domains Matter
| Benefit | System Domain | Custom Domain |
|---|---|---|
| Deliverability | Good | Better (proper SPF/DKIM/DMARC on your domain) |
| Branding | agent@acme.usenaive.ai | agent@acme.com |
| Reply handling | Works | Works + looks professional |
| Setup time | Instant | 5-60 minutes (DNS propagation) |
Error Handling
| Error | Cause | Recovery |
|---|---|---|
duplicate_record | Domain already registered to this or another company | Use GET /v1/domains to check existing domains |
provider_error | Failed to register domain with the email or domain provider | Retry after a few seconds |
resource_not_found | Domain ID doesn’t exist or belongs to another company | Use GET /v1/domains for valid IDs |
invalid_input (not available) | Domain is not available for purchase | Try a different domain name |
invalid_input (max domains) | Already at the 3 purchased domain limit | Remove an existing domain before purchasing |
feature_not_configured (reason: "FEATURE_DISABLED") | DNS edit endpoints called while AGENT_DNS_EDIT_ENABLED=false | Set the env var and restart the API |
feature_not_configured (reason: "MOCK_MODE_REJECTED") | DNS edits attempted without VERCEL_REGISTRAR_TOKEN and without DOMAIN_MOCK=true | Configure a Vercel token, or enable mock for local development |
forbidden (reason: "SYSTEM_DOMAIN") | Tried to edit a system domain | Use a custom or purchased domain |
forbidden (reason: "PROTECTED_RECORD") | Tried to write/delete DMARC, DKIM, or inbound MX/TXT | Contact support if a change is genuinely needed |
rate_limited (reason: "RATE_LIMITED") | Per-company (5/min, 20/hr) or platform write budget exhausted | Honor the Retry-After header |
duplicate_record (reason: "UNOWNED_RECORD_REQUIRES_ACK") | Overwriting/deleting a record not created by Naive | Re-issue with acknowledge_unowned: true |