Skip to main content
Every top-level Field Model V1 entity is managed through one parametric CRUD surface, keyed on {entityType}: The {entityType} path segment is the lowercase kebab-case slug (exposure, not Exposure); the PascalCase form is rejected. The set of available fields for each type is configured per company — discover it via the /configuration endpoint below. One entity adds two action endpoints alongside the CRUD surface, because their work is more than setting a field: an event closes and re-opens through the dedicated Event Lifecycle endpoints. A PATCH that changes eventStatus directly is rejected with 409 (GuardedStatusFieldWrite) — see Flow-written status fields.

The six entity types

There are exactly six CRUD entity types. Each links to its configuration schema:
Policy is not a CRUD entity. Policy has a read-only /configuration schema (its slug policy is valid on the configuration endpoint) but no generic create/update/delete — its writes go through the Policy Transaction endpoints. It is intentionally absent from the CRUD surface and from the /entity-types discovery response.
Custom objects are embedded-only. A custom object is never a top-level CRUD entity and never appears here or in /entity-types. Custom objects surface only as Object / Object List field definitions inside an entity’s /configuration response (for example, an embedded coverage list on a Quote). You read and write them as nested values on their owning entity — there is no /entities/{customObject} route.

Response envelope

Get and list responses use the generic entity envelope — identical for every entity type. All entity-specific field values live inside fieldModelV1Data (keyed by field referenceId); the envelope adds only system metadata: There is no top-level companyId and no cross-entity enrichment — linked entities are not embedded; query them separately. List responses wrap the envelopes as { items: [...], hasMore, totalCount }.
createdAt/updatedAt are epoch-second integers, not ISO strings.

referenceId-keyed request bodies

The request body for create and update is a flat JSON object whose keys are field referenceIds from the company’s field configuration — the same shape for every entity type:
Discover the valid referenceIds (and which are required) via the /configuration endpoint. Date fields are objects, not plain strings: { "date": "YYYY-MM-DD", "timezone": "America/New_York" }. Create returns { id }; delete returns { id, deleted: true }.

PATCH merge semantics (never PUT)

Updates are PATCH only. The handler does a partial merge onto the existing fieldModelV1Data:
  • A field provided with a value → updated.
  • A field set to null → cleared.
  • A field omitted → left unchanged.
Never PUT. PUT would imply full replacement, which these endpoints do not do. A PUT to a by-id entity route is rejected with 405 Method Not Allowed and an Allow: PATCH header. Use PATCH.

Configuration discovery

GET /entities/{entityType}/configuration returns a JSON Schema (draft 2020-12) describing the fields available for creating or updating that type; option-set fields carry an enum/oneOf of valid values. Every field is included — the schema is deliberately complete so you can read the whole shape — but each field advertises its write tier so you know which ones are yours to send. Embedded objects (custom objects) appear here as nested Object/Object List properties under their join field. The required array is your create contract: exactly the fields you must supply, with everything the platform produces for you already subtracted (join fields, calculated fields, server-seeded defaults). Supply all of them and a create cannot be rejected for missing input; omit any and you get one InvalidEntityShape 400 listing every field you missed, before anything is written. It is not a PATCH contract — an update merges only the fields you send. The configuration endpoint also accepts policy (read-only schema), in addition to the six CRUD slugs. Policy has no generic create endpoint, so its required reflects the fields your configuration marks required in the UI rather than a create contract.

Field write tiers

Each field property carries the marks below. readOnly is the standard JSON Schema flag a generic tool keys off; the x-* companions say why precisely. A field may carry more than one mark (a value that is both calculated and system-owned is marked as both). The rule of thumb: if a field is readOnly, do not send it; otherwise it is yours to set — including a computed-default field, which is calculated but caller-wins. What happens if you send a readOnly field anyway depends on the tier, and one tier is unforgiving: a calculated or system-owned value is quietly recomputed or ignored, but sending a generated value the platform did not originate fails the whole request with a 400 (GeneratedFieldWrite). Resending the exact stored value is tolerated on an update, but the safe rule is to omit a generated field entirely — inside an array (an embedded exposure) even an exact resend is refused. System ownership wins over every other mark. A generated declaration wins over the two calculated tiers: a generated field stays readOnly even when its expression is the self-referential keep-if-supplied idiom — in that case the idiom describes how the platform carries its own value forward, not an invitation to supply one. The slimmed schema surfaced by the MCP get_entity_schema tool carries the same tiers as readOnly, calculated, computedDefault, systemOwned, and generated booleans.

Flow-written status fields

A status field can be settable and still not be yours to move. eventStatus is published as a computed default — and on create it genuinely is settable, which is how a historical import loads claims that were already closed. On an update it is not: a PATCH that changes it returns 409 (GuardedStatusFieldWrite), and the message names the action to use instead. The reason is that the status does not travel alone. Closing an event also stamps its close date and appends to the event’s open/close-history log, and the lifecycle dates a claim reports — opened on, previously closed on, re-opened on — are read from that log. A status moved on its own would leave the log silently disagreeing with the claim. So the transition has its own endpoints — Close Event and Re-open Event — which do all of it in one transaction; see the Event Lifecycle section. Sending eventStatus with the value it already holds is always accepted, so an update that merely echoes it is unaffected.

Entity-type discovery

GET /entity-types is the top-level discovery endpoint. It returns the fixed set of six CRUD entity-type descriptors — each with display names, links to its /entities/{entityType} collection and /entities/{entityType}/configuration schema, and the per-action permission keys a client needs. It takes no inputs beyond the path companyId and is gated on company.configuration:export. Use it to bootstrap a client that doesn’t already know the entity model.

Embedded exposures

Some entity types embed exposures inline rather than only linking them by id. A Quote, Policy, or Submission can keep its own copy of an exposure’s data (see Exposure for why), carried in an embedded-exposure field — an array of exposures inside the host’s payload. In the /configuration schema such a field appears as an array whose items are a oneOf of two modes:
  • Reference moderequired: ["id"]. The item carries the id (uuid) of an existing exposure. Every other exposure field is present but optional; any you supply override that field on the embedded copy.
  • Create mode — no id. A new exposure is created inline. Its required is derived from the Exposure configuration (the same fields a standalone exposure create requires); join fields (e.g. referencingPolicies, contacts) are never required.
Both modes carry the same field write tiers as a standalone exposure — the Exposure’s system-owned fields (referencing*) are marked readOnly + x-system-owned inside each mode, so an embedded item should not send them either. Rating and tax output containers (exposureRatingResponse, crossSegmentRatingOutputs, inscipherTaxPlan) are settable on an embedded exposure. Hosted rating does not persist its results, so writing them back is how they are stored — see Rating.

The three payload shapes

Which mode applies is decided purely by payload shape — whether id is present — never by who is calling. An already-embedded item keyed by an id the host already holds re-uses the stored copy as its base (it is not re-copied from the canonical record); any fields you send still overlay it.

Membership is a restatement

Providing an embedded-exposure field restates the complete membership of that field (a JSON-Merge-Patch convention):
  • Items present are kept (referenced) or created.
  • A stored item omitted from the array is removed from the host.
  • Omitting the field entirely is a no-op — the stored membership is untouched.
A single-cardinality embedded field behaves the same way, restating the one item.

As-if-inserted validation

After the copy/override merge, every embedded item is validated as if it were being inserted as a standalone exposure against the current Exposure configuration — field shapes, calculated-value resolution, tenant invariants, type casting, and entity invariants, the same checks a standalone exposure create runs. This applies in reference mode too: embedding pre-existing canonical data that violates today’s configuration fails the write. A failure is a structured 400 whose message names the offending item by path (e.g. additionalExposures[1]):

Permissions

Permissions follow the format company.{entity}:{action} (actionread | create | update | delete). However, the exact key varies by type — the platform reuses existing permission families rather than minting a new one per entity. Treat these as the source of truth (and confirm at runtime via the per-type permissions block in the /entity-types response): The per-type /configuration endpoint has its own read permission (e.g. company.event:export for Event, company.insured:read for Exposure) — see the Get Entity Configuration endpoint.

Listing, filtering & sorting

The list endpoint supports: