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

# Effective Dates & the Policy Timeline

> The two clocks behind every transaction — effectiveDate vs transactionTimestamp — and the one rule that ties them together

This page is the single source of truth for how the Policy API thinks about time. Every transaction carries two dates that do different jobs, and it's critical to get the distinction and relationship between them right.

If you haven't seen the transaction/delta/segment model yet, skim [Concepts](/api-reference/policies/concepts) first — this page assumes you know what a delta and a segment are.

## Two independent date axes

Every transaction moves along two independent axes:

* **`effectiveDate`** — *where on the policy term* a change lands. For an endorsement, it's when the new coverage begins; for a cancellation, when coverage ends. It must fall **within the policy term** — `policyStartDate <= effectiveDate <= policyEndDate`, both bounds inclusive. It can sit anywhere in that window: in the past (a backdated correction) or the future (a change booked ahead of time).
* **`transactionTimestamp`** — *when the decision was booked*. This is the audit / booking axis — the wall-clock moment the change entered the system. It defaults to "now," but you can set it explicitly when importing history (e.g. aligning to a bordereau booking date).

The distinction matters:

|                       | `effectiveDate`                                    | `transactionTimestamp`                                                    |
| --------------------- | -------------------------------------------------- | ------------------------------------------------------------------------- |
| **What it answers**   | "Where in the policy term does this change apply?" | "When did we record this decision?"                                       |
| **Axis**              | Policy-term time                                   | Audit / booking time                                                      |
| **Can move backward** | Yes — backdate freely within the term              | No — must move forward (see [Monotonicity](#precedence-and-monotonicity)) |
| **Default**           | None — you always supply it                        | `now()` if omitted                                                        |
| **What it is NOT**    | Not "when we found out"                            | Not "when coverage changes"                                               |

<Note>
  **`effectiveDate` and `transactionTimestamp` move on independent axes.** `effectiveDate` may backdate or post-date freely within the term; `transactionTimestamp` is the audit axis and only moves forward. A transaction booked today (a forward-moving `transactionTimestamp`) can carry an `effectiveDate` weeks in the past.
</Note>

## The effective-date rule

There is exactly one rule binding a change to where it lands on the term:

> **A per-segment delta's `startDate` must equal the transaction's `effectiveDate`.** (`delta.startDate == effectiveDate`)

The plain-language why: *the change **is** the start of the range.* An endorsement's "effective date" and "the date the new value starts applying" are not two different facts that happen to coincide — they are the same fact by construction. The effective date is the left edge of every range the transaction writes.

Because of this, a single transaction cannot mix deltas with different `startDate`s — they all start at the one `effectiveDate`. If two changes need to start on different dates, that's two transactions. (Each delta still carries its own `endDate`, so changes in one transaction can have different *durations* — bump a limit for the rest of the term *and* add an insured for the summer only — they just all *start* at the effective date.)

A delta whose `startDate` doesn't match the transaction's `effectiveDate` is rejected with `400` (`InvalidDelta`):

```
Per-segment delta startDate (2025-05-01) for path "policy.deductible" must equal transaction effectiveDate (2025-04-01)
```

<Note>
  **Two exemptions.** The [whole-term root fields](/api-reference/policies/concepts#whole-term-root-fields) — `policy.policyNumber`, `policy.policyStartDate`, `policy.policyEndDate`, `policy.previousPolicy` — hold term-invariant values, so a delta that touches one of them must state the *whole term* as its window rather than start at the effective date. And the cancel/reinstate `cancellationEffectiveOnDate` marker is written uniformly across the whole term by the server, so it's exempt too — you never author that one at all. For every other path, the rule above is the one you work with.
</Note>

## No independent take-effect date

**You cannot set a "take-effect" date that is independent of where the change's range starts.** There is no third time axis.

It is tempting to imagine three dates:

1. when you *decided* (the booking timestamp),
2. when the change's range *starts* (the delta's `startDate`), and
3. some separate date when the change *legally takes effect*.

The API has the first two. It deliberately does **not** have the third — and not because of a missing feature. It's because that third date doesn't correspond to any real insurance semantic. **A change always takes effect exactly when its range starts** — the change *is* the start of the range, and `effectiveDate` is that one date. Where it lands on the term doesn't change the rule, only *where* that single date sits:

* **Past** — a backdated correction, or coverage agreed to apply from an earlier date.
* **Present** — a change taking effect today.
* **Future** — a change booked ahead of time (usually an agreed-upon change, but potentially a forward-looking correction).

In every case it's the same single date — the moment the change begins to apply — never a take-effect date separate from where the range starts.

<Note>
  **"Correction" vs. "change" is a business distinction, not an API one.** A *correction* makes the policy reflect what it should have been — typically backdated, because a value was wrong or a change was agreed earlier but booked late. A *change* is a genuine evolution of the contract from its effective date onward. The API models both identically: an endorsement with an `effectiveDate`.
</Note>

There is no meaningful in-between — no case where "the date it takes effect" and "the date its range starts" are different dates that both matter. Once you see that, the imagined two-dimensional grid (term-date × take-effect-date) collapses to one dimension. A change happens on the term, full stop; *when you booked it* is metadata on the audit trail, not a third coverage axis. (This is the [bitemporal resolution](https://github.com/AI-Insurance/app/issues/22773) the model is built on.)

So the recurring question — *"how do I make this change take effect on date X but have its range start on date Y?"* — has no API surface, by design. Take-effect and range-start are the same date: endorse effective on the date the change should begin to apply, whether that's in the past or the future.

## Worked examples

### Backdate: a cancellation discovered late

The insured's coverage actually ended March 15, but nobody told you until June 1. The cancellation's effective date is the *true past date*; the booking timestamp is *now*.

```json POST /v1/policies/{policyId}/transaction/cancel theme={null}
{
  "cancellationDate": "2025-03-15",
  "transactionTimestamp": "2025-06-01T14:30:00.000Z",
  "fullTermPricingInfo": {
    "pricingComponents": [
      { "label": "Policy Premium", "group": "Policy Invoice", "kind": "Premium", "value": 21000 },
      { "label": "Surplus Lines Tax", "group": "Policy Invoice", "kind": "Taxes", "value": 1050 },
      { "label": "Cancellation Fee", "group": "Policy Invoice", "kind": "Fees", "earningBasis": "fully-earned-at-inception", "value": 500 }
    ]
  }
}
```

The pricing here is optional and shown for completeness — omit `fullTermPricingInfo` entirely and the server derives it. When you do supply it, the existing charges keep their own `label`/`group`/`kind` and carry exactly what they had earned through March 15, while `Cancellation Fee` is a new charge and so must declare `earningBasis: "fully-earned-at-inception"`. The Cancel Policy Transaction endpoint reference carries the full rule.

`effectiveDate` (the cancellation date) is March 15 — backdated. `transactionTimestamp` is June 1 — the audit axis recording when you booked it. The policy reads as cancelled from March 15 onward, and the audit trail shows the decision was made June 1.

### Book ahead: a future-effective endorsement

A coverage change is agreed today (June 1) but doesn't begin until August 1. The effective date is in the *future*; the booking timestamp is *now*.

```json POST /v1/policies/{policyId}/transaction/endorse theme={null}
{
  "effectiveDate": "2025-08-01",
  "transactionTimestamp": "2025-06-01T09:00:00.000Z",
  "deltas": [
    {
      "startDate": "2025-08-01",
      "endDate": "2025-12-31",
      "path": "policy.additionalExposures[id = 'exp-1'].bedCount",
      "action": "Overwrite",
      "value": 140
    }
  ]
}
```

Note `startDate` equals `effectiveDate` (the one rule). The change starts August 1 and runs to term end; it was booked June 1. Nothing "activates" later than its range start — the range start *is* when it takes effect.

### "Add Exposure 2 as though March 1, decided June 1"

You decide on June 1 to add an exposure, and it should have been on the policy since March 1. The instinct is to look for a way to *decouple* the dates — "set it live as of March 1 but mark that we only decided in June." **You don't.** You endorse it as a **retroactive correction**: a normal endorsement whose range starts March 1.

```json POST /v1/policies/{policyId}/transaction/endorse theme={null}
{
  "effectiveDate": "2025-03-01",
  "transactionTimestamp": "2025-06-01T11:00:00.000Z",
  "deltas": [
    {
      "startDate": "2025-03-01",
      "endDate": "2025-12-31",
      "path": "policy.additionalExposures",
      "action": "Add",
      "value": {
        "id": "exp-2",
        "exposureType": "OutpatientClinic",
        "facilityName": "Greenfield West Clinic",
        "bedCount": 0
      }
    }
  ]
}
```

The exposure's range starts March 1; the "we decided in June" fact lives entirely in `transactionTimestamp` and the version history. There is no separate field for it because there is no separate axis for it.

The one wrinkle: what if it's not the *whole change* that should reach back, but only the *legal bearing of certain fields*? That's the next section.

## Retroactive legal bearing

> *Retroactive legal bearing beyond a change's effective range is modeled with tenant-defined fields, not a separate effective-date axis.*

Sometimes a value has legal consequences for dates *outside* the segment it lives in. The canonical example is `policyType` on an occurrence vs. claims-made form. Suppose an underwriter switches `policyType` from `claims-made` to `occurrence` effective April 1, *intending the occurrence terms to apply retroactively* to incidents that predate April 1.

You model this exactly as you'd expect from the rule above — endorse effective April 1, producing two segments:

| Segment range           | `policyType`  |
| ----------------------- | ------------- |
| `[policyStart, Mar 31]` | `claims-made` |
| `[Apr 1, policyEnd]`    | `occurrence`  |

The April-onward segment's `occurrence` value has bearing on incident dates *before* its own range. The segment's date range tells you the policy window; it does **not**, by itself, tell you how far the terms inside it reach. That "reach-back" is a property of the *field's meaning*, captured by **an additional field the tenant configures into their own schema** — for instance a value like `policyTypeRetroactiveApplication` set to `Start` on that segment, declaring that the `occurrence` value evaluates claims since the policy start:

| Segment range           | `policyType`  | `policyTypeRetroactiveApplication` |
| ----------------------- | ------------- | ---------------------------------- |
| `[policyStart, Mar 31]` | `claims-made` | `null`                             |
| `[Apr 1, policyEnd]`    | `occurrence`  | `Start`                            |

<Warning>
  **This is a domain-modeling pattern, not a built-in framework feature.** There is no `policyTypeRetroactiveApplication` field that ships with the API — it's an *illustration* of a field a tenant would define for the rare lines where they need it. The framework does not enumerate retroactive-bearing fields in advance. Whether a value reaches outside its segment is a property of that field's semantics, expressed with ordinary configured fields, never with a hidden second effective-date axis.
</Warning>

This is why the "third date" never materializes: the genuinely hard cases — a value bearing on dates outside its range — are handled by *more fields inside the segment*, leaving the temporal model itself one-dimensional.

## Precedence and monotonicity

Two final rules govern how the two clocks interact across an entire policy history.

### Cross-transaction precedence — latest version wins on overlap

A single transaction's deltas are applied to the segments that overlap them. Across the policy's *whole* history, **later transactions win on overlap**. Each transaction produces a [version](/api-reference/policies/concepts#versions) with a sequential `policyVersion`, and that ordinal is the tiebreaker: when more than one transaction has written the same `path` over the same term-date, the resolved value is the one from the **highest `policyVersion`**. The system materializes state by folding transactions forward in `policyVersion` order, so reading the resolved value at a date is the dual — walk the history newest-first and take the first writer that addresses that `(path, date)`.

This is the deliberate precedence model, not an accident of storage order, and it is exactly what makes **backdated corrections** work: a later transaction with a *lower* `effectiveDate` can overwrite the value an earlier transaction set for the same date range, because the later transaction has the higher version. There is no per-transaction "priority" field — insertion order (`policyVersion`) is the sole arbiter of overlap.

<Note>
  **Within a single transaction, overlap is forbidden, not resolved.** Newest-wins only orders deltas *across* transactions. Two deltas in the *same* transaction that target the same path (or an ancestor/descendant of it) have no version ordering between them, so the API rejects them up front (see [Within-Transaction Path Conflicts](/api-reference/policies/concepts#within-transaction-path-conflicts)).
</Note>

### Transaction timestamp monotonicity

When you set `transactionTimestamp` explicitly, it must be **`>=` the largest `transactionTimestamp` already recorded on that policy**. The audit axis only moves forward. An out-of-order timestamp is rejected (`400`, `InvalidRequest`):

```
transactionTimestamp (2025-05-01T00:00:00.000Z) is earlier than the latest existing transaction on this policy (2025-06-01T14:30:00.000Z)
```

This constrains the **audit axis only** — the order in which decisions were booked. Effective dates may still **backdate freely**: a transaction booked today can carry an `effectiveDate` weeks in the past (that's the [backdate example](#backdate-a-cancellation-discovered-late) above). Omit `transactionTimestamp` to default to the current time, which is monotonic by construction and skips this check.

This is the symmetry at the heart of the temporal model: **`transactionTimestamp` can't go backwards; `effectiveDate` backdates freely.** One axis records the unalterable order in which you learned and decided things; the other places each change wherever it belongs on the policy term.
