# Brands tools

> MCP tools for Brands: brands_create, brands_domains_add, brands_domains_list, brands_domains_remove, brands_domains_verify, brands_get, brands_go_live, brands_go_live_checklist, brands_list, brands_update.

- `brands_create`: Create a brand
- `brands_domains_add`: Add a custom domain to a brand
- `brands_domains_list`: List a brand's custom domains
- `brands_domains_remove`: Remove a custom domain from a brand
- `brands_domains_verify`: Check a custom domain again
- `brands_get`: Get a brand
- `brands_go_live`: Take a brand live
- `brands_go_live_checklist`: Get a brand's go-live checklist
- `brands_list`: List brands
- `brands_update`: Update a brand

## `brands_create`

**Create a brand.** Operation [`brands.create`](https://ownrecord.io/docs/reference/brands-create.md) · Organization level · permission `brands:write` · risk tier `write` · since M1

Creates a brand (a storefront or product line) under one of the organization's legal entities. The brand
starts in test mode only (`status: test_only`); it goes live through the go-live checklist.

- `slug` is unique across Ownrecord: it appears in checkout URLs and API keys.
- `invoiceSeries.invoice` (and `creditNote`, default `{invoice}-CN`) must be unused by every other brand of
  the same entity: each brand numbers its invoices in its own series within the entity.
- `statementDescriptor` defaults to the name in capitals.

The plan limits the number of brands (`brands.max`). Needs `brands:write`: organization owners and admins,
or an org or entity API key with that scope (a test-mode key creates the brand like any other key: brands
aren't mode-separated).

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Arguments

| Argument              | Type         | Required | Description                                                                                                                                                                                              |
| --------------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entityId`            | ID (`ent_…`) | yes      | –                                                                                                                                                                                                        |
| `name`                | string       | yes      | (1–120 characters)                                                                                                                                                                                       |
| `slug`                | string       | yes      | (3–40 characters)                                                                                                                                                                                        |
| `invoiceSeries`       | object       | yes      | –                                                                                                                                                                                                        |
| `statementDescriptor` | string       | no       | (5–22 characters)                                                                                                                                                                                        |
| `branding`            | object       | no       | –                                                                                                                                                                                                        |
| `entity_id`           | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization.                                                                                                                       |
| `brand_id`            | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.                                                                                                                              |
| `idempotency_key`     | string       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`             | boolean      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A field is missing or malformed (`details.issues`).
- [`slug_invalid`](https://ownrecord.io/docs/reference/errors.md#slug_invalid): The slug has the wrong format or is a reserved word.
- [`slug_taken`](https://ownrecord.io/docs/reference/errors.md#slug_taken): Another brand already uses this slug (brand slugs are unique on Ownrecord).
- [`invoice_series_taken`](https://ownrecord.io/docs/reference/errors.md#invoice_series_taken): Another brand of the entity already uses the invoice or credit-note series.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): The entity doesn't exist or is outside the request's scope.
- [`scope_mismatch`](https://ownrecord.io/docs/reference/errors.md#scope_mismatch): The request is scoped to one brand; brands are created at an entity.
- [`entitlement_required`](https://ownrecord.io/docs/reference/errors.md#entitlement_required): The plan's `brands.max` is reached.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't create brands here.

### Examples

**A brand with its own invoice series**

Arguments:

```json
{
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "name": "Acme App",
  "slug": "acme-app",
  "invoiceSeries": {
    "invoice": "ACM"
  },
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "object": "brand",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "slug": "acme-app",
  "name": "Acme App",
  "status": "test_only",
  "isPlatformBrand": false,
  "branding": {
    "logoUrl": null,
    "iconUrl": null,
    "primaryColor": null,
    "accentColor": null,
    "supportEmail": null,
    "supportUrl": null,
    "termsUrl": null,
    "privacyUrl": null
  },
  "statementDescriptor": "ACME APP",
  "statementDescriptorSuffix": null,
  "invoiceSeries": {
    "invoice": "ACM",
    "creditNote": "ACM-CN"
  },
  "billingSettings": {
    "dunning": {
      "retryOffsetsDays": [1, 3, 5, 7],
      "finalAction": "unpaid"
    },
    "entitlementGraceDays": 7,
    "sendInvoice": {
      "graceDays": 14
    },
    "cardExpiryReminders": true,
    "pausedEntitlements": false,
    "paymentBehavior": "allow_incomplete"
  },
  "policies": {
    "breakageRecognition": "at_expiry",
    "supportRefundLimits": [],
    "disputeWalletFreeze": true,
    "taxIdUnavailable": "charge_as_b2c"
  },
  "portalSettings": {
    "allowPlanChanges": true,
    "planChangeMatrix": [],
    "allowCancel": true,
    "cancelBehavior": "period_end",
    "collectCancelReason": true,
    "allowQuantityChanges": false
  },
  "checkoutSettings": {
    "locales": ["en", "hr"],
    "collectPhone": false,
    "requireBusinessDetails": "auto"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z"
}
```

**With support and legal links**

Arguments:

```json
{
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "name": "Acme App",
  "slug": "acme-app",
  "invoiceSeries": {
    "invoice": "ACM",
    "creditNote": "ACM-CN"
  },
  "statementDescriptor": "ACME APP",
  "branding": {
    "supportEmail": "help@acme.example",
    "termsUrl": "https://acme.example/terms",
    "privacyUrl": "https://acme.example/privacy"
  },
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "object": "brand",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "slug": "acme-app",
  "name": "Acme App",
  "status": "test_only",
  "isPlatformBrand": false,
  "branding": {
    "logoUrl": null,
    "iconUrl": null,
    "primaryColor": null,
    "accentColor": null,
    "supportEmail": "help@acme.example",
    "supportUrl": null,
    "termsUrl": "https://acme.example/terms",
    "privacyUrl": "https://acme.example/privacy"
  },
  "statementDescriptor": "ACME APP",
  "statementDescriptorSuffix": null,
  "invoiceSeries": {
    "invoice": "ACM",
    "creditNote": "ACM-CN"
  },
  "billingSettings": {
    "dunning": {
      "retryOffsetsDays": [1, 3, 5, 7],
      "finalAction": "unpaid"
    },
    "entitlementGraceDays": 7,
    "sendInvoice": {
      "graceDays": 14
    },
    "cardExpiryReminders": true,
    "pausedEntitlements": false,
    "paymentBehavior": "allow_incomplete"
  },
  "policies": {
    "breakageRecognition": "at_expiry",
    "supportRefundLimits": [],
    "disputeWalletFreeze": true,
    "taxIdUnavailable": "charge_as_b2c"
  },
  "portalSettings": {
    "allowPlanChanges": true,
    "planChangeMatrix": [],
    "allowCancel": true,
    "cancelBehavior": "period_end",
    "collectCancelReason": true,
    "allowQuantityChanges": false
  },
  "checkoutSettings": {
    "locales": ["en", "hr"],
    "collectPhone": false,
    "requireBusinessDetails": "auto"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z"
}
```

## `brands_domains_add`

**Add a custom domain to a brand.** Operation [`brands.domains.add`](https://ownrecord.io/docs/reference/brands-domains-add.md) · Organization level · permission `brands:write` · risk tier `write` · since M9

Adds a custom domain on which the brand's hosted checkout (`purpose: checkout`) or customer portal
(`purpose: portal`) is served, and attaches it to Ownrecord's hosting. The answer has the DNS records to create
at your DNS provider:

- a CNAME for a subdomain such as `pay.acme.com` (recommended), or A records for a domain at the apex;
- a TXT record `_ownrecord-challenge.{host}` with the value `ownrecord-verify={token}`, which proves the domain is
  yours. The token is new for every domain added;
- a TXT record of the hosting provider, when it needs its own proof because another account uses the domain.

The domain is `pending` until its DNS points at Ownrecord and the challenge record is in place; then
`brands.domains.verify` marks it `verified`. From then on, checkout links of the brand (the session `url`,
waitlist and invoice emails) use the domain, without the brand's slug: `https://pay.acme.com/cs_…`.

A brand has at most one checkout and one portal domain, and a host belongs to one brand only. The plan limits the
custom domains of the whole organization (`custom_domains.max`). Needs `brands:write`.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true`

### Arguments

| Argument          | Type                        | Required | Description                                                                                                                                                                                              |
| ----------------- | --------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`brd_…`)                | yes      | –                                                                                                                                                                                                        |
| `host`            | string                      | yes      | The host name, e.g. `pay.acme.com`: one you registered, without https\:// or a path. (at most 300 characters)                                                                                            |
| `purpose`         | one of `checkout`, `portal` | yes      | `checkout` serves the brand's hosted checkout, `portal` its customer portal.                                                                                                                             |
| `entity_id`       | ID (`ent_…`)                | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization.                                                                                                                       |
| `brand_id`        | ID (`brd_…`)                | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.                                                                                                                              |
| `idempotency_key` | string                      | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean                     | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A field is missing or malformed.
- [`domain_invalid`](https://ownrecord.io/docs/reference/errors.md#domain_invalid): The host isn't one a brand can use (a URL, an IP address, a reserved name; `details.reason`), or the domain provider refused it.
- [`domain_taken`](https://ownrecord.io/docs/reference/errors.md#domain_taken): The host is attached to a brand already (this one or another).
- [`domain_purpose_taken`](https://ownrecord.io/docs/reference/errors.md#domain_purpose_taken): The brand already has a domain for this purpose.
- [`domain_unavailable`](https://ownrecord.io/docs/reference/errors.md#domain_unavailable): Another project or account at the hosting provider holds the domain.
- [`entitlement_required`](https://ownrecord.io/docs/reference/errors.md#entitlement_required): The organization's plan allows no more custom domains.
- [`domain_provider_error`](https://ownrecord.io/docs/reference/errors.md#domain_provider_error): The domain provider failed; nothing was added.
- [`domain_dns_unavailable`](https://ownrecord.io/docs/reference/errors.md#domain_dns_unavailable): DNS didn't answer for the challenge record; nothing was added.
- [`domain_provider_not_configured`](https://ownrecord.io/docs/reference/errors.md#domain_provider_not_configured): Custom domains aren't set up on this deployment.
- [`upstream_timeout`](https://ownrecord.io/docs/reference/errors.md#upstream_timeout): The domain provider or DNS didn't answer in time; nothing was added.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No brand with this ID is visible to the request.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The brand changed at the same time; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't change this brand's settings.

### Examples

**A checkout domain**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "host": "pay.acme-app.com",
  "purpose": "checkout",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```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
  }
}
```

**A portal domain whose hosting provider asks for its own TXT record too**

The domain is used by another account at the hosting provider, which asks for proof of ownership as well.

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "host": "billing.acme-app.com",
  "purpose": "portal",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "object": "brand_domain",
  "host": "billing.acme-app.com",
  "purpose": "portal",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "status": "pending",
  "verifiedAt": null,
  "url": "https://billing.acme-app.com",
  "dnsRecords": [
    {
      "type": "CNAME",
      "name": "billing.acme-app.com",
      "label": "billing",
      "value": "cname.vercel-dns.com",
      "purpose": "routing"
    },
    {
      "type": "TXT",
      "name": "_ownrecord-challenge.billing.acme-app.com",
      "label": "_ownrecord-challenge.billing",
      "value": "ownrecord-verify=c18d7e2f90ab4c3d8e5f6a7b1c2d3e4f",
      "purpose": "ownership"
    },
    {
      "type": "TXT",
      "name": "_vercel.acme-app.com",
      "label": "_vercel",
      "value": "vc-domain-verify=billing.acme-app.com,9b1f6c2e8d3a4f5b6c7d",
      "purpose": "ownership"
    }
  ],
  "check": {
    "checkedAt": "2026-10-20T09:00:00.000Z",
    "ownershipVerified": false,
    "dnsConfigured": false,
    "challengeVerified": false,
    "problems": [
      {
        "code": "ownership_unverified",
        "message": "The domain is used elsewhere on the hosting platform. Add the TXT record below to prove it's yours."
      },
      {
        "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
  }
}
```

## `brands_domains_list`

**List a brand's custom domains.** Operation [`brands.domains.list`](https://ownrecord.io/docs/reference/brands-domains-list.md) · Organization level · permission `brands:read` · risk tier `read` · since M9

Lists the brand's custom domains (at most one for checkout and one for the customer portal) with their status and
the DNS records to create, and how many custom domains the organization uses against its plan's limit
(`custom_domains.max`, counted across all brands).

A `pending` domain doesn't serve anything yet; links keep using Ownrecord's own address until it is `verified`.
The records shown here are the general ones; `brands.domains.verify` asks the domain provider again and returns
the records it recommends for this domain, including a TXT record when it needs proof of ownership.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument    | Type         | Required | Description                                                                        |
| ----------- | ------------ | -------- | ---------------------------------------------------------------------------------- |
| `id`        | ID (`brd_…`) | yes      | –                                                                                  |
| `entity_id` | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization. |
| `brand_id`  | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.        |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): The brand ID is malformed.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No brand with this ID is visible to the request.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see this brand's settings.

### Examples

**A verified checkout domain**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "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": null
    }
  ],
  "usage": {
    "used": 1,
    "limit": 2
  }
}
```

**A checkout domain waiting for its DNS record**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "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": null
    }
  ],
  "usage": {
    "used": 1,
    "limit": 2
  }
}
```

## `brands_domains_remove`

**Remove a custom domain from a brand.** Operation [`brands.domains.remove`](https://ownrecord.io/docs/reference/brands-domains-remove.md) · Organization level · permission `brands:write` · risk tier `write` · since M9

Removes one of the brand's custom domains. Its hosted pages stop being served on it within a minute, links sent
earlier that use it stop working, and new links use Ownrecord's own address (or the brand's other verified
domain). The domain is then detached from Ownrecord's hosting in the background.

The domain stops counting against the plan's `custom_domains.max` at once, and can be added again later, to
this brand or another. Needs `brands:write`.

Annotations: `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Arguments

| Argument          | Type         | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`brd_…`) | yes      | –                                                                                                                                                                                                        |
| `host`            | string       | yes      | The domain's host name, e.g. `pay.acme.com`. (1–300 characters)                                                                                                                                          |
| `reason`          | string       | no       | Why, for the audit log. (1–500 characters)                                                                                                                                                               |
| `entity_id`       | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization.                                                                                                                       |
| `brand_id`        | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.                                                                                                                              |
| `idempotency_key` | string       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A field is missing or malformed.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): The brand isn't visible to the request, or has no domain with this host.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The brand changed at the same time; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't change this brand's settings.

### Example

**Remove the checkout domain**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "host": "pay.acme-app.com",
  "reason": "Moving checkout to pay.acme.io",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "object": "brand_domain",
  "host": "pay.acme-app.com",
  "purpose": "checkout",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "deleted": true
}
```

## `brands_domains_verify`

**Check a custom domain again.** Operation [`brands.domains.verify`](https://ownrecord.io/docs/reference/brands-domains-verify.md) · Organization level · permission `brands:write` · risk tier `write` · since M9

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.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: true`

### Arguments

| Argument          | Type         | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`brd_…`) | yes      | –                                                                                                                                                                                                        |
| `host`            | string       | yes      | The domain's host name, e.g. `pay.acme.com`. (1–300 characters)                                                                                                                                          |
| `entity_id`       | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization.                                                                                                                       |
| `brand_id`        | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.                                                                                                                              |
| `idempotency_key` | string       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A field is missing or malformed.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): The brand isn't visible to the request, or has no domain with this host.
- [`domain_provider_error`](https://ownrecord.io/docs/reference/errors.md#domain_provider_error): The domain provider failed; nothing changed.
- [`domain_dns_unavailable`](https://ownrecord.io/docs/reference/errors.md#domain_dns_unavailable): DNS didn't answer for the challenge record; nothing changed.
- [`domain_provider_not_configured`](https://ownrecord.io/docs/reference/errors.md#domain_provider_not_configured): Custom domains aren't set up on this deployment.
- [`upstream_timeout`](https://ownrecord.io/docs/reference/errors.md#upstream_timeout): The domain provider or DNS didn't answer in time; nothing changed.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The brand changed at the same time; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't change this brand's settings.

### Examples

**DNS points at Ownrecord and the challenge record is in place**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "host": "pay.acme-app.com",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```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**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "host": "pay.acme-app.com",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```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
  }
}
```

## `brands_get`

**Get a brand.** Operation [`brands.get`](https://ownrecord.io/docs/reference/brands-get.md) · Organization level · permission `brands:read` · risk tier `read` · since M1

Returns one brand by its ID (`brd_…`): name, slug, status, branding, statement descriptor and invoice
series. A brand outside the request's scope (another organization, another entity of an entity-scoped
request, or a sibling of a brand-scoped one) is `resource_not_found`.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument    | Type         | Required | Description                                                                        |
| ----------- | ------------ | -------- | ---------------------------------------------------------------------------------- |
| `id`        | ID (`brd_…`) | yes      | –                                                                                  |
| `entity_id` | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization. |
| `brand_id`  | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.        |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No brand with this ID is visible to the request.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see brands here.

### Example

**Read a brand**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4"
}
```

Result `output`:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "object": "brand",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "slug": "acme-app",
  "name": "Acme App",
  "status": "test_only",
  "isPlatformBrand": false,
  "branding": {
    "logoUrl": null,
    "iconUrl": null,
    "primaryColor": null,
    "accentColor": null,
    "supportEmail": null,
    "supportUrl": null,
    "termsUrl": null,
    "privacyUrl": null
  },
  "statementDescriptor": "ACME APP",
  "statementDescriptorSuffix": null,
  "invoiceSeries": {
    "invoice": "ACM",
    "creditNote": "ACM-CN"
  },
  "billingSettings": {
    "dunning": {
      "retryOffsetsDays": [1, 3, 5, 7],
      "finalAction": "unpaid"
    },
    "entitlementGraceDays": 7,
    "sendInvoice": {
      "graceDays": 14
    },
    "cardExpiryReminders": true,
    "pausedEntitlements": false,
    "paymentBehavior": "allow_incomplete"
  },
  "policies": {
    "breakageRecognition": "at_expiry",
    "supportRefundLimits": [],
    "disputeWalletFreeze": true,
    "taxIdUnavailable": "charge_as_b2c"
  },
  "portalSettings": {
    "allowPlanChanges": true,
    "planChangeMatrix": [],
    "allowCancel": true,
    "cancelBehavior": "period_end",
    "collectCancelReason": true,
    "allowQuantityChanges": false
  },
  "checkoutSettings": {
    "locales": ["en", "hr"],
    "collectPhone": false,
    "requireBusinessDetails": "auto"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z"
}
```

## `brands_go_live`

**Take a brand live.** Operation [`brands.go_live`](https://ownrecord.io/docs/reference/brands-go-live.md) · Organization level · permission `brands:admin` · risk tier `security` · since M12

Moves the brand from test mode (`test_only`) to `live`, so it can take real payments, once every item of its
go-live checklist holds (`brands.go_live_checklist`). The brand's live processor connection is tested at the
processor first, and the test result is recorded on the connection. If anything is missing the call fails with
`go_live_requirements_unmet` and `details.unmet` lists what. A brand that is live already is returned as is.

After this, live API keys can be created for the brand and its live checkout sessions are accepted.

Call it in live mode, with a sign-in in the last 15 minutes in the console. Security tier for agents (it needs
approval). Needs `brands:admin` (organization owners and admins).

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: true`

### Arguments

| Argument          | Type         | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`brd_…`) | yes      | –                                                                                                                                                                                                        |
| `entity_id`       | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization.                                                                                                                       |
| `brand_id`        | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.                                                                                                                              |
| `idempotency_key` | string       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.
- **Mode:** live-mode keys only.

### Errors

- [`go_live_requirements_unmet`](https://ownrecord.io/docs/reference/errors.md#go_live_requirements_unmet): A requirement isn't met (`details.unmet`, `details.items`); the brand stays in test mode.
- [`mode_not_supported`](https://ownrecord.io/docs/reference/errors.md#mode_not_supported): The request is in test mode.
- [`processor_request_failed`](https://ownrecord.io/docs/reference/errors.md#processor_request_failed): The processor couldn't be reached for the connection test.
- [`upstream_timeout`](https://ownrecord.io/docs/reference/errors.md#upstream_timeout): The processor didn't answer the connection test in time.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The brand changed at the same time; try again.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such brand in your scope.
- [`livemode_not_allowed`](https://ownrecord.io/docs/reference/errors.md#livemode_not_allowed): You have no live-mode access.
- [`fresh_auth_required`](https://ownrecord.io/docs/reference/errors.md#fresh_auth_required): Your last sign-in is older than 15 minutes.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't take this brand live.

### Example

**Every item holds**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "brand": {
    "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
    "object": "brand",
    "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
    "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
    "slug": "acme-app",
    "name": "Acme App",
    "status": "live",
    "isPlatformBrand": false,
    "branding": {
      "logoUrl": null,
      "iconUrl": null,
      "primaryColor": null,
      "accentColor": null,
      "supportEmail": null,
      "supportUrl": null,
      "termsUrl": null,
      "privacyUrl": null
    },
    "statementDescriptor": "ACME APP",
    "statementDescriptorSuffix": null,
    "invoiceSeries": {
      "invoice": "ACM",
      "creditNote": "ACM-CN"
    },
    "billingSettings": {
      "dunning": {
        "retryOffsetsDays": [1, 3, 5, 7],
        "finalAction": "unpaid"
      },
      "entitlementGraceDays": 7,
      "sendInvoice": {
        "graceDays": 14
      },
      "cardExpiryReminders": true,
      "pausedEntitlements": false,
      "paymentBehavior": "allow_incomplete"
    },
    "policies": {
      "breakageRecognition": "at_expiry",
      "supportRefundLimits": [],
      "disputeWalletFreeze": true,
      "taxIdUnavailable": "charge_as_b2c"
    },
    "portalSettings": {
      "allowPlanChanges": true,
      "planChangeMatrix": [],
      "allowCancel": true,
      "cancelBehavior": "period_end",
      "collectCancelReason": true,
      "allowQuantityChanges": false
    },
    "checkoutSettings": {
      "locales": ["en", "hr"],
      "collectPhone": false,
      "requireBusinessDetails": "auto"
    },
    "createdAt": "2026-10-13T12:00:00.000Z",
    "updatedAt": "2026-10-13T12:00:00.000Z"
  },
  "checklist": {
    "object": "go_live_checklist",
    "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
    "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
    "brandStatus": "live",
    "ready": true,
    "live": true,
    "items": [
      {
        "requirement": "plan_livemode",
        "met": true,
        "reason": null
      },
      {
        "requirement": "org_status",
        "met": true,
        "reason": null
      },
      {
        "requirement": "legal_accepted",
        "met": true,
        "reason": null
      },
      {
        "requirement": "entity_verified",
        "met": true,
        "reason": null
      },
      {
        "requirement": "invoice_settings",
        "met": true,
        "reason": null
      },
      {
        "requirement": "home_registration",
        "met": true,
        "reason": null
      },
      {
        "requirement": "processor_live",
        "met": true,
        "reason": null
      },
      {
        "requirement": "brand_urls",
        "met": true,
        "reason": null
      },
      {
        "requirement": "brand_status",
        "met": true,
        "reason": null
      }
    ],
    "processor": {
      "connectionId": "pcon_01m4tx5nj8emwbyf7y9vcf47xd",
      "processor": "stripe",
      "status": "active",
      "lastTestOk": true,
      "lastTestAt": "2026-10-26T09:00:00.000Z"
    },
    "legalVersions": {
      "terms": "1.0",
      "dpa": "1.0"
    },
    "legalAccepted": {
      "terms": true,
      "dpa": false
    }
  }
}
```

## `brands_go_live_checklist`

**Get a brand's go-live checklist.** Operation [`brands.go_live_checklist`](https://ownrecord.io/docs/reference/brands-go-live-checklist.md) · Organization level · permission `brands:read` · risk tier `read` · since M12

Returns what the brand needs before it can go live, each requirement `met` or not with the reason:

- `plan_livemode`: the organization's plan includes live mode;
- `org_status`: the organization is active (not restricted, suspended, closing or closed);
- `legal_accepted`: the current Terms of Service and Data Processing Agreement are accepted;
- `entity_verified`: the legal entity's tax ID is verified;
- `invoice_settings`: the entity's invoice details are complete;
- `home_registration`: the entity's home registration is `registered`, with a number (live mode);
- `processor_live`: the brand has a live processor connection whose last connection test passed (going live
  tests it again);
- `brand_urls`: the brand has its terms and privacy URLs;
- `brand_status`: the brand is in test mode (`test_only`), not suspended.

Live API keys and live checkout sessions are refused until the brand is live. Reading the checklist calls
no processor.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument    | Type         | Required | Description                                                                        |
| ----------- | ------------ | -------- | ---------------------------------------------------------------------------------- |
| `id`        | ID (`brd_…`) | yes      | –                                                                                  |
| `entity_id` | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization. |
| `brand_id`  | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.        |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such brand in your scope.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't read this brand.

### Example

**Two items left**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4"
}
```

Result `output`:

```json
{
  "object": "go_live_checklist",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "brandStatus": "test_only",
  "ready": false,
  "live": false,
  "items": [
    {
      "requirement": "plan_livemode",
      "met": true,
      "reason": null
    },
    {
      "requirement": "org_status",
      "met": true,
      "reason": null
    },
    {
      "requirement": "legal_accepted",
      "met": false,
      "reason": "An owner or admin must accept the current version of the Data Processing Agreement."
    },
    {
      "requirement": "entity_verified",
      "met": true,
      "reason": null
    },
    {
      "requirement": "invoice_settings",
      "met": true,
      "reason": null
    },
    {
      "requirement": "home_registration",
      "met": true,
      "reason": null
    },
    {
      "requirement": "processor_live",
      "met": true,
      "reason": null
    },
    {
      "requirement": "brand_urls",
      "met": false,
      "reason": "The brand needs its privacy URL."
    },
    {
      "requirement": "brand_status",
      "met": true,
      "reason": null
    }
  ],
  "processor": {
    "connectionId": "pcon_01m4tx5nj8emwbyf7y9vcf47xd",
    "processor": "stripe",
    "status": "active",
    "lastTestOk": true,
    "lastTestAt": "2026-10-26T09:00:00.000Z"
  },
  "legalVersions": {
    "terms": "1.0",
    "dpa": "1.0"
  },
  "legalAccepted": {
    "terms": true,
    "dpa": false
  }
}
```

## `brands_list`

**List brands.** Operation [`brands.list`](https://ownrecord.io/docs/reference/brands-list.md) · Organization level · permission `brands:read` · risk tier `read` · since M1

Lists brands, newest first. What a request sees depends on its scope: an organization-level request every
brand of every entity (filter with `entityId`), a request inside one entity all of that entity's brands,
and a request inside one brand only that brand.

Paginate with `limit` (1–100, default 10) and `starting_after` (the `next_cursor` of the previous page).

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument         | Type         | Required | Description                                                                        |
| ---------------- | ------------ | -------- | ---------------------------------------------------------------------------------- |
| `entityId`       | ID (`ent_…`) | no       | –                                                                                  |
| `limit`          | integer      | no       | (1–100)                                                                            |
| `starting_after` | ID (`brd_…`) | no       | –                                                                                  |
| `entity_id`      | ID (`ent_…`) | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization. |
| `brand_id`       | ID (`brd_…`) | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.        |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A filter or pagination value is malformed.
- [`scope_mismatch`](https://ownrecord.io/docs/reference/errors.md#scope_mismatch): `entityId` names an entity outside the request's scope.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see brands here.

### Example

**Every brand of one entity**

Arguments:

```json
{
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "brd_01m4tx5nj8emwbyf7y9vcf47x5",
      "object": "brand",
      "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
      "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
      "slug": "acme-labs",
      "name": "Acme Labs",
      "status": "test_only",
      "isPlatformBrand": false,
      "branding": {
        "logoUrl": null,
        "iconUrl": null,
        "primaryColor": null,
        "accentColor": null,
        "supportEmail": null,
        "supportUrl": null,
        "termsUrl": null,
        "privacyUrl": null
      },
      "statementDescriptor": "ACME APP",
      "statementDescriptorSuffix": null,
      "invoiceSeries": {
        "invoice": "LAB",
        "creditNote": "LAB-CN"
      },
      "billingSettings": {
        "dunning": {
          "retryOffsetsDays": [1, 3, 5, 7],
          "finalAction": "unpaid"
        },
        "entitlementGraceDays": 7,
        "sendInvoice": {
          "graceDays": 14
        },
        "cardExpiryReminders": true,
        "pausedEntitlements": false,
        "paymentBehavior": "allow_incomplete"
      },
      "policies": {
        "breakageRecognition": "at_expiry",
        "supportRefundLimits": [],
        "disputeWalletFreeze": true,
        "taxIdUnavailable": "charge_as_b2c"
      },
      "portalSettings": {
        "allowPlanChanges": true,
        "planChangeMatrix": [],
        "allowCancel": true,
        "cancelBehavior": "period_end",
        "collectCancelReason": true,
        "allowQuantityChanges": false
      },
      "checkoutSettings": {
        "locales": ["en", "hr"],
        "collectPhone": false,
        "requireBusinessDetails": "auto"
      },
      "createdAt": "2026-10-13T12:00:00.000Z",
      "updatedAt": "2026-10-13T12:00:00.000Z"
    },
    {
      "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
      "object": "brand",
      "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
      "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
      "slug": "acme-app",
      "name": "Acme App",
      "status": "test_only",
      "isPlatformBrand": false,
      "branding": {
        "logoUrl": null,
        "iconUrl": null,
        "primaryColor": null,
        "accentColor": null,
        "supportEmail": null,
        "supportUrl": null,
        "termsUrl": null,
        "privacyUrl": null
      },
      "statementDescriptor": "ACME APP",
      "statementDescriptorSuffix": null,
      "invoiceSeries": {
        "invoice": "ACM",
        "creditNote": "ACM-CN"
      },
      "billingSettings": {
        "dunning": {
          "retryOffsetsDays": [1, 3, 5, 7],
          "finalAction": "unpaid"
        },
        "entitlementGraceDays": 7,
        "sendInvoice": {
          "graceDays": 14
        },
        "cardExpiryReminders": true,
        "pausedEntitlements": false,
        "paymentBehavior": "allow_incomplete"
      },
      "policies": {
        "breakageRecognition": "at_expiry",
        "supportRefundLimits": [],
        "disputeWalletFreeze": true,
        "taxIdUnavailable": "charge_as_b2c"
      },
      "portalSettings": {
        "allowPlanChanges": true,
        "planChangeMatrix": [],
        "allowCancel": true,
        "cancelBehavior": "period_end",
        "collectCancelReason": true,
        "allowQuantityChanges": false
      },
      "checkoutSettings": {
        "locales": ["en", "hr"],
        "collectPhone": false,
        "requireBusinessDetails": "auto"
      },
      "createdAt": "2026-10-13T12:00:00.000Z",
      "updatedAt": "2026-10-13T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## `brands_update`

**Update a brand.** Operation [`brands.update`](https://ownrecord.io/docs/reference/brands-update.md) · Organization level · permission `brands:write` · risk tier `write` · since M1

Renames a brand or changes its slug, branding (logo, icon, colors, support email and URL, terms and privacy
URLs), statement descriptor or billing settings. Send only the fields to change; `null` clears an optional
value (for a billing setting: back to its default).

Billing settings: `dunning.retryOffsetsDays` (retries after a failed charge, days after the first failure,
default 1, 3, 5, 7), `dunning.finalAction` (`unpaid`, the default, or `canceled` after the last retry),
`entitlementGraceDays` (features stay on while past due, default 7), `sendInvoice.graceDays` (a send_invoice
invoice is overdue this many days after its due date, default 14), `cardExpiryReminders` (default on),
`pausedEntitlements` (paused subscriptions keep their features, default off) and `paymentBehavior` (default
for immediate changes invoiced to a card). They apply from the next step of each subscription.

Policies: `breakageRecognition` says when expired paid credits become breakage revenue: `at_expiry` (the
default) or `manual`, which holds them as expired credits until your finance team recognises them with a ledger
adjustment. It applies to credits that expire from then on. `disputeWalletFreeze` (default on) freezes the
customer's wallets when a dispute opens; a won dispute unfreezes the wallets it froze. `supportRefundLimits`
sets the most the support role may refund at once, per currency (100 of a currency without one; finance has no
limit). `taxIdUnavailable` says what a sale does when a business customer's tax ID couldn't be checked because
the register didn't answer (spec §9.1): `charge_as_b2c` (the default: tax is charged as for a consumer, and a
credit note can refund it once the ID validates) or `block` (the sale is refused with `tax_id_unavailable` until
the register answers). It applies to quotes from then on.

Customer portal (`portalSettings`): whether customers may switch plans (`allowPlanChanges`, along
`planChangeMatrix`: one row per price naming the prices it may switch to, in the same mode and currency; no row
means no switch), cancel (`allowCancel`, `cancelBehavior` `period_end` or `immediate`, `collectCancelReason`)
and change seats (`allowQuantityChanges`). The matrix is checked against the brand's prices and replaced whole.

Languages (`checkoutSettings.locales`): the brand's languages for hosted pages and customer emails, its default
first. A customer's own language and their browser's come first; the brand's default is the fallback.

A new slug changes the brand's checkout URLs; existing API keys keep working. The invoice series can't
change once set. Needs `brands:write` (organization owners and admins, or an API key with that scope).

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument                    | Type           | Required | Description                                                                                                                                                                                              |
| --------------------------- | -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                        | ID (`brd_…`)   | yes      | –                                                                                                                                                                                                        |
| `name`                      | string         | no       | (1–120 characters)                                                                                                                                                                                       |
| `slug`                      | string         | no       | (3–40 characters)                                                                                                                                                                                        |
| `branding`                  | object         | no       | –                                                                                                                                                                                                        |
| `statementDescriptor`       | string         | no       | (5–22 characters)                                                                                                                                                                                        |
| `statementDescriptorSuffix` | string or null | no       | –                                                                                                                                                                                                        |
| `billingSettings`           | object         | no       | Billing settings; each field: a value sets it, null resets it to the default, absent leaves it.                                                                                                          |
| `policies`                  | object         | no       | Accounting policies; each field: a value sets it, null resets it to the default, absent leaves it.                                                                                                       |
| `portalSettings`            | object         | no       | The customer portal's settings; send only what changes.                                                                                                                                                  |
| `checkoutSettings`          | object         | no       | Hosted page settings; send only what changes.                                                                                                                                                            |
| `entity_id`                 | ID (`ent_…`)   | no       | Optional: act inside this legal entity (ent\_…) instead of the whole organization.                                                                                                                       |
| `brand_id`                  | ID (`brd_…`)   | no       | Optional: act inside this brand (brd\_…) instead of the whole organization.                                                                                                                              |
| `idempotency_key`           | string         | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`                   | boolean        | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Scope:** the whole organization, or one legal entity or brand of it with `entity_id` or `brand_id`.
- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): No field to change was sent, or one is malformed; or a plan-change matrix row names a price that isn't the brand's recurring licensed price, an archived target, or a target in another mode or currency (`details.issues`).
- [`slug_invalid`](https://ownrecord.io/docs/reference/errors.md#slug_invalid): The slug has the wrong format or is a reserved word.
- [`slug_taken`](https://ownrecord.io/docs/reference/errors.md#slug_taken): Another brand already uses this slug.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No brand with this ID is visible to the request.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The brand changed at the same time; retry.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't change brands here.

### Examples

**Set the support email and legal links**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "branding": {
    "supportEmail": "help@acme.example",
    "termsUrl": "https://acme.example/terms",
    "privacyUrl": "https://acme.example/privacy"
  },
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "object": "brand",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "slug": "acme-app",
  "name": "Acme App",
  "status": "test_only",
  "isPlatformBrand": false,
  "branding": {
    "logoUrl": null,
    "iconUrl": null,
    "primaryColor": null,
    "accentColor": null,
    "supportEmail": "help@acme.example",
    "supportUrl": null,
    "termsUrl": "https://acme.example/terms",
    "privacyUrl": "https://acme.example/privacy"
  },
  "statementDescriptor": "ACME APP",
  "statementDescriptorSuffix": null,
  "invoiceSeries": {
    "invoice": "ACM",
    "creditNote": "ACM-CN"
  },
  "billingSettings": {
    "dunning": {
      "retryOffsetsDays": [1, 3, 5, 7],
      "finalAction": "unpaid"
    },
    "entitlementGraceDays": 7,
    "sendInvoice": {
      "graceDays": 14
    },
    "cardExpiryReminders": true,
    "pausedEntitlements": false,
    "paymentBehavior": "allow_incomplete"
  },
  "policies": {
    "breakageRecognition": "at_expiry",
    "supportRefundLimits": [],
    "disputeWalletFreeze": true,
    "taxIdUnavailable": "charge_as_b2c"
  },
  "portalSettings": {
    "allowPlanChanges": true,
    "planChangeMatrix": [],
    "allowCancel": true,
    "cancelBehavior": "period_end",
    "collectCancelReason": true,
    "allowQuantityChanges": false
  },
  "checkoutSettings": {
    "locales": ["en", "hr"],
    "collectPhone": false,
    "requireBusinessDetails": "auto"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```

**Cancel after the last of three retries**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "billingSettings": {
    "dunning": {
      "retryOffsetsDays": [2, 4, 7],
      "finalAction": "canceled"
    }
  },
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "object": "brand",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "slug": "acme-app",
  "name": "Acme App",
  "status": "test_only",
  "isPlatformBrand": false,
  "branding": {
    "logoUrl": null,
    "iconUrl": null,
    "primaryColor": null,
    "accentColor": null,
    "supportEmail": null,
    "supportUrl": null,
    "termsUrl": null,
    "privacyUrl": null
  },
  "statementDescriptor": "ACME APP",
  "statementDescriptorSuffix": null,
  "invoiceSeries": {
    "invoice": "ACM",
    "creditNote": "ACM-CN"
  },
  "billingSettings": {
    "dunning": {
      "retryOffsetsDays": [2, 4, 7],
      "finalAction": "canceled"
    },
    "entitlementGraceDays": 7,
    "sendInvoice": {
      "graceDays": 14
    },
    "cardExpiryReminders": true,
    "pausedEntitlements": false,
    "paymentBehavior": "allow_incomplete"
  },
  "policies": {
    "breakageRecognition": "at_expiry",
    "supportRefundLimits": [],
    "disputeWalletFreeze": true,
    "taxIdUnavailable": "charge_as_b2c"
  },
  "portalSettings": {
    "allowPlanChanges": true,
    "planChangeMatrix": [],
    "allowCancel": true,
    "cancelBehavior": "period_end",
    "collectCancelReason": true,
    "allowQuantityChanges": false
  },
  "checkoutSettings": {
    "locales": ["en", "hr"],
    "collectPhone": false,
    "requireBusinessDetails": "auto"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```

**Hold expired credits until finance recognises the breakage**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "policies": {
    "breakageRecognition": "manual"
  },
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "object": "brand",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "slug": "acme-app",
  "name": "Acme App",
  "status": "test_only",
  "isPlatformBrand": false,
  "branding": {
    "logoUrl": null,
    "iconUrl": null,
    "primaryColor": null,
    "accentColor": null,
    "supportEmail": null,
    "supportUrl": null,
    "termsUrl": null,
    "privacyUrl": null
  },
  "statementDescriptor": "ACME APP",
  "statementDescriptorSuffix": null,
  "invoiceSeries": {
    "invoice": "ACM",
    "creditNote": "ACM-CN"
  },
  "billingSettings": {
    "dunning": {
      "retryOffsetsDays": [1, 3, 5, 7],
      "finalAction": "unpaid"
    },
    "entitlementGraceDays": 7,
    "sendInvoice": {
      "graceDays": 14
    },
    "cardExpiryReminders": true,
    "pausedEntitlements": false,
    "paymentBehavior": "allow_incomplete"
  },
  "policies": {
    "breakageRecognition": "manual",
    "supportRefundLimits": [],
    "disputeWalletFreeze": true,
    "taxIdUnavailable": "charge_as_b2c"
  },
  "portalSettings": {
    "allowPlanChanges": true,
    "planChangeMatrix": [],
    "allowCancel": true,
    "cancelBehavior": "period_end",
    "collectCancelReason": true,
    "allowQuantityChanges": false
  },
  "checkoutSettings": {
    "locales": ["en", "hr"],
    "collectPhone": false,
    "requireBusinessDetails": "auto"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```

**Refuse sales while a business customer's tax ID can't be checked**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "policies": {
    "taxIdUnavailable": "block"
  },
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "object": "brand",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "slug": "acme-app",
  "name": "Acme App",
  "status": "test_only",
  "isPlatformBrand": false,
  "branding": {
    "logoUrl": null,
    "iconUrl": null,
    "primaryColor": null,
    "accentColor": null,
    "supportEmail": null,
    "supportUrl": null,
    "termsUrl": null,
    "privacyUrl": null
  },
  "statementDescriptor": "ACME APP",
  "statementDescriptorSuffix": null,
  "invoiceSeries": {
    "invoice": "ACM",
    "creditNote": "ACM-CN"
  },
  "billingSettings": {
    "dunning": {
      "retryOffsetsDays": [1, 3, 5, 7],
      "finalAction": "unpaid"
    },
    "entitlementGraceDays": 7,
    "sendInvoice": {
      "graceDays": 14
    },
    "cardExpiryReminders": true,
    "pausedEntitlements": false,
    "paymentBehavior": "allow_incomplete"
  },
  "policies": {
    "breakageRecognition": "at_expiry",
    "supportRefundLimits": [],
    "disputeWalletFreeze": true,
    "taxIdUnavailable": "block"
  },
  "portalSettings": {
    "allowPlanChanges": true,
    "planChangeMatrix": [],
    "allowCancel": true,
    "cancelBehavior": "period_end",
    "collectCancelReason": true,
    "allowQuantityChanges": false
  },
  "checkoutSettings": {
    "locales": ["en", "hr"],
    "collectPhone": false,
    "requireBusinessDetails": "auto"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```

**Let support refund up to €250 at once**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "policies": {
    "supportRefundLimits": [
      {
        "amount": "250000000",
        "currency": "EUR"
      }
    ]
  },
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "object": "brand",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "slug": "acme-app",
  "name": "Acme App",
  "status": "test_only",
  "isPlatformBrand": false,
  "branding": {
    "logoUrl": null,
    "iconUrl": null,
    "primaryColor": null,
    "accentColor": null,
    "supportEmail": null,
    "supportUrl": null,
    "termsUrl": null,
    "privacyUrl": null
  },
  "statementDescriptor": "ACME APP",
  "statementDescriptorSuffix": null,
  "invoiceSeries": {
    "invoice": "ACM",
    "creditNote": "ACM-CN"
  },
  "billingSettings": {
    "dunning": {
      "retryOffsetsDays": [1, 3, 5, 7],
      "finalAction": "unpaid"
    },
    "entitlementGraceDays": 7,
    "sendInvoice": {
      "graceDays": 14
    },
    "cardExpiryReminders": true,
    "pausedEntitlements": false,
    "paymentBehavior": "allow_incomplete"
  },
  "policies": {
    "breakageRecognition": "at_expiry",
    "supportRefundLimits": [
      {
        "amount": "250000000",
        "currency": "EUR"
      }
    ],
    "disputeWalletFreeze": true,
    "taxIdUnavailable": "charge_as_b2c"
  },
  "portalSettings": {
    "allowPlanChanges": true,
    "planChangeMatrix": [],
    "allowCancel": true,
    "cancelBehavior": "period_end",
    "collectCancelReason": true,
    "allowQuantityChanges": false
  },
  "checkoutSettings": {
    "locales": ["en", "hr"],
    "collectPhone": false,
    "requireBusinessDetails": "auto"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```

**Let customers switch between Basic and Pro in the portal**

Arguments:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "portalSettings": {
    "allowPlanChanges": true,
    "planChangeMatrix": [
      {
        "fromPriceId": "price_01m4tx5nj8emwbyf7y9vcf47xc",
        "toPriceIds": ["price_01m4tx5nj8emwbyf7y9vcf47xd"]
      },
      {
        "fromPriceId": "price_01m4tx5nj8emwbyf7y9vcf47xd",
        "toPriceIds": ["price_01m4tx5nj8emwbyf7y9vcf47xc"]
      }
    ],
    "collectCancelReason": true
  },
  "checkoutSettings": {
    "locales": ["de", "en"]
  },
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "object": "brand",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "slug": "acme-app",
  "name": "Acme App",
  "status": "test_only",
  "isPlatformBrand": false,
  "branding": {
    "logoUrl": null,
    "iconUrl": null,
    "primaryColor": null,
    "accentColor": null,
    "supportEmail": null,
    "supportUrl": null,
    "termsUrl": null,
    "privacyUrl": null
  },
  "statementDescriptor": "ACME APP",
  "statementDescriptorSuffix": null,
  "invoiceSeries": {
    "invoice": "ACM",
    "creditNote": "ACM-CN"
  },
  "billingSettings": {
    "dunning": {
      "retryOffsetsDays": [1, 3, 5, 7],
      "finalAction": "unpaid"
    },
    "entitlementGraceDays": 7,
    "sendInvoice": {
      "graceDays": 14
    },
    "cardExpiryReminders": true,
    "pausedEntitlements": false,
    "paymentBehavior": "allow_incomplete"
  },
  "policies": {
    "breakageRecognition": "at_expiry",
    "supportRefundLimits": [],
    "disputeWalletFreeze": true,
    "taxIdUnavailable": "charge_as_b2c"
  },
  "portalSettings": {
    "allowPlanChanges": true,
    "planChangeMatrix": [
      {
        "fromPriceId": "price_01m4tx5nj8emwbyf7y9vcf47xc",
        "toPriceIds": ["price_01m4tx5nj8emwbyf7y9vcf47xd"]
      },
      {
        "fromPriceId": "price_01m4tx5nj8emwbyf7y9vcf47xd",
        "toPriceIds": ["price_01m4tx5nj8emwbyf7y9vcf47xc"]
      }
    ],
    "allowCancel": true,
    "cancelBehavior": "period_end",
    "collectCancelReason": true,
    "allowQuantityChanges": false
  },
  "checkoutSettings": {
    "locales": ["de", "en"],
    "collectPhone": false,
    "requireBusinessDetails": "auto"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z"
}
```
