Skip to main content

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.

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 ({slug}.usenaive.ai), your own custom domain (agent@acme.com), or a domain purchased directly through the API.

CLI First

# List + connect + verify domain
naive domains
naive domains connect example.com
naive domains verify <domain-id>

# Inspect and edit DNS records on a connected domain
naive domains zone-records <domain-id>
naive domains set-record <domain-id> --type CNAME --name www --value shops.myshopify.com
naive domains delete-record <domain-id> <record-id>

Tools

ToolTypeDescription
list_domainsCoreList all domains for the company with status (includes app_connect_status)
connect_domainCoreRegister a custom domain (BYOD) for email
resend_setup_recordsCoreView the Resend email-provider setup records required for verification (read-only)
verify_domainCoreTrigger DNS verification check
search_domainCoreCheck domain availability and price
purchase_domainCorePurchase a domain via checkout
list_dns_recordsDNS editList every DNS record on the live Vercel zone (with provider IDs)
set_dns_recordDNS editCreate or replace a DNS record (A/AAAA/CNAME/MX/TXT/CAA). Apex A/AAAA writes flip the domain to agent_managed.
delete_dns_recordDNS editDelete 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

TypeAuto-provisioned?ExampleUse case
SystemYes (on registration)acme-corp.usenaive.aiQuick start, testing, default sending
Custom (BYOD)No (manual connect)acme.comBranded emails, own registrar
PurchasedNo (buy via API)acme.comBranded 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

curl https://api.usenaive.ai/v1/domains \
  -H "Authorization: Bearer nv_sk_your_key"
Response:
{
  "domains": [
    {
      "id": "dom-uuid-1",
      "domain": "acme-corp.usenaive.ai",
      "status": "active",
      "dns_status": "provisioned",
      "byod": false
    }
  ]
}
If status is pending_dns, trigger verification:
curl -X POST https://api.usenaive.ai/v1/domains/dom-uuid-1/verify \
  -H "Authorization: Bearer nv_sk_your_key"

Custom Domain Setup (BYOD)

To send emails from your own domain instead of *.usenaive.ai:
1

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.
curl -X POST https://api.usenaive.ai/v1/domains/connect \
  -H "Authorization: Bearer nv_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "acme.com" }'
Response:
{
  "id": "dom-uuid-2",
  "domain": "acme.com",
  "status": "pending_dns",
  "dns_records": [
    { "type": "MX", "name": "acme.com", "value": "feedback-smtp.us-east-1.amazonses.com", "priority": 10 },
    { "type": "TXT", "name": "acme.com", "value": "v=spf1 include:amazonses.com ~all" },
    { "type": "CNAME", "name": "resend._domainkey.acme.com", "value": "resend.domainkey.us-east-1.amazonses.com" }
  ]
}
2

Configure DNS records

Go to your domain registrar’s DNS management panel and add each record from the response:
TypeNameValue
MXacme.comfeedback-smtp.us-east-1.amazonses.com (priority 10)
TXTacme.comv=spf1 include:amazonses.com ~all
CNAMEresend._domainkey.acme.comresend.domainkey.us-east-1.amazonses.com
DNS propagation typically takes 5-30 minutes, but can take up to 48 hours in some cases.
3

Check DNS records status

View per-record verification status:
curl https://api.usenaive.ai/v1/domains/dom-uuid-2/dns-records \
  -H "Authorization: Bearer nv_sk_your_key"
Response:
{
  "domain": "acme.com",
  "domain_id": "dom-uuid-2",
  "records": [
    { "type": "MX", "name": "acme.com", "value": "...", "status": "verified" },
    { "type": "TXT", "name": "acme.com", "value": "...", "status": "pending" },
    { "type": "CNAME", "name": "resend._domainkey.acme.com", "value": "...", "status": "pending" }
  ]
}
4

Verify the domain

Once you’ve added records and waited for propagation, trigger verification:
curl -X POST https://api.usenaive.ai/v1/domains/dom-uuid-2/verify \
  -H "Authorization: Bearer nv_sk_your_key"
Response (success):
{
  "domain": "acme.com",
  "domain_id": "dom-uuid-2",
  "verified": true,
  "status": "active",
  "hint": "Domain verified! You can now create email inboxes on this domain."
}
If verification fails, wait a few more minutes for DNS propagation and try again.
5

Create inboxes and start sending

Once the domain is active, create email addresses and send:
curl -X POST https://api.usenaive.ai/v1/email/inboxes \
  -H "Authorization: Bearer nv_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "local_part": "hello", "domain_id": "dom-uuid-2" }'
Your agent can now send email from hello@acme.com.

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

curl "https://api.usenaive.ai/v1/domains/search?domain=acme.com" \
  -H "Authorization: Bearer nv_sk_your_key"
Response:
{
  "domain": "acme.com",
  "available": true,
  "price": 14.99,
  "priceInCents": 1499,
  "currency": "usd"
}
If available is false, try a different domain name.

Purchase a domain

curl -X POST https://api.usenaive.ai/v1/domains/purchase \
  -H "Authorization: Bearer nv_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "acme.com" }'
Response:
{
  "checkout_url": "https://checkout.example.com/c/pay/cs_live_...",
  "session_id": "cs_live_...",
  "domain_id": "dom-uuid-3",
  "domain": "acme.com",
  "price": "$14.99",
  "expires_at": "2026-05-04T02:30:00.000Z",
  "hint": "Open the checkout URL to complete payment. The domain will be registered automatically.",
  "next_steps": [{ "command": "naive domains", "description": "Check domain status after payment" }]
}
1

Search for a domain

Use GET /v1/domains/search?domain=yourdomain.com to check if the domain is available and see its price.
2

Start purchase

Call POST /v1/domains/purchase with the domain name. This creates a checkout session and returns a checkout_url.
3

Complete payment

Open the checkout_url in a browser to complete payment. The checkout session expires after 30 minutes.
4

Domain registered automatically

After payment, the domain is registered through the domain registrar and DNS is configured automatically. Check status with GET /v1/domains.
The checkout URL expires. If payment is not completed, the domain reservation is released and you’ll need to call POST /v1/domains/purchase again.

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:
EndpointReturnsMutable?MCP tool
GET /v1/domains/:id/dns-recordsResend email-provider setup records the user must add at their registrar to pass verificationNonaive_resend_setup_records
GET /v1/domains/:id/zone-recordsEvery record currently in the live Vercel zone, with provider record IDsReadnaive_list_dns_records
POST /v1/domains/:id/zone-recordsCreate or replace a record on the live zoneWritenaive_set_dns_record
DELETE /v1/domains/:id/zone-records/:recordIdDelete a record from the live zoneWritenaive_delete_dns_record

List every record in the zone

curl https://api.usenaive.ai/v1/domains/dom-uuid-2/zone-records \
  -H "Authorization: Bearer nv_sk_your_key"
{
  "domain": "acme.com",
  "domain_id": "dom-uuid-2",
  "mock": false,
  "records": [
    { "id": "rec_abc", "type": "A", "name": "", "value": "76.76.21.21", "ttl": 60, "comment": "naive:owned;company=co_...;agent=ag_...;ts=1717..." },
    { "id": "rec_def", "type": "CNAME", "name": "www", "value": "shops.myshopify.com", "ttl": 60 }
  ]
}

Create or replace a record

curl -X POST https://api.usenaive.ai/v1/domains/dom-uuid-2/zone-records \
  -H "Authorization: Bearer nv_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "type": "CNAME", "name": "www", "value": "shops.myshopify.com" }'
The default 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.
{
  "ok": true,
  "record": { "id": "rec_xyz", "type": "CNAME", "name": "www", "value": "shops.myshopify.com" },
  "mock": false,
  "ownership": "naive",
  "replaced_count": 1,
  "path": "patch"
}

Delete a record

curl -X DELETE https://api.usenaive.ai/v1/domains/dom-uuid-2/zone-records/rec_xyz \
  -H "Authorization: Bearer nv_sk_your_key"

Safety rules

These rules are enforced by the API and surface via error.reason codes:
RuleReason code
Allowed types only: A, AAAA, CNAME, MX, TXT, CAADISALLOWED_RECORD_TYPE
Wildcards (* or *.foo) are rejectedDISALLOWED_RECORD_TYPE
TTL must be 60-86400 secondsINVALID_TTL
MX records require a 0-65535 priorityINVALID_MX_PRIORITY
CNAME at apex is not permittedCNAME_AT_APEX
CNAME cannot coexist with A/AAAA/MX/TXT at the same name (RFC 1912)CNAME_COEXISTENCE
CAA values must use an allowlisted CACA_NOT_ALLOWED
System domains (registrar=system) are read-onlySYSTEM_DOMAIN
DMARC and DKIM TXT records are protectedPROTECTED_RECORD
Inbound subdomain MX/TXT (e.g. agents.acme.com) is protectedPROTECTED_RECORD
Per-company rate limit: 5 mutations/min, 20/hrRATE_LIMITED (sets Retry-After)
Overwriting a record not created by Naive requires acknowledge_unowned: trueUNOWNED_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 a comment 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 to activity_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:
curl -N https://api.usenaive.ai/v1/events \
  -H "Authorization: Bearer nv_sk_your_key" \
  -H "Accept: text/event-stream"
Currently emitted: domain.updated and activity.logged.

Mock mode

If VERCEL_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

BenefitSystem DomainCustom Domain
DeliverabilityGoodBetter (proper SPF/DKIM/DMARC on your domain)
Brandingagent@acme.usenaive.aiagent@acme.com
Reply handlingWorksWorks + looks professional
Setup timeInstant5-60 minutes (DNS propagation)

Error Handling

ErrorCauseRecovery
duplicate_recordDomain already registered to this or another companyUse GET /v1/domains to check existing domains
provider_errorFailed to register domain with the email or domain providerRetry after a few seconds
resource_not_foundDomain ID doesn’t exist or belongs to another companyUse GET /v1/domains for valid IDs
invalid_input (not available)Domain is not available for purchaseTry a different domain name
invalid_input (max domains)Already at the 3 purchased domain limitRemove an existing domain before purchasing
feature_not_configured (reason: "FEATURE_DISABLED")DNS edit endpoints called while AGENT_DNS_EDIT_ENABLED=falseSet 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=trueConfigure a Vercel token, or enable mock for local development
forbidden (reason: "SYSTEM_DOMAIN")Tried to edit a system domainUse a custom or purchased domain
forbidden (reason: "PROTECTED_RECORD")Tried to write/delete DMARC, DKIM, or inbound MX/TXTContact support if a change is genuinely needed
rate_limited (reason: "RATE_LIMITED")Per-company (5/min, 20/hr) or platform write budget exhaustedHonor the Retry-After header
duplicate_record (reason: "UNOWNED_RECORD_REQUIRES_ACK")Overwriting/deleting a record not created by NaiveRe-issue with acknowledge_unowned: true

Typical Workflows (Agent Perspective)

Purchasing a new domain

1. GET /v1/domains/search?domain=acme.com
   → available: true, price: "$14.99"

2. POST /v1/domains/purchase { domain: "acme.com" }
   → checkout_url: "https://checkout.example.com/..."
   → Share checkout URL with user: "Complete payment to register the domain"

3. [User completes checkout]

4. GET /v1/domains
   → domain: "acme.com", status: "active"
   → "Your domain is registered and ready!"

5. POST /v1/email/inboxes { local_part: "hello", domain_id: "dom-uuid" }
   → address: hello@acme.com — ready to send

Connecting an existing domain (BYOD)

1. POST /v1/domains/connect { domain: "acme.com" }
   → status: "pending_dns", dns_records: [MX, TXT, CNAME]
   → Share DNS records with user: "Add these at your registrar"

2. [User adds DNS records at their registrar]

3. GET /v1/domains/dom-uuid/dns-records
   → Some records still pending
   → "DNS is still propagating, try again in a few minutes"

4. POST /v1/domains/dom-uuid/verify
   → verified: true
   → "Your domain is verified and ready!"

5. POST /v1/email/inboxes { local_part: "support", domain_id: "dom-uuid" }
   → address: support@acme.com — ready to send