Skip to main content
Domains are part of Identity. A domain belongs to the organization, not to one persona — two identities can send from the same domain.
A domain is what makes an email inbox deliverable. Connect a domain you own (external) or mint the organization’s system subdomain (system), then publish the DNS records the API hands back.

Three readiness tracks

Verification is not one boolean. A domain advances along three independent tracks, and each gates a different capability: A send is refused with a retryable job_not_ready until dns_status is provisioned. Receiving is unaffected by dns_status and waits on inbound_status.

The domain object

string
Unique id (e.g. dom_01H...).
string
Always domain.
string
The fully-qualified name, lowercase, no trailing dot.
string
external (a domain you own), system (a subdomain minted for the org), or purchased (bought with Naive credits).
string
Overall lifecycle state of the domain record — pending_dns, active, or failed.
string
Mail-sending track. See the table above.
string
Mail-receiving track.
string
App/HTTP track.
object[]
The DNS records to publish. Same shape as List DNS records.
string | null
When verification last ran.
string
Creation timestamp.

Connect a domain

POST /v1/domains — scope agents:write Idempotent on name: connecting a domain that already exists returns the existing record rather than a duplicate.
string
external (a domain you own) or system (a subdomain minted under the org slug). Defaults to external. Purchases use the separately privileged endpoint below.
string
The domain to connect. Required for kind: "external"; omit for system and the API mints the subdomain.
boolean
Also request inbound (MX) records. Defaults to true.
Errors — validation_failed (400) when name is not a valid FQDN or kind is unknown; insufficient_scope (403) without agents:write.

Buy a domain with Naive credits

First call POST /v1/domains/quote with { "name": "example.com" } using billing:read. It returns authoritative availability plus registration_price_micro_usd and renewal_price_micro_usd. Quoting does not reserve the domain or move credits. Then call POST /v1/domains/purchase using billing:write, an Idempotency-Key, and:
The API rechecks availability and price, refuses premium domains, and will not buy if the live price exceeds the explicit ceiling. It debits the organization’s Naive-credit balance before the registrar call. The platform registrar account funds the external purchase; this endpoint neither accepts nor forwards a customer card. Automatic renewal is off. A completed registration returns 201. A registration still processing returns 202 with Cache-Control: no-store; retry the same request with the same idempotency key to reconcile it. You do not have to: the platform checks every pending purchase about every 10 minutes and finishes it. A confirmed registrar rejection refunds the debit. An ambiguous outcome stays pending, because completed registrations are non-refundable and an eager refund could give away a domain. If no registration exists a day later, the debit is refunded. A refused or refunded attempt never blocks the next one. Top up after a 402 and buy again. Delete a failed purchase and buy again with a new Idempotency-Key; the new attempt is charged on its own. An Idempotency-Key names one attempt: reusing the key of a refunded attempt is refused, and can never register a free domain. Errors — insufficient_credits (402); validation_failed (400) for an unavailable domain (including one another organization is buying), missing idempotency key, or a price above the ceiling; state_conflict (409) when the registrar rejected the purchase (credits refunded), when a failed purchase of the name must be deleted first, when the Idempotency-Key belongs to a finished attempt, or when another request for the name is still starting (retry shortly); feature_not_configured (501) when the platform registrar account is not configured.

List domains

GET /v1/domains — scope agents:read
integer
Page size. See Pagination.
string
Cursor from the previous page.
Returns a paginated list of domain objects.

Retrieve a domain

GET /v1/domains/{id} — scope agents:read Returns the domain object. not_found (404) if the id does not belong to your organization.

Verify a domain

POST /v1/domains/{id}/verify — scope agents:write Re-runnable. Each readiness track advances on its own, so calling verify after publishing only the SPF/DKIM records moves dns_status and leaves inbound_status where it was. The domain object comes back as the answer — read the three status fields to see what changed.
Errors — not_found (404); insufficient_scope (403). A track that is still unpublished is not an error: it simply stays pending.

List DNS records

GET /v1/domains/{id}/records — scope agents:read Always allowed on every kind — reading the zone is never the dangerous half.
string
TXT, CNAME, or MX.
string
The record host, relative to the registrable zone.
string
The record value.
integer | null
MX priority; null for TXT and CNAME.
boolean
true for records the platform owns — DKIM, _dmarc, and the inbound MX. An agent may read a managed record and must never edit or delete it: removing the DKIM record breaks the signature on its own mail.

Delete a domain

DELETE /v1/domains/{id} — scope agents:write Returns the standard deleted envelope. Inboxes on the domain stop delivering; released addresses are not reusable until the domain is reconnected and re-verified.

Email & SMS

Give a persona an inbox on this domain.