Reference
Lookup tables for CustomDomain™: enums, DNS record types, webhook events, error codes, environment variables, webhook signatures and the default edge record.
Enums
ConnectionStatus: pending, propagating, live, failed.
SetupType: automatic, manual, async, mcp.
Role: owner, admin, member, viewer.
PlanId: starter, startup, growth, premium, enterprise (and free, retired on 2026-09-29: it still names a legacy workspace moving to a paid plan, and is never offered).
BillingState: trialing, active, past_due, grace, blocked, comped, canceled, legacy_free (see Plans & quotas).
Environment: production, staging, development.
DNS record types: A, AAAA, CNAME, CAA, MX, NS, SRV, TXT, plus the
meta-types SPFM (SPF-merge), APEXCNAME (a CNAME at the zone root, realized
per provider) and REDIR301.
One-click setup template shapes (the provider-hosted one-click protocol;
customdomain.ai catalog shipped in prod):
web: apex-a, apex-aaaa, apex-cname, apex-www, subdomain, www,
wildcard, customdomain, website, clickfunnels; ownership: verify, connect-verified (apex A + ownership TXT
applied together); caa; email: email-spf, email-dkim, email-dmarc,
email-mx, email-full; and redirect (301 forward).
Webhook events (the complete set; see
webhooks): connection.created,
connection.applied, connection.live, connection.failed,
connection.records.updated, connection.reapply.failed,
connection.records_outdated, connection.disconnected,
domain.record_missing, domain.record_restored, domain.purchased,
purchase.error, purchase.confirmation.expired, secure_status,
power_status, certificate.issued, certificate.renewed,
certificate.failed, certificate.expiring.
Error envelope & codes
Every error is { "code", "title", "details"? }. code is a stable machine
string:
code | HTTP | Meaning |
|---|---|---|
invalid_request | 400 | Malformed or missing input. |
unauthorized | 401 | Missing/invalid credential. |
forbidden | 403 | The credential is valid but may not do this (a role or scope too low). |
not_found | 404 | Object doesn't exist or isn't in your tenant. |
conflict | 409 | The request conflicts with the resource's current state. |
rate_limited | 429 | Too many requests; slow down and retry shortly. |
reach_quota | 429 | The workspace has made as many reads at DNS providers, to find out what a key reaches, as it may this hour. Retry-After is the seconds until the hour ends. See provider accounts. |
billing_required | 402 | The workspace has no active subscription or trial, so new connections and new credentials (API keys, applications, rotated secrets) are refused (existing ones keep working). A new workspace's first ten domains (its free domains) are the exception: they connect without a card, and the next one is refused. details says why and where an owner or admin can fix it. See Plans & quotas. |
connections_unavailable | 402 | The same refusal as the tenant's end users see it, with a neutral message that says nothing about the tenant's billing or account. A connect-scoped widget token (your customer in the widget) gets it in place of billing_required, quota_exceeded and workspace_suspended. |
quota_exceeded | 402 | A Starter workspace connected its allowance of 10 domains in any 12 months (only where the deployment enforces quotas). |
workspace_suspended | 403 | CustomDomain™ support suspended the workspace. New connections, every connect step on a domain it already has (a key apply, a provider sign in, one-click setup, the Route 53 access role, the hosted zone and :reapply) and the start of a domain purchase are refused. Reads, rechecks and removals keep working, and connected domains keep their records. Only support can resume the workspace. |
sandbox_not_allowed | 403 | A test mode application's key or widget token, or a test mode domain, was used on something outside the test flow (SSL, Power, sign in, bulk, agents, purchases and the rest). details names what was refused. |
sandbox_domain_required | 400 | A test mode application was given a name that does not end in .test. |
sandbox_domain_reserved | 400 | A live application was given a .test name. Those are for test mode. |
sandbox_limit | 409 | The test mode allowance is used up: 3 test mode applications per workspace, 100 test domains per application. |
service_unavailable | 503 | An optional engine (Sell, templates, billing, webhooks) isn't configured. |
internal_error | 500 | Server error (generic title + a logged request id). |
Environment variables
Control-plane variables read CUSTOMDOMAIN_* first and fall back to the legacy
JUSTEASY_* prefix. Variables that matter for API consumers are called out on
the page that uses them.
Webhook signature
- Headers:
X-JE-Timestamp: <unix seconds>andX-JE-Signature: sha256=<hex>. - Algorithm: HMAC-SHA256 over
${timestamp}.${rawBody}(the timestamp, a dot, then the exact request body), keyed by the endpoint's signing secret (whsec_…). Reject deliveries whose timestamp is outside a ~5-minute window. See Verifying signatures.
Default edge record
A connection with no applied records yet points at the edge with a synthesized
CNAME <host> → edge.customdomain.ai (TTL 3600), overridable with
JUSTEASY_EDGE_CNAME_TARGET. At a zone root the record is realized per
provider (ALIAS/ANAME/flattened CNAME, or A records to the edge IPs); see
Root domains.
Resources
- Architecture: the two-plane design, data model, and request flows.
openapi-v1.yaml: the OpenAPI document the API reference is generated from; also served live atGET /v1/openapi.json.
FAQ
Short answers to common questions about connecting domains: which providers connect automatically, how records get written, whether keys are stored, and apex domains.
Changelog
How CustomDomain™ ships updates without numbered releases, the additive-only /v1 API versioning policy, and a running list of notable product milestones.