curl --request POST \
--url https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"fields": [],
"pages": [],
"cards": [],
"cardPageRelationships": [],
"optionSets": [],
"customObjects": [],
"fieldLocations": [],
"ratingWorkflows": [],
"entityInvariants": []
}
'import requests
url = "https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate"
payload = {
"fields": [],
"pages": [],
"cards": [],
"cardPageRelationships": [],
"optionSets": [],
"customObjects": [],
"fieldLocations": [],
"ratingWorkflows": [],
"entityInvariants": []
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
fields: [],
pages: [],
cards: [],
cardPageRelationships: [],
optionSets: [],
customObjects: [],
fieldLocations: [],
ratingWorkflows: [],
entityInvariants: []
})
};
fetch('https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'fields' => [
],
'pages' => [
],
'cards' => [
],
'cardPageRelationships' => [
],
'optionSets' => [
],
'customObjects' => [
],
'fieldLocations' => [
],
'ratingWorkflows' => [
],
'entityInvariants' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate"
payload := strings.NewReader("{\n \"fields\": [],\n \"pages\": [],\n \"cards\": [],\n \"cardPageRelationships\": [],\n \"optionSets\": [],\n \"customObjects\": [],\n \"fieldLocations\": [],\n \"ratingWorkflows\": [],\n \"entityInvariants\": []\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"fields\": [],\n \"pages\": [],\n \"cards\": [],\n \"cardPageRelationships\": [],\n \"optionSets\": [],\n \"customObjects\": [],\n \"fieldLocations\": [],\n \"ratingWorkflows\": [],\n \"entityInvariants\": []\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"fields\": [],\n \"pages\": [],\n \"cards\": [],\n \"cardPageRelationships\": [],\n \"optionSets\": [],\n \"customObjects\": [],\n \"fieldLocations\": [],\n \"ratingWorkflows\": [],\n \"entityInvariants\": []\n}"
response = http.request(request)
puts response.read_body{
"isValid": true,
"issues": []
}Validate Configuration
Checks whether a structured JSON body is a well-formed FMV1 configuration, WITHOUT applying any changes. The body is the structured FMV1 configuration shape (the same body import accepts). Use this to check for errors before running an import.
It is a true dry run — it performs no writes.
It checks configuration shape only. It does not examine the company’s
stored records: no records are read, so the response time does not depend on
how much data the company holds. isValid: true means the configuration is
well-formed — it makes no claim about whether stored records conform to it.
To ask that second question — would my already-stored records still fit? —
start a data validation run:
POST /configuration/data-validation-runs kicks off a background scan of
every stored record (against a candidate configuration you send, or against the
live configuration if you send no body), and
GET /configuration/data-validation-runs/{runId} reports its progress and
counts. Use validate first for a fast, data-free shape check, then a run
when you need to know which stored records a configuration would break.
The checks it runs are the config-shape ruleset import also runs: the spec
pass (entity selectors, field references, locations, …), the rater and
form-template existence checks, and the uniqueness/projectability gates. Note
that a clean result here is necessary but not sufficient for a successful
import: import additionally compares the incoming configuration against the
company’s current one and refuses changes that would reshape existing data
(dropped fields, changed types or cardinality, dropped option-set values)
unless ?force=true is passed. Validate does not run that comparison.
Findings are located in the JSON config itself, never by a spreadsheet
cell: each issue carries fieldPath segments into the config body, e.g.
["fields", "3", "cardinality"]. An unknown or document-wide location is
[]. The response is { isValid, issues }; each issue has severity: error
or warning. Warnings never make isValid false. Fetch /configuration/schema for the
machine-readable JSON Schema of the body.
Required permission: configuration.validate
This endpoint requires an API key that holds the configuration permissions — the Admin role does. See Authentication for how to create API keys with specific roles.
curl --request POST \
--url https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"fields": [],
"pages": [],
"cards": [],
"cardPageRelationships": [],
"optionSets": [],
"customObjects": [],
"fieldLocations": [],
"ratingWorkflows": [],
"entityInvariants": []
}
'import requests
url = "https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate"
payload = {
"fields": [],
"pages": [],
"cards": [],
"cardPageRelationships": [],
"optionSets": [],
"customObjects": [],
"fieldLocations": [],
"ratingWorkflows": [],
"entityInvariants": []
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
fields: [],
pages: [],
cards: [],
cardPageRelationships: [],
optionSets: [],
customObjects: [],
fieldLocations: [],
ratingWorkflows: [],
entityInvariants: []
})
};
fetch('https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'fields' => [
],
'pages' => [
],
'cards' => [
],
'cardPageRelationships' => [
],
'optionSets' => [
],
'customObjects' => [
],
'fieldLocations' => [
],
'ratingWorkflows' => [
],
'entityInvariants' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate"
payload := strings.NewReader("{\n \"fields\": [],\n \"pages\": [],\n \"cards\": [],\n \"cardPageRelationships\": [],\n \"optionSets\": [],\n \"customObjects\": [],\n \"fieldLocations\": [],\n \"ratingWorkflows\": [],\n \"entityInvariants\": []\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"fields\": [],\n \"pages\": [],\n \"cards\": [],\n \"cardPageRelationships\": [],\n \"optionSets\": [],\n \"customObjects\": [],\n \"fieldLocations\": [],\n \"ratingWorkflows\": [],\n \"entityInvariants\": []\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/validate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"fields\": [],\n \"pages\": [],\n \"cards\": [],\n \"cardPageRelationships\": [],\n \"optionSets\": [],\n \"customObjects\": [],\n \"fieldLocations\": [],\n \"ratingWorkflows\": [],\n \"entityInvariants\": []\n}"
response = http.request(request)
puts response.read_body{
"isValid": true,
"issues": []
}Authorizations
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.
Path Parameters
Company identifier
Body
- Option 1
- Option 2
A COMPLETE configuration body (the same body import accepts). A
delta/patch body is rejected with a 400 — typed changes go to the
patch endpoint (POST .../configuration/patch).
Field definitions, keyed by entity + reference id.
One top-level field definition, keyed by entity + reference id. Declare its
type with structured typeInfo. Legacy type and string-encoded typeArgs
remain accepted; when both representations are supplied they must agree
semantically. A conflict is rejected with the field identity.
Exports use typeInfo without the legacy duplicate properties.
- Option 1
- Option 2
Show child attributes
Show child attributes
Page definitions.
Show child attributes
Show child attributes
Card definitions.
Show child attributes
Show child attributes
Placements of cards onto pages.
Show child attributes
Show child attributes
Option-set type declarations.
Show child attributes
Show child attributes
Custom-object type declarations.
Show child attributes
Show child attributes
Option sets and their options.
Show child attributes
Show child attributes
Custom-object sub-field definitions, joined to object types.
Show child attributes
Show child attributes
Field placements (the layout) onto cards.
Show child attributes
Show child attributes
Rating workflow definitions.
Show child attributes
Show child attributes
Per-entity invariant conditions enforced on every write.
Show child attributes
Show child attributes
Forms-logic rules (quote-flow auto-add rules). Optional — existing payloads predate the "Forms" tab; absent ⇒ no rules.
Show child attributes
Show child attributes
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.
Show child attributes
Show child attributes
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.
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.
Columns are explicitly declared. A surface with no declarations exports only its platform columns. An optional rowSource makes a declaration available when exporting one row per item of that saved Object/List field.
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.
A row must carry spreadsheetType, or both valueType and cardinality.
- Option 1
- Option 2
Show child attributes
Show child attributes
Declared list columns — the complete tenant-facing column set of each entity list page. Optional: existing payloads predate the slice, and absent ⇒ no declared tenant columns, so each list renders only its platform/system columns.
Rows declare membership, not order or default visibility. The platform's
per-page defaults and each user's saved view own those choices; the stored
configuration sorts rows canonically by (surface, key).
Omitted from an export when the company has declared none, rather than emitted as an empty array.
Show child attributes
Show child attributes
Tenant-defined list filters, one per (surface, key). Together with
page-owned system filters, these rows are the complete user-visible filter
catalog. listColumns contributes no filters.
Optional for wire compatibility with payloads that predate the slice. Absent or empty means that each entity list offers only its page-owned system filters. Omitted from an export when the company has none.
Show child attributes
Show child attributes
Explicitly authored rules determine which values move between Policy and Quote, their destinations, and transformations. Authors maintain each direction and transaction scope independently. Matching field names imply no mapping; an absent optional mapping is valid and causes no conversion write to that destination. A rule may directly assign a source value or transform it, including writing to a differently named destination.
Each rule is identified by its (direction, transactionType,
destinationPath) triple; duplicates are rejected. The stored
configuration sorts the section canonically by that triple. Import
validates explicit rules and requires all framework-pinned rows, including
on first import. Omitting or emptying the section is invalid, even with
force enabled. Preserve the authored slice through whole-configuration imports. Field edits require
reviewing affected rules against business intent.
Show child attributes
Show child attributes
Server-derived memory for retired data addresses. Records have exactly one of three kinds: top-level field, custom-object subfield, or option-set value. A submitted copy is accepted for export/import round trips but is non-authoritative; the server derives the next set from the previous immutable configuration version. Omitted when empty.
- Option 1
- Option 2
- Option 3
Show child attributes
Show child attributes
Spreadsheet types for built-in (platform) export columns, one per
(surface, column). Optional: absent or empty means platform columns
keep their built-in formatting. column must name a built-in column of the
surface (checked by name at import). Omitted from an export when empty.
Show child attributes
Show child attributes
Response
Validation completed
Configuration shape validation only; stored records are not read. Warnings never make isValid false.
