> ## 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.

# List Data Validation Run Findings

> Returns the **per-record findings** of a **data validation run** — which of the
company's stored records would not survive the scanned configuration, and
exactly what is wrong with each one.

One finding is one record that did **not** cleanly adhere. Records that would
persist cleanly are counted in the run's `recordsAdhering` total and produce no
finding here, so a run over a clean book returns an empty page.

### Why this is a separate, paginated endpoint

**`GET /configuration/data-validation-runs/{runId}` is a poll**, made roughly
once a second, and a run over a broken book can produce tens of thousands of
findings. Carrying them on the poll would re-transfer the whole set on every
read, so the poll stays fixed-size and the findings are pulled here, page by
page, once the run has something to say.

### Paging and grouping

`page` and `pageSize` are the standard 1-based pagination parameters
(`pageSize` defaults to 50 and is capped at 500 — a larger value is clamped,
not rejected). Findings come back oldest-first, and `totalCount` **ignores the
pagination**.

**`totalCount` is only stable once the run is finished.** Wait for the run's
`status` to reach a terminal value (`succeeded` or `failed`) before sizing a
whole pull from the first page: a run still `queued` or `running` appends
findings as it scans, so page 1 of a live run can report a `totalCount` of 40
and page 2 report 95. Findings only ever append — they never reorder — so pages
you have already read stay valid either way.

**`entityType` is how you group.** The API returns a flat list and does not
group for you, because grouping is a presentation concern: request one entity
type at a time to build a per-type view. The filter narrows `totalCount` too, so
the count always describes the items beside it. An `entityType` this run
validated but found no problem in is a legitimate empty page; an `entityType`
that is not a real entity type is a `400`, **not** an empty page — see below.

### Reading a finding

- **`extraKeys`** — keys the record holds that the configuration does not
  declare. Suspect, not necessarily fatal.
- **`violations`** — fields whose value would **fail** a write. Read
  `fieldPath` (ordered segments) rather than splitting the legacy dotted
  `field`; both are `null` / `[]` for a record-level failure that names no
  single field.
- **`adheres` is always `false`.** A finding exists only for a record that did
  not adhere, so the field carries no information here. It is part of the shape
  rather than omitted, and a `true` value cannot occur.

Run ids are scoped to their company: a run id belonging to another company
returns `404`, exactly as an id that exists nowhere does.

### How long findings stay readable

A run's findings stay readable for as long as the run exists — nothing removes a
run or its findings on a timer, and however long ago it completed this endpoint
still returns them. **A run's age is never a failure:** a run that exists returns
its findings however long ago it finished, and no response reports a run as too
old to read.

**Required permission:** `company.configuration:export`

<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 get /api/v1/companies/{companyId}/configuration/data-validation-runs/{runId}/findings
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/data-validation-runs/{runId}/findings:
    get:
      tags:
        - FMV1 Configuration
      summary: List Data Validation Run Findings
      description: >
        Returns the **per-record findings** of a **data validation run** — which
        of the

        company's stored records would not survive the scanned configuration,
        and

        exactly what is wrong with each one.


        One finding is one record that did **not** cleanly adhere. Records that
        would

        persist cleanly are counted in the run's `recordsAdhering` total and
        produce no

        finding here, so a run over a clean book returns an empty page.


        ### Why this is a separate, paginated endpoint


        **`GET /configuration/data-validation-runs/{runId}` is a poll**, made
        roughly

        once a second, and a run over a broken book can produce tens of
        thousands of

        findings. Carrying them on the poll would re-transfer the whole set on
        every

        read, so the poll stays fixed-size and the findings are pulled here,
        page by

        page, once the run has something to say.


        ### Paging and grouping


        `page` and `pageSize` are the standard 1-based pagination parameters

        (`pageSize` defaults to 50 and is capped at 500 — a larger value is
        clamped,

        not rejected). Findings come back oldest-first, and `totalCount`
        **ignores the

        pagination**.


        **`totalCount` is only stable once the run is finished.** Wait for the
        run's

        `status` to reach a terminal value (`succeeded` or `failed`) before
        sizing a

        whole pull from the first page: a run still `queued` or `running`
        appends

        findings as it scans, so page 1 of a live run can report a `totalCount`
        of 40

        and page 2 report 95. Findings only ever append — they never reorder —
        so pages

        you have already read stay valid either way.


        **`entityType` is how you group.** The API returns a flat list and does
        not

        group for you, because grouping is a presentation concern: request one
        entity

        type at a time to build a per-type view. The filter narrows `totalCount`
        too, so

        the count always describes the items beside it. An `entityType` this run

        validated but found no problem in is a legitimate empty page; an
        `entityType`

        that is not a real entity type is a `400`, **not** an empty page — see
        below.


        ### Reading a finding


        - **`extraKeys`** — keys the record holds that the configuration does
        not
          declare. Suspect, not necessarily fatal.
        - **`violations`** — fields whose value would **fail** a write. Read
          `fieldPath` (ordered segments) rather than splitting the legacy dotted
          `field`; both are `null` / `[]` for a record-level failure that names no
          single field.
        - **`adheres` is always `false`.** A finding exists only for a record
        that did
          not adhere, so the field carries no information here. It is part of the shape
          rather than omitted, and a `true` value cannot occur.

        Run ids are scoped to their company: a run id belonging to another
        company

        returns `404`, exactly as an id that exists nowhere does.


        ### How long findings stay readable


        A run's findings stay readable for as long as the run exists — nothing
        removes a

        run or its findings on a timer, and however long ago it completed this
        endpoint

        still returns them. **A run's age is never a failure:** a run that
        exists returns

        its findings however long ago it finished, and no response reports a run
        as too

        old to read.


        **Required permission:** `company.configuration:export`


        <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: listFmv1DataValidationRunFindings
      parameters:
        - $ref: '#/components/parameters/companyId'
        - name: runId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: >
            The run identifier returned by **`POST

            /configuration/data-validation-runs`**. Runs are scoped to their
            company: a

            run id belonging to another company returns `404`, exactly as an id
            that

            exists nowhere does.
        - name: page
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 9007199254740991
            default: 1
          description: >
            1-based page number. Defaults to the first page.


            A `page` above 9007199254740991, or a `page` and `pageSize` whose
            product

            skips past that many rows, is a `400` — such a page is past the end
            of any

            collection, and the offset it asks for is not a number the database
            can

            seek to.
        - name: pageSize
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            default: 50
            maximum: 500
          description: >
            Number of findings per page (default 50, maximum 500).


            A `pageSize` above 500 is **silently clamped to 500** rather than

            rejected — it is not a `400`. Read the length of `items` rather than

            assuming you received the size you asked for, and page through the
            rest.

            The cap exists because an unbounded page over a badly broken book
            would

            load every finding row into one response, which is the exact cost
            this

            endpoint is paginated to avoid.


            The one exception is a `pageSize` above 9007199254740991, which is a
            `400`

            rather than a clamp: that is not a page size, it is a number too
            large for

            the pagination arithmetic to be meaningful.
        - name: entityType
          in: query
          required: false
          schema:
            type: string
            enum:
              - Event
              - Exposure
              - Quote
              - Submission
              - Person
              - Organization
              - Policy
          description: >
            Return only the findings for this entity type, and count only those
            in

            `totalCount`. Omit it for every finding the run produced.


            A value that is not one of the listed entity types is a `400`, not
            an empty

            page: `?entityType=Policies` answering `{"items":[],"totalCount":0}`
            would

            read as "none of my Policy records have a problem", which is
            silently wrong

            on the one endpoint whose job is to report problems.
      responses:
        '200':
          description: One page of the run's findings, plus the unpaginated total
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataValidationFindingsPageResponse'
              examples:
                findings:
                  summary: A record with an undeclared key and two failing values
                  value:
                    items:
                      - entityType: Policy
                        entityId: POL-1001 v2 @2025-01-01
                        adheres: false
                        extraKeys:
                          - legacyBrokerCode
                        violations:
                          - field: insured.tin
                            fieldPath:
                              - insured
                              - tin
                            message: Expected string, received number
                          - field: null
                            fieldPath: []
                            message: A policy must carry at least one exposure
                      - entityType: Exposure
                        entityId: 018f2c7a-6b3d-7c4e-9a11-4d2b8f0c5e91
                        adheres: false
                        extraKeys: []
                        violations:
                          - field: exposureType
                            fieldPath:
                              - exposureType
                            message: >-
                              "notARealExposureType" is not a key of option set
                              exposureType
                    totalCount: 27
                clean:
                  summary: The run found nothing — an empty page, not an error
                  value:
                    items: []
                    totalCount: 0
        '400':
          description: >
            Bad Request — the run id is missing or is not a UUID, `page` /
            `pageSize` is

            not a positive integer or is above 9007199254740991, the requested
            page

            skips past that many rows, or `entityType` is not one of the listed
            entity

            types. A well-formed run id that belongs to no run of this company
            is a

            `404`, not a `400`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalidEntityType:
                  summary: Unknown entityType — never reported as an empty page
                  value:
                    error:
                      code: INVALID_ENTITY_TYPE
                      message: >-
                        Invalid entityType. Must be one of: Event, Exposure,
                        Quote, Submission, Person, Organization, Policy.
                invalidRunId:
                  summary: Missing or malformed run id
                  value:
                    error:
                      code: INVALID_RUN_ID
                      message: A run id path parameter is required, and must be a UUID.
                invalidPageSize:
                  summary: Non-positive-integer pagination value
                  value:
                    error:
                      code: BadRequest
                      message: 'Invalid pageSize: must be a positive integer'
                pageOutOfRange:
                  summary: A page number past the end of any collection
                  value:
                    error:
                      code: BadRequest
                      message: 'Invalid page: must be no greater than 9007199254740991'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: >
            Not Found — no run with this id exists for this company. A run id
            belonging

            to a different company is reported the same way, so this response
            never

            reveals whether an id exists elsewhere, and never returns its
            findings as an

            empty page. A run's age never produces this response: a run that
            exists

            returns its findings however long ago it finished.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notFound:
                  summary: Unknown run id for this company
                  value:
                    error:
                      code: NotFound
                      message: >-
                        Data-validation run 7c9e6679-7425-40de-944b-e07fc1f90ae7
                        not found.
        '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:
    DataValidationFindingsPageResponse:
      type: object
      description: >
        One page of a data-validation run's per-record findings, plus the total
        number

        of findings matching the request. A finding exists only for a record the
        scan

        judged NOT to adhere, so a run over a clean book returns an empty page.
      required:
        - items
        - totalCount
      properties:
        items:
          type: array
          description: The findings on this page, oldest-first
          items:
            $ref: '#/components/schemas/DataValidationFinding'
        totalCount:
          type: integer
          description: >
            How many findings match this request IGNORING the pagination. When

            `entityType` is sent it counts only that entity type, so it always

            describes the items beside it. It is NOT any of the run's `records*`

            counters: those count records SCANNED (clean ones included, which
            write no

            finding at all), and none of them is broken down by entity type.


            It is only **stable between pages once the run has reached a
            terminal

            status** (`succeeded` or `failed`). A run still `queued` or
            `running`

            appends findings as it scans, so page 1 can report a smaller total
            than a

            later page of the same run. Findings only ever append and never
            reorder, so

            already-read pages stay valid. Poll the run to a terminal status
            before

            sizing a whole pull from the first page.
    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
    DataValidationFinding:
      type: object
      description: >
        ONE stored record a data-validation run judged not to adhere to the
        scanned

        configuration — which record, and exactly what is wrong with it.
      required:
        - entityType
        - entityId
        - adheres
        - extraKeys
        - violations
      properties:
        entityType:
          type: string
          enum:
            - Event
            - Exposure
            - Quote
            - Submission
            - Person
            - Organization
            - Policy
          description: The entity type this record belongs to
        entityId:
          type: string
          description: >-
            The record's identifier: the entity id for a generic-CRUD record, or
            the human-readable composite segment id (policy number, version,
            start date) for a Policy segment
        adheres:
          type: boolean
          description: >-
            ALWAYS `false`. A finding exists only for a record that did not
            cleanly adhere, so this field carries no information on this
            endpoint — it is present because the finding shape names it, and
            because the same shape is produced by the per-record classifier,
            where it is a real verdict. Do not read a `true` here as meaningful;
            it cannot occur.
        extraKeys:
          type: array
          description: >-
            Keys this record holds that the scanned configuration does not
            declare. Suspect, not necessarily fatal — the record would still
            write, minus these values.
          items:
            type: string
        violations:
          type: array
          description: >-
            The fields holding a value that would FAIL a write under the scanned
            configuration. Empty when the record's only problem is undeclared
            keys.
          items:
            type: object
            required:
              - field
              - fieldPath
              - message
            properties:
              field:
                type: string
                nullable: true
                description: >-
                  The offending field as a single dotted string
                  (`coverages[0].limit`), or `null` for a record-level failure
                  that names no single field (a violated invariant)
              fieldPath:
                type: array
                description: >-
                  The same locator as ordered, machine-stable segments
                  (`["coverages", "0", "limit"]`) — parse this rather than
                  splitting `field`. `[]` for a record-level failure, and `[]`
                  for a finding recorded before this locator existed.
                items:
                  type: string
              message:
                type: string
                description: Why the value would be rejected
  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`.

````