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

# Overview

The FMV1 Configuration API provides JSON-native endpoints for exporting and importing company configuration. This is the programmatic interface to the configuration pipeline.

A **Seed** endpoint also puts a company into a usable configuration state directly from the framework's code-defined starter content — no configuration body required. It runs the same validate → compare → apply pipeline over starter content built in-memory. Choose what gets seeded with either a named `starterSheet` variant or an explicit `modules` selection (checkbox granularity), and set `replace: true` to overwrite a company that is already configured.

The **seed surface** is machine-discoverable: **List Seed Options** (`GET /configuration/seed/options`) returns the valid starter variants and the default *plus the full starter-module catalog* (each module's axis group and defaults), and **Generate Seed Configuration** (`POST /configuration/seed/generate`) returns the exact JSON config a seed *would* apply — without seeding or mutating the company — ready to review/edit and POST to **import**.

## Authentication

<Warning>
  These endpoints require an API key created with the **FMV1\_CONFIGURATION\_MANAGER** role. Standard API keys (e.g., those with `CARRIER_ADMIN`) will not have the required permissions.

  To create an API key with this role, go to **Settings > API Keys** and select the **FMV1 Configuration Manager** role when generating the key.
</Warning>

***

## Workflow

The JSON round-trip workflow is:

1. **Export** — Pull current config as a structured JSON body
2. Edit the JSON
3. **Import** — Apply the edited JSON body to the database

`export` → edit → `import` is a lossless, machine-to-machine round-trip.

To put a fresh company into a usable baseline configuration, **Seed** the framework's code-defined starter content directly — a single call.

The seed workflow is: **List Seed Options** to discover the variants → **Generate Seed Configuration** to get the starter config as JSON (no mutation) → edit it → **Import** to apply it.

***

## API Endpoints

The Configuration Management endpoints are listed in the left navigation:

* **Export Configuration** (`POST /configuration/export`) — Return current config as a structured JSON body. The output is the exact shape **import** accepts, so `export` → edit → `import` is a lossless round-trip.
* **Get Configuration Metadata** (`POST /configuration/metadata`) — Return a stable config version token plus last-modified / last-imported timestamps, *without* the full config body. Use it for cheap staleness checks before pulling the full **export** payload.
* **Import Configuration** (`POST /configuration/import`) — Apply a structured JSON configuration body to the database.
* **Seed Configuration** (`POST /configuration/seed`) — Apply code-defined starter content to the database. Select it by named `starterSheet` variant or explicit `modules`; refuses to overwrite an already-configured company unless `replace: true`.
* **List Seed Options** (`GET /configuration/seed/options`) — Discover the valid `starterSheet` variants, their descriptions, and the default, plus the full starter-module catalog for the `modules` selection
* **Generate Seed Configuration** (`POST /configuration/seed/generate`) — Return the JSON config a seed would apply, without seeding or mutating the company. The output is import-compatible.
* **Start Data Validation Run** (`POST /configuration/data-validation-runs`) — Start a background scan that classifies **every stored record** against a configuration. Send a complete configuration body to scan a candidate, or **send no body at all** to scan the live configuration. Returns a `runId` to poll.
* **Get Data Validation Run** (`GET /configuration/data-validation-runs/{runId}`) — Poll a run's `status` and its running counts of records scanned, adhering, holding undeclared keys, and breaking.
* **List Data Validation Run Findings** (`GET /configuration/data-validation-runs/{runId}/findings`) — Page through *which* records would not survive the scanned configuration and *why*. Paginated (`page` / `pageSize`) because a poll must not re-transfer thousands of findings; filter with `entityType` to build a per-type view.

***

## Checking configuration against stored data

**Validate** and **compare** are configuration-only: neither reads a single stored record, which is why both answer instantly regardless of how much data a company holds. They tell you a configuration is well-formed and how it differs from the current one — not whether the records you already have would survive it.

**Data validation runs** answer that second question. A run reads every stored record of every entity type and classifies each one, so the natural order before a risky import is: **validate** (is the config well-formed?) → **compare** (what changes?) → a **run** (which of my records would break?) → **import**.

Four properties are worth knowing before you build against it:

* **A run snapshots its configuration when it starts**, so a verdict cannot drift — an import that lands mid-scan does not change what the scan is measuring against.
* **Starts collapse and are capped.** Starting a run whose configuration matches one already in flight returns that run's id with `outcome: "duplicate"` instead of starting a second scan (pass `?force=true` to override). A company may have **3** runs in flight; a 4th distinct configuration is refused with a `429` carrying `inFlightRunIds` so you can poll those instead.
* **The counts and the detail are separate reads.** The poll is fixed-size — `status` plus four counts — and **findings** is a separate, paginated endpoint, so polling a run costs the same whether the book is clean or badly broken. Poll until the run is terminal, then page the findings once.
* **A run's status and findings stay readable for as long as the run exists.** Nothing removes a run or its findings on a timer, so a run started months ago still answers in full. **A run's age is never a failure** on either by-id read: neither reports a run as too old to read, and a run id that belongs to no run of this company is a `404`.

***

## Permissions

| Operation                         | Required Permission            |
| --------------------------------- | ------------------------------ |
| Export                            | `company.configuration:export` |
| Import                            | `company.configuration:import` |
| Seed                              | `company.configuration:import` |
| List Seed Options                 | `company.configuration:export` |
| Generate Seed Configuration       | `company.configuration:export` |
| Start Data Validation Run         | `company.configuration:export` |
| Get Data Validation Run           | `company.configuration:export` |
| List Data Validation Run Findings | `company.configuration:export` |

***

## Framework-Required Rows

Some configuration rows are required by the framework: the import rejects a configuration that omits them or changes their type, cardinality, or join shape. Most framework-required rows also have a fixed calculated value the import enforces byte-for-byte, but a few are flagged **customer-supplied**: the row's presence and shape are enforced, while its JEXL calculated-value expression is yours to author and maintain. The main example is `referencedExposures` on Quote, Policy, and Submission.

***

## Configuration Categories

The compare endpoint returns change counts for each category:

| Category           | Description                                        |
| ------------------ | -------------------------------------------------- |
| `fieldDefinitions` | Field definitions (name, type, reference ID, etc.) |
| `optionSets`       | Option set values for dropdown fields              |
| `customObjects`    | Custom object type definitions                     |
| `cardDefinitions`  | UI card layout definitions                         |
| `cardSections`     | Sections within cards                              |
| `fieldLocations`   | Where fields appear on cards                       |
