Skip to content

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 | true when 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 example matches example.com;
  • Searching for domain matches mydomain.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 available
  • HTTP 422: Not a valid domain, or missing hostname param.
  • 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, otherwise cname
  • replace_records: Boolean | for a dns connect 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 | domain blank 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 | a dns connect hit existing records — retry with replace_records: true (body includes conflicts)
  • 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. errors names the site holding it
  • 422 invalid_domain | validation or ownership rejection — including a dns connect 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:

  1. POST .../request_certificate returns a DNS-01 TXT challenge to add at your DNS.
  2. Add the TXT record at your DNS provider.
  3. 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, or custom
  • 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.