Skip to main content

Create domain

Attach a custom domain to an environment and start verifying it.

The domain is created in the pending state and does not serve traffic until verification succeeds. Verification runs in the background and polls DNS, so it is eventually consistent.

The response returns dnsRecords: every record needed to finish setup, already resolved for whether this domain is an apex or a subdomain. Create every entry exactly as given. One record establishes routing and one proves ownership, and both are needed: whether ownership can be inferred from the routing record depends on how your provider publishes it, and a name another workspace has already verified can only be claimed through the ownership record. Neither is knowable before the records exist.

When your DNS provider supports Domain Connect, the response also carries a domainConnect object; opening its url applies the same records at the provider in one step. The object is absent when the shortcut is unavailable.

Domains are unique per workspace, so the same name cannot be attached to two environments. Attaching a domain that already exists in your workspace returns a 409 conflict.

How many domains you may attach is set by your plan. Attaching one beyond that allowance returns a 403; upgrade the plan or remove a domain you no longer need.

Important: verification stops after 24 hours without the required DNS records, and the domain moves to failed.

Required Permissions

Your root key must have one of the following permissions:

  • environment.*.create_domain (to attach domains to any environment)
  • environment.<environment_id>.create_domain (to attach domains to a specific environment)
2 min read
post/v2/domains.createDomain
Request example
Response
post/v2/domains.createDomain

Authorization

Authorizationstringheaderrequired#

Unkey uses bearer tokens for authentication. Public integrations use root keys, while the dashboard proxy uses short-lived JWTs.
To authenticate, include the token in the Authorization header of each request:

Root keys have specific permissions attached to them, controlling what operations they can perform. Legacy permissions use tuple strings like api.*.create_key; resource permissions use Unkey Resource Names plus actions, like unkey:v1:ws_123:keyspaces/*#create_key.
Security best practices:

  • Keep root keys secure and never expose them in client-side code
  • Use different root keys for different environments
  • Rotate keys periodically, especially after team member departures
  • Create keys with minimal necessary permissions following least privilege principle
  • Monitor key usage with audit logs.

Body

application/json
projectstringrequired#

Identifies a resource by either its unique ID or its slug.
Accepts a prefixed ID (such as 'proj_' or 'app_') or a slug.

Length: 3–255Pattern: ^[a-zA-Z0-9_-]+$

appstringrequired#

Identifies a resource by either its unique ID or its slug.
Accepts a prefixed ID (such as 'proj_' or 'app_') or a slug.

Length: 3–255Pattern: ^[a-zA-Z0-9_-]+$

environmentstringrequired#

Identifies a resource by either its unique ID or its slug.
Accepts a prefixed ID (such as 'proj_' or 'app_') or a slug.

Length: 3–255Pattern: ^[a-zA-Z0-9_-]+$

domainstringrequired#

Fully qualified domain name to attach to the environment, without a scheme, port, or path.
Must be unique across your entire workspace: the same name cannot be attached to two environments.

The name must sit under a registrable domain: 'api.acme.co.uk' is accepted, the public suffix
'co.uk' itself is not. Internationalized names may be sent in Unicode or Punycode form; either
way the domain is stored and returned in its canonical form, lowercase ASCII with Unicode labels
Punycode encoded, and the DNS records in the response use that form.

Length: 4–253

Responses

application/json
Domain created and verification started. The domain is pending until the DNS records resolve.
metaobjectrequired#
Metadata object included in every API response. This provides context about the request and is essential for debugging, audit trails, and support inquiries. The requestId is particularly important when troubleshooting issues with the Unkey support team.
Show child attributes
requestIdstringrequired#
A unique id for this request. Always include this ID when contacting support about a specific API request. This identifier allows Unkey's support team to trace the exact request through logs and diagnostic systems to provide faster assistance.
dataobjectrequired#
Show child attributes
domainIdstringrequired#

Identifies a resource by either its unique ID or its slug.
Accepts a prefixed ID (such as 'proj_' or 'app_') or a slug.

Length: 3–255Pattern: ^[a-zA-Z0-9_-]+$

dnsRecordsobject[]required#

Every DNS record needed to finish setting up this domain, ready to create at your provider.
The list already accounts for whether the domain is an apex or a subdomain, so no further
branching is needed: create each entry as given.

One record establishes routing and one proves ownership. Create all of them: whether ownership
can be inferred from the routing record depends on how your provider publishes it, and a name
another workspace has already verified can only be claimed through the ownership record.
Neither is knowable before the records exist.

Items: min 1

Show child attributes
typeenum<string>required#

Record type to create. ALIAS is not a real DNS record type: it means an apex-compatible
alias, which providers expose as ALIAS, ANAME, or a flattened CNAME. Apex domains cannot
hold a plain CNAME, so they receive ALIAS where a subdomain receives CNAME.

Options:CNAMEALIASTXT
namestringrequired#

Fully qualified name of the record, ready to use as-is.

Some providers want a name relative to the zone instead. Drop the zone and its trailing dot:
in zone acme.com, api.acme.com becomes api and _unkey.api.acme.com becomes
_unkey.api. A name equal to the zone itself is usually entered as @.

Length: 1–253

valuestringrequired#

The value to set on the record, exactly as given, including any prefix.
Do not trim or reformat it: verification compares the published record against this string.

Use the lowest TTL your provider allows until the domain is verified. Verification polls DNS,
so a long TTL keeps a stale value cached and can burn the verification window on a value you
have already corrected. Raise it afterwards if you want.

Length: 1–512

ttlintegerrequired#

Seconds a resolver may cache this record. Set it in your provider alongside the record's
name and value.

Range: >= 1

verifiedbooleanrequired#

Whether Unkey has read this record back with the expected value. Use it to see which
records are still outstanding.

False does not always mean the record is missing. A provider that does not expose the
published value to a DNS lookup, such as a proxied or flattened routing record, leaves
this false for as long as it serves traffic; such a domain verifies through its TXT
record instead. Always false on a domain no check has run against yet.

notestring#

What this record is for and any provider-specific caveat that applies to it.
Worth surfacing to whoever edits the DNS zone. Treat it as optional: it carries
no data the record itself needs, so a future record type may omit it.

Length: max 512

domainConnectobject#

One-click setup at the domain's DNS provider. Omitted entirely when the provider does not support
Domain Connect or discovery failed, so the object's presence is the signal that the shortcut is
available and both of its fields are filled.

Show child attributes
providerstringrequired#

Display name of the DNS provider the domain is delegated to, such as 'Cloudflare'. Discovered
from the domain's nameservers, so it reflects where DNS is actually hosted rather than where the
domain was registered.

Length: max 256

urlstringrequired#

Signed Domain Connect URL that pre-fills the records in dnsRecords at the provider. Open it in a
browser and the domain owner approves them in one step instead of entering them by hand. The URL is
signed with an Unkey key, so it cannot be constructed or altered by the caller.

Intended for a browser, not a script: after approval the provider sends the browser to this
workspace's app settings page in the Unkey dashboard, so it suits a caller who administers this
workspace. Anyone who does not have access to it approves the records successfully but lands on a
page they cannot open. Approving is what writes the records; verification then proceeds on its own,
so nothing depends on completing that return trip.

Length: max 2048