curl --request POST \
--url https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/import \
--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/import"
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/import', 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/import",
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/import"
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/import")
.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/import")
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{
"success": true,
"message": "Successfully imported configuration."
}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=trueis 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.
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.
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: configuration.edit; with force=true,
company.reset — and the Company must be in Sandbox mode (a Live Company
answers 409 CompanyModeConflict for everyone).
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.
curl --request POST \
--url https://go.aiinsurance.io/api/v1/companies/{companyId}/configuration/import \
--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/import"
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/import', 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/import",
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/import"
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/import")
.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/import")
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{
"success": true,
"message": "Successfully imported configuration."
}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
Query Parameters
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.
Sandbox only. Setting this requires the company.reset permission
(the same one as the data wipes), and the Company must be in Sandbox
mode: a Live Company answers 409 CompanyModeConflict whoever asks.
Demote it to Sandbox first.
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.
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
(POST .../configuration/metadata) for the canonical body returned by
configuration export. 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 the hash of the current export projection, even
when the immutable stored snapshot uses a legacy wire shape.
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.
^[0-9a-f]{64}$"9f2c0b1d4e6a8c3f5b7d9e1a2c4e6f80a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9"
Body
- Option 1
- Option 2
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.
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.
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
