> ## Documentation Index
> Fetch the complete documentation index at: https://docs.closient.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Preview a brand merge

> Dry run of merging the donor brand into this brand. Changes nothing. Returns what would move (products, lots, serials, resolver rules) and which profile fields would lose the donor's value. Call this before `POST /brands/{id}/merge`: the merge is permanent. Requires the Owner or Manager role on the organization that owns both brands; a read-only key is refused with 403.



## OpenAPI

````yaml /openapi/openapi-brands.json get /brands/api/v1/brands/{id}/merge-preview
openapi: 3.1.0
info:
  title: Brands API
  version: 1.0.0
  description: >
    Brand management: list, create, update, and manage brand profiles.


    ## Authentication


    All endpoints require an API key passed via the `X-API-Key` HTTP header,
    unless otherwise noted.


    ```

    X-API-Key: csb_<body>_<checksum>

    ```


    Generate API keys in **Settings > API Keys** in your dashboard, or via the
    Account API.

    Session-based (cookie) authentication is also accepted for browser-based
    access.


    ## Rate Limits


    | Tier        | Requests / minute | Requests / day |

    |-------------|-------------------|----------------|

    | Default     | 300               | 10,000         |

    | Custom      | Contact us        | Contact us     |


    Those windows are per key. Your plan also caps each organization per hour,
    across every key,

    login and session acting for it: 100 requests on Free (or while a
    subscription is lapsed),

    10,000 on Business. While a subscription is lapsed, any method other than
    `GET`, `HEAD` and

    `OPTIONS` returns `403`.


    Rate-limit headers are included on every response so callers can
    self-throttle without

    hitting our 429s ("informed governor"):


    - `RateLimit-Policy` — every active window, e.g. `300;w=60, 10000;w=86400`

    - `RateLimit-Limit` — quota for the **most-restrictive** currently-active
    window

    - `RateLimit-Remaining` — requests left in that window

    - `RateLimit-Reset` — seconds until that window resets (relative; clock-skew
    safe)


    Legacy `X-RateLimit-*` aliases are also emitted for back-compat.
    `X-RateLimit-Reset`

    keeps the absolute Unix-timestamp shape to avoid breaking existing
    consumers.


    When rate-limited, you receive `429 Too Many Requests` with a
    `retry_after_seconds` field

    in the error envelope and a `Retry-After` header.


    ## Pagination


    List endpoints return paginated results in this envelope:


    ```json

    {
      "data": [...],
      "pagination": {
        "page": 1,
        "page_size": 25,
        "total_count": 342,
        "total_pages": 14,
        "has_next": true,
        "has_previous": false
      }
    }

    ```


    Use `?page=2&page_size=50` query parameters. Maximum page size is 100.


    ## Error Responses


    All errors conform to [RFC 9457 Problem
    Details](https://www.rfc-editor.org/rfc/rfc9457)

    with `Content-Type: application/problem+json`:


    ```json

    {
      "type": "https://closient.com/docs/errors/not_found",
      "title": "Not Found",
      "status": 404,
      "detail": "The requested resource was not found.",
      "error_code": "not_found",
      "retryable": false,
      "timestamp": "2026-03-31T12:00:00+00:00"
    }

    ```


    Common error codes: `unauthorized` (401), `forbidden` (403), `not_found`
    (404),

    `validation_error` (422), `rate_limited` (429), `internal_error` (500).


    ## Versioning and Deprecation


    Every response from an operation in this document carries
    `Closient-API-Version` (its `info.version`).

    An operation marked `deprecated` answers with `Deprecation: @<Unix seconds>`

    ([RFC 9745](https://www.rfc-editor.org/rfc/rfc9745)), `Sunset: <HTTP-date>`
    once a

    retirement date is set ([RFC 8594](https://www.rfc-editor.org/rfc/rfc8594)),
    and

    `Link` entries for `rel="deprecation"`, `rel="sunset"` and
    `rel="successor-version"`.

    After its sunset it answers `410 Gone` naming the replacement. Policy:

    https://docs.closient.com/api-versioning
  termsOfService: https://www.closient.com/terms/
servers:
  - url: https://www.closient.com
security: []
tags:
  - name: Brands
    description: Brand CRUD operations.
  - name: Brand Locator (Public)
    description: >-
      Unauthenticated endpoints that power the embeddable brand product-locator
      widget. Mounted under the `/public/` prefix so the widget CORS middleware
      opens CORS for them.
  - name: Brand GLNs
    description: The GS1 Global Location Numbers a brand uses, one of them primary.
  - name: Brand locations
    description: A brand's physical locations, at most one of them headquarters.
  - name: Brand editors
    description: >-
      People granted edit access to one brand. Signed-in Owner or Manager
      sessions only; API keys and OAuth tokens are refused.
  - name: Brand media intake
    description: >-
      The address consumers may send photos and video to directly, saved only
      with the brand's acknowledgment that Closient never receives that media.
  - name: Brand assets
    description: 'A brand''s media library: logos, banners, icons, documents and other files.'
  - name: Brand video pages
    description: >-
      Brand-styled landing pages built around a YouTube video, reused by
      resolver rules.
  - name: Brand story pages
    description: >-
      Brand-styled product story pages made of ordered content blocks, reused by
      resolver rules.
  - name: Brand merge
    description: >-
      Preview, then permanently merge one brand into another in the same
      organization.
  - name: Brand link pages
    description: >-
      Brand-styled link-in-bio pages made of ordered blocks, reused by resolver
      rules.
externalDocs:
  description: Closient Documentation
  url: https://docs.closient.com
paths:
  /brands/api/v1/brands/{id}/merge-preview:
    get:
      tags:
        - Brand merge
      summary: Preview a brand merge
      description: >-
        Dry run of merging the donor brand into this brand. Changes nothing.
        Returns what would move (products, lots, serials, resolver rules) and
        which profile fields would lose the donor's value. Call this before
        `POST /brands/{id}/merge`: the merge is permanent. Requires the Owner or
        Manager role on the organization that owns both brands; a read-only key
        is refused with 403.
      operationId: preview_brand_merge
      parameters:
        - in: path
          name: id
          schema:
            description: URL-safe identifier of the brand that stays (the survivor).
            format: shortuuid
            maxLength: 22
            minLength: 22
            pattern: ^[23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{22}$
            title: Id
            type: string
          required: true
          description: URL-safe identifier of the brand that stays (the survivor).
        - in: query
          name: donor_id
          schema:
            description: >-
              URL-safe identifier of the brand to absorb (the donor). It is
              deleted by the merge. It must belong to the same organization as
              the survivor.
            format: shortuuid
            maxLength: 22
            minLength: 22
            pattern: ^[23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{22}$
            title: Donor Id
            type: string
          required: true
          description: >-
            URL-safe identifier of the brand to absorb (the donor). It is
            deleted by the merge. It must belong to the same organization as the
            survivor.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BrandMergePreviewOut'
        '400':
          description: Bad Request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ErrorOut'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ErrorOut'
        '403':
          description: Forbidden
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ErrorOut'
        '404':
          description: Not Found
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ErrorOut'
        '405':
          description: Method Not Allowed
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ErrorOut'
        '422':
          description: Unprocessable Content
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ErrorOut'
        '429':
          description: Too Many Requests
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ErrorOut'
      security:
        - APIKeyHeaderAuth: []
        - OAuthTokenAuth: []
        - CookieGatedSessionAuth: []
components:
  schemas:
    BrandMergePreviewOut:
      description: What merging the donor into the survivor would do. Nothing has changed.
      examples:
        - donor:
            id: 7mQ2vLkR9sTnWc4HbXyD3e
            name: Acme Snack Co (Demo)
          donor_counts:
            lots: 9
            products: 12
            rules: 1
            serials: 0
          donor_has_logo: true
          field_collisions:
            - donor_value: https://acmesnackco.example
              field: website
              survivor_value: https://acmesnacks.example
          products_reassigned: 12
          survivor:
            id: SBjSX6xUJPPHQKUXBpYX2C
            name: Acme Snacks (Demo)
          survivor_counts:
            lots: 55
            products: 40
            rules: 3
            serials: 0
          survivor_has_logo: true
      properties:
        survivor:
          $ref: '#/components/schemas/BrandMergeBrandOut'
          description: The brand that stays.
        donor:
          $ref: '#/components/schemas/BrandMergeBrandOut'
          description: The brand that would be absorbed and deleted.
        products_reassigned:
          description: Products that would move from the donor to the survivor.
          title: Products Reassigned
          type: integer
        survivor_counts:
          $ref: '#/components/schemas/BrandMergeCountsOut'
          description: What the survivor carries today.
        donor_counts:
          $ref: '#/components/schemas/BrandMergeCountsOut'
          description: What the donor carries today and would move.
        field_collisions:
          description: >-
            Profile fields where the donor's value would be dropped in favour of
            the survivor's.
          items:
            $ref: '#/components/schemas/BrandMergeFieldCollisionOut'
          title: Field Collisions
          type: array
        donor_has_logo:
          description: Whether the donor has a logo.
          title: Donor Has Logo
          type: boolean
        survivor_has_logo:
          description: Whether the survivor has a logo.
          title: Survivor Has Logo
          type: boolean
      required:
        - survivor
        - donor
        - products_reassigned
        - survivor_counts
        - donor_counts
        - field_collisions
        - donor_has_logo
        - survivor_has_logo
      title: BrandMergePreviewOut
      type: object
    ErrorOut:
      description: |-
        RFC 9457 Problem Details response.

        All API errors are returned in this format with Content-Type:
        application/problem+json.
      examples:
        - detail: The requested resource was not found.
          error_code: not_found
          retryable: false
          status: 404
          timestamp: '2026-03-31T12:00:00+00:00'
          title: Not Found
          type: https://closient.com/docs/errors/not_found
        - detail: Validation error.
          details:
            - loc:
                - body
                - name
              msg: Field required
              type: missing
          error_code: validation_error
          retryable: false
          status: 422
          timestamp: '2026-03-31T12:00:00+00:00'
          title: Validation Error
          type: https://closient.com/docs/errors/validation_error
        - detail: Rate limit exceeded. Please try again later.
          error_code: rate_limited
          retry_after: 31
          retryable: true
          status: 429
          timestamp: '2026-03-31T12:00:00+00:00'
          title: Rate Limited
          type: https://closient.com/docs/errors/rate_limited
      properties:
        type:
          description: URI reference identifying the error type.
          title: Type
          type: string
        title:
          description: Short human-readable summary of the error.
          title: Title
          type: string
        status:
          description: HTTP status code.
          title: Status
          type: integer
        detail:
          description: Human-readable explanation of this specific occurrence.
          title: Detail
          type: string
        error_code:
          description: Machine-readable error code (e.g. not_found, unauthorized).
          title: Error Code
          type: string
        retryable:
          default: false
          description: Whether retrying the same request can succeed.
          title: Retryable
          type: boolean
        timestamp:
          description: ISO 8601 timestamp of when the error occurred.
          title: Timestamp
          type: string
        retry_after:
          anyOf:
            - type: integer
            - type: 'null'
          description: Seconds to wait before retrying (when applicable).
          title: Retry After
        owner_action_required:
          anyOf:
            - type: boolean
            - type: 'null'
          description: Whether the error requires account owner intervention.
          title: Owner Action Required
        details:
          description: Additional context (validation errors, etc.).
          title: Details
      required:
        - type
        - title
        - status
        - detail
        - error_code
        - timestamp
      title: ErrorOut
      type: object
    BrandMergeBrandOut:
      description: One of the two brands in a merge.
      examples:
        - id: SBjSX6xUJPPHQKUXBpYX2C
          name: Acme Snacks (Demo)
      properties:
        id:
          description: URL-safe brand identifier.
          format: shortuuid
          maxLength: 22
          minLength: 22
          pattern: ^[23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{22}$
          title: Id
          type: string
        name:
          description: Brand display name.
          title: Name
          type: string
      required:
        - id
        - name
      title: BrandMergeBrandOut
      type: object
    BrandMergeCountsOut:
      description: What a brand carries that travels with it in a merge.
      examples:
        - lots: 55
          products: 40
          rules: 3
          serials: 0
      properties:
        products:
          description: Products listed under the brand.
          title: Products
          type: integer
        lots:
          description: Production lots across those products.
          title: Lots
          type: integer
        serials:
          description: Serialized units across those products.
          title: Serials
          type: integer
        rules:
          description: Resolver rules scoped to the brand.
          title: Rules
          type: integer
      required:
        - products
        - lots
        - serials
        - rules
      title: BrandMergeCountsOut
      type: object
    BrandMergeFieldCollisionOut:
      description: >-
        A profile field both brands have a value for. The survivor's value
        stays; the donor's is dropped.
      examples:
        - donor_value: https://acme-snack-co.example
          field: website
          survivor_value: https://acme-snacks.example
      properties:
        field:
          description: Name of the brand profile field, such as `website` or `description`.
          title: Field
          type: string
        survivor_value:
          description: The value that stays after the merge.
          title: Survivor Value
          type: string
        donor_value:
          description: The value that is dropped by the merge.
          title: Donor Value
          type: string
      required:
        - field
        - survivor_value
        - donor_value
      title: BrandMergeFieldCollisionOut
      type: object
  securitySchemes:
    APIKeyHeaderAuth:
      type: apiKey
      in: header
      name: X-API-Key
    OAuthTokenAuth:
      type: http
      scheme: bearer
    CookieGatedSessionAuth:
      type: apiKey
      in: cookie
      name: sessionid

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.