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

# Import Configuration

> Imports FMV1 configuration from a **structured JSON body** into the database.
The body is the structured FMV1 configuration shape. This runs the full
pipeline: validate, compare, and apply changes.

On success the response returns `success: true`. If validation fails, **no
changes are applied** and the endpoint returns a `400` with a
`config-validation-failed` error carrying the validation problems. The
validation errors are located by configuration coordinate (entity / field).

### Breaking changes

A config that drops a field, or changes a field's type or cardinality
(Single↔List), would reshape data already stored under the current config. In
that case the import is **rejected with a `409`** (`fmv1-config-breaking-changes`)
and **nothing is written** — unless one of the following applies:

- **The instance holds no entity data.** With no stored records to
  invalidate, the breaking-change check is skipped automatically and the
  import proceeds. (Common while onboarding a fresh instance.)
- **`?force=true` is set.** The import proceeds anyway and the existing
  entity data is **left untouched** — values for a dropped field remain in the
  stored JSON, and a type/cardinality change leaves old-shaped values behind.

<Warning>
**`force` should almost never be used.** It deliberately overrides the one
safeguard that keeps your stored data consistent with your config. Use it
**only when you are 100% certain that every existing record is forwards-compatible
with the config you are importing** — i.e. nothing stored will be invalidated
by the change.

This *is* possible even when the change looks breaking. For example, dropping
a field is safe to force **if no record actually holds a value for that
field** (the breaking-change check is purely structural — it compares config
to config and does not inspect your data, so it flags the drop regardless).
But if you are not certain, do **not** force: reset in order instead —
**clear financial records first** (`POST .../financials/deleteAll`) if the
company holds any, **then clear the affected entity data**
(`POST .../entities/{entityType}/deleteAll` — it rejects with `409` while
financials still hold a live claim on the type), **then re-import** without
`force` — or fix the config so it is non-breaking. A wrong `force` leaves
orphaned/mismatched data that can break reads and downstream behavior.
</Warning>

### Dry run

`?dryRun=true` runs this endpoint as a **prediction**: every gate a real
import runs is executed — the validation *and* the breaking-change check —
and then the request stops before persisting. Nothing is written: no
configuration version, no import history. So a `200` under `dryRun` means a
real import of the same body would be accepted, and a `400`/`409` means it
would be rejected for exactly that reason. `force` applies under `dryRun`
too, so `?dryRun=true&force=true` previews the forced import.

**Required permission:** `company.configuration:import`. A `force` import
additionally requires `company.entity-data:deleteAll`, which is granted by
the **SUPER_ADMIN** role only — so only a SUPER_ADMIN key can force past the
breaking-change gate; a Configuration-Manager key that sets `?force=true`
gets a `403`.

<Warning>
This endpoint modifies the company's FMV1 configuration. Changes take effect immediately
and affect all users of the company. It runs a destructive smart import: configuration
rows present in the company but not in the JSON body are removed. Use the **compare**
endpoint first to preview changes.
</Warning>

<Note>
This endpoint requires an API key created with the **FMV1_CONFIGURATION_MANAGER** role.
See [Authentication](/api-reference/authentication) for how to create API keys with specific roles.
</Note>




## OpenAPI

````yaml /openapi/generated-external-api.yaml post /api/v1/companies/{companyId}/configuration/import
openapi: 3.0.3
info:
  title: AI Insurance External API
  description: External API for AI Insurance platform
  version: 1.0.0
  contact:
    email: support@aiinsurance.io
servers:
  - url: https://go.aiinsurance.io
    description: Production
security:
  - ApiKeyAuth: []
paths:
  /api/v1/companies/{companyId}/configuration/import:
    post:
      tags:
        - FMV1 Configuration
      summary: Import Configuration
      description: >
        Imports FMV1 configuration from a **structured JSON body** into the
        database.

        The body is the structured FMV1 configuration shape. This runs the full

        pipeline: validate, compare, and apply changes.


        On success the response returns `success: true`. If validation fails,
        **no

        changes are applied** and the endpoint returns a `400` with a

        `config-validation-failed` error carrying the validation problems. The

        validation errors are located by configuration coordinate (entity /
        field).


        ### Breaking changes


        A config that drops a field, or changes a field's type or cardinality

        (Single↔List), would reshape data already stored under the current
        config. In

        that case the import is **rejected with a `409`**
        (`fmv1-config-breaking-changes`)

        and **nothing is written** — unless one of the following applies:


        - **The instance holds no entity data.** With no stored records to
          invalidate, the breaking-change check is skipped automatically and the
          import proceeds. (Common while onboarding a fresh instance.)
        - **`?force=true` is set.** The import proceeds anyway and the existing
          entity data is **left untouched** — values for a dropped field remain in the
          stored JSON, and a type/cardinality change leaves old-shaped values behind.

        <Warning>

        **`force` should almost never be used.** It deliberately overrides the
        one

        safeguard that keeps your stored data consistent with your config. Use
        it

        **only when you are 100% certain that every existing record is
        forwards-compatible

        with the config you are importing** — i.e. nothing stored will be
        invalidated

        by the change.


        This *is* possible even when the change looks breaking. For example,
        dropping

        a field is safe to force **if no record actually holds a value for that

        field** (the breaking-change check is purely structural — it compares
        config

        to config and does not inspect your data, so it flags the drop
        regardless).

        But if you are not certain, do **not** force: reset in order instead —

        **clear financial records first** (`POST .../financials/deleteAll`) if
        the

        company holds any, **then clear the affected entity data**

        (`POST .../entities/{entityType}/deleteAll` — it rejects with `409`
        while

        financials still hold a live claim on the type), **then re-import**
        without

        `force` — or fix the config so it is non-breaking. A wrong `force`
        leaves

        orphaned/mismatched data that can break reads and downstream behavior.

        </Warning>


        ### Dry run


        `?dryRun=true` runs this endpoint as a **prediction**: every gate a real

        import runs is executed — the validation *and* the breaking-change check
        —

        and then the request stops before persisting. Nothing is written: no

        configuration version, no import history. So a `200` under `dryRun`
        means a

        real import of the same body would be accepted, and a `400`/`409` means
        it

        would be rejected for exactly that reason. `force` applies under
        `dryRun`

        too, so `?dryRun=true&force=true` previews the forced import.


        **Required permission:** `company.configuration:import`. A `force`
        import

        additionally requires `company.entity-data:deleteAll`, which is granted
        by

        the **SUPER_ADMIN** role only — so only a SUPER_ADMIN key can force past
        the

        breaking-change gate; a Configuration-Manager key that sets
        `?force=true`

        gets a `403`.


        <Warning>

        This endpoint modifies the company's FMV1 configuration. Changes take
        effect immediately

        and affect all users of the company. It runs a destructive smart import:
        configuration

        rows present in the company but not in the JSON body are removed. Use
        the **compare**

        endpoint first to preview changes.

        </Warning>


        <Note>

        This endpoint requires an API key created with the
        **FMV1_CONFIGURATION_MANAGER** role.

        See [Authentication](/api-reference/authentication) for how to create
        API keys with specific roles.

        </Note>
      operationId: importFmv1Configuration
      parameters:
        - $ref: '#/components/parameters/companyId'
        - name: force
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: >
            When `true`, bypass the breaking-change check and import even if the
            new

            config would invalidate data already stored under the current
            config.

            Existing entity data is **left in place** (not cleared) and may no
            longer

            match the new config. Any value other than `true` (including
            omission) is

            treated as `false`.


            **Almost never set this.** Only use `force` when you are 100%
            certain

            every existing record is forwards-compatible with the incoming
            config

            (nothing stored will be invalidated) — e.g. a dropped field that no

            record holds a value for. When unsure, clear the affected entity
            data and

            re-import without `force` instead.


            **Requires the SUPER_ADMIN role** (the
            `company.entity-data:deleteAll`

            permission) — a non-SUPER_ADMIN key that sets this gets a `403`.
        - name: dryRun
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: >
            When `true`, run the import but **write nothing**. Every gate a real

            import runs is executed — the full configuration validation *and*
            the

            breaking-change check, honouring `force` and whether the instance
            holds

            entity data — and the request then stops before persisting. No

            configuration version is written and no import history is recorded.


            Because this is the same code path as a real import stopped one step

            short, the answer is a faithful **prediction** of what importing
            this

            exact body would do right now: `200` means the real import would be

            accepted, `400` means validation would reject it, and `409`

            (`fmv1-config-breaking-changes`) means the breaking-change check
            would

            reject it. Combine with `?force=true` to preview the forced import

            instead.


            Any value other than `true` (including omission) is treated as
            `false` —

            i.e. the import **commits**.
        - name: expectedContentHash
          in: query
          required: false
          schema:
            type: string
            pattern: ^[0-9a-f]{64}$
            example: 9f2c0b1d4e6a8c3f5b7d9e1a2c4e6f80a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9
          description: >
            Optional **compare-and-swap precondition**. When supplied, the
            import is

            admitted only while the company's configuration still has this
            content

            hash — the value reported as `contentHash` by the **metadata**
            endpoint

            (`GET .../configuration/metadata`). Omit it and no precondition is

            applied.


            The check runs **inside the import's own transaction**, so a caller
            that

            read the configuration and then imports cannot be raced by a
            concurrent

            configuration write: this turns read-then-import into one atomic

            operation rather than merely narrowing the window.


            A mismatch is a `409` with code `fmv1-config-expected-hash-mismatch`
            and

            **nothing is written**. The error body carries `currentContentHash`
            and

            `currentVersion`, so you can re-read, rebase your changes onto the

            current configuration and retry without an extra round trip.

            `currentContentHash` is `null` when the company has no configuration

            version at all — which is also a mismatch, never a pass.


            The value must be a sha256 written as **64 lowercase hex
            characters**;

            anything else is a `400` (`fmv1-config-expected-hash-malformed`)
            rather

            than a silently skipped precondition.


            Evaluated under `?dryRun=true` as well, so a dry run predicts this

            verdict exactly as it predicts the validation and breaking-change
            ones.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfigurationJsonImportRequest'
              description: >
                A COMPLETE configuration body. To apply a small edit without
                sending

                the whole config, use the typed-change **patch** endpoint

                (`POST .../configuration/patch`) instead — this endpoint rejects
                a

                delta/patch body with a `400`.
            examples:
              default:
                summary: Import a structured configuration body
                value:
                  fields: []
                  pages: []
                  cards: []
                  cardPageRelationships: []
                  optionSetTypes: []
                  objectTypes: []
                  optionSets: []
                  objects: []
                  objectPrimitives: []
                  fieldLocations: []
                  ratingWorkflows: []
                  entityInvariants: []
      responses:
        '200':
          description: Import completed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigurationImportResponse'
              examples:
                success:
                  summary: Successful import
                  value:
                    success: true
                    message: Successfully imported configuration.
                dryRun:
                  summary: '`?dryRun=true` — every gate passed, nothing written'
                  value:
                    success: true
                    message: 'Dry run: configuration is valid; nothing was written.'
        '400':
          description: Bad Request - Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                validationFailure:
                  summary: Configuration failed validation (no changes applied)
                  value:
                    error:
                      code: config-validation-failed
                      message: Config validation failed
                      userMessages:
                        - Event is missing the spec-required field "eventType".
                invalidProperties:
                  summary: Request body shape validation failure
                  value:
                    error:
                      code: InvalidProperties
                      message: 'Invalid input: expected array, received string'
                      details:
                        - field: fields
                          message: 'Invalid input: expected array, received string'
                malformedConfig:
                  summary: Well-typed body that could not be assembled into a config
                  value:
                    error:
                      code: json-config-malformed
                      message: Failed to assemble a candidate config from the JSON body
                      userMessages:
                        - >-
                          The configuration JSON is well-typed but structurally
                          invalid and could not be processed. Check the field
                          locations, types, and references.
                malformedExpectedContentHash:
                  summary: '`?expectedContentHash=` is not a sha256 (no changes applied)'
                  value:
                    error:
                      code: fmv1-config-expected-hash-malformed
                      message: >-
                        The expectedContentHash query parameter must be a sha256
                        digest written as 64 lowercase hex characters — the
                        exact value the configuration export/metadata endpoints
                        report. No changes were made.
                      userMessages:
                        - >-
                          The expectedContentHash query parameter must be a
                          sha256 digest written as 64 lowercase hex characters —
                          the exact value the configuration export/metadata
                          endpoints report. No changes were made.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: >
            Conflict — **nothing is written** in any case. One of: the config
            has

            breaking changes that would invalidate data already stored under the

            current config and the instance holds entity data

            (`fmv1-config-breaking-changes`) — clear the affected entity data
            and

            re-import (or retry with `?force=true`); the supplied

            `?expectedContentHash=` does not match the company's current

            configuration (`fmv1-config-expected-hash-mismatch`) — the
            configuration

            changed since you read it, so re-read, rebase and retry (the error
            body

            names the current hash); a concurrent configuration

            write for this company committed first

            (`fmv1-config-concurrent-write`) — retry the request; the config
            newly

            declares `termInvariant: true` on a field that live policies already

            disagree on across the segments of their current version

            (`fmv1-config-term-invariant-divergence`) — make each named policy
            agree

            on that field across its term, or drop the declaration, then retry;
            or

            the stored configuration would hold duplicate rows that cannot
            derive

            unique runtime identifiers (`fmv1-config-not-projectable`) — remove
            the

            duplicate rows, then retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                breakingChanges:
                  summary: Config would invalidate existing data (no changes applied)
                  value:
                    error:
                      code: fmv1-config-breaking-changes
                      message: Config would invalidate existing data
                      userMessages:
                        - >-
                          The incoming configuration cannot be imported because
                          1 config change(s) would reshape data already stored
                          under the current config. No changes were made. To
                          proceed, either clear the affected entity data — POST
                          /api/v1/companies/{companyId}/entities/{entityType}/deleteAll
                          for Exposure; if the company holds financial records,
                          deleteAll rejects with 409 until they are cleared
                          first via POST
                          /api/v1/companies/{companyId}/financials/deleteAll
                          (financial configuration is kept) — then re-import, or
                          re-send this request with ?force=true to import anyway
                          and leave the existing data in place.
                termInvariantDivergence:
                  summary: >-
                    Config flags a field term-invariant that live policies
                    disagree on (no changes applied)
                  value:
                    error:
                      code: fmv1-config-term-invariant-divergence
                      message: >-
                        Config flags a field term-invariant that live policies
                        disagree on
                      userMessages:
                        - >-
                          The configuration was NOT applied because it declares
                          termInvariant: true on field(s) that live policies
                          already disagree on across the segments of their
                          current version: Policy.renewalDesk (2
                          policy/policies: 6f2b1c30-9a44-4d18-9f2e-7c1a0b5d3e91,
                          c81d4a72-3e55-4b09-8a6d-2f9e7c0b1a34). A
                          term-invariant field must hold ONE value for the whole
                          policy term, and the platform enforces that on every
                          write — so applying this declaration would leave those
                          policies unwritable, with no way to correct them (the
                          check refuses to choose between the segments' values).
                          Make each listed policy agree on the field across its
                          term first — endorse it across the whole term, or
                          correct the data — then re-import. Alternatively, drop
                          the termInvariant declaration from the field(s) above.
                expectedContentHashMismatch:
                  summary: >-
                    `?expectedContentHash=` is stale — the config moved (no
                    changes applied)
                  value:
                    error:
                      code: fmv1-config-expected-hash-mismatch
                      message: >-
                        Config content-hash precondition failed:
                        expectedContentHash
                        "9f2c0b1d4e6a8c3f5b7d9e1a2c4e6f80a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9"
                        does not match the company's current content hash
                        "4d1e8f60b2a94c73e5f18a06d3b7c9e21f4a6580c2d9e3b7a1c5d9e0f2a4b6c8".
                        The configuration changed since it was read. No changes
                        were made — re-read the current configuration, rebase
                        your changes onto it, and retry.
                      userMessages:
                        - >-
                          Config content-hash precondition failed:
                          expectedContentHash
                          "9f2c0b1d4e6a8c3f5b7d9e1a2c4e6f80a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9"
                          does not match the company's current content hash
                          "4d1e8f60b2a94c73e5f18a06d3b7c9e21f4a6580c2d9e3b7a1c5d9e0f2a4b6c8".
                          The configuration changed since it was read. No
                          changes were made — re-read the current configuration,
                          rebase your changes onto it, and retry.
                      expectedContentHash: >-
                        9f2c0b1d4e6a8c3f5b7d9e1a2c4e6f80a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9
                      currentContentHash: >-
                        4d1e8f60b2a94c73e5f18a06d3b7c9e21f4a6580c2d9e3b7a1c5d9e0f2a4b6c8
                      currentVersion: 12
        '500':
          $ref: '#/components/responses/InternalServerError'
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
components:
  parameters:
    companyId:
      name: companyId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Company identifier
  schemas:
    ConfigurationJsonImportRequest:
      type: object
      description: >
        Structured FMV1 configuration body. This is both the body the **import**

        endpoint accepts and the body the **export** endpoint returns, so

        `export` → (edit) → `import` is a lossless round-trip. Each property is
        an

        array of config rows; the import runs them through the full validate →

        compare → apply pipeline.


        The per-element schemas below are the canonical FMV1 configuration model
        and

        the single source of truth for this payload.
      required:
        - fields
        - pages
        - cards
        - cardPageRelationships
        - optionSetTypes
        - objectTypes
        - optionSets
        - objects
        - objectPrimitives
        - fieldLocations
        - ratingWorkflows
        - entityInvariants
      properties:
        fields:
          type: array
          description: Field definitions, keyed by entity + reference id.
          items:
            $ref: '#/components/schemas/Fmv1ConfigField'
        pages:
          type: array
          description: Page definitions.
          items:
            $ref: '#/components/schemas/Fmv1ConfigPage'
        cards:
          type: array
          description: Card definitions.
          items:
            $ref: '#/components/schemas/Fmv1ConfigCard'
        cardPageRelationships:
          type: array
          description: Placements of cards onto pages.
          items:
            $ref: '#/components/schemas/Fmv1ConfigCardPageRelationship'
        optionSetTypes:
          type: array
          description: Option-set type declarations.
          items:
            $ref: '#/components/schemas/Fmv1ConfigOptionSetType'
        objectTypes:
          type: array
          description: Custom-object type declarations.
          items:
            $ref: '#/components/schemas/Fmv1ConfigObjectType'
        optionSets:
          type: array
          description: Option sets and their options.
          items:
            $ref: '#/components/schemas/Fmv1ConfigOptionSet'
        objects:
          type: array
          description: Custom-object sub-field definitions, joined to object types.
          items:
            $ref: '#/components/schemas/Fmv1ConfigObjectDefinition'
        objectPrimitives:
          type: array
          description: Object-primitive sub-field definitions (Address / Date / Currency).
          items:
            $ref: '#/components/schemas/Fmv1ConfigObjectDefinition'
        fieldLocations:
          type: array
          description: Field placements (the layout) onto cards.
          items:
            $ref: '#/components/schemas/Fmv1ConfigFieldLocation'
        ratingWorkflows:
          type: array
          description: Rating workflow definitions.
          items:
            $ref: '#/components/schemas/Fmv1ConfigRatingWorkflow'
        entityInvariants:
          type: array
          description: Per-entity invariant conditions enforced on every write.
          items:
            $ref: '#/components/schemas/Fmv1ConfigEntityInvariant'
        formLogicRules:
          type: array
          description: |
            Forms-logic rules (quote-flow auto-add rules). Optional — existing
            payloads predate the "Forms" tab; absent ⇒ no rules.
          items:
            $ref: '#/components/schemas/Fmv1ConfigFormLogicRule'
        smartTags:
          type: array
          description: >
            Smart tags — the named values resolved into generated documents.
            Optional:

            existing payloads predate the slice, and absent ⇒ no smart tags,
            which is

            also how a company that has not yet moved to this format is
            recognised.


            Omitted from an export when the company has none, rather than
            emitted as an

            empty array.
          items:
            $ref: '#/components/schemas/Fmv1ConfigSmartTag'
        exportSurfaces:
          type: array
          description: >
            Declared export columns — the tenant-facing column set of each
            export

            surface (the seven entity exports plus the bordereau). Optional:
            existing

            payloads predate the slice, and absent ⇒ no declared columns, which
            is also

            how a surface that still offers every configured field is
            recognised.


            Activation is per surface: a surface with at least one row here
            resolves its

            whole tenant column set through those rows, in the order they
            appear; a

            surface with none behaves exactly as it did before this section
            existed.


            Omitted from an export when the company has declared none, rather
            than

            emitted as an empty array.
          items:
            $ref: '#/components/schemas/Fmv1ConfigExportSurfaceRow'
    ConfigurationImportResponse:
      type: object
      description: Result of importing FMV1 configuration
      required:
        - success
      properties:
        success:
          type: boolean
          description: Whether the import completed successfully
        message:
          type: string
          description: Human-readable status message
    ErrorResponse:
      type: object
      description: Standard error response for all external API endpoints
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable error code
              example: VALIDATION_ERROR
            message:
              type: string
              description: Human-readable error message
              example: 'submissionId: Required field is missing'
            userMessages:
              type: array
              description: >-
                Clean, verbatim-displayable messages — one entry per failure,
                free of error-code tags, field paths, and internal noise.
                Suitable for showing to end users as-is.
              items:
                type: string
              example:
                - Exposures of type 'company' require an address
            details:
              type: array
              description: Additional details for validation errors (field-level errors)
              items:
                type: object
                properties:
                  field:
                    type: string
                    description: The field that caused the error
                    example: submissionId
                  message:
                    type: string
                    description: Description of the field error
                    example: Required field is missing
    Fmv1ConfigField:
      type: object
      description: >
        One top-level field definition (a parsed *Fields* sheet row), keyed by
        entity

        + reference id. Mirrors the `FieldDefinition` shape.
      required:
        - entitySelector
        - referenceId
        - type
        - typeArgs
        - typeInfo
        - cardinality
      properties:
        entitySelector:
          type: string
          description: The entity the field belongs to (e.g. "Exposure").
        referenceId:
          type: string
          description: Stable per-entity reference id for the field.
        definition:
          type: string
          description: Optional human description of the field.
        type:
          type: string
          description: The field-type kind discriminant.
        typeArgs:
          type: string
          description: >-
            The type-specific argument string (JSON of the kind's `args`, or
            empty for argless kinds).
        typeInfo:
          $ref: '#/components/schemas/Fmv1ConfigFieldTypeInfo'
        cardinality:
          type: string
          description: >-
            Single value or list. Authoritative for the field's Single/List
            axis.
          enum:
            - Single
            - List
        calculatedValue:
          type: string
          description: Optional calculated-value expression.
        exportLabel:
          type: string
          minLength: 1
          maxLength: 200
          description: >
            Optional column-header override for exports (entity export +
            bordereau).

            Must be unique per entity (case-insensitive) and must not collide
            with a

            system export column or a fixed bordereau header.
        generated:
          type: string
          enum:
            - allocated
            - composed
          description: >
            Declares the field server-generated. Client-supplied values are

            rejected on every ordinary write surface ("echo, never originate")
            and

            the value freezes once computed on a Policy. `allocated` = issued by
            an

            allocator (e.g. a sequence mint) and carried forward across a
            renewal

            chain; `composed` = deterministically recomputed from other fields

            (requires a calculated value).
        termInvariant:
          type: boolean
          description: >
            Declares the field term-invariant: it holds one value across every

            segment of a policy version, so a write to it applies to the whole
            term.

            Accepted on `Policy` field rows only — no other entity is segmented
            —

            and `true` is rejected together with a calculated value, because the

            calculated-value resolver runs per segment. Absent means `false`.
        systemManaged:
          type: boolean
          description: >
            Declares the field system-managed: the platform owns its canonical
            value.

            Client-supplied values are silently dropped on every generic write
            channel;

            the write continues and the platform-maintained value wins. Omit the

            property for caller-owned fields.
    Fmv1ConfigPage:
      type: object
      description: >-
        One page definition (a parsed *Onboarding* sheet page row). Mirrors the
        `Page` shape.
      required:
        - name
        - pageType
      properties:
        name:
          type: string
          description: Human-readable page name.
        pageType:
          type: string
          description: How the page renders.
          enum:
            - input
            - viewEdit
            - readonly
        pageKey:
          type: string
          description: >-
            Optional stable key for the page (referenced by card-page
            relationships).
        entity:
          type: string
          description: Optional entity-in-context for the page.
    Fmv1ConfigCard:
      type: object
      description: >-
        One card definition (a parsed *Cards* sheet band-B row). Mirrors the
        `Card` shape.
      required:
        - entity
        - name
      properties:
        entity:
          type: string
          description: Entity-in-context for the card.
        name:
          type: string
          description: Human-readable card name.
        cardKey:
          type: string
          description: >-
            Optional stable key for the card (referenced by card-page
            relationships and field locations).
        cardType:
          type: string
          description: Optional card render type (e.g. "input", "viewEdit", "readonly").
        columnCount:
          type: number
          description: Optional number of layout columns on the card.
        displayCondition:
          type: string
          description: >
            Optional JEXL condition gating whether the card is shown — the

            expression string of the card's `{ expression }` display condition
            (the

            same shape carried by a field location's `displayCondition`).
            Omitted

            when the card has no display condition.
        cardTitleExpression:
          type: string
          description: Optional expression computing the card's title at render time.
    Fmv1ConfigCardPageRelationship:
      type: object
      description: |
        A static placement of a card onto a page (band C of the *Cards* sheet).
        Many-to-many; `displayPosition` orders cards within a page. Mirrors the
        `CardPageRelationship` shape.
      required:
        - cardKey
        - pageKey
        - displayPosition
      properties:
        cardKey:
          type: string
          description: The placed card's `cardKey`.
        pageKey:
          type: string
          description: The hosting page's `pageKey`.
        displayPosition:
          type: number
          description: Ordering of this card within the page.
    Fmv1ConfigOptionSetType:
      type: object
      description: An option-set type declaration. Mirrors the `OptionSetType` shape.
      required:
        - name
      properties:
        name:
          type: string
          description: The option-set type name.
    Fmv1ConfigObjectType:
      type: object
      description: A custom-object type declaration. Mirrors the `ObjectType` shape.
      required:
        - key
        - name
        - pluralizedName
      properties:
        key:
          type: string
          description: Stable key for the custom-object type.
        name:
          type: string
          description: Singular display name.
        pluralizedName:
          type: string
          description: Plural display name.
        displayNameExpression:
          type: string
          description: Optional expression computing an instance's display name.
    Fmv1ConfigOptionSet:
      type: object
      description: An option set and its options. Mirrors the `OptionSet` shape.
      required:
        - name
        - options
      properties:
        name:
          type: string
          description: The option-set name.
        options:
          type: array
          description: The options in the set.
          items:
            type: object
            required:
              - label
              - key
              - groupLabel
            properties:
              label:
                type: string
                description: Human-readable option label.
              key:
                type: string
                description: Stable option key (the stored value).
              groupLabel:
                type: string
                description: Group label for visually grouping options (may be empty).
    Fmv1ConfigObjectDefinition:
      type: object
      description: >
        A custom-object (or object-primitive) definition: a named object joined
        to

        its sub-field definitions. Mirrors the `ObjectDefinition` shape — the
        element

        type of both the `objects` and `objectPrimitives` arrays.
      required:
        - name
        - subFields
      properties:
        name:
          type: string
          description: >-
            The object's name (the object-type key for `objects`, or the
            primitive name for `objectPrimitives`).
        subFields:
          type: array
          description: The object's sub-field definitions.
          items:
            $ref: '#/components/schemas/Fmv1ConfigSubField'
    Fmv1ConfigFieldLocation:
      type: object
      description: >
        One field placement (the layout) onto a card — a parsed *Field
        Locations*

        sheet row. Mirrors the `FieldLocationDefinition` shape. The nullable

        presentation fields (`inputModality`, `inputModalityArgs`,
        `displayFormat`,

        `displayFormatArgs`) carry an explicit `null` on the wire (the parser
        emits

        `null`, not omission).
      required:
        - cardKey
        - entity
        - fieldReferenceId
        - label
        - sectionPosition
        - inputModality
        - inputModalityArgs
        - displayFormat
        - displayFormatArgs
      properties:
        cardKey:
          type: string
          description: The card this field is placed on (its `cardKey`).
        entity:
          type: string
          description: The entity-in-context for the placement.
        fieldReferenceId:
          type: string
          description: The placed field's `referenceId`.
        subFieldReferenceId:
          type: string
          description: >-
            Optional sub-field reference id (dot-notation placement into a
            custom-object field).
        label:
          type: string
          description: The label rendered for the field at this location.
        sectionPosition:
          type: object
          description: The 1-based grid coordinates of the placement on the card.
          required:
            - row
            - column
            - rowSpan
            - columnSpan
            - zAxis
          properties:
            row:
              type: integer
              minimum: 1
              description: 1-based grid row.
            column:
              type: integer
              minimum: 1
              description: 1-based grid column.
            rowSpan:
              type: integer
              minimum: 1
              description: Number of grid rows spanned.
            columnSpan:
              type: integer
              minimum: 1
              description: Number of grid columns spanned.
            zAxis:
              type: integer
              minimum: 1
              description: Stacking order.
        inputModality:
          type: string
          nullable: true
          description: >
            The edit-mode input control, or `null` when the placement is
            read-only /

            has no input. One of the framework input modalities.
          enum:
            - Text Box
            - Text Area
            - Comma Separated List
            - Phone Number (US/Canada)
            - Phone Number with Country Code
            - Exposure Name Picker
            - Number Box
            - Percentage Whole Number
            - Percentage Decimal
            - Comma Separated Numbers
            - Dollar Number
            - Switch
            - Checkbox
            - Labeled Dropdown
            - Single Select Dropdown
            - Multi Select Dropdown
            - Single Select Radio
            - Single Select Checkbox
            - Multi Select Checkbox
            - Address Typeahead
            - Currency Input
            - Custom Object Card List
            - Custom Object Table Input
            - Date Picker
            - String Or Number Input
            - ThirdPartyTypeaheadSingle
            - Relation Key
            - Relation Key Inline
            - Relation Keys
            - Relation Keys Inline
            - Embedded Exposure Single Select Dropdown
            - Embedded Exposure Table
            - Embedded Exposure Table Input
        inputModalityArgs:
          type: object
          nullable: true
          additionalProperties: true
          description: Modality-specific arguments, or `null` when none apply.
        displayFormat:
          type: string
          nullable: true
          description: >
            The read-mode display format, or `null` when none applies. One of
            the

            framework display formats.
          enum:
            - Plain Text
            - Comma Separated List
            - Phone Number
            - Hyperlink
            - Plain Number
            - Percentage
            - Dollar Number - Cents
            - Dollar Number - Nearest Dollar
            - Dollar Number - Optional Cents
            - Yes/No
            - True/False
            - Switch
            - Checkbox
            - Labeled
            - Date Format - mm/dd/yyyy
            - Date Format - yyyy-mm-dd
            - Address Format
            - Currency Format
            - Custom Object Card List
            - Custom Object Table Display
            - String Or Number Display
            - Pricing Table
            - Relation Link
            - Relation Links
            - Relation Card List
            - Relation Table
            - Embedded Exposure Card List
            - Embedded Exposure Table Display
        displayFormatArgs:
          type: object
          nullable: true
          additionalProperties: true
          description: Display-format-specific arguments, or `null` when none apply.
        displayCondition:
          type: string
          description: >-
            Optional JEXL condition gating whether the field is shown at this
            location.
        requiredCondition:
          type: string
          description: Optional JEXL condition making the field required at this location.
        autoSetCalculation:
          type: string
          description: Optional expression whose result is auto-written to the field.
        autoSetTrigger:
          type: string
          description: When the auto-set calculation writes its value to the target field.
          enum:
            - UNSET
            - UNSET_OR_DEPENDENCY_CHANGE
            - VISIBLE_OR_DEPENDENCY_CHANGE
        autoSetConfirmation:
          type: string
          description: Optional confirmation message shown before an auto-set overwrite.
        autoSetScope:
          type: string
          description: >
            Where the auto-set rule stays live in the client. `page` (the
            default when

            omitted) evaluates the rule only while its card's page is the active

            surface; `entity-global` evaluates it on every surface that edits
            the

            entity, for the whole editing session. Client-side only: server-side

            rating always evaluates all configured quote-level rules regardless
            of

            scope. `entity-global` cannot be combined with `autoSetConfirmation`
            or

            the `VISIBLE_OR_DEPENDENCY_CHANGE` trigger. On item cards (cards
            rendered

            per list item), `entity-global` installs the card's per-item rules
            for

            the whole session and is supported only when every list rendering
            the

            card is a top-level List field on the entity — nested and relation
            lists

            are rejected at validation.
          enum:
            - page
            - entity-global
    Fmv1ConfigRatingWorkflow:
      type: object
      description: >
        A single named rating workflow: a name plus an ordered list of stages. A

        company has many of these (no versions); the quote flow selects one by
        name

        at rating time. Mirrors the `RatingWorkflow` shape.
      required:
        - name
        - stages
      properties:
        name:
          type: string
          description: >-
            The workflow name (non-empty; selected by the quote flow at rating
            time).
        stages:
          type: array
          description: The ordered rating stages.
          items:
            type: object
            required:
              - stageType
              - raterSpec
              - callOncePerPath
              - outputPath
            properties:
              stageType:
                type: string
                description: >-
                  The stage the rater runs in. Must agree with the rater's own
                  stage.
                enum:
                  - segment
                  - fullTerm
              raterSpec:
                type: object
                description: The rater to run and its opaque, rater-specific arguments.
                required:
                  - raterType
                  - args
                  - raterDebugName
                properties:
                  raterType:
                    type: string
                    description: Canonical, stage-qualified rater identifier.
                    enum:
                      - segment-exposure-inscipher
                      - segment-policy-inscipher
                      - full-term-inscipher
                      - segment-inscipher-tax-plan-builder
                      - segment-policy-inscipher-tax-plan-builder
                      - full-term-inscipher-tax-plan-builder
                      - segment-google-sheets
                      - full-term-google-sheets
                      - segment-exposure-aufort
                      - segment-policy-aufort
                      - full-term-aufort
                      - segment-hyperformula
                      - full-term-hyperformula
                      - segment-stub
                      - full-term-stub
                  args:
                    type: object
                    additionalProperties: true
                    description: >-
                      Opaque, rater-specific configuration. Each rater reads the
                      keys it needs.
                  raterDebugName:
                    type: string
                    description: Human-readable name for the rater step (non-empty).
              callOncePerPath:
                type: string
                description: >
                  The `quote`-rooted dot-path naming the container to iterate
                  (e.g.

                  `quote` to run once for the whole quote, `quote.exposures` to
                  run

                  once per exposure). No brackets and no empty segments.
              outputPath:
                type: string
                description: Where the stage writes its computed output.
    Fmv1ConfigEntityInvariant:
      type: object
      description: >
        One per-entity invariant condition enforced on every write (a parsed
        *Entity

        Invariants* sheet row). Mirrors the `EntityInvariantDefinition` shape.
      required:
        - entity
        - condition
        - errorMessage
      properties:
        entity:
          type: string
          description: Top-level entity the invariant applies to (e.g. "Exposure").
        condition:
          type: string
          description: JEXL condition that must evaluate `true` on every write.
        errorMessage:
          type: string
          description: Error message returned when the condition fails.
    Fmv1ConfigFormLogicRule:
      type: object
      description: |
        One forms-logic rule — an auto-add rule for the quote flow. Mirrors the
        `FormLogicRuleDefinition` shape.
      required:
        - formNumber
        - rank
      properties:
        formNumber:
          type: string
          description: >
            The referenced form template's `FM-XXXX` number. Must match an
            existing,

            non-deleted Quote or Policy form template for the company.
        rank:
          type: integer
          minimum: 1
          description: |
            Evaluation / ordering rank — a positive integer, unique within the
            company. Lower ranks are evaluated (and added) first.
        addCondition:
          type: string
          description: >
            JEXL condition gating whether the form is auto-added to the quote
            flow.

            Empty or absent ⇒ the form is NOT auto-added (it stays available for

            manual selection); "always add" is an explicit `true`.
        transactionTypes:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            enum:
              - newBusiness
              - endorsement
              - renewal
              - cancellation
              - reinstatement
          description: >
            The quote transaction types this rule applies to. Absent ⇒ new
            business

            only — what every rule authored before this field existed meant.
            When

            present the list must be non-empty and must not repeat a value.


            The scope is a filter, not a replacement for `addCondition`: a form
            that

            behaves differently across transaction types stays ONE rule (a
            rule's

            identity is its `formNumber`) whose condition branches on the
            `quoteType`

            field — for example

            `quoteType == "newBusiness" || (quoteType == "endorsement" &&
            someFieldChanged)`.
    Fmv1ConfigSmartTag:
      type: object
      description: >
        One smart tag — a named value destined to be resolved into a generated
        document

        at generation time. Mirrors the `SmartTagDefinition` shape.


        This section is the ONLY place a smart tag can be declared: a document

        generation resolves its anchors from these rows.


        A tag's identity is the (`formType`, `key`) PAIR: there is one row per
        form

        type, so a tag offered on several form types is several rows sharing a
        `key`

        and a `name`. Names are therefore unique within a form type, not
        company-wide.
      required:
        - key
        - name
        - formType
        - expression
        - valueType
        - cardinality
      properties:
        key:
          type: string
          minLength: 1
          description: >
            The tag's identifier within its form type, and the only input to the

            anchor written into a document template. Changing it changes the
            anchor,

            so an existing template stops resolving the tag.
        name:
          type: string
          minLength: 1
          maxLength: 200
          description: >
            Display name shown in the tag catalog and the insertion drawer. Must
            be

            unique within a form type (sibling rows on other form types share
            it).

            Must not be whitespace-only.
        formType:
          type: string
          enum:
            - event
            - quote-flow
            - quote-bind-flow
            - insured
          description: >
            The form type this tag belongs to. It determines which entity
            contexts

            `expression` may read: `event` reads `event`, `quote-flow` and

            `quote-bind-flow` both read `policy` (the quote on a quote form, the

            policy segment on a policy form), and `insured` reads `exposure`.


            `unknown` is not accepted — a form with no category cannot be
            generated,

            so a tag scoped to it could never resolve.
        expression:
          type: string
          minLength: 1
          description: >
            JEXL evaluated when a document is generated, against the form type's

            contexts, each addressed by name — for example `policy.quoteNumber`
            or

            `exposure.glClaimLimit ? exposure.glClaimLimit :
            policy.glClaimLimit`.


            Usually just a pointer at a field. Arithmetic belongs in a
            calculated

            field with the tag pointing at it; a computing expression here earns
            its

            keep only when the value must come from more than one context, or on
            an

            `insured` or `quote-flow` form, where field formulas are not
            recomputed at

            generation.


            A tag's expression may only READ. Functions that allocate a number
            from a

            counter, return a different value per call, read the wall clock, or
            need an

            entity being saved are rejected — a tag's expression also runs for
            the

            sidebar preview and the out-of-date check, so those would burn real

            numbers or mark every document permanently out of sync. Cross-record

            `LINKED_*` reads stay available.
        valueType:
          $ref: '#/components/schemas/Fmv1ConfigFieldTypeInfo'
          description: >
            The tag's own type, driving formatting (dates, currency, option
            labels,

            table columns). It no longer comes from a field, so it must carry

            everything the formatter needs.


            `Join`, `Pointer` and `SubHeader` are accepted but reported as a
            warning:

            they are not values, so the tag lists as unsupported and resolves to

            nothing on every document. They are not rejected outright because a
            tag

            migrated from a field of one of those types is existing
            configuration, and

            refusing it would fail the whole import rather than the one dead
            tag. An

            option set or custom object the configuration does not define IS
            rejected —

            that name resolves to nothing anywhere, so nothing can describe what
            the

            tag was meant to show.
        cardinality:
          type: string
          enum:
            - Single
            - List
          description: >
            Required, and not inferred from `valueType`: the formatter branches
            on it

            independently, so a `List` of objects renders as a table where a
            `Single`

            renders as text.
        emptyText:
          type: string
          nullable: true
          minLength: 1
          maxLength: 200
          description: >
            Text substituted when the tag resolves to nothing — for example

            "Generated when bound". Absent ⇒ the default behaviour: the anchor
            is left

            unresolved and a warning is reported. An explicit `null` is accepted
            and

            means the same as absent — it is what the get-configuration surface

            serves for a tag with no empty text. Must not be whitespace-only,
            which

            would substitute invisibly and report success.
    Fmv1ConfigExportSurfaceRow:
      type: object
      description: >
        One declared export column — a tenant-facing column on one export
        surface.

        Mirrors the `ExportSurfaceRowDefinition` shape.


        A surface is one column set: each of the seven entity exports, plus the

        bordereau. `bordereau` reads Policy fields like the `policy` surface
        does, but

        it is a separate report with its own fixed columns, so it is its own
        surface.


        A column's identity is the (`surface`, `key`) PAIR: there is one row per

        surface, so the same `key` on two surfaces is two independent columns
        that may

        disagree about everything else. Labels are therefore unique within a
        surface,

        not company-wide.


        ACTIVATION IS DATA-DRIVEN, and per surface. A surface with NO rows here
        offers

        and exports every configured field, exactly as it did before this
        section

        existed. A surface with at least one row resolves its whole tenant
        column set

        through these rows and offers nothing else. Declaring the first column
        for a

        surface is therefore a behaviour change for that surface, not an
        addition to

        it.


        COLUMN ORDER IS CANONICAL, not authored: the stored configuration sorts
        this

        section by (`surface`, `key`), so the order rows appear in a request
        body does

        not survive the round trip. Read a column's position off its key, never
        off its

        position here.
      required:
        - surface
        - key
        - label
        - expression
        - valueType
        - cardinality
      properties:
        surface:
          type: string
          enum:
            - event
            - exposure
            - quote
            - submission
            - person
            - organization
            - policy
            - bordereau
          description: >
            The surface this column belongs to. The seven entity-export values
            are the

            same slugs the export endpoints take in their paths. `expression`
            must

            reference a field on the surface's entity — `Policy` for both
            `policy` and

            `bordereau`.
        key:
          type: string
          minLength: 1
          description: >
            The column's identifier within its surface — what an export request
            names

            and what a saved column selection carries (on the bordereau, as

            `field:<key>`). Changing it changes which saved selections resolve.
        label:
          type: string
          minLength: 1
          maxLength: 1000
          description: >
            The column header the export renders. Must not be empty or

            whitespace-only, and is capped at 1000 characters — a sanity bound,
            not a

            style rule, because today's headers include prose-length labels
            resolved

            from form layouts.


            Two further rules are checked but do NOT reject the configuration;
            each

            returns a WARNING, because these headers are what the exports
            already

            print:

              - a label should be unique within its surface (case-insensitively);
              - a label should not name a column that surface already emits — a system
                export column (`id`, `ID`, `Created At`, `Updated At`) on any surface,
                plus the 12 fixed bordereau headers (`Policy Number`, `Insured Name`,
                …) on the `bordereau` surface only.

            Either produces two columns under one header, which is worth telling
            an

            author about but is not grounds for refusing a configuration that
            merely

            describes today's export.
        expression:
          type: string
          minLength: 1
          description: >
            What the column reads. In this version it must be exactly the
            `referenceId`

            of a field declared on the surface's entity — a bare reference, with
            no

            operators, paths, or function calls. Anything else is rejected at
            import.


            The narrowness is deliberate, not a gap: the export path projects a
            whole

            result page straight out of stored configuration data in one
            request, so

            evaluating an expression per row per column is not something it can
            do

            yet. Asynchronous exports are the prerequisite for a richer grammar;

            widening it later is additive, so declaring the column vocabulary
            now costs

            nothing.
        valueType:
          $ref: '#/components/schemas/Fmv1ConfigFieldTypeInfo'
          description: >
            The column's own type, driving cell formatting (dates, currency,
            option

            labels). It does not come from the referenced field, so it must
            carry

            everything the formatter needs.
        cardinality:
          type: string
          enum:
            - Single
            - List
          description: >
            Required, and not inferred from `valueType`: cell formatting
            branches on it

            independently, so a `List` renders differently from a `Single`.
    Fmv1ConfigFieldTypeInfo:
      type: object
      description: >
        Structured field-type model — a `kind` discriminant plus kind-specific

        `args`. The typed form of the legacy "Field Type" string. The field's

        Single/List axis is NOT here — it lives on the separate `cardinality`
        field,

        which is authoritative.


        Argless kinds (`Text`, `Number`, `Boolean`, `SubHeader`, `Date`,
        `Address`,

        `AddressV2`, `Currency`, `StringOrNumber`, `Percentage`,
        `EmbeddedExposure`)

        carry only `kind` (no `args`). Arg-bearing kinds carry a kind-specific
        `args`

        object:

        - `OptionSet` → `{ optionSetName }`

        - `Object` → `{ objectKey }`

        - `Join` → join args (`{ sourceEntity, targetEntity, sourceCardinality,
        targetCardinality, qualifier?, whoAmI }`)

        - `Pointer` → `{ targetEntity }`


        `EmbeddedExposure` embeds Exposure value(s) inline and takes no
        arguments —

        the embedded target is not configurable.
      required:
        - kind
      properties:
        kind:
          type: string
          description: The per-field type discriminant.
          enum:
            - Text
            - Number
            - Boolean
            - SubHeader
            - Date
            - Address
            - AddressV2
            - Currency
            - StringOrNumber
            - Percentage
            - OptionSet
            - Object
            - EmbeddedExposure
            - Join
            - Pointer
        args:
          type: object
          additionalProperties: true
          description: >
            Kind-specific argument payload. Omitted for argless kinds. For
            arg-bearing

            kinds the shape depends on `kind`:


            - `OptionSet`: `{ optionSetName: string }`

            - `Object`: `{ objectKey: string }`

            - `Join`: `{ sourceEntity: string, targetEntity: string,
            sourceCardinality: "1"|"N", targetCardinality: "1"|"N", qualifier?:
            string, whoAmI: "source"|"target" }`

            - `Pointer`: `{ targetEntity: string }`
    Fmv1ConfigSubField:
      type: object
      description: >
        A custom-object sub-field definition — pure data, structurally a subset
        of a

        top-level *Fields*-sheet field. Presentation (label / input modality /

        display format) and per-placement conditional logic are owned by the
        layout

        (cards + field locations), not by the sub-field definition. Mirrors the

        `SubField` shape.
      required:
        - referenceId
        - typeInfo
      properties:
        referenceId:
          type: string
          description: Stable reference id for the sub-field within its object.
        definition:
          type: string
          description: Optional human description of the sub-field.
        typeInfo:
          $ref: '#/components/schemas/Fmv1ConfigFieldTypeInfo'
        cardinality:
          type: string
          description: Optional Single value or list axis for the sub-field.
          enum:
            - Single
            - List
        generated:
          type: string
          enum:
            - allocated
            - composed
          description: >
            Declares the sub-field server-generated. Client-supplied values are

            rejected on every ordinary write surface ("echo, never originate")
            and

            the value freezes once computed on a Policy. `allocated` = issued by
            an

            allocator (e.g. a sequence mint); `composed` = deterministically

            recomputed from other fields (requires a calculated value).
        systemManaged:
          type: boolean
          description: >
            Declares the sub-field system-managed: the platform owns its
            canonical

            value. Client-supplied values are silently dropped on every generic
            write

            channel; the write continues and the platform-maintained value wins.
            Omit

            the property for caller-owned sub-fields.
  responses:
    Unauthorized:
      description: Unauthorized - Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            missingApiKey:
              summary: Missing API key
              value:
                error:
                  code: AuthenticationError
                  message: API key authentication required
                  userMessages:
                    - API key authentication required
            invalidApiKey:
              summary: >-
                Invalid API key (e.g. unknown key, or a Bearer token used
                instead of an API key)
              value:
                error:
                  code: AuthenticationError
                  message: Invalid API key
                  userMessages:
                    - Invalid API key
    Forbidden:
      description: Forbidden - Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            insufficientPermissions:
              summary: Insufficient permissions
              value:
                error:
                  code: AuthorizationError
                  message: User is not authorized to perform the requested action
                  userMessages:
                    - User is not authorized to perform the requested action
            companyMismatch:
              summary: A valid API key naming another company in the URL
              value:
                error:
                  code: AuthorizationError
                  message: API key is not scoped to the requested company
                  userMessages:
                    - API key is not scoped to the requested company
    InternalServerError:
      description: Internal Server Error - Unexpected error occurred
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            internalError:
              summary: Unexpected server error
              value:
                error:
                  code: UncaughtActionError
                  message: Uncaught error occurred in <actionName>
                  userMessages:
                    - An unexpected error occurred. Please try again later.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        API key authentication. Send your raw API key as the `Authorization`
        header value with NO scheme prefix — `Authorization: YOUR-API-KEY`. Do
        NOT prefix it with `Bearer ` or `ApiKey `, and do not use an `X-API-Key`
        header; those are not accepted.
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        User-principal OAuth 2.0 Bearer authentication. Send a user-scoped Auth0
        access token (audience = the app API audience) as `Authorization: Bearer
        <jwt>`. The request resolves to the user's identity and is authorized by
        their Role on the `{companyId}` in the path — the same role-based
        permissions the web app enforces. This is the path the MCP connector
        uses to act on a user's behalf; endpoints that accept it list both
        `BearerAuth` and `ApiKeyAuth`.

````