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

# The pricing stack

> The four layers a policy sits at, the intended layer a saved quote binds at, and the layerChange a change names when it moves a policy between layers

A policy's money comes in three parts: its pricing (`fullTermPricingInfo`), its
billing (`fullTermBillingInfo`) and its active invoices. Each part builds on the
one before it, so a policy, and a saved quote, sits at one layer of a **pricing
stack**. A saved quote states the layer its bind should leave the policy at, and a
change without a saved quote names any move it makes between layers; the platform
checks both.

This page applies to companies that invoice: policy invoicing turned on, and a
Quote billing calculation other than `NO_BILLING()`. At any other company the
pricing stack endpoints and `layerChange` return
`403 finv2-policy-invoicing-disabled`, and nothing here is checked.

## The four layers

| Layer | Pricing | Billing | Active invoices |
| - | - | - | - |
| `base` | None | None | None |
| `pricing` | Stated | None | None |
| `billing` | Stated | Stated | None |
| `invoicing` | Stated | Stated | Invoices that add up to the billing, unless the billing owes nothing |

A layer is read from four facts, the **stack state**:

* whether `fullTermPricingInfo` is stated (a zero total counts);
* whether `fullTermBillingInfo` is stated (one with no `lines` counts);
* how many of the policy's invoices are active, that is, neither voided nor
  deleted;
* whether the billing owes nothing: every line is 0, or there are no lines.

Billing that owes nothing needs no invoices, so a zero bill with no invoices sits
at both `billing` and `invoicing`. No layer accepts billing without pricing, or
active invoices without billing, so a change that would leave a policy in either
state fails its layer check.

## The intended layer

A saved quote's **intended layer** is the layer its bind is meant to leave the
policy at. Choose it with
[Set Quote Intended Layer](/api-reference/policy-invoicing/set-quote-intended-layer):

```json PUT /api/v1/companies/{companyId}/quotes/{quoteId}/intended-layer theme={null}
{ "intendedLayer": "invoicing" }
```

[Get Quote Intended Layer](/api-reference/policy-invoicing/get-quote-intended-layer)
reads it, and the app's Billing step chooses the same value with its **Intended
layer** card, so either side of a quote can set it. It is stored on the quote apart
from its fields: the entity endpoints neither return nor take it, and it is never
copied, so every quote starts with none, including one saved from a generated
draft, copied in the app or created for a renewal. Choosing a layer changes nothing
else on the quote; you make its pricing, billing and invoices match.

Three requests check it, all with the same rule: the bind must leave the policy at
the intended layer.

* **The bind**: [Bind Quote](/api-reference/field-model-policy-transactions/bind-quote)
  and the by-reference form of
  [Bind Quote by Reference or by Value](/api-reference/field-model-policy-transactions/bind-quote-by-reference-or-by-value).
  Neither body takes a layer.
* **[Check Bind Conditions](/api-reference/field-model-policy-validation/check-bind-conditions)**,
  which reports a failure as an `invariant` entry in `failures`.
* **Complete**: [Update Entity](/api-reference/entities/update-entity) moving a
  quote's `quoteStatus` to `complete`.

A quote with no intended layer can be neither bound nor completed:
`422 finv2-quote-intended-layer-required`. A bind that would leave the policy at
another layer returns `422 finv2-quote-intended-layer-mismatch`, and nothing is
saved. The message names the intended layer, the layer the bind would reach, the
first part that differs, checked in the order pricing, billing, invoices, and the
request that fixes it, with your ids. Choosing the layer the bind would reach is
the other way out.

The check reads what the bind would write. Pricing and billing are the ones the
bind prepares: the converted policy's for new business and renewal; for an
endorsement, the quote's pricing or, when it states none, the policy's; for a
cancellation or reinstatement, the pricing the platform derives. The billing is
always the quote's own. Active invoices are counted after the plan the bind
applies:

* an endorsement, cancellation or reinstatement quote counts the policy's active
  invoices, less the plan's voids, plus its creates, so a bind of no invoices keeps
  them all;
* a new business or renewal quote counts only the plan's creates: a renewal never
  inherits the expiring term's invoices.

A saved quote's bind runs no other layer check, so an endorsement, cancellation or
reinstatement quote can take a policy to any layer its data reaches, with one
limit: it keeps the policy's pricing, so on a policy with pricing it cannot bind at
`base`. The message then says so, and points to the `pricing` layer or higher.

| What differs | The fix through the API |
| - | - |
| The quote states pricing; the layer has none | Clear its rating with [Clear Quote Rating](/api-reference/policy-invoicing/clear-quote-rating), or send `fullTermPricingInfo: null` with [Update Entity](/api-reference/entities/update-entity) for pricing a client sent (see [Clearing pricing](#clearing-pricing)). |
| The quote states no pricing; the layer needs it | Rate the quote and save the results: `POST …/quotes/{quoteId}/rate`, then `PATCH …/entities/quote/{quoteId}`. |
| The quote states billing; the layer has none | Change the quote's billing inputs until its billing calculation states none. The configuration export shows the calculation and the company's billing conditions. |
| The quote states no billing; the layer needs it | Rate and save the quote again so it states `fullTermBillingInfo`; if it still states none, the configuration export shows why. |
| The policy would have active invoices; the layer has none | A change quote: attach a plan that voids every active invoice with `PUT …/invoicing/plan`, then bind with `invoices: "saved"`. A new business or renewal quote: bind without invoices, after clearing a saved plan with `DELETE …/invoicing/plan`. |
| The policy would have no invoices; the layer is `invoicing` | Generate the invoices with `POST …/invoicing/generate` and bind with `invoices: "saved"`, or bind with `invoices: "generate"`. |

## Doors

Which check a request gets depends on its door
([Invoicing a policy](/api-reference/invoicing/overview)):

| Door | Requests | Layer check |
| - | - | - |
| Quote door | A saved quote's bind, by id or by reference; Check Bind Conditions; Complete | The intended layer |
| Value door, a change to a term | [Endorse](/api-reference/field-model-policy-transactions/endorse-policy-transaction), [Cancel](/api-reference/field-model-policy-transactions/cancel-policy-transaction) and [Reinstate](/api-reference/field-model-policy-transactions/reinstate-policy-transaction), and bind by value of an `ENDORSE`, `CANCEL` or `REINSTATE` | Continuity: the policy keeps its layer unless `layerChange` names the move ([Layer changes](#layer-changes)) |
| Value door, a new term | [New Business](/api-reference/field-model-policy-transactions/new-business-transaction) and [Renew](/api-reference/field-model-policy-transactions/renew-policy-transaction), and bind by value of a `NEW_BUSINESS` or `RENEW` | None: the term's own pricing, billing and invoices set its layer, and they take no `layerChange` |
| After the bind | [Update Policy Invoices](/api-reference/financials/update-policy-invoices) | Continuity, naming only a move between `billing` and `invoicing` ([The invoice batch](#the-invoice-batch)) |

Every door runs the [integrity checks](#integrity-checks) as well.

## Layer changes

A change to an existing term without a saved quote states its own pricing, billing
and invoices, so the costly mistake is leaving something out: an endorsement that
forgets `fullTermBillingInfo` would drop the policy's billing. So such a change
keeps the policy at its current layer, unless its request names the move in
`layerChange`:

```json theme={null}
{ "layerChange": { "from": "billing", "to": "pricing" } }
```

* **Without `layerChange`**, the change passes when the policy ends at the layer it
  started at. Billing that owes nothing counts at both `billing` and `invoicing`.
* **With `layerChange`**, the policy must sit at `from` before the change and at `to`
  after it.
* `layerChange` only names the move; the request's own data makes it. Nothing is
  written differently because of it.

These are the moves you will meet most:

| Move | What makes it |
| - | - |
| `billing` → `pricing` | Leaving `fullTermBillingInfo` out on a billed policy: billing you do not state stores none |
| `pricing` → `billing` | Stating `fullTermBillingInfo` on a policy that has none |
| `invoicing` → `billing` | An `invoicePlan` that voids every active invoice and keeps the billing |
| `invoicing` → `pricing` | An `invoicePlan` that voids every active invoice, with `fullTermBillingInfo` left out |
| `billing` → `invoicing` | An `invoicePlan` that gives the policy its first invoices |

A plan that voids every active invoice and creates none leaves nothing to add up,
so it needs no billing, which is why one change can take a policy from `invoicing`
straight to `pricing`. A change keeps the policy's pricing unless it restates it,
and a cancellation or reinstatement derives its pricing, so no change takes a priced
policy to `base`.

A change that moves the policy without naming the move, or names a move it does not
make, returns `422 finv2-policy-layer-change` and saves nothing. The message names
the move the change makes and, where the request can name it, the exact
`layerChange` to send. Naming one layer as both `from` and `to` returns `400`, as
does a `layerChange` on a new term or on a saved quote's bind.

### The invoice batch

[Update Policy Invoices](/api-reference/financials/update-policy-invoices) changes
a bound policy's invoices and nothing else. It keeps the policy at its layer, so a
plan that voids every active invoice, or gives a policy with none its first
invoices, names the move:

| The plan | `layerChange` |
| - | - |
| Voids every active invoice | `{ "from": "invoicing", "to": "billing" }` |
| Gives a policy with no active invoices its first | `{ "from": "billing", "to": "invoicing" }` |

Those are the only two moves the batch can name, because it changes neither pricing
nor billing; any other returns `422 finv2-policy-layer-change`, pointing you to a
policy transaction. An ordinary re-plan that keeps some invoices names none. The
app's Policy Financials page asks for the same confirmation in Configure billing
before it saves such a plan.

## Integrity checks

Three checks run at every door, whatever the layer:

* **The receivable check.** Stated billing's `receivable` lines add up to
  `fullTermPricingInfo.total`; otherwise `400 finv2-policy-billing-checksum`.
* **Invoice conservation.** The policy's active invoices add up to its billing, line
  by line, to the cent ([Invoice plans](/api-reference/invoicing/invoice-plans)).
* **The invoice ledger.** A void names the version of the invoice it read
  (`409 finv2-invoice-head-conflict` when it changed since), and an invoice with
  payments is never voided.

Conservation is arithmetic: no invoices add up to nothing. Whether a change may
leave a policy with none is its layer's question, answered by the intended layer or
by continuity.

## Clearing pricing

Two requests remove a quote's pricing, for a quote whose intended layer is `base`:

* **[Clear Quote Rating](/api-reference/policy-invoicing/clear-quote-rating)**,
  `DELETE …/quotes/{quoteId}/rating`, clears everything the quote's rating workflow
  owns, its pricing included. Use it for pricing a rating produced.
* **[Update Entity](/api-reference/entities/update-entity)** with
  `fullTermPricingInfo: null`, `PATCH …/entities/quote/{quoteId}`, clears pricing a
  client sent rather than a rating.

Either save recalculates the quote's billing, and billing conditions that need
pricing hold it at `null`. Neither touches the quote's invoice plan. A change quote
keeps the policy's pricing, so clearing its rating does not take it to `base`.

## Examples

The policy in these examples belongs to a company that invoices, and its billing
states one line, `Premium` on the `Policy Invoice` type. The names are this example
company's; use your company's own.

### 1. Bind an endorsement that voids every invoice

The policy has two unpaid invoices: it sits at `invoicing`. An endorsement quote,
saved and rated for its change, should also stop invoicing the policy.

1. Read the policy's invoices and note each active invoice's `id` and
   `headJournalId`:
   `GET /api/v1/companies/{companyId}/financials/invoices?linkedPolicy={policyId}`.

2. Attach a plan that voids both and creates none:

   ```json PUT /api/v1/companies/{companyId}/quotes/{quoteId}/invoicing/plan theme={null}
   {
     "incurredDate": "2026-10-15",
     "voidInvoices": [
       { "invoiceId": "550e8400-e29b-41d4-a716-446655440711", "headJournalId": "550e8400-e29b-41d4-a716-446655440911" },
       { "invoiceId": "550e8400-e29b-41d4-a716-446655440712", "headJournalId": "550e8400-e29b-41d4-a716-446655440912" }
     ],
     "creates": []
   }
   ```

3. Choose the layer the bind should leave the policy at:

   ```json PUT /api/v1/companies/{companyId}/quotes/{quoteId}/intended-layer theme={null}
   { "intendedLayer": "billing" }
   ```

4. Optionally, check the bind:
   `GET /api/v1/companies/{companyId}/quotes/{quoteId}/bind-conditions?invoices=saved`
   answers `"readyToBind": true`.

5. Bind the plan:

   ```json POST /api/v1/companies/{companyId}/quotes/{quoteId}/bind theme={null}
   { "invoices": "saved" }
   ```

**Outcome:** the policy has the endorsement's pricing and billing and no active
invoices: the `billing` layer. Had the intended layer been `invoicing`, the bind
would have returned `422 finv2-quote-intended-layer-mismatch`, saying the policy
would have no active invoices while its billing owes an amount. This works only
while neither invoice has a payment; a paid invoice cannot be voided. A saved
endorsement must change something on the policy: a plan alone is not a change, so
to void every invoice without one, use the invoice batch (example 3).

### 2. Add billing with an endorsement by value

The policy is priced at 1,200.00 and has no billing: it sits at `pricing`. The
endorsement states its first billing, so it names the move.

```json POST /api/v1/companies/{companyId}/policies/{policyId}/transaction/endorse theme={null}
{
  "effectiveDate": "2026-10-15",
  "fullTermBillingInfo": {
    "lines": [
      { "invoiceType": "Policy Invoice", "lineItem": "Premium", "direction": "receivable", "amount": 1200 }
    ]
  },
  "layerChange": { "from": "pricing", "to": "billing" }
}
```

**Outcome:** the policy is at `billing`: pricing and billing, and no invoices yet.
Without `layerChange`, the endorsement returns `422 finv2-policy-layer-change`:

```text theme={null}
This change moves policy 550e8400-e29b-41d4-a716-446655440000 from the pricing layer (pricing, no billing) to the billing layer (pricing and billing, no active invoices), and a change that moves a policy between layers must name the move. To make it, send layerChange: {"from":"pricing","to":"billing"} with the request; to keep policy 550e8400-e29b-41d4-a716-446655440000 at the pricing layer, change the request so it does not move.
```

Bind by value names it the same way, beside its `data`.

### 3. Void every invoice with the batch

The policy has two unpaid invoices, and its insured will pay outside the platform.

```json POST /api/v1/companies/{companyId}/financials/policies/{policyId}/invoices/batch theme={null}
{
  "incurredDate": "2026-10-15",
  "voidInvoices": [
    { "invoiceId": "550e8400-e29b-41d4-a716-446655440711", "headJournalId": "550e8400-e29b-41d4-a716-446655440911" },
    { "invoiceId": "550e8400-e29b-41d4-a716-446655440712", "headJournalId": "550e8400-e29b-41d4-a716-446655440912" }
  ],
  "creates": [],
  "layerChange": { "from": "invoicing", "to": "billing" }
}
```

**Outcome:** both invoices are voided and the policy is at `billing`; its pricing
and billing do not change. Without `layerChange`, the batch returns
`422 finv2-policy-layer-change`:

```text theme={null}
This plan voids every active invoice of policy 550e8400-e29b-41d4-a716-446655440000, which moves it from the invoicing layer (pricing, billing and active invoices) to the billing layer (pricing and billing, no active invoices), and a change that moves a policy between layers must name the move. To make it, send layerChange: {"from":"invoicing","to":"billing"} with the plan; to keep policy 550e8400-e29b-41d4-a716-446655440000 at the invoicing layer, change the plan so it does not move.
```

To invoice the policy again later, send a batch that creates its invoices with
`"layerChange": { "from": "billing", "to": "invoicing" }`.

## Refusals

| Code | When | The fix |
| - | - | - |
| `422 finv2-quote-intended-layer-required` | A saved quote's bind, or Complete, at a quote with no intended layer. Check Bind Conditions reports it as a failure. | Choose a layer with `PUT …/quotes/{quoteId}/intended-layer`, then try again. |
| `422 finv2-quote-intended-layer-mismatch` | A saved quote's bind, or Complete, would leave the policy at another layer than its intended layer. Check Bind Conditions reports it as a failure. | The message names the part that differs and its fix ([the table above](#the-intended-layer)), or choose the layer the bind would reach. |
| `422 finv2-policy-layer-change` | A change to a term without a saved quote, or the invoice batch, moves the policy without naming the move, names a move it does not make, or names a move the batch cannot make. | Send the `layerChange` the message names, or change the request so it keeps the policy's layer. |
| `400` | A `layerChange` names one layer as both `from` and `to`, or rides a request that takes none: a new term, or a saved quote's bind. | Name two different layers, or leave `layerChange` out. |
| `403 finv2-policy-invoicing-disabled` | The company does not invoice, and the request reads or sets an intended layer, clears a rating, or sends a `layerChange`. | Leave `layerChange` out. Support turns invoicing on. |

[Quote invoicing](/api-reference/invoicing/quote-invoicing) covers a quote's invoice
settings and plan, and [Invoice plans](/api-reference/invoicing/invoice-plans) every
refusal of a plan.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.