Skip to content

Check a custom domain again

The brands.domains.verify operation: POST /api/v1/brands/{id}/domains/{host}/verify in the REST API and the brands_domains_verify tool in the MCP server.

POST/api/v1/brands/{id}/domains/{host}/verify

Asks the domain provider to verify one of the brand's custom domains again, looks up the domain's _ownrecord-challenge TXT record, and records the answer: verified once the domain is attached, its DNS points at Ownrecord and the challenge record carries the domain's token (ownrecord-verify={token}); pending otherwise, with the records still needed in dnsRecords and what is missing in check.problems.

A verified domain that no longer passes, for example because the challenge record was deleted, goes back to pending: its pages stop being served and links stop using it until it is verified again. DNS changes can take a few hours to be seen; checking again is safe at any time.

At a glance

  • REST: POST /api/v1/brands/{id}/domains/{host}/verify
  • MCP tool: brands_domains_verify
  • Operation ID: brands.domains.verify
  • Scope: Organization, or one of its legal entities or brands
  • Permission: brands:write
  • Risk tier: write
  • Dry run: Supported (send Ownrecord-Dry-Run: true)
  • Idempotency: Idempotency-Key required
  • Modes: Test and live
  • Authentication: API key with the brands:write scope
  • Since: M9

Request

Path parameters

FieldTypeDescription
idbrd_… ID
hoststringThe domain's host name, e.g. pay.acme.com.

Body

This operation takes no body.

Response

200 OK with a JSON object:

FieldTypeDescription
object"brand_domain"
hoststringThe host name, lower case (internationalised names in their xn-- form).
purposeone of checkout, portalcheckout serves hosted checkout, portal the customer portal.
brandIdstring
statusone of pending, verifiedverified once the host points at Ownrecord and the challenge TXT record proves it's yours; pending until then.
verifiedAtdate-time or null
urlstringThe domain's base URL; hosted pages are served under it once it's verified.
dnsRecordsarray of objectThe DNS records to create at the domain's DNS provider: routing, Ownrecord's challenge TXT record, and the hosting provider's TXT record when it asks for one.
dnsRecords[].typeone of A, AAAA, CNAME, TXT
dnsRecords[].namestring
dnsRecords[].labelstring
dnsRecords[].valuestring
dnsRecords[].purposeone of routing, ownership
checkobject or null

Examples

DNS points at Ownrecord and the challenge record is in place

HTTP
POST /api/v1/brands/brd_01m4tx5nj8emwbyf7y9vcf47x4/domains/pay.acme-app.com/verify
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14

Response (200):

JSON
{
  "object": "brand_domain",
  "host": "pay.acme-app.com",
  "purpose": "checkout",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "status": "verified",
  "verifiedAt": "2026-10-20T09:40:00.000Z",
  "url": "https://pay.acme-app.com",
  "dnsRecords": [
    {
      "type": "CNAME",
      "name": "pay.acme-app.com",
      "label": "pay",
      "value": "cname.vercel-dns.com",
      "purpose": "routing"
    },
    {
      "type": "TXT",
      "name": "_ownrecord-challenge.pay.acme-app.com",
      "label": "_ownrecord-challenge.pay",
      "value": "ownrecord-verify=5e0c9a1b7d3f42e8a6b0c4d2e9f17a35",
      "purpose": "ownership"
    }
  ],
  "check": {
    "checkedAt": "2026-10-20T09:40:00.000Z",
    "ownershipVerified": true,
    "dnsConfigured": true,
    "challengeVerified": true,
    "problems": [],
    "simulated": false
  }
}

Still waiting for the DNS records

HTTP
POST /api/v1/brands/brd_01m4tx5nj8emwbyf7y9vcf47x4/domains/pay.acme-app.com/verify
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14

Response (200):

JSON
{
  "object": "brand_domain",
  "host": "pay.acme-app.com",
  "purpose": "checkout",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "status": "pending",
  "verifiedAt": null,
  "url": "https://pay.acme-app.com",
  "dnsRecords": [
    {
      "type": "CNAME",
      "name": "pay.acme-app.com",
      "label": "pay",
      "value": "cname.vercel-dns.com",
      "purpose": "routing"
    },
    {
      "type": "TXT",
      "name": "_ownrecord-challenge.pay.acme-app.com",
      "label": "_ownrecord-challenge.pay",
      "value": "ownrecord-verify=5e0c9a1b7d3f42e8a6b0c4d2e9f17a35",
      "purpose": "ownership"
    }
  ],
  "check": {
    "checkedAt": "2026-10-20T09:00:00.000Z",
    "ownershipVerified": true,
    "dnsConfigured": false,
    "challengeVerified": false,
    "problems": [
      {
        "code": "dns_not_configured",
        "message": "The host doesn't point at Ownrecord yet. Create the DNS records below; changes can take up to a few hours."
      },
      {
        "code": "challenge_missing",
        "message": "Ownrecord can't find its _ownrecord-challenge TXT record yet. Create it with the value below to prove the domain is yours."
      }
    ],
    "simulated": false
  }
}

Errors

CodeHTTP statusWhenWhat to do
validation_failed400A field is missing or malformed.Fix the fields listed in details.issues and send the request again.
resource_not_found404The brand isn't visible to the request, or has no domain with this host.Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.
domain_provider_error502The domain provider failed; nothing changed.Retry after a short wait. If it keeps failing, contact support with the request ID.
domain_dns_unavailable502DNS didn't answer for the challenge record; nothing changed.Retry after a short wait. Nothing was changed.
domain_provider_not_configured500Custom domains aren't set up on this deployment.Contact support: the deployment's domain provider credentials are missing.
upstream_timeout502The domain provider or DNS didn't answer in time; nothing changed.Retry the request with the same Idempotency-Key after a short wait.
version_conflict409The brand changed at the same time; retry.Fetch the object again and retry the change.
permission_denied403You can't change this brand's settings.Ask an organization owner or admin for a role or scope that allows this action.

It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.