Domains
All domains will be routed through our high performance CDN provider, bunny. Each domain can be connected by changing your nameservers, or by setting a cname record.
These endpoints manage the hostnames attached to WordPress sites. For
registrar operations (registering/transferring a TLD) see
Domain Registration. OAuth scopes: lookup/query
require domains:read; per-site domain writes require sites:write.
Lookup and Query
List All Domains
List all domains accessible to a user.
GET /api/domains
Params (optional)
- page: Integer | page number (default: 1)
- per_page: Integer | records per page (default: 50, max: 100)
Returned Params
- domains: Array
- id: String
- hostname: String
- site_id: String
- site_pending_delete: Boolean |
truewhen the domain's site is soft-deleted and awaiting purge - account_id: String
- created_at: DateTime
- updated_at: DateTime
- dns_zone: Object | null | associated dns zone
- id: String
- name: String
- dnssec: Boolean
- dnssec_data: Object (nil if dnssec = false)
- created_at: DateTime
- updated_at: DateTime
- account: Object
- id: String
- name: String
- nameservers: Array
- dns_zone_records: Array | associated dns records
- id: String
- record_type: Integer
- ttl: Integer
- value: String
- name: String
- priority: Integer
- weight: Integer
- port: Integer
- flags: Integer
- record_tag: String
- comment: String
- created_at: DateTime
- updated_at: DateTime
Load Domain by ID
GET /api/domains/:id
Returns a single domain object with the same fields as a list entry.
Search by Domain Name
POST /api/domains/query
This is a helper endpoint to find a domain without knowing its ID.
The param q supports substring matching anywhere in the hostname. For example:
- Searching for
examplematchesexample.com; - Searching for
domainmatchesmydomain.com.
Params
- q: String
Returns the same shape as List All Domains.
Check if a domain is available
POST /api/domains/available
This will check if the domain is valid, and if it already exists in our system.
Params
- hostname: String
Use the HTTP status code to determine availability / validity:
HTTP 200: Exists and not availableHTTP 422: Not a valid domain, or missinghostnameparam.HTTP 404: Does not exist — valid and available.
Returned Params
- status: String
Site Endpoints
Locked sites
Every endpoint in this section resolves the site first. A site that is
pending deletion is locked and returns 403 with
code: "pending_delete" and a delete_scheduled_at timestamp — restore the
site to work with its domains again.
A site suspended for an unpaid invoice is locked the same way and returns
402 with code: "service_suspended", plus an invoice object carrying
number and hosted_url so you can send the customer to pay it.
List all domains for a site
GET /api/sites/:site_id/domains
Returned Params
- domains: Array
- id: String
- hostname: String
- dns_zone: Object | associated dns zone
- dns_zone_records: Array | associated dns records
- created_at: DateTime
- updated_at: DateTime
Create a Domain
POST /api/sites/:site_id/domains
Connects a domain to the site. If the site has no domain yet this becomes the
primary domain; otherwise it is added as an alias. Provisioning runs
asynchronously — returns 202 and creates a task.
provision_method must be the exact string dns to provision via nameservers;
any other value (or omission) is treated as cname.
Re-connecting a hostname that is already attached to this site is a no-op — it returns the existing domain rather than creating a second one.
Params
- domain: String (required) | FQDN
- provision_method: String | the exact string
dns, otherwisecname - replace_records: Boolean | for a
dnsconnect only — confirms that conflicting DNS records already present in the zone may be replaced. See below.
DNS conflicts
A dns connect fails with 422 conflicts_found when the zone we host
already has address-bearing records at that hostname, rather than writing
competing records. The response carries a conflicts array so you can show
what would be replaced — each entry has record_guid, type, host,
value and site_name (null when the record is not tied to a site).
Retry the same request with replace_records: true to delete those records
and continue.
Errors
These errors carry a machine-readable code alongside errors. Not every
4xx on these site endpoints does — a permission failure (403), and an
unknown domain id on promote/remove/certificate (404), return only an
errors array. Branch on the status code as well as on code.
- 400
domain_blank|domainblank or missing - 402
service_suspended| the site is suspended for an unpaid invoice - 403
pending_delete| the site is pending deletion and locked - 403 (no code) | the credential's user has no edit permission on the site
- 422
conflicts_found| adnsconnect hit existing records — retry withreplace_records: true(body includesconflicts) - 422
hostname_in_use| the hostname is already connected to another site in the same billing account family — including a site that is pending deletion and therefore no longer listed.errorsnames the site holding it - 422
invalid_domain| validation or ownership rejection — including adnsconnect for a root another customer has registered with us
Returned params for DNS (provision_method = dns)
- id: String
- ns1: String | first nameserver (from the site's account)
- ns2: String | second nameserver (from the site's account)
Returned params for cname (default)
- id: String
- cname: String
Promote a domain
PATCH /api/sites/:site_id/domains/:id
This takes no parameters and will promote the requested domain to the primary
domain. The existing primary domain will become an alias. Returns 202.
Remove
This will remove the domain. If it is the primary domain, the site will be
updated to use the default temporary url. It's recommended to first promote your
new domain to the primary domain before deleting it. Returns 202.
DELETE /api/sites/:site_id/domains/:id
Pre-provision SSL Certificate (External DNS)
Obtain a Bunny SSL certificate via a DNS-01 challenge for a hostname whose DNS is hosted elsewhere — before pointing it at CloudPress.
Wildcard certificates are not supported
An earlier version of this page documented a wildcard parameter and a
wildcard response field. Neither exists — the API never read the parameter
and never returned the field. Wildcard certificates are deliberately
unsupported on this flow: the hostname has to be registered on the pull zone
before the challenge is issued, and a *.host registration cannot be
validated before DNS is pointed at us. Request a certificate per hostname.
Requires CDN active for the site, else 409 cdn_not_active. Scope:
sites:read for status; the request, complete and refresh steps require
sites:write and site write/modify permission. Those three steps proxy
a Bunny call — an upstream Bunny failure returns 502.
The flow:
POST .../request_certificatereturns a DNS-01 TXT challenge to add at your DNS.- Add the TXT record at your DNS provider.
POST .../complete_certificate— Bunny validates and issues the certificate.
Certificate Status
GET /api/sites/:site_id/domains/:id/certificate
Returned Params
- provision_type: String | null,
http01,dns01_external, orcustom - challenge: Object | null | the DNS-01 TXT record(s) to add (while pending)
- requested_at: Timestamp | null
- issued_at: Timestamp | null
- active: Boolean
Request Certificate (step 1)
POST /api/sites/:site_id/domains/:id/request_certificate
Takes no parameters. Returns the DNS-01 TXT challenge to add at your DNS.
Complete Certificate (step 2)
POST /api/sites/:site_id/domains/:id/complete_certificate
Call once the TXT record is live; Bunny validates and issues the certificate.
Refresh Certificate
POST /api/sites/:site_id/domains/:id/refresh_certificate
Re-reads the certificate status at Bunny and resolves the domain if a certificate is already active — for example one issued out of band through the standard flow — and re-attempts completion if the challenge is still pending. It never requests a new TXT challenge, so it is safe to call repeatedly while you wait. Same scope and permissions as the request/complete steps.