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

# Upload a logo

> Raw body (not multipart), Content-Type image/png|image/jpeg|image/webp, hard cap 2 MiB (413 idv_logo_too_large above). The image is decoded (400 idv_logo_invalid_image for undecodable bytes or sides under 32px), downscaled aspect-preserving when the max side exceeds 1024px, and re-encoded to PNG (alpha preserved, ancillary metadata stripped). Each upload mints an IMMUTABLE asset id; prior assets are retained so session copies keep resolving. 409 idv_theme_archived while archived.




## OpenAPI

````yaml /api-reference/idv-svc.json put /themes/{themeId}/logo
openapi: 3.1.0
info:
  title: Minerva IDV API
  version: 1.0.0-draft
  description: >-
    Customer API for identity verification. Create verification sessions for
    profiles, deliver invites, track capture progress, and read review outcomes.
    For the end-to-end flow, see the [IDV Integration
    Guide](/api-reference/idv-integration-guide). Reviewer-safe responses never
    expose storage keys, checksums, wrapped keys, encryption context, raw OCR,
    or raw model output. Decrypted personal data is available only on `GET
    /sessions/{sessionId}/data`, to an authenticated in-scope principal, with
    fail-closed auditing.
servers:
  - url: https://api.gominerva.com/idv/v1
    description: The production API server for Minerva identity verification
security: []
tags:
  - name: sessions
    description: >-
      Verification sessions and invite delivery (application or dashboard
      authentication).
  - name: capture
    description: End-user capture flow (session or invite credentials).
  - name: review
    description: Review decisions and audited structured-data access.
  - name: workflows
    description: Workflow configuration for verification sessions.
  - name: themes
    description: Branding themes and managed logo assets.
paths:
  /themes/{themeId}/logo:
    put:
      tags:
        - themes
      summary: Upload a logo
      description: >
        Raw body (not multipart), Content-Type image/png|image/jpeg|image/webp,
        hard cap 2 MiB (413 idv_logo_too_large above). The image is decoded (400
        idv_logo_invalid_image for undecodable bytes or sides under 32px),
        downscaled aspect-preserving when the max side exceeds 1024px, and
        re-encoded to PNG (alpha preserved, ancillary metadata stripped). Each
        upload mints an IMMUTABLE asset id; prior assets are retained so session
        copies keep resolving. 409 idv_theme_archived while archived.
      parameters:
        - $ref: '#/components/parameters/ThemeId'
      requestBody:
        required: true
        content:
          image/png:
            schema:
              type: string
              format: binary
          image/jpeg:
            schema:
              type: string
              format: binary
          image/webp:
            schema:
              type: string
              format: binary
      responses:
        '200':
          description: Updated theme (logo reference swapped)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ThemeResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          description: Body over 2 MiB (idv_logo_too_large)
      security:
        - applicationApiKey: []
        - dashboardUserToken: []
components:
  parameters:
    ThemeId:
      name: themeId
      in: path
      required: true
      schema:
        type: string
        pattern: ^idvt_[0-9a-f]{24}$
  schemas:
    ThemeResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: '#/components/schemas/ThemeObject'
    ThemeObject:
      type: object
      description: >
        Workspace-scoped reusable presentation block (replaces the inline
        workflow branding). Non-PII but an INJECTION SURFACE into the consumer
        page and invite email, so every field is a closed grammar; logos are
        managed uploads only (external URLs and SVG are unsupported by design).
      required:
        - theme_id
        - tenant_id
        - workspace_id
        - name
        - status
        - is_default
        - logo
        - logo_square
        - colors
        - font_family
        - created_at
        - updated_at
        - revision
      properties:
        theme_id:
          type: string
          pattern: ^idvt_[0-9a-f]{24}$
        tenant_id:
          type: string
        workspace_id:
          type: string
        name:
          type: string
          minLength: 1
          maxLength: 120
        status:
          $ref: '#/components/schemas/LifecycleStatus'
        is_default:
          type: boolean
          description: >-
            At most one default per tenant+workspace; the FIRST created theme
            auto-defaults (even as a draft).
        company_name:
          description: Optional; null when unset.
          oneOf:
            - type: string
              minLength: 1
              maxLength: 80
            - type: 'null'
        logo:
          oneOf:
            - $ref: '#/components/schemas/ThemeLogo'
            - type: 'null'
        logo_square:
          description: Square (1:1) QR-centre mark; an INDEPENDENT asset slot from `logo`.
          oneOf:
            - $ref: '#/components/schemas/ThemeLogo'
            - type: 'null'
        colors:
          $ref: '#/components/schemas/ThemeColors'
        font_family:
          type: string
          description: '"" = unset; otherwise the closed allowlist.'
          enum:
            - ''
            - Inter
            - system-ui
            - Roboto
            - Open Sans
            - Lato
            - Montserrat
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        revision:
          type: integer
          minimum: 1
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              example: idv_session_not_found
            message:
              type: string
    LifecycleStatus:
      type: string
      description: >
        Shared management lifecycle (workflows AND themes): draft -> active ->
        archived, archived -> active (restore), draft -> archived. Transitions
        only via the idempotent POST /activate and /archive endpoints, never via
        PUT.
      enum:
        - draft
        - active
        - archived
    ThemeLogo:
      type: object
      description: >
        Managed logo asset reference. IMMUTABLE per asset_id: a replacement
        upload mints a new asset and retains prior objects so session copies
        keep resolving. Storage keys never appear on any wire.
      required:
        - asset_id
        - content_type
        - width
        - height
      properties:
        asset_id:
          type: string
          pattern: ^[0-9a-f]{24}$
        content_type:
          type: string
          enum:
            - image/png
          description: Always image/png after the upload re-encode.
        width:
          type: integer
          minimum: 1
        height:
          type: integer
          minimum: 1
    ThemeColors:
      type: object
      description: >
        Canonical lowercase `#rrggbb` values only (validated + normalized at
        write time). Each color is null/omitted when unset.
      properties:
        primary:
          oneOf:
            - type: string
              pattern: ^#[0-9a-f]{6}$
            - type: 'null'
        primary_foreground:
          oneOf:
            - type: string
              pattern: ^#[0-9a-f]{6}$
            - type: 'null'
        background:
          oneOf:
            - type: string
              pattern: ^#[0-9a-f]{6}$
            - type: 'null'
        foreground:
          oneOf:
            - type: string
              pattern: ^#[0-9a-f]{6}$
            - type: 'null'
        accent:
          oneOf:
            - type: string
              pattern: ^#[0-9a-f]{6}$
            - type: 'null'
  responses:
    BadRequest:
      description: Validation error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: >-
        Missing or invalid credentials (uniform for token plane, no existence
        oracle)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Resolved dashboard user lacks the required capability or scope
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Session/workflow not found or out of scope (indistinguishable by design)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: Domain-state conflict (e.g. concurrent update, illegal transition)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    applicationApiKey:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        `Authorization: Api-Key <application_api_key>` (legacy `X-Api-Key` also
        accepted).
    dashboardUserToken:
      type: apiKey
      in: header
      name: x-user-token
      description: >
        Dashboard session token for Minerva dashboard users. Management routes
        accept it alongside an application API key; any in-scope workspace user
        (member or above) can read sessions and record review decisions, while
        workflow and theme writes need a management-capable role. Send it with
        the `x-tenant-id` and `x-workspace-id` headers; a header/access mismatch
        is refused with 403, and when dashboard authentication is disabled (the
        default) any request carrying this header gets a uniform 401.

````