Update a brand
The brands.update operation: PATCH /api/v1/brands/{id} in the REST API and the brands_update tool in the MCP server.
/api/v1/brands/{id}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).
At a glance
- REST:
PATCH /api/v1/brands/{id} - MCP tool:
brands_update - Operation ID:
brands.update - 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-Keyoptional - Modes: Test and live
- Authentication: API key with the
brands:writescope - Since: M1
Request
Path parameters
| Field | Type | Description |
|---|---|---|
id | brd_… ID |
Body
A JSON object (Content-Type: application/json):
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | |
slug | string | No | |
branding | object | No | |
branding.logoUrl | string or null | No | |
branding.iconUrl | string or null | No | |
branding.primaryColor | string or null | No | |
branding.accentColor | string or null | No | |
branding.supportEmail | string or null | No | |
branding.supportUrl | string or null | No | |
branding.termsUrl | string or null | No | |
branding.privacyUrl | string or null | No | |
statementDescriptor | string | No | |
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. |
billingSettings.dunning | object | No | |
billingSettings.dunning.retryOffsetsDays | array of integer (1 to 60) or null | No | Retries after a failed charge, in days after the first failure (strictly increasing). |
billingSettings.dunning.finalAction | one of unpaid, canceled or null | No | After the last retry: unpaid (kept, features off) or canceled. |
billingSettings.entitlementGraceDays | integer (0 to 60) or null | No | While past due, features stay on for this many days after the failure. |
billingSettings.sendInvoice | object | No | |
billingSettings.sendInvoice.graceDays | integer (0 to 90) or null | No | A send_invoice invoice is overdue this many days after its due date. |
billingSettings.cardExpiryReminders | boolean or null | No | |
billingSettings.pausedEntitlements | boolean or null | No | |
billingSettings.paymentBehavior | one of allow_incomplete, pending_if_incomplete or null | No | |
policies | object | No | Accounting policies; each field: a value sets it, null resets it to the default, absent leaves it. |
policies.breakageRecognition | one of at_expiry, manual or null | No | When expired paid credits become breakage revenue: at_expiry (the default) or manual (held as expired credits until finance recognises them with a ledger adjustment). null resets it to the default. |
policies.supportRefundLimits | array of object or null | No | The most the support role may refund at once, one amount per currency (finance has no limit). A currency without one has the default of 100 of it (100.00 EUR). The list replaces the brand's; null removes them all. |
policies.disputeWalletFreeze | boolean or null | No | Freeze the customer's wallets when a dispute opens (default true); a won dispute unfreezes them. null resets it to the default. |
policies.taxIdUnavailable | one of charge_as_b2c, block or null | No | What a sale does when the customer's tax ID couldn't be checked because the register (VIES, HMRC, the Swiss UID register) didn't answer: charge_as_b2c (the default: tax is charged as for a consumer; a credit note can refund it once the ID validates) or block (the sale is refused with tax_id_unavailable until the register answers). null resets it to the default. |
portalSettings | object | No | The customer portal's settings; send only what changes. |
portalSettings.allowPlanChanges | boolean | No | Customers may switch plans in the portal, along planChangeMatrix. |
portalSettings.planChangeMatrix | array of object or null | No | Replaces the whole matrix; null or an empty list removes it (no plan switch in the portal). |
portalSettings.allowCancel | boolean | No | Customers may cancel in the portal. |
portalSettings.cancelBehavior | one of period_end, immediate | No | period_end (default: it runs to the end of the paid period) or immediate. |
portalSettings.collectCancelReason | boolean | No | Ask why the customer cancels. |
portalSettings.allowQuantityChanges | boolean | No | Customers may change seats of licensed items. |
checkoutSettings | object | No | Hosted page settings; send only what changes. |
checkoutSettings.locales | array of one of en, de, fr, hr, it, es | No | The brand's languages for hosted pages and customer emails, its default first (en, de, fr, hr, it, es). A default, not a restriction: a customer's own language still comes first. |
Response
200 OK with a JSON object:
| Field | Type | Description |
|---|---|---|
id | brd_… ID | |
object | "brand" | |
orgId | org_… ID | |
entityId | ent_… ID | |
slug | string | |
name | string | |
status | one of draft, test_only, live, suspended | |
isPlatformBrand | boolean | |
branding | object | |
branding.logoUrl | string or null | |
branding.iconUrl | string or null | |
branding.primaryColor | string or null | |
branding.accentColor | string or null | |
branding.supportEmail | string or null | |
branding.supportUrl | string or null | |
branding.termsUrl | string or null | |
branding.privacyUrl | string or null | |
statementDescriptor | string | |
statementDescriptorSuffix | string or null | |
invoiceSeries | object | |
invoiceSeries.invoice | string | |
invoiceSeries.creditNote | string | |
billingSettings | object | |
billingSettings.dunning | object | |
billingSettings.dunning.retryOffsetsDays | array of integer | |
billingSettings.dunning.finalAction | one of unpaid, canceled | |
billingSettings.entitlementGraceDays | integer | |
billingSettings.sendInvoice | object | |
billingSettings.sendInvoice.graceDays | integer | |
billingSettings.cardExpiryReminders | boolean | |
billingSettings.pausedEntitlements | boolean | |
billingSettings.paymentBehavior | one of allow_incomplete, pending_if_incomplete | |
policies | object | |
policies.breakageRecognition | one of at_expiry, manual | at_expiry (the default) or manual (held as expired credits until recognised). |
policies.supportRefundLimits | array of object | The support role's refund limit per currency (M8); a currency not listed has the default of 100 of it. |
policies.supportRefundLimits[].amount | string | Integer micros as a string. |
policies.supportRefundLimits[].currency | string | |
policies.disputeWalletFreeze | boolean | Freeze the customer's wallets when a dispute opens (default true; §19.2, M8). |
policies.taxIdUnavailable | one of charge_as_b2c, block | What a sale does when a tax ID couldn't be checked (§9.1 step 1; M13): charge_as_b2c (the default) or block. |
portalSettings | object | |
portalSettings.allowPlanChanges | boolean | Customers may switch plans along planChangeMatrix (no row: no switch). |
portalSettings.planChangeMatrix | array of object | One row per price: a subscription item on fromPriceId may switch to any of toPriceIds (same mode and currency). |
portalSettings.planChangeMatrix[].fromPriceId | price_… ID | |
portalSettings.planChangeMatrix[].toPriceIds | array of price_… ID | |
portalSettings.allowCancel | boolean | Customers may cancel their subscriptions. |
portalSettings.cancelBehavior | one of period_end, immediate | A portal cancellation ends the subscription at the period end (default) or at once. |
portalSettings.collectCancelReason | boolean | The portal asks why the customer cancels. |
portalSettings.allowQuantityChanges | boolean | Customers may change the quantity (seats) of licensed items. |
checkoutSettings | object | |
checkoutSettings.locales | array of string | The brand's languages, its default first. A default, not a restriction: a customer's own language comes first. |
checkoutSettings.collectPhone | boolean | |
checkoutSettings.requireBusinessDetails | one of auto, always, never | |
createdAt | date-time | |
updatedAt | date-time |
Examples
Set the support email and legal links
PATCH /api/v1/brands/brd_01m4tx5nj8emwbyf7y9vcf47x4
Authorization: Bearer or_test_…
Content-Type: application/json
{
"branding": {
"supportEmail": "help@acme.example",
"termsUrl": "https://acme.example/terms",
"privacyUrl": "https://acme.example/privacy"
}
}
Response (200):
{
"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
PATCH /api/v1/brands/brd_01m4tx5nj8emwbyf7y9vcf47x4
Authorization: Bearer or_test_…
Content-Type: application/json
{
"billingSettings": {
"dunning": {
"retryOffsetsDays": [
2,
4,
7
],
"finalAction": "canceled"
}
}
}
Response (200):
{
"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
PATCH /api/v1/brands/brd_01m4tx5nj8emwbyf7y9vcf47x4
Authorization: Bearer or_test_…
Content-Type: application/json
{
"policies": {
"breakageRecognition": "manual"
}
}
Response (200):
{
"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
PATCH /api/v1/brands/brd_01m4tx5nj8emwbyf7y9vcf47x4
Authorization: Bearer or_test_…
Content-Type: application/json
{
"policies": {
"taxIdUnavailable": "block"
}
}
Response (200):
{
"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
PATCH /api/v1/brands/brd_01m4tx5nj8emwbyf7y9vcf47x4
Authorization: Bearer or_test_…
Content-Type: application/json
{
"policies": {
"supportRefundLimits": [
{
"amount": "250000000",
"currency": "EUR"
}
]
}
}
Response (200):
{
"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
PATCH /api/v1/brands/brd_01m4tx5nj8emwbyf7y9vcf47x4
Authorization: Bearer or_test_…
Content-Type: application/json
{
"portalSettings": {
"allowPlanChanges": true,
"planChangeMatrix": [
{
"fromPriceId": "price_01m4tx5nj8emwbyf7y9vcf47xc",
"toPriceIds": [
"price_01m4tx5nj8emwbyf7y9vcf47xd"
]
},
{
"fromPriceId": "price_01m4tx5nj8emwbyf7y9vcf47xd",
"toPriceIds": [
"price_01m4tx5nj8emwbyf7y9vcf47xc"
]
}
],
"collectCancelReason": true
},
"checkoutSettings": {
"locales": [
"de",
"en"
]
}
}
Response (200):
{
"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"
}
Errors
| Code | HTTP status | When | What to do |
|---|---|---|---|
validation_failed | 400 | 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). | Fix the fields listed in details.issues and send the request again. |
slug_invalid | 400 | The slug has the wrong format or is a reserved word. | Choose a slug such as acme or acme-app. Reserved words such as api, console and docs can't be used. |
slug_taken | 409 | Another brand already uses this slug. | Choose another slug. Organization and brand slugs are unique across Ownrecord; entity slugs are unique within an organization. |
resource_not_found | 404 | No brand with this ID is visible to the request. | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
version_conflict | 409 | The brand changed at the same time; retry. | Fetch the object again and retry the change. |
permission_denied | 403 | You can't change brands here. | 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_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.