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

# Set your brand's display identity (client self-serve, configure plane)

> Sets or updates YOUR brand's display-identity block — the display name, the logo references, and the token-set the platform-controlled surfaces render — using your own API key. Display identity ONLY: the body's `display` block is the only field read here, so the theme, the redirect allowlist, and the charge topology stay operator-controlled and cannot be set through this surface. The brand is the one your key owns (resolved from X-Brand-Id); the brand_id in the path must match it (any mismatch, foreign, or unknown brand answers 404, never 403). Validated UNCHANGED by the platform's brand-config validator: a `copy` key, an unknown display key, a non-https and non-root-relative logo reference, or an unknown token slot is refused (422, issues verbatim). The version is server-minted; the write is durable before it activates and audited (which principal, when).



## OpenAPI

````yaml /openapi/public-openapi.json put /v1/account/brands/{brand_id}/config
openapi: 3.1.0
info:
  title: Purple API
  version: 0.0.0
servers:
  - url: https://api.dev.purplelabelmd.com
security: []
tags:
  - description: Sign a patient in and out and manage the browser session.
    name: Sessions & authentication
  - description: Run the server-driven intake questionnaire question by question.
    name: Intake
  - description: Address autocomplete for the intake flow.
    name: Addresses
  - description: Read the public status of a patient's enrollment.
    name: Enrollment
  - description: Track a client's onboarding progress.
    name: Onboarding
  - description: Read back the platform configuration stored for your brand.
    name: Brand configuration
  - description: Enable offerings for your brand and set their display copy.
    name: Offering configuration
  - description: Start and track payments for an order.
    name: Payments
  - description: Subscribe to platform events and manage delivery endpoints.
    name: Webhooks
paths:
  /v1/account/brands/{brand_id}/config:
    put:
      tags:
        - Brand configuration
      summary: Set your brand's display identity (client self-serve, configure plane)
      description: >-
        Sets or updates YOUR brand's display-identity block — the display name,
        the logo references, and the token-set the platform-controlled surfaces
        render — using your own API key. Display identity ONLY: the body's
        `display` block is the only field read here, so the theme, the redirect
        allowlist, and the charge topology stay operator-controlled and cannot
        be set through this surface. The brand is the one your key owns
        (resolved from X-Brand-Id); the brand_id in the path must match it (any
        mismatch, foreign, or unknown brand answers 404, never 403). Validated
        UNCHANGED by the platform's brand-config validator: a `copy` key, an
        unknown display key, a non-https and non-root-relative logo reference,
        or an unknown token slot is refused (422, issues verbatim). The version
        is server-minted; the write is durable before it activates and audited
        (which principal, when).
      operationId: setAccountBrandDisplay
      parameters:
        - $ref: '#/components/parameters/BrandIdHeader'
        - $ref: '#/components/parameters/IdempotencyKeyHeader'
        - description: the brand whose display identity to set (must match X-Brand-Id).
          in: path
          name: brand_id
          required: true
          schema:
            pattern: ^brd_[A-Za-z0-9][A-Za-z0-9_-]*$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AccountBrandDisplayWriteRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  data:
                    $ref: '#/components/schemas/BrandConfigView'
                required:
                  - data
                type: object
          description: the stored configuration (the same projection you read back)
        '400':
          $ref: '#/components/responses/EdgeBadRequest'
        '401':
          $ref: '#/components/responses/EdgeUnauthorized'
        '404':
          $ref: '#/components/responses/EdgeNotFound'
        '422':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/BrandConfigProblem'
          description: the display block was refused by the validator (issues verbatim)
        '503':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/EdgeProblem'
          description: >-
            the durable brand-config store is unavailable (nothing activated;
            retryable)
      security:
        - apiKey: []
components:
  parameters:
    BrandIdHeader:
      description: >-
        Opaque brand id (brd_...). Validated to belong to the resolved client
        (§2).
      in: header
      name: X-Brand-Id
      required: true
      schema:
        pattern: ^brd_[A-Za-z0-9][A-Za-z0-9_-]*$
        type: string
    IdempotencyKeyHeader:
      description: >-
        Accepted on mutations; carried in trusted context, enforced in a later
        WI.
      in: header
      name: Idempotency-Key
      required: false
      schema:
        type: string
  schemas:
    AccountBrandDisplayWriteRequest:
      additionalProperties: false
      description: >-
        The client self-serve display-write payload. The ONLY field is
        `display`: the client sets or updates its own brand display-identity
        block with its own API key. There is deliberately no theme,
        redirect-allowlist, or charge-topology field — those stay
        operator-controlled; a client cannot set them here (any such key is
        barred by additionalProperties:false and, at runtime, is never read).
        The block is merged onto the existing configuration and validated
        UNCHANGED by the brand-config validator (the wording wall: a `copy` key
        is refused 422, issues verbatim).
      properties:
        display:
          allOf:
            - $ref: '#/components/schemas/BrandDisplay'
          description: >-
            the per-brand display-identity block to set or update (merged onto
            the existing config).
      required:
        - display
      type: object
    BrandConfigView:
      additionalProperties: false
      description: >-
        A brand's platform configuration as the platform holds it — clinically
        inert BY CONSTRUCTION (the validator enforces a top-level allowlist + a
        recursive clinical-shaped-key denylist). The client readback and the
        operator view return this EXACT shape (one projection, no drift).
        Presentation and commercial reach only; never a clinical field.
        additionalProperties:false mirrors the validator's top-level allowlist.
      properties:
        brand_id:
          pattern: ^brd_[A-Za-z0-9][A-Za-z0-9_-]*$
          type: string
        commercial:
          additionalProperties:
            type: string
          description: commercial references (storefront/pricing ids) — references only.
          type: object
        copy:
          additionalProperties:
            type: string
          description: brand-overridable copy slots (slot id → display string).
          type: object
        display:
          allOf:
            - $ref: '#/components/schemas/BrandDisplay'
          description: >-
            The per-brand display-identity block (name, logo, token-set), if
            set. Identity only; never a clinical copy channel. When absent, this
            brand has no display identity and the brand-display serve returns
            the explicit purple fallback.
        enabled_therapies:
          description: >-
            therapy references this brand offers (references, never
            definitions). This is also the field the intake surface reads to
            decide which questionnaire a patient is served at a journeyless
            first touch: one enabled therapy serves that therapy's questionnaire
            always; more than one requires the entry link to name an `offering`;
            none serves the platform's shared questionnaire. Values must be
            therapy keys the clinical service currently serves; the vocabulary
            is not enumerated here — it is clinical's committed roster (`GET
            /v1/instrument-bundles/roster`), which grows as new shared intakes
            ship.
          items:
            type: string
          type: array
        redirect_allowlist:
          description: >-
            absolute-https return-URL prefixes permitted as post-completion
            redirect targets.
          items:
            format: uri
            type: string
          type: array
        theme:
          additionalProperties:
            oneOf:
              - type: string
              - type: number
          description: presentation theme tokens — scalar leaves only (string or number).
          type: object
        updated_at:
          description: when this configuration was last written.
          format: date-time
          type: string
        version:
          description: server-minted monotone version (e.g. v3); the caller never sets it.
          type: string
      required:
        - brand_id
        - version
      type: object
    BrandConfigProblem:
      allOf:
        - $ref: '#/components/schemas/EdgeProblem'
        - properties:
            issues:
              description: the validator issues, verbatim (code · where · message).
              items:
                properties:
                  code:
                    type: string
                  message:
                    type: string
                  where:
                    type: string
                required:
                  - code
                  - where
                  - message
                type: object
              type: array
          type: object
      description: >-
        The brand-config validator's refusal (422): an RFC-7807 problem carrying
        the validator's issues VERBATIM (the clinically-inert guarantee's
        refusal — a clinical-shaped field or a non-absolute-https allowlist
        entry). The operator surface renders them unrephrased.
    EdgeProblem:
      description: RFC 7807 problem+json error body.
      properties:
        detail:
          type: string
        status:
          type: integer
        title:
          type: string
        type:
          format: uri-reference
          type: string
      required:
        - type
        - title
        - status
      type: object
    BrandDisplay:
      additionalProperties: false
      description: >-
        The per-brand display-identity block: the brand's display name, logo
        references, and token-set that the platform-controlled surfaces (portal,
        checkout door, identity) resolve per request. Identity only — never a
        clinical copy channel: a `copy` key here is refused, and the
        brand-config clinical-shaped-key denylist scans it like everything else.
        It is a sibling of `theme`, not a replacement: `theme` is the
        render-time instrument overlay, while these tokens are the brand-chrome
        identity the platform surfaces render — disjoint consumers, no
        precedence.
      properties:
        display_name:
          description: >-
            the brand's display name shown to patients (document titles, login
            shell).
          minLength: 1
          type: string
        logo:
          additionalProperties: false
          description: >-
            logo references per mode — each an absolute https URL or a
            root-relative path ('/…').
          properties:
            dark:
              description: >-
                dark-mode logo reference (absolute https URL or root-relative
                path).
              type: string
            light:
              description: >-
                light-mode logo reference (absolute https URL or root-relative
                path).
              type: string
          type: object
        support_email:
          description: optional brand support email.
          format: email
          type: string
        tokens:
          allOf:
            - $ref: '#/components/schemas/BrandDisplayTokens'
      required:
        - display_name
      type: object
    BrandDisplayTokens:
      additionalProperties: false
      description: >-
        The brand token-set family (colors per mode, base radius, and type
        families), mirroring the design-system brand-token shape minus name and
        logo (which are hoisted to display_name and logo). A partial override
        set: a brand supplies any subset of the known slots; completeness for
        stylesheet generation is achieved downstream by merging onto the default
        brand fallback. String leaves only; unknown token slots are refused.
      properties:
        colors:
          additionalProperties: false
          description: >-
            semantic color slots per mode — BrandColorSet keys → CSS color
            strings.
          properties:
            dark:
              additionalProperties:
                type: string
              description: dark-mode color slots (BrandColorSet keys → CSS color strings).
              type: object
            light:
              additionalProperties:
                type: string
              description: light-mode color slots (BrandColorSet keys → CSS color strings).
              type: object
          type: object
        radius:
          description: base corner radius, e.g. '0.625rem'.
          type: string
        typography:
          additionalProperties: false
          properties:
            fontMono:
              type: string
            fontSans:
              type: string
          type: object
      type: object
  responses:
    EdgeBadRequest:
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/EdgeProblem'
      description: malformed or absent X-Brand-Id
    EdgeUnauthorized:
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/EdgeProblem'
      description: >-
        missing, unknown, or revoked API key - one opaque body (no
        enumeration/timing oracle, §1)
    EdgeNotFound:
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/EdgeProblem'
      description: >-
        brand not found for this tenant - identical for unknown and foreign
        brands (404, never 403; no cross-tenant existence leak, §2)
  securitySchemes:
    apiKey:
      description: 'Per-client API key (M2M). Presented as `Authorization: Bearer <key>`.'
      scheme: bearer
      type: http

````