@epilot/pricing-client 3.58.2 → 3.59.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/openapi.json CHANGED
@@ -2534,6 +2534,7 @@
2534
2534
  "type": "power",
2535
2535
  "billing_period": "monthly",
2536
2536
  "postal_code": "04109",
2537
+ "city": "Leipzig",
2537
2538
  "consumption": 3500,
2538
2539
  "association_id": "123456789"
2539
2540
  }
@@ -2574,6 +2575,13 @@
2574
2575
  "amount_decimal": "50.00"
2575
2576
  }
2576
2577
  }
2578
+ },
2579
+ "inputs": {
2580
+ "type": "power",
2581
+ "consumptionHT": 3500,
2582
+ "zipCode": "04109",
2583
+ "city": "Leipzig",
2584
+ "billingPeriod": "monthly"
2577
2585
  }
2578
2586
  }
2579
2587
  }
@@ -3414,7 +3422,7 @@
3414
3422
  },
3415
3423
  "/v1/conditional-pricing/{slug}/condition-sets": {
3416
3424
  "get": {
3417
- "description": "Returns the condition sets built in for one conditional entity type: the situations a\nconditional Product, Price or Coupon is commonly varied by, ready to be copied into that\nschema's `conditions` array and extended or modified from there.\n\nWhich sets exist depends on the schema — an offer window is a Product's dimension, a delivery\narea is a Price's and a Coupon's — so only the sets built in for `slug` are returned.\n\nStatic, read-only reference data. The catalog is the same for every organization and is not\napplied to any schema by this endpoint — adding conditions to a schema stays an Entity API\nwrite.\n",
3425
+ "description": "Returns the condition sets built in for one conditional entity type, ready to copy into that schema's `conditions` array. Read-only, and the same for every organization.",
3418
3426
  "operationId": "$getConditionSets",
3419
3427
  "summary": "$getConditionSets",
3420
3428
  "tags": [
@@ -3458,7 +3466,7 @@
3458
3466
  },
3459
3467
  "/v1/conditional-pricing:resolve": {
3460
3468
  "post": {
3461
- "description": "Resolves which of a conditional entity's variants apply, and returns each one composed: the\nbase entity overlaid with the values of the version in effect at `as_of`.\n\nResolution is two selections in a fixed order — the variant, then the version by `as_of`. It\nis always scoped to one logical entity, so it stays a cheap, predictable lookup rather than\nan open search.\n\n**The variant is selected one of two ways, and the body says which.** A `context` describes a\nsituation and is matched against the conditions each variant pins. A `variant_id` names one\nvariant and skips matching entirely. Exactly one of the two: a body carrying both, or\nneither, is a `400`.\n\nMatching follows two rules worth knowing before assembling a context. A condition a variant\ndoes **not** pin matches any value, which is what lets a condition be added to a schema\nwithout breaking the variants that already exist. A condition **missing from `context`**,\nhowever, does not satisfy one a variant pinned: an incomplete integration resolves to\nnothing rather than silently matching another segment's variants.\n\nWhen nothing matches, the entity's `default` variant is returned if it has one. There is no\nimplicit fallback to the unmodified base entity: an empty `results` means nothing applies to\nthis situation, and the base entity's values are not an answer to it. A pin does not reach\nthat fallback at all: it asks for one variant by name, and answers with it or with a 404.\n\n`options.hydrate` returns the entities a relation attribute references in place of the\nreferences, on either branch.\n\nAvailability is a separate mechanism and is never consulted here.\n",
3469
+ "description": "Returns the variants of one conditional entity that apply, each composed: the base entity\noverlaid with the version in effect at `as_of`.\n\nSelect the variant either by `context`, matched against the conditions each variant pins, or\nby `variant_id`. Exactly one of the two. A condition a variant leaves unpinned matches any\nvalue; a condition absent from `context` matches only variants that leave it unpinned.\n\nWhen no variant matches, the entity's `default` variant is returned, or `results` is empty.\n`options.hydrate` replaces relation references with the entities they reference.\n",
3462
3470
  "operationId": "$resolveConditionalEntity",
3463
3471
  "summary": "$resolveConditionalEntity",
3464
3472
  "tags": [
@@ -3473,7 +3481,7 @@
3473
3481
  },
3474
3482
  "examples": {
3475
3483
  "Pin a variant at a recorded instant": {
3476
- "summary": "What an order shows — the numbers the customer agreed to",
3484
+ "summary": "The numbers an order recorded",
3477
3485
  "value": {
3478
3486
  "schema": "price",
3479
3487
  "entity_id": "price-sp26d1yo",
@@ -3482,7 +3490,7 @@
3482
3490
  }
3483
3491
  },
3484
3492
  "Pin a variant as it stands now": {
3485
- "summary": "What a contract shows — what is billable today",
3493
+ "summary": "What a contract is billed at today",
3486
3494
  "value": {
3487
3495
  "schema": "price",
3488
3496
  "entity_id": "price-sp26d1yo",
@@ -3508,7 +3516,7 @@
3508
3516
  },
3509
3517
  "responses": {
3510
3518
  "200": {
3511
- "description": "The variants that apply, each composed with the version in effect. Empty when nothing\napplies and the entity has no `default` variant. With `resolve_one`, exactly one result;\nwith a pin, exactly one or a 404.\n",
3519
+ "description": "The variants that apply, each composed with the version in effect. Empty when none\napplies and the entity has no `default` variant.\n",
3512
3520
  "content": {
3513
3521
  "application/json": {
3514
3522
  "schema": {
@@ -3516,7 +3524,7 @@
3516
3524
  },
3517
3525
  "examples": {
3518
3526
  "A hydrated composite price": {
3519
- "summary": "`price_components` holds the component entities the variant's override names, fetched after composition",
3527
+ "summary": "The component entities the variant's override names, fetched after composition",
3520
3528
  "value": {
3521
3529
  "results": [
3522
3530
  {
@@ -3535,13 +3543,21 @@
3535
3543
  "_id": "price-base-fee-46045",
3536
3544
  "_schema": "price",
3537
3545
  "unit_amount": 1290,
3538
- "unit_amount_currency": "EUR"
3546
+ "unit_amount_currency": "EUR",
3547
+ "$relation": {
3548
+ "entity_id": "price-base-fee-46045",
3549
+ "attribute": "price_components"
3550
+ }
3539
3551
  },
3540
3552
  {
3541
3553
  "_id": "price-kwh-46045",
3542
3554
  "_schema": "price",
3543
3555
  "unit_amount": 32,
3544
- "unit_amount_currency": "EUR"
3556
+ "unit_amount_currency": "EUR",
3557
+ "$relation": {
3558
+ "entity_id": "price-kwh-46045",
3559
+ "attribute": "price_components"
3560
+ }
3545
3561
  }
3546
3562
  ]
3547
3563
  }
@@ -3549,7 +3565,7 @@
3549
3565
  }
3550
3566
  },
3551
3567
  "A variant carrying overrides that did not apply": {
3552
- "summary": "`unit_amount_currency` reads as the entity's own value rather than the variant's, and `_inert_overrides` says why each stored override was passed over",
3568
+ "summary": "`unit_amount_currency` reads as the entity's own value, and `_inert_overrides` says why each stored override was passed over",
3553
3569
  "value": {
3554
3570
  "results": [
3555
3571
  {
@@ -3582,7 +3598,7 @@
3582
3598
  }
3583
3599
  },
3584
3600
  "400": {
3585
- "description": "The context is not usable against this schema: it names an undefined condition\n(`CONDITION_UNDEFINED`), applies an operator the condition's type does not support\n(`OPERATOR_UNSUPPORTED`), carries a value malformed for its type (`CONTEXT_FORMAT_INVALID`),\nor selects more variants than one response may carry (`TOO_MANY_MATCHES`).\n\nOr the body did not pick a branch: it carries both `context` and `variant_id`, or\nneither, or sends `resolve_one` beside a pin. Those are request-validation failures, so\nthey carry a message and neither `code` nor `details` — testing `code` for absence is how\na client tells one from the four coded failures above.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
3601
+ "description": "`CONDITION_UNDEFINED`, `OPERATOR_UNSUPPORTED`, `CONTEXT_FORMAT_INVALID`,\n`DEFAULT_MARKER_RESERVED`, `TOO_MANY_MATCHES`, `VALID_FROM_INVALID`,\n`ENTITY_TYPE_MISMATCH`. A body carrying both `context` and `variant_id`, or neither,\nfails validation and carries no `code`.\n",
3586
3602
  "content": {
3587
3603
  "application/json": {
3588
3604
  "schema": {
@@ -3592,7 +3608,7 @@
3592
3608
  }
3593
3609
  },
3594
3610
  "404": {
3595
- "description": "No such schema (`SCHEMA_NOT_FOUND`) or entity (`ENTITY_NOT_FOUND`).\n\nOn the pinned branch: the entity has no such variant, or the variant belongs to another\nentity (`VARIANT_NOT_FOUND`), or it has no version in effect at `as_of` because its first\none is later (`NO_ACTIVE_VERSION`, carrying the instant in `details.as_of`). Context\nmatching drops such a variant from the results instead — a set may lose a member where a\npin naming one cannot answer with silence. It is not `VERSION_NOT_FOUND`: a pin names no\n`valid_from`, and the variant's versions all exist — none is in effect yet.\n\nOn the context branch, with `resolve_one`: nothing applied to the context and the entity\nhas no `default` variant (`NO_MATCHES`). That says the addressing was right and nothing\nserves this situation; without `resolve_one` it is a `200` carrying an empty `results`.\n",
3611
+ "description": "`SCHEMA_NOT_FOUND`, `ENTITY_NOT_FOUND`, and on the pinned branch `VARIANT_NOT_FOUND` or\n`NO_ACTIVE_VERSION`. With `resolve_one`, `NO_MATCHES`; without it, no match is a `200`\ncarrying an empty `results`.\n",
3596
3612
  "content": {
3597
3613
  "application/json": {
3598
3614
  "schema": {
@@ -3602,7 +3618,7 @@
3602
3618
  }
3603
3619
  },
3604
3620
  "409": {
3605
- "description": "Several variants apply while a single result was requested (`AMBIGUOUS_RESOLUTION`); the\ncandidates are in `details`.\n",
3621
+ "description": "`AMBIGUOUS_RESOLUTION`, `CONDITION_UNREADABLE`, `VARIANT_PIN_UNDECLARED`,\n`ENTITY_NOT_CONDITIONAL`.\n",
3606
3622
  "content": {
3607
3623
  "application/json": {
3608
3624
  "schema": {
@@ -3610,23 +3626,13 @@
3610
3626
  }
3611
3627
  }
3612
3628
  }
3613
- },
3614
- "501": {
3615
- "description": "A field published ahead of its behaviour was used: `variant_id`, or `options.hydrate` set\nto `true`. Declining it is how a deployed stage says the field exists and does not work\nyet, rather than quietly returning the `default` variant or unhydrated references.\n\nAnswered ahead of every other check, so a body that also names a schema that does not\nexist gets this rather than a `404`.\n\nRead `message`. The body is the shared `Error` shape rather than\n`ConditionalPricingError` — there is no code for \"not built yet\" — as it is on every 501\nthis API answers, so one branch covers all of them.\n",
3616
- "content": {
3617
- "application/json": {
3618
- "schema": {
3619
- "$ref": "#/components/schemas/Error"
3620
- }
3621
- }
3622
- }
3623
3629
  }
3624
3630
  }
3625
3631
  }
3626
3632
  },
3627
3633
  "/v1/conditional-pricing/{slug}/entities/{entity_id}/variants": {
3628
3634
  "post": {
3629
- "description": "Creates one variant of a conditional entity, together with the first version carrying its\nvalues: a variant always has at least one version.\n\nThe body pins the situation the variant applies to. Pins are exact values only — predicates\nare a read-side concept and are rejected here — and are stored canonicalized for their\ncondition's type, so two spellings of one instant, or one town written two ways, are one\nvariant rather than two that no context can tell apart.\n\nThree write rules are worth knowing before the first call:\n\n- A variant must pin at least one condition or be marked `default`. A variant pinning nothing\n would be a universal wildcard matching every resolve, which is a far more dangerous thing\n than a fallback and far easier to create by accident.\n- `default` is a property of the variant, set by the `default` flag, and is never a value in\n `conditions` — not even `false`. A `default` variant cannot pin anything else, and an entity\n can have only one; a second is refused as `TUPLE_CONFLICT`. Any entity may have one;\n nothing is declared in the schema to allow it.\n- Condition values are immutable afterwards. A variant's identity is the situation it applies\n to, and orders and contracts pin it. **A condition added to a schema that already has\n variants is effectively one-way**: every existing variant is a wildcard on the new\n dimension, but the first variant that pins it is ambiguous against all of them, and\n retro-pinning the others is blocked by this same rule.\n\nAttribute values are applied only for attributes currently carrying `overridable_attribute`.\nMetadata and non-overridable fields present in the body are not applied rather than rejected,\nand every one but the metadata is named in the response's `warnings`, so a client working from\na slightly stale schema snapshot still succeeds and still learns which fields did not land.\nMetadata is never named, since a client echoing back a payload it read carries it in every\nbody.\n\n`variant_id` is always server-generated and returned, and is not accepted in the body — the\nrequest schema admits no such property. It is the durable key orders and contracts pin.\n",
3635
+ "description": "Creates one variant together with its first version.\n\n`conditions` pins the situation the variant applies to, as exact values. A variant must pin\nat least one condition or be marked `default`, of which an entity may have one, and its\ncondition values are fixed once created.\n\nValues are stored only for attributes carrying `overridable_attribute`; the rest are\nreported in `warnings` rather than rejected. `variant_id` is server-generated.\n",
3630
3636
  "operationId": "$createConditionalVariant",
3631
3637
  "summary": "$createConditionalVariant",
3632
3638
  "tags": [
@@ -3674,7 +3680,7 @@
3674
3680
  },
3675
3681
  "examples": {
3676
3682
  "A body naming attributes this variant may not override": {
3677
- "summary": "The write succeeded and `values` holds what was stored, so the two attributes the warning names are absent from it",
3683
+ "summary": "The write succeeded, and the attributes the warning names are absent from `values`",
3678
3684
  "value": {
3679
3685
  "variant_id": "var-46045",
3680
3686
  "entity_id": "price-sp26d1yo",
@@ -3715,7 +3721,7 @@
3715
3721
  }
3716
3722
  },
3717
3723
  "400": {
3718
- "description": "The variant cannot be created as described: it pins nothing and is not the default\n(`VARIANT_UNPINNED`), pins a condition the schema does not declare\n(`CONDITION_UNDEFINED`), pins a `select` value the condition's vocabulary does not admit\n(`CONDITION_VALUE_INVALID`), carries a value malformed for its condition's type\n(`PIN_FORMAT_INVALID`), or the entity already holds every variant it may hold\n(`VARIANT_LIMIT_REACHED`).\n\nSeveral refusals on this response carry no code, and testing `code` for absence is how\nthey are told from the five above: pinning the fallback marker directly under either of\nits names (`default` or `_default`), marking a variant `default` while it also pins a\nreal condition, pinning a condition whose declared type this deploy cannot read, and a\n`valid_from` this store cannot sort by. Each is an integration mistake rather than one\nbad row in a source file.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
3724
+ "description": "`VARIANT_UNPINNED`, `CONDITION_UNDEFINED`, `CONDITION_VALUE_INVALID`,\n`PIN_FORMAT_INVALID`, `DEFAULT_MARKER_RESERVED`, `DEFAULT_VARIANT_PINS_CONDITIONS`,\n`VALID_FROM_INVALID`, `VALUE_UNSTORABLE`, `IDENTIFIER_INVALID`, `ENTITY_TYPE_MISMATCH`.\n",
3719
3725
  "content": {
3720
3726
  "application/json": {
3721
3727
  "schema": {
@@ -3724,8 +3730,18 @@
3724
3730
  }
3725
3731
  }
3726
3732
  },
3733
+ "403": {
3734
+ "description": "The token names no organization this API can act for",
3735
+ "content": {
3736
+ "application/json": {
3737
+ "schema": {
3738
+ "$ref": "#/components/schemas/Error"
3739
+ }
3740
+ }
3741
+ }
3742
+ },
3727
3743
  "404": {
3728
- "description": "No such schema (`SCHEMA_NOT_FOUND`), or no such entity under it (`ENTITY_NOT_FOUND`).\n",
3744
+ "description": "`SCHEMA_NOT_FOUND`, `ENTITY_NOT_FOUND`.",
3729
3745
  "content": {
3730
3746
  "application/json": {
3731
3747
  "schema": {
@@ -3735,7 +3751,7 @@
3735
3751
  }
3736
3752
  },
3737
3753
  "409": {
3738
- "description": "Another variant of this entity already pins this exact combination of condition values\n(`TUPLE_CONFLICT`, naming it in `details.conflicting_variant_id`) — which is also how a\nsecond `default` variant is refused — or the entity's items are being written\nconcurrently (`WRITE_CONFLICT`, retryable).\n",
3754
+ "description": "`TUPLE_CONFLICT` (also how a second `default` variant is refused), `WRITE_CONFLICT`,\n`CONDITION_UNREADABLE`, `CONDITION_UNCONFIGURED`, `VARIANT_LIMIT_REACHED`,\n`ENTITY_NOT_CONDITIONAL`.\n",
3739
3755
  "content": {
3740
3756
  "application/json": {
3741
3757
  "schema": {
@@ -3749,7 +3765,7 @@
3749
3765
  },
3750
3766
  "/v1/conditional-pricing/{slug}/entities/{entity_id}/variants:list": {
3751
3767
  "post": {
3752
- "description": "Lists a conditional entity's variants and the conditions each one pins — the browse, filter\nand search read behind the Entity UI's variant screens.\n\nA `POST` because the condition filter is a structured object and needs a body; nothing is\nwritten. Every property in that body is optional, so `{}` is the whole of \"the first ten\nvariants, in `variant_id` order\" — but the body itself is required, so send `{}` rather than\nnothing at all.\n\nThree ways to narrow, and they combine. `conditions` filters on the pins themselves and takes\nthe same seven predicates a resolve context does; `search` is free text over pinned values;\n`sort` orders by one pin. **A variant matches the filter only where it pins the condition** —\nthe one place a filter and a resolve context differ: asking for the variants pinning postal\ncode 46045 does not return every variant that pins no postal code at all.\n\nRows report what is *stored*, not what resolves: no version data, no `_revision` and no\n`_inert_overrides`.\n\nPaging is by offset for the first pages and by an opaque `cursor` beyond them. `size` defaults\nto 10 and is clamped at 1000; a `from` past the offset window is refused rather than clamped,\nand the refusal names the cursor to continue with.\n\n**Published ahead of the behaviour.** No handler serves this yet, so a deployed stage answers\n`501`. The examples below are what a consumer builds against in the meantime.\n",
3768
+ "description": "Lists a conditional entity's variants and the conditions each one pins. A `POST` because the\ncondition filter is a structured object; nothing is written. The body is required, so send\n`{}` for the first page.\n\n`conditions` filters on the pins, taking the same predicates a resolve context does, and\nmatches a variant only where it pins that condition. `search` is free text over pinned\nvalues; `sort` orders by one pin.\n\nOffset paging up to the search index's window, then the `cursor` from `next`.\n",
3753
3769
  "operationId": "$listConditionalVariants",
3754
3770
  "summary": "$listConditionalVariants",
3755
3771
  "tags": [
@@ -3790,7 +3806,7 @@
3790
3806
  "value": {}
3791
3807
  },
3792
3808
  "Find the entity's fallback variant": {
3793
- "summary": "The variant served when nothing else applies — at most one per entity",
3809
+ "summary": "The variant served when nothing else applies",
3794
3810
  "value": {
3795
3811
  "conditions": {
3796
3812
  "default": true
@@ -3798,7 +3814,7 @@
3798
3814
  }
3799
3815
  },
3800
3816
  "Filter, search and sort together": {
3801
- "summary": "The variants pinning a consumption band in either segment, ordered by postal code",
3817
+ "summary": "Variants pinning a consumption band in either segment, ordered by postal code",
3802
3818
  "value": {
3803
3819
  "conditions": {
3804
3820
  "segment": {
@@ -3869,7 +3885,7 @@
3869
3885
  }
3870
3886
  },
3871
3887
  "400": {
3872
- "description": "The listing cannot be served as described.\n\nThe filter is checked against the schema exactly as a resolve context is, by the same\ncode, so it reports the same three codes: a condition the schema does not declare\n(`CONDITION_UNDEFINED`), a predicate the condition's type does not support\n(`OPERATOR_UNSUPPORTED`), or a value malformed for its type (`CONTEXT_FORMAT_INVALID`).\n\nTwo paging refusals carry codes of their own, and the fix for each is a different request:\na `from` plus `size` reaching past the offset window (`OFFSET_WINDOW_EXCEEDED`, naming all\nthree numbers in `details`; page on with the last response's `next` instead), and a\n`cursor` that is malformed or was issued for a different listing (`CURSOR_INVALID`; start\nthe listing again without one).\n\nA `sort` naming something other than a `conditions.<name>` of a sortable type carries a\nmessage and neither `code` nor `details`, as does a body this schema rejects outright.\nTesting `code` for absence is how those are told from the five above.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
3888
+ "description": "`CONDITION_UNDEFINED`, `OPERATOR_UNSUPPORTED`, `CONTEXT_FORMAT_INVALID`, `SORT_INVALID`,\n`OFFSET_WINDOW_EXCEEDED`, `CURSOR_INVALID`, `ENTITY_TYPE_MISMATCH`.\n",
3873
3889
  "content": {
3874
3890
  "application/json": {
3875
3891
  "schema": {
@@ -3878,8 +3894,18 @@
3878
3894
  }
3879
3895
  }
3880
3896
  },
3897
+ "403": {
3898
+ "description": "The token names no organization this API can act for",
3899
+ "content": {
3900
+ "application/json": {
3901
+ "schema": {
3902
+ "$ref": "#/components/schemas/Error"
3903
+ }
3904
+ }
3905
+ }
3906
+ },
3881
3907
  "404": {
3882
- "description": "No such schema (`SCHEMA_NOT_FOUND`), or no such entity under it (`ENTITY_NOT_FOUND`).\n\nAn entity that exists and has no variants is a `200` carrying an empty `results` and\n`hits: 0` — having none is an answer, not a missing resource.\n",
3908
+ "description": "`SCHEMA_NOT_FOUND`, `ENTITY_NOT_FOUND`. An entity with no variants is a `200` carrying\nan empty `results` and `hits: 0`.\n",
3883
3909
  "content": {
3884
3910
  "application/json": {
3885
3911
  "schema": {
@@ -3888,12 +3914,12 @@
3888
3914
  }
3889
3915
  }
3890
3916
  },
3891
- "501": {
3892
- "description": "Published ahead of the behaviour. This operation is declared and not yet dispatched, so\nevery request to it is answered here until the listing behaviour lands — which is how a\ndeployed stage says \"this exists and does not work yet\" rather than answering with an\nempty page a client would read as an empty entity.\n\nRead `message`. The body is the shared `Error` shape, as it is on every 501 this API\nanswers, so one \"not built yet\" branch covers all of them.\n",
3917
+ "409": {
3918
+ "description": "`CONDITION_UNREADABLE`, raised only where the filter or `sort` names that condition, and\n`ENTITY_NOT_CONDITIONAL`.\n",
3893
3919
  "content": {
3894
3920
  "application/json": {
3895
3921
  "schema": {
3896
- "$ref": "#/components/schemas/Error"
3922
+ "$ref": "#/components/schemas/ConditionalPricingError"
3897
3923
  }
3898
3924
  }
3899
3925
  }
@@ -3903,7 +3929,7 @@
3903
3929
  },
3904
3930
  "/v1/conditional-pricing/{slug}/entities/{entity_id}/variants:tree": {
3905
3931
  "post": {
3906
- "description": "The variants list, each row carrying the version in effect at `as_of` — the Entity UI's main\nediting screen in one call rather than one call per row.\n\nEverything the variants list accepts, filtering, search, sort and paging alike, means the same\nhere. Three differences, all from the version lookup each row costs: the body takes an\n`as_of`, `size` is clamped at 100 rather than 1000, and a variant with no version to show is\nomitted from `results` (see `VariantTree`).\n\nEvery row carries a `version`, and a `status` saying which one it got. A variant always has at\nleast one version, so at any instant either a version is in effect (`active`) or every\nversion of that variant is still ahead of it (`scheduled`) — in which case `version` is that\nupcoming first one, which is what makes a staged variant visible on the screen rather than\nblank.\n\nThe version on a row carries no `_revision`. An editing screen re-reads the one version it is\nabout to write through that version's own `GET`, which is strongly consistent, and writes with\nthe revision it returns.\n\nThe base entity is not part of this response. The screen's standard-price row is an ordinary\nentity read, and a variant's full timeline is the versions list.\n\n**Published ahead of the behaviour.** No handler serves this yet, so a deployed stage answers\n`501`.\n",
3932
+ "description": "The variants list, each row carrying the version in effect at `as_of` and a `status` saying\nwhether that version is `active` or still `scheduled`.\n\nTakes everything the variants list takes, plus `as_of`. `size` is clamped at 100, and a\nvariant mid-delete is omitted from `results`.\n",
3907
3933
  "operationId": "$getConditionalVariantTree",
3908
3934
  "summary": "$getConditionalVariantTree",
3909
3935
  "tags": [
@@ -3947,7 +3973,7 @@
3947
3973
  }
3948
3974
  },
3949
3975
  "The screen at a future date": {
3950
- "summary": "What the table will look like once next year's versions take effect",
3976
+ "summary": "The table once next year's versions take effect",
3951
3977
  "value": {
3952
3978
  "as_of": "2027-03-15T00:00:00Z",
3953
3979
  "conditions": {
@@ -4040,7 +4066,7 @@
4040
4066
  }
4041
4067
  },
4042
4068
  "400": {
4043
- "description": "The listing cannot be served as described — the variants list's `400` word for word, plus\nan `as_of` that is not a timestamp this API can read.\n\nThe filter is checked against the schema exactly as a resolve context is, by the same\ncode, so it reports the same three codes: a condition the schema does not declare\n(`CONDITION_UNDEFINED`), a predicate the condition's type does not support\n(`OPERATOR_UNSUPPORTED`), or a value malformed for its type (`CONTEXT_FORMAT_INVALID`).\n\nTwo paging refusals carry codes of their own, and the fix for each is a different request:\na `from` plus `size` reaching past the offset window (`OFFSET_WINDOW_EXCEEDED`, naming all\nthree numbers in `details`; page on with the last response's `next` instead), and a\n`cursor` that is malformed or was issued for a different listing (`CURSOR_INVALID`; start\nthe listing again without one).\n\nA `sort` naming something other than a `conditions.<name>` of a sortable type carries a\nmessage and neither `code` nor `details`, as does a body this schema rejects outright.\nTesting `code` for absence is how those are told from the five above.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
4069
+ "description": "`CONDITION_UNDEFINED`, `OPERATOR_UNSUPPORTED`, `CONTEXT_FORMAT_INVALID`, `SORT_INVALID`,\n`OFFSET_WINDOW_EXCEEDED`, `CURSOR_INVALID`, `VALID_FROM_INVALID`,\n`ENTITY_TYPE_MISMATCH`.\n",
4044
4070
  "content": {
4045
4071
  "application/json": {
4046
4072
  "schema": {
@@ -4049,8 +4075,18 @@
4049
4075
  }
4050
4076
  }
4051
4077
  },
4078
+ "403": {
4079
+ "description": "The token names no organization this API can act for",
4080
+ "content": {
4081
+ "application/json": {
4082
+ "schema": {
4083
+ "$ref": "#/components/schemas/Error"
4084
+ }
4085
+ }
4086
+ }
4087
+ },
4052
4088
  "404": {
4053
- "description": "No such schema (`SCHEMA_NOT_FOUND`), or no such entity under it (`ENTITY_NOT_FOUND`).\n\nAn entity that exists and has no variants is a `200` carrying an empty `results` and\n`hits: 0` — having none is an answer, not a missing resource.\n",
4089
+ "description": "`SCHEMA_NOT_FOUND`, `ENTITY_NOT_FOUND`. An entity with no variants is a `200` carrying\nan empty `results` and `hits: 0`.\n",
4054
4090
  "content": {
4055
4091
  "application/json": {
4056
4092
  "schema": {
@@ -4059,12 +4095,12 @@
4059
4095
  }
4060
4096
  }
4061
4097
  },
4062
- "501": {
4063
- "description": "Published ahead of the behaviour. This operation is declared and not yet dispatched, so\nevery request to it is answered here until the listing behaviour lands — which is how a\ndeployed stage says \"this exists and does not work yet\" rather than answering with an\nempty page a client would read as an empty entity.\n\nRead `message`. The body is the shared `Error` shape, as it is on every 501 this API\nanswers, so one \"not built yet\" branch covers all of them.\n",
4098
+ "409": {
4099
+ "description": "`CONDITION_UNREADABLE`, raised only where the filter or `sort` names that condition, and\n`ENTITY_NOT_CONDITIONAL`.\n",
4064
4100
  "content": {
4065
4101
  "application/json": {
4066
4102
  "schema": {
4067
- "$ref": "#/components/schemas/Error"
4103
+ "$ref": "#/components/schemas/ConditionalPricingError"
4068
4104
  }
4069
4105
  }
4070
4106
  }
@@ -4074,7 +4110,7 @@
4074
4110
  },
4075
4111
  "/v1/conditional-pricing/{slug}/entities/{entity_id}/variants/{variant_id}": {
4076
4112
  "get": {
4077
- "description": "Returns the version of this variant that is currently in effect — the one with the latest\n`valid_from` at or before now.\n\nThe \"open this variant\" read: no date arithmetic is asked of the caller, and what comes back\ncarries the `_revision` a write to that version has to be sent with, so an editing screen can\nload and save without working out which version it is looking at.\n\nWhat is returned is the version's own attribute overrides, not the base entity overlaid with\nthem. Composing the two is what `:resolve` answers.\n\nA variant staged ahead of its launch has versions but none of them in effect, and is reported\nas having none rather than as not existing — the two are fixed differently.\n",
4113
+ "description": "Returns the version of this variant in effect now — the latest `valid_from` at or before now\n— with the `_revision` a write to it must carry.\n\nThese are the version's own overrides; `:resolve` composes them onto the entity.\n",
4078
4114
  "operationId": "$getActiveConditionalVariantVersion",
4079
4115
  "summary": "$getActiveConditionalVariantVersion",
4080
4116
  "tags": [
@@ -4124,7 +4160,7 @@
4124
4160
  }
4125
4161
  },
4126
4162
  "400": {
4127
- "description": "Invalid request, e.g. the slug names no conditional entity type.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
4163
+ "description": "`IDENTIFIER_INVALID`, `ENTITY_TYPE_MISMATCH`.",
4128
4164
  "content": {
4129
4165
  "application/json": {
4130
4166
  "schema": {
@@ -4133,8 +4169,28 @@
4133
4169
  }
4134
4170
  }
4135
4171
  },
4172
+ "403": {
4173
+ "description": "The token names no organization this API can act for",
4174
+ "content": {
4175
+ "application/json": {
4176
+ "schema": {
4177
+ "$ref": "#/components/schemas/Error"
4178
+ }
4179
+ }
4180
+ }
4181
+ },
4136
4182
  "404": {
4137
- "description": "No entity with that id (`ENTITY_NOT_FOUND`), no such variant under it\n(`VARIANT_NOT_FOUND`), or it has no version in effect at the instant addressed\n(`NO_ACTIVE_VERSION`). A variant whose versions are all still\nscheduled has none in effect, which is reported as such rather than as a missing\nvariant. Selecting the\nversion in effect and reading it are two reads, so a delete landing between them is\nanswered `VERSION_NOT_FOUND`.\n",
4183
+ "description": "`ENTITY_NOT_FOUND`, `VARIANT_NOT_FOUND`, `NO_ACTIVE_VERSION`, `VERSION_NOT_FOUND`.",
4184
+ "content": {
4185
+ "application/json": {
4186
+ "schema": {
4187
+ "$ref": "#/components/schemas/ConditionalPricingError"
4188
+ }
4189
+ }
4190
+ }
4191
+ },
4192
+ "409": {
4193
+ "description": "`ENTITY_NOT_CONDITIONAL`.",
4138
4194
  "content": {
4139
4195
  "application/json": {
4140
4196
  "schema": {
@@ -4146,7 +4202,7 @@
4146
4202
  }
4147
4203
  },
4148
4204
  "put": {
4149
- "description": "Replaces the values of the version currently in effect, wholesale.\n\nThe body is the complete set of attribute overrides: an attribute the variant may override and\nthat is absent from it stops being overridden. Attributes the variant may **not** override are\nnot applied where the body carries them, and their stored value is kept rather than dropped.\n\nEditing the version in effect is the ordinary way a live price is corrected, and warns about\nnothing: what changes is what that version *says*, not which version is in effect.\n\nNeither `valid_from` nor `conditions` can be changed here. Both are accepted when they match\nwhat is stored, so a client building its body from the version it loaded need not strip them\nout first, and both are refused when they name something else.\n",
4205
+ "description": "Replaces the values of the version in effect. The body is the complete set of overrides: an\noverridable attribute absent from it stops being overridden, and one the variant may not\noverride keeps its stored value.\n\n`valid_from` and `conditions` are accepted only unchanged.\n",
4150
4206
  "operationId": "$replaceActiveConditionalVariantVersion",
4151
4207
  "summary": "$replaceActiveConditionalVariantVersion",
4152
4208
  "tags": [
@@ -4206,7 +4262,7 @@
4206
4262
  }
4207
4263
  },
4208
4264
  "400": {
4209
- "description": "The write cannot be applied as described: it would move the version it addresses to another\n`valid_from`, or change the conditions its variant is pinned to — both identity rather than\ncontent, and both fixed at creation. Also when `_revision` is missing or is not a revision\nmarker.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
4265
+ "description": "`VALID_FROM_IMMUTABLE`, `VALID_FROM_INVALID`, `VALUE_UNSTORABLE`, `IDENTIFIER_INVALID`,\n`ENTITY_TYPE_MISMATCH`.\n",
4210
4266
  "content": {
4211
4267
  "application/json": {
4212
4268
  "schema": {
@@ -4215,8 +4271,18 @@
4215
4271
  }
4216
4272
  }
4217
4273
  },
4274
+ "403": {
4275
+ "description": "The token names no organization this API can act for",
4276
+ "content": {
4277
+ "application/json": {
4278
+ "schema": {
4279
+ "$ref": "#/components/schemas/Error"
4280
+ }
4281
+ }
4282
+ }
4283
+ },
4218
4284
  "404": {
4219
- "description": "No such schema (`SCHEMA_NOT_FOUND`), no entity with that id (`ENTITY_NOT_FOUND`), no such\nvariant under it (`VARIANT_NOT_FOUND`), or the variant has no version in effect at the\ninstant addressed (`NO_ACTIVE_VERSION`). A\nvariant whose versions are all still scheduled has none in effect, which is reported as\nsuch rather than as a missing variant. Selecting the\nversion in effect and reading it are two reads, so a delete landing between them is\nanswered `VERSION_NOT_FOUND`.\n",
4285
+ "description": "`SCHEMA_NOT_FOUND`, `ENTITY_NOT_FOUND`, `VARIANT_NOT_FOUND`, `NO_ACTIVE_VERSION`,\n`VERSION_NOT_FOUND`.\n",
4220
4286
  "content": {
4221
4287
  "application/json": {
4222
4288
  "schema": {
@@ -4226,7 +4292,7 @@
4226
4292
  }
4227
4293
  },
4228
4294
  "409": {
4229
- "description": "The version has been written since `_revision` was read (`WRITE_CONFLICT`, retryable after\nre-reading the version).\n",
4295
+ "description": "`WRITE_CONFLICT`, `VARIANT_CONDITIONS_IMMUTABLE`, `ENTITY_NOT_CONDITIONAL`.",
4230
4296
  "content": {
4231
4297
  "application/json": {
4232
4298
  "schema": {
@@ -4238,7 +4304,7 @@
4238
4304
  }
4239
4305
  },
4240
4306
  "patch": {
4241
- "description": "Changes only the fields it names on the version currently in effect.\n\nEverything the body does not mention is left as stored — the \"just nudge this number\" write. A\n`null` is a value like any other rather than a deletion; a client that wants an attribute to\nstop being overridden sends the complete snapshot without it through `PUT`.\n\nAttempting to change a pinned condition value is refused, as on every version write: a\nvariant's conditions are fixed at creation.\n",
4307
+ "description": "Changes only the fields it names on the version in effect. `null` sets a value rather than\nremoving an override; use the replace operation to remove one.\n",
4242
4308
  "operationId": "$patchActiveConditionalVariantVersion",
4243
4309
  "summary": "$patchActiveConditionalVariantVersion",
4244
4310
  "tags": [
@@ -4298,7 +4364,7 @@
4298
4364
  }
4299
4365
  },
4300
4366
  "400": {
4301
- "description": "The write cannot be applied as described: it would move the version it addresses to another\n`valid_from`, or change the conditions its variant is pinned to — both identity rather than\ncontent, and both fixed at creation. Also when `_revision` is missing or is not a revision\nmarker.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
4367
+ "description": "`VALID_FROM_IMMUTABLE`, `VALID_FROM_INVALID`, `VALUE_UNSTORABLE`, `IDENTIFIER_INVALID`,\n`ENTITY_TYPE_MISMATCH`.\n",
4302
4368
  "content": {
4303
4369
  "application/json": {
4304
4370
  "schema": {
@@ -4307,8 +4373,18 @@
4307
4373
  }
4308
4374
  }
4309
4375
  },
4376
+ "403": {
4377
+ "description": "The token names no organization this API can act for",
4378
+ "content": {
4379
+ "application/json": {
4380
+ "schema": {
4381
+ "$ref": "#/components/schemas/Error"
4382
+ }
4383
+ }
4384
+ }
4385
+ },
4310
4386
  "404": {
4311
- "description": "No such schema (`SCHEMA_NOT_FOUND`), no entity with that id (`ENTITY_NOT_FOUND`), no such\nvariant under it (`VARIANT_NOT_FOUND`), or the variant has no version in effect at the\ninstant addressed (`NO_ACTIVE_VERSION`). A\nvariant whose versions are all still scheduled has none in effect, which is reported as\nsuch rather than as a missing variant. Selecting the\nversion in effect and reading it are two reads, so a delete landing between them is\nanswered `VERSION_NOT_FOUND`.\n",
4387
+ "description": "`SCHEMA_NOT_FOUND`, `ENTITY_NOT_FOUND`, `VARIANT_NOT_FOUND`, `NO_ACTIVE_VERSION`,\n`VERSION_NOT_FOUND`.\n",
4312
4388
  "content": {
4313
4389
  "application/json": {
4314
4390
  "schema": {
@@ -4318,7 +4394,7 @@
4318
4394
  }
4319
4395
  },
4320
4396
  "409": {
4321
- "description": "The version has been written since `_revision` was read (`WRITE_CONFLICT`, retryable after\nre-reading the version).\n",
4397
+ "description": "`WRITE_CONFLICT`, `VARIANT_CONDITIONS_IMMUTABLE`, `ENTITY_NOT_CONDITIONAL`.",
4322
4398
  "content": {
4323
4399
  "application/json": {
4324
4400
  "schema": {
@@ -4330,7 +4406,7 @@
4330
4406
  }
4331
4407
  },
4332
4408
  "delete": {
4333
- "description": "Removes one variant of a conditional entity: the condition tuple it holds, its registration\nin the search index, and every version it accumulated.\n\nTwo phases. The first frees the tuple and deregisters the variant, and is what makes the\ncombination of condition values immediately reusable — the second removes the version rows in\nbatches afterwards. A response arrives only once both have finished for this request, but the\ntuple is reusable from the moment the first completes, whether or not the second did: a\nvariant with more versions than one transaction can carry is the ordinary case, not an edge\none. An interrupted delete is safe to send again; it picks up where it stopped.\n\nNothing is archived. A variant an order or contract pins stops resolving, and hydration drops\nthe reference leniently rather than failing the read.\n\nThis removes the **variant**, not one of its versions. To remove a single version, name it on\n`…/variants/{variant_id}/versions/{valid_from}` — including the one currently in effect, which\nhas no shorthand delete: a delete names the version it removes.\n",
4409
+ "description": "Removes one variant: its condition tuple, its index entry and all its versions. The tuple\nbecomes reusable, and an interrupted delete is safe to send again.\n\nOrders and contracts pinning the variant stop resolving. To remove a single version, address\nit under `versions/{valid_from}`.\n",
4334
4410
  "operationId": "$deleteConditionalVariant",
4335
4411
  "summary": "$deleteConditionalVariant",
4336
4412
  "tags": [
@@ -4380,7 +4456,7 @@
4380
4456
  }
4381
4457
  },
4382
4458
  "400": {
4383
- "description": "Invalid request, e.g. the slug names no conditional entity type.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
4459
+ "description": "`IDENTIFIER_INVALID`, `ENTITY_TYPE_MISMATCH`.",
4384
4460
  "content": {
4385
4461
  "application/json": {
4386
4462
  "schema": {
@@ -4389,8 +4465,18 @@
4389
4465
  }
4390
4466
  }
4391
4467
  },
4468
+ "403": {
4469
+ "description": "The token names no organization this API can act for",
4470
+ "content": {
4471
+ "application/json": {
4472
+ "schema": {
4473
+ "$ref": "#/components/schemas/Error"
4474
+ }
4475
+ }
4476
+ }
4477
+ },
4392
4478
  "404": {
4393
- "description": "No entity with that id (`ENTITY_NOT_FOUND`), or it has no such variant\n(`VARIANT_NOT_FOUND`).\n",
4479
+ "description": "`ENTITY_NOT_FOUND`, `VARIANT_NOT_FOUND`.",
4394
4480
  "content": {
4395
4481
  "application/json": {
4396
4482
  "schema": {
@@ -4400,7 +4486,7 @@
4400
4486
  }
4401
4487
  },
4402
4488
  "409": {
4403
- "description": "The variant's items are being written concurrently (`WRITE_CONFLICT`, retryable).",
4489
+ "description": "`WRITE_CONFLICT`, `ENTITY_NOT_CONDITIONAL`.",
4404
4490
  "content": {
4405
4491
  "application/json": {
4406
4492
  "schema": {
@@ -4414,7 +4500,7 @@
4414
4500
  },
4415
4501
  "/v1/conditional-pricing/{slug}/entities/{entity_id}/variants/{variant_id}/versions": {
4416
4502
  "get": {
4417
- "description": "Lists one variant's versions — its whole timeline, oldest first, which is what expanding a row\nof the tree loads.\n\nIts paging differs from the two variant reads: cursor paging only, no `from` and no `size`,\nand **no `hits`**.\n\nTwo paging facts a client gets wrong if it assumes otherwise. **A page may be shorter than\n`limit`, or empty, and still carry a `next`**, so a client pages until `next` is absent rather\nthan until a page looks short. And **a cursor belongs to one variant and one `order`**:\nreplaying one against another variant, or against the opposite order, is a `400` rather than a\nplausible-looking wrong page.\n\nVersions carry no `_revision` here. An editing screen re-reads the one version it is about to\nwrite through that version's own `GET`, which is strongly consistent, and writes with the\nrevision it returns.\n\n**Published ahead of the behaviour.** No handler serves this yet, so a deployed stage answers\n`501`.\n",
4503
+ "description": "Lists one variant's versions. Cursor paging only: a page may be short or empty and still\ncarry a `next`, so page until `next` is absent. A cursor is bound to one variant and one\n`order`.\n",
4418
4504
  "operationId": "$listConditionalVariantVersions",
4419
4505
  "summary": "$listConditionalVariantVersions",
4420
4506
  "tags": [
@@ -4454,7 +4540,7 @@
4454
4540
  {
4455
4541
  "in": "query",
4456
4542
  "name": "limit",
4457
- "description": "Versions per page. Defaults to 100, which is also the maximum; a larger value is clamped\nsilently. A variant's timeline is usually short enough to fit one page.\n",
4543
+ "description": "Versions per page. Defaults to 100, which is also the maximum; a larger value is clamped.",
4458
4544
  "schema": {
4459
4545
  "type": "integer",
4460
4546
  "minimum": 1,
@@ -4466,7 +4552,7 @@
4466
4552
  {
4467
4553
  "in": "query",
4468
4554
  "name": "order",
4469
- "description": "Which end of the timeline to read from: `asc` oldest first, `desc` newest first. Defaults\nto `asc`.\n\nBaked into every cursor this read issues: a cursor resumes one direction, and replaying it\nagainst the other is a `400`.\n",
4555
+ "description": "Which end of the timeline to read from. Baked into every cursor this read issues.",
4470
4556
  "schema": {
4471
4557
  "type": "string",
4472
4558
  "enum": [
@@ -4481,7 +4567,7 @@
4481
4567
  {
4482
4568
  "in": "query",
4483
4569
  "name": "cursor",
4484
- "description": "Continue from a previous response's `next`. Opaque: it encodes the position and the order\nit was issued for, and nothing a client should read or construct.\n",
4570
+ "description": "Continue from a previous response's `next`. Opaque.",
4485
4571
  "schema": {
4486
4572
  "type": "string"
4487
4573
  },
@@ -4499,7 +4585,7 @@
4499
4585
  },
4500
4586
  "examples": {
4501
4587
  "A short page that is not the last one": {
4502
- "summary": "One version and a `next` — a page shorter than `limit` says nothing about whether the timeline has ended",
4588
+ "summary": "One version and a `next` — a short page does not mean the timeline has ended",
4503
4589
  "value": {
4504
4590
  "results": [
4505
4591
  {
@@ -4523,7 +4609,7 @@
4523
4609
  }
4524
4610
  },
4525
4611
  "The last page": {
4526
- "summary": "No `next`, which is the only reliable end of the timeline",
4612
+ "summary": "No `next`, which is the end of the timeline",
4527
4613
  "value": {
4528
4614
  "results": [
4529
4615
  {
@@ -4550,7 +4636,7 @@
4550
4636
  }
4551
4637
  },
4552
4638
  "400": {
4553
- "description": "A `cursor` that is malformed, belongs to another variant, or was issued for the opposite\norder is `CURSOR_INVALID`, with `details.reason` saying which. The fix is the same for all\nthree: read the timeline again without a cursor.\n\nA `limit` below 1 and an `order` that is neither `asc` nor `desc` carry a message and\nneither `code` nor `details`; testing `code` for absence is how they are told from the one\nabove.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
4639
+ "description": "`CURSOR_INVALID`, `ENTITY_TYPE_MISMATCH`.",
4554
4640
  "content": {
4555
4641
  "application/json": {
4556
4642
  "schema": {
@@ -4559,8 +4645,18 @@
4559
4645
  }
4560
4646
  }
4561
4647
  },
4648
+ "403": {
4649
+ "description": "The token names no organization this API can act for",
4650
+ "content": {
4651
+ "application/json": {
4652
+ "schema": {
4653
+ "$ref": "#/components/schemas/Error"
4654
+ }
4655
+ }
4656
+ }
4657
+ },
4562
4658
  "404": {
4563
- "description": "No such schema (`SCHEMA_NOT_FOUND`), no such entity under it (`ENTITY_NOT_FOUND`), or the\nentity has no such variant (`VARIANT_NOT_FOUND`) — which is also the answer for a variant\nbelonging to a *different* entity: a variant id alone addresses nothing.\n\nThe entity is established before the timeline is read, so a caller who mistyped the entity\nid is never sent to fix the variant id, and one who sent the wrong slug hears about the\nslug.\n",
4659
+ "description": "`ENTITY_NOT_FOUND`, `VARIANT_NOT_FOUND`.",
4564
4660
  "content": {
4565
4661
  "application/json": {
4566
4662
  "schema": {
@@ -4569,12 +4665,12 @@
4569
4665
  }
4570
4666
  }
4571
4667
  },
4572
- "501": {
4573
- "description": "Published ahead of the behaviour. This operation is declared and not yet dispatched, so\nevery request to it is answered here until the listing behaviour lands — which is how a\ndeployed stage says \"this exists and does not work yet\" rather than answering with an\nempty page a client would read as an empty entity.\n\nRead `message`. The body is the shared `Error` shape, as it is on every 501 this API\nanswers, so one \"not built yet\" branch covers all of them.\n",
4668
+ "409": {
4669
+ "description": "`ENTITY_NOT_CONDITIONAL`.",
4574
4670
  "content": {
4575
4671
  "application/json": {
4576
4672
  "schema": {
4577
- "$ref": "#/components/schemas/Error"
4673
+ "$ref": "#/components/schemas/ConditionalPricingError"
4578
4674
  }
4579
4675
  }
4580
4676
  }
@@ -4582,7 +4678,7 @@
4582
4678
  }
4583
4679
  },
4584
4680
  "post": {
4585
- "description": "Appends a version to a variant: a new set of values taking effect at its own instant.\n\nThis is how a price changes. No version carries an end date and nothing is superseded\nexplicitly — the version in effect at an instant is simply the one with the latest `valid_from`\nat or before it, so appending a later version is the whole of \"this is the new price from then\non\". A version dated in the future is staged and excluded from resolution until its date.\n\n**A version is never refused for being late.** A `valid_from` in the past is written like any\nother and answered with warnings in `warnings` naming what it moved — what resolves now, what a\npast-dated read returns, or both. Correcting a price that took effect last week is ordinary\nwork.\n\nWhat is refused is appending at a `valid_from` the variant already has: that write means either\n\"replace it\" or \"and also this\", and only the caller knows which. The two operations both\nexist, on the dated version path.\n\nThe variant's `conditions` are its identity and are fixed at creation; they may be sent back\nunchanged but never changed.\n",
4681
+ "description": "Appends a version taking effect at its own instant. The version in effect at any instant is\nthe one with the latest `valid_from` at or before it; a future one is staged until its date.\n\nA past `valid_from` is accepted and reported in `warnings`. One the variant already has is\nrefused — replace or patch that version instead.\n",
4586
4682
  "operationId": "$appendConditionalVariantVersion",
4587
4683
  "summary": "$appendConditionalVariantVersion",
4588
4684
  "tags": [
@@ -4642,7 +4738,7 @@
4642
4738
  }
4643
4739
  },
4644
4740
  "400": {
4645
- "description": "The version cannot be appended as described: the body would change the variant's conditions,\nor `valid_from` is not a timestamp this store can sort by.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
4741
+ "description": "`VALID_FROM_INVALID`, `VALUE_UNSTORABLE`, `IDENTIFIER_INVALID`, `ENTITY_TYPE_MISMATCH`.",
4646
4742
  "content": {
4647
4743
  "application/json": {
4648
4744
  "schema": {
@@ -4651,8 +4747,18 @@
4651
4747
  }
4652
4748
  }
4653
4749
  },
4750
+ "403": {
4751
+ "description": "The token names no organization this API can act for",
4752
+ "content": {
4753
+ "application/json": {
4754
+ "schema": {
4755
+ "$ref": "#/components/schemas/Error"
4756
+ }
4757
+ }
4758
+ }
4759
+ },
4654
4760
  "404": {
4655
- "description": "No such schema (`SCHEMA_NOT_FOUND`), no entity with that id (`ENTITY_NOT_FOUND`), or no\nsuch variant under it (`VARIANT_NOT_FOUND`).\n",
4761
+ "description": "`SCHEMA_NOT_FOUND`, `ENTITY_NOT_FOUND`, `VARIANT_NOT_FOUND`.",
4656
4762
  "content": {
4657
4763
  "application/json": {
4658
4764
  "schema": {
@@ -4662,7 +4768,7 @@
4662
4768
  }
4663
4769
  },
4664
4770
  "409": {
4665
- "description": "The variant already has a version at that `valid_from` (`VERSION_CONFLICT`) — append means\nappend, never an implicit overwrite. Replace or patch that version instead.\n",
4771
+ "description": "`VERSION_CONFLICT`, `VARIANT_CONDITIONS_IMMUTABLE`, `ENTITY_NOT_CONDITIONAL`.",
4666
4772
  "content": {
4667
4773
  "application/json": {
4668
4774
  "schema": {
@@ -4676,7 +4782,7 @@
4676
4782
  },
4677
4783
  "/v1/conditional-pricing/{slug}/entities/{entity_id}/variants/{variant_id}/versions/{valid_from}": {
4678
4784
  "get": {
4679
- "description": "Returns one specific version of a variant, by the instant it takes effect — what a form editing\nthat version loads.\n\nExact, never nearest: an instant the variant has no version at is a not-found rather than the\nversion that would be in effect at it. That question is the shorthand read's, or `:resolve`'s.\n",
4785
+ "description": "Returns one version by the instant it takes effect. Exact, never nearest.",
4680
4786
  "operationId": "$getConditionalVariantVersion",
4681
4787
  "summary": "$getConditionalVariantVersion",
4682
4788
  "tags": [
@@ -4716,7 +4822,7 @@
4716
4822
  {
4717
4823
  "in": "path",
4718
4824
  "name": "valid_from",
4719
- "description": "The version to address, by the instant it takes effect.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time\n(`2026-01-01T00:00:00Z`), to at most millisecond precision. Written any accepted way: it is\ncanonicalized before it is matched, so the spelling a read returned and the spelling a\nhuman typed address the same version.\n",
4825
+ "description": "The version to address, by the instant it takes effect. An RFC 3339 date\n(`2026-01-01`, read as midnight UTC) or date-time, to at most millisecond precision,\ncanonicalized before it is matched.\n",
4720
4826
  "schema": {
4721
4827
  "type": "string"
4722
4828
  },
@@ -4736,7 +4842,7 @@
4736
4842
  }
4737
4843
  },
4738
4844
  "400": {
4739
- "description": "Invalid request, e.g. a `valid_from` that is not a timestamp this store can sort by.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
4845
+ "description": "`VALID_FROM_INVALID`, `IDENTIFIER_INVALID`, `ENTITY_TYPE_MISMATCH`.",
4740
4846
  "content": {
4741
4847
  "application/json": {
4742
4848
  "schema": {
@@ -4745,8 +4851,28 @@
4745
4851
  }
4746
4852
  }
4747
4853
  },
4854
+ "403": {
4855
+ "description": "The token names no organization this API can act for",
4856
+ "content": {
4857
+ "application/json": {
4858
+ "schema": {
4859
+ "$ref": "#/components/schemas/Error"
4860
+ }
4861
+ }
4862
+ }
4863
+ },
4748
4864
  "404": {
4749
- "description": "No entity with that id (`ENTITY_NOT_FOUND`), no such variant under this schema\n(`VARIANT_NOT_FOUND`), or no version at that instant (`VERSION_NOT_FOUND`).\n",
4865
+ "description": "`ENTITY_NOT_FOUND`, `VARIANT_NOT_FOUND`, `VERSION_NOT_FOUND`.",
4866
+ "content": {
4867
+ "application/json": {
4868
+ "schema": {
4869
+ "$ref": "#/components/schemas/ConditionalPricingError"
4870
+ }
4871
+ }
4872
+ }
4873
+ },
4874
+ "409": {
4875
+ "description": "`ENTITY_NOT_CONDITIONAL`.",
4750
4876
  "content": {
4751
4877
  "application/json": {
4752
4878
  "schema": {
@@ -4758,7 +4884,7 @@
4758
4884
  }
4759
4885
  },
4760
4886
  "put": {
4761
- "description": "Replaces one version's values wholesale, addressed by its `valid_from`.\n\nEditable whatever its date, scheduled or past. Writing a superseded version is answered with a\nwarning naming what a past-dated read now returns; it is not refused.\n\nAttributes the variant may not override are not applied where the body carries them, and their\nstored value is preserved rather than dropped.\n",
4887
+ "description": "Replaces one version's values, whatever its date. Attributes the variant may not override\nkeep their stored value. Writing a superseded version is reported in `warnings`.\n",
4762
4888
  "operationId": "$replaceConditionalVariantVersion",
4763
4889
  "summary": "$replaceConditionalVariantVersion",
4764
4890
  "tags": [
@@ -4798,7 +4924,7 @@
4798
4924
  {
4799
4925
  "in": "path",
4800
4926
  "name": "valid_from",
4801
- "description": "The version to address, by the instant it takes effect.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time\n(`2026-01-01T00:00:00Z`), to at most millisecond precision. Written any accepted way: it is\ncanonicalized before it is matched, so the spelling a read returned and the spelling a\nhuman typed address the same version.\n",
4927
+ "description": "The version to address, by the instant it takes effect. An RFC 3339 date\n(`2026-01-01`, read as midnight UTC) or date-time, to at most millisecond precision,\ncanonicalized before it is matched.\n",
4802
4928
  "schema": {
4803
4929
  "type": "string"
4804
4930
  },
@@ -4828,7 +4954,7 @@
4828
4954
  }
4829
4955
  },
4830
4956
  "400": {
4831
- "description": "The write cannot be applied as described: it would move the version it addresses to another\n`valid_from`, or change the conditions its variant is pinned to — both identity rather than\ncontent, and both fixed at creation. Also when `_revision` is missing or is not a revision\nmarker.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
4957
+ "description": "`VALID_FROM_IMMUTABLE`, `VALID_FROM_INVALID`, `VALUE_UNSTORABLE`, `IDENTIFIER_INVALID`,\n`ENTITY_TYPE_MISMATCH`.\n",
4832
4958
  "content": {
4833
4959
  "application/json": {
4834
4960
  "schema": {
@@ -4837,8 +4963,18 @@
4837
4963
  }
4838
4964
  }
4839
4965
  },
4966
+ "403": {
4967
+ "description": "The token names no organization this API can act for",
4968
+ "content": {
4969
+ "application/json": {
4970
+ "schema": {
4971
+ "$ref": "#/components/schemas/Error"
4972
+ }
4973
+ }
4974
+ }
4975
+ },
4840
4976
  "404": {
4841
- "description": "No such schema (`SCHEMA_NOT_FOUND`), no entity with that id (`ENTITY_NOT_FOUND`), no such\nvariant under it (`VARIANT_NOT_FOUND`), or no version at that instant\n(`VERSION_NOT_FOUND`).\n",
4977
+ "description": "`SCHEMA_NOT_FOUND`, `ENTITY_NOT_FOUND`, `VARIANT_NOT_FOUND`, `VERSION_NOT_FOUND`.\n",
4842
4978
  "content": {
4843
4979
  "application/json": {
4844
4980
  "schema": {
@@ -4848,7 +4984,7 @@
4848
4984
  }
4849
4985
  },
4850
4986
  "409": {
4851
- "description": "The version has been written since `_revision` was read (`WRITE_CONFLICT`, retryable after\nre-reading the version).\n",
4987
+ "description": "`WRITE_CONFLICT`, `VARIANT_CONDITIONS_IMMUTABLE`, `ENTITY_NOT_CONDITIONAL`.",
4852
4988
  "content": {
4853
4989
  "application/json": {
4854
4990
  "schema": {
@@ -4860,7 +4996,7 @@
4860
4996
  }
4861
4997
  },
4862
4998
  "patch": {
4863
- "description": "Changes only the fields it names on one version, addressed by its `valid_from`.\n\nEverything the body does not mention is left as stored. A partial update that tries to change a\npinned condition value is refused: condition values are immutable after a variant is created.\n",
4999
+ "description": "Changes only the fields it names on one version.",
4864
5000
  "operationId": "$patchConditionalVariantVersion",
4865
5001
  "summary": "$patchConditionalVariantVersion",
4866
5002
  "tags": [
@@ -4900,7 +5036,7 @@
4900
5036
  {
4901
5037
  "in": "path",
4902
5038
  "name": "valid_from",
4903
- "description": "The version to address, by the instant it takes effect.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time\n(`2026-01-01T00:00:00Z`), to at most millisecond precision. Written any accepted way: it is\ncanonicalized before it is matched, so the spelling a read returned and the spelling a\nhuman typed address the same version.\n",
5039
+ "description": "The version to address, by the instant it takes effect. An RFC 3339 date\n(`2026-01-01`, read as midnight UTC) or date-time, to at most millisecond precision,\ncanonicalized before it is matched.\n",
4904
5040
  "schema": {
4905
5041
  "type": "string"
4906
5042
  },
@@ -4930,7 +5066,7 @@
4930
5066
  }
4931
5067
  },
4932
5068
  "400": {
4933
- "description": "The write cannot be applied as described: it would move the version it addresses to another\n`valid_from`, or change the conditions its variant is pinned to — both identity rather than\ncontent, and both fixed at creation. Also when `_revision` is missing or is not a revision\nmarker.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
5069
+ "description": "`VALID_FROM_IMMUTABLE`, `VALID_FROM_INVALID`, `VALUE_UNSTORABLE`, `IDENTIFIER_INVALID`,\n`ENTITY_TYPE_MISMATCH`.\n",
4934
5070
  "content": {
4935
5071
  "application/json": {
4936
5072
  "schema": {
@@ -4939,8 +5075,18 @@
4939
5075
  }
4940
5076
  }
4941
5077
  },
5078
+ "403": {
5079
+ "description": "The token names no organization this API can act for",
5080
+ "content": {
5081
+ "application/json": {
5082
+ "schema": {
5083
+ "$ref": "#/components/schemas/Error"
5084
+ }
5085
+ }
5086
+ }
5087
+ },
4942
5088
  "404": {
4943
- "description": "No such schema (`SCHEMA_NOT_FOUND`), no entity with that id (`ENTITY_NOT_FOUND`), no such\nvariant under it (`VARIANT_NOT_FOUND`), or no version at that instant\n(`VERSION_NOT_FOUND`).\n",
5089
+ "description": "`SCHEMA_NOT_FOUND`, `ENTITY_NOT_FOUND`, `VARIANT_NOT_FOUND`, `VERSION_NOT_FOUND`.\n",
4944
5090
  "content": {
4945
5091
  "application/json": {
4946
5092
  "schema": {
@@ -4950,7 +5096,7 @@
4950
5096
  }
4951
5097
  },
4952
5098
  "409": {
4953
- "description": "The version has been written since `_revision` was read (`WRITE_CONFLICT`, retryable after\nre-reading the version).\n",
5099
+ "description": "`WRITE_CONFLICT`, `VARIANT_CONDITIONS_IMMUTABLE`, `ENTITY_NOT_CONDITIONAL`.",
4954
5100
  "content": {
4955
5101
  "application/json": {
4956
5102
  "schema": {
@@ -4962,7 +5108,7 @@
4962
5108
  }
4963
5109
  },
4964
5110
  "delete": {
4965
- "description": "Removes one version of a variant.\n\nWithdrawing a scheduled adjustment is what this is for, and deleting a future version warns\nabout nothing — nothing that has resolved, or could have resolved, changes. Deleting a version\nthat has taken effect is allowed too and answered with a warning: it changes what a past-dated\nread returns, and if it was the version in effect it changes what resolves now.\n\n**A variant's last remaining version cannot be deleted.** Such a variant would still hold its\ncondition tuple and still be selectable, and then resolve to nothing — which is a variant delete\nwearing a version delete's clothes. Delete the variant instead; that frees the tuple too.\n\nThe variant itself is untouched: it keeps its conditions, its tuple and its place in the index.\n",
5111
+ "description": "Removes one version. What the removal moves is reported in `warnings`. A variant's last\nremaining version cannot be removed — delete the variant instead.\n",
4966
5112
  "operationId": "$deleteConditionalVariantVersion",
4967
5113
  "summary": "$deleteConditionalVariantVersion",
4968
5114
  "tags": [
@@ -5002,7 +5148,7 @@
5002
5148
  {
5003
5149
  "in": "path",
5004
5150
  "name": "valid_from",
5005
- "description": "The version to address, by the instant it takes effect.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time\n(`2026-01-01T00:00:00Z`), to at most millisecond precision. Written any accepted way: it is\ncanonicalized before it is matched, so the spelling a read returned and the spelling a\nhuman typed address the same version.\n",
5151
+ "description": "The version to address, by the instant it takes effect. An RFC 3339 date\n(`2026-01-01`, read as midnight UTC) or date-time, to at most millisecond precision,\ncanonicalized before it is matched.\n",
5006
5152
  "schema": {
5007
5153
  "type": "string"
5008
5154
  },
@@ -5012,7 +5158,7 @@
5012
5158
  {
5013
5159
  "in": "query",
5014
5160
  "name": "_revision",
5015
- "description": "The revision marker read from the version being deleted. The delete is refused if the\nversion has been written since.\n\nThe same marker the write bodies carry as `_revision`.\n",
5161
+ "description": "The revision read from the version being deleted. The delete is refused if the version\nhas been written since.\n",
5016
5162
  "schema": {
5017
5163
  "type": "integer",
5018
5164
  "minimum": 1
@@ -5033,7 +5179,7 @@
5033
5179
  }
5034
5180
  },
5035
5181
  "400": {
5036
- "description": "The version cannot be removed because it is the variant's only one\n(`LAST_VERSION_UNDELETABLE`) — such a variant would keep its condition tuple, stay\nselectable and resolve to nothing, so delete the variant instead, which frees the tuple\ntoo.\n\nThe refusals around it carry no code, and testing `code` for absence is how they are told\nfrom it: a missing or unreadable `_revision`, a `valid_from` this store cannot sort by,\nand an id this store cannot key by.\n\nAlso refused here: an entity id belonging to another type than the slug names\n(`ENTITY_TYPE_MISMATCH`), and an entity that was never created as a conditional one\n(`ENTITY_NOT_CONDITIONAL`).\n",
5182
+ "description": "`VALID_FROM_INVALID`, `IDENTIFIER_INVALID`, `ENTITY_TYPE_MISMATCH`.",
5037
5183
  "content": {
5038
5184
  "application/json": {
5039
5185
  "schema": {
@@ -5042,8 +5188,18 @@
5042
5188
  }
5043
5189
  }
5044
5190
  },
5191
+ "403": {
5192
+ "description": "The token names no organization this API can act for",
5193
+ "content": {
5194
+ "application/json": {
5195
+ "schema": {
5196
+ "$ref": "#/components/schemas/Error"
5197
+ }
5198
+ }
5199
+ }
5200
+ },
5045
5201
  "404": {
5046
- "description": "No entity with that id (`ENTITY_NOT_FOUND`), no such variant under this schema\n(`VARIANT_NOT_FOUND`), or no version at that instant (`VERSION_NOT_FOUND`).\n",
5202
+ "description": "`ENTITY_NOT_FOUND`, `VARIANT_NOT_FOUND`, `VERSION_NOT_FOUND`.",
5047
5203
  "content": {
5048
5204
  "application/json": {
5049
5205
  "schema": {
@@ -5053,7 +5209,7 @@
5053
5209
  }
5054
5210
  },
5055
5211
  "409": {
5056
- "description": "The version has been written since `_revision` was read (`WRITE_CONFLICT`, retryable after\nre-reading the version).\n",
5212
+ "description": "`LAST_VERSION_UNDELETABLE`, `WRITE_CONFLICT`, `ENTITY_NOT_CONDITIONAL`.",
5057
5213
  "content": {
5058
5214
  "application/json": {
5059
5215
  "schema": {
@@ -5067,7 +5223,7 @@
5067
5223
  },
5068
5224
  "/v1/conditional-pricing/{slug}/variants:batchUpsert": {
5069
5225
  "post": {
5070
- "description": "Writes up to 100 variants or versions in one call — the endpoint a bulk importer drives a\nrefresh cycle through, so hundreds of thousands of keys are a stream of calls rather than a\ncall per key.\n\n**One schema in the path, one entity per item.** A single call may name several entities, so\nit can refresh a whole tariff hierarchy — a composite price and its components together —\nand the entity id rides each item instead of the path.\n\n**An item addresses a condition tuple, never a `variant_id`.** An upsert creates a variant\nthat has no id yet. The id it created, or found, is on the result entry.\n\nEach item's outcome is derived from what is stored, with no mode for the caller to declare,\nin this order: an unknown tuple is `variant_created`, a known tuple with no version at the\nitem's `valid_from` is `version_created`, and an existing version at that exact instant is\n`updated` — or `skipped`, which is reserved for a write whose values are identical to what is\nstored, so re-running an unchanged import still reads as a no-op. `version_created` is\ndistinct from `variant_created` so an importer's counts can tell \"new postal codes appeared\"\nfrom \"existing variants got their scheduled adjustment\".\n\nAn item without `valid_from` is a current-state, last-write-wins write, and has no `skipped`\ndetection. A `valid_from` in the past changes nothing about the outcome — it is written like\nany other and answered with the timeline warnings on that item. An importer stamping one\n`valid_from` across a batch therefore sees backdate warnings on every item; omitting the\nfield is how it avoids them.\n\n**Items addressing the same variant apply in array order; items addressing different\nvariants are processed in parallel.** Here \"the same variant\" is the same `(entity_id,\ncondition tuple)`, so two items sharing a tuple and a `valid_from` apply in order and the\nlast one wins. There is no cross-item rollback.\n\n**This write is unguarded.** No `_revision` is accepted on an item or returned on an entry;\nan editing screen that needs a guard re-reads the one version it is about to write through\nits own `GET`.\n\nThree refusals a client would otherwise expect do not occur here. `VERSION_CONFLICT` never\ndoes: an existing `valid_from` is a replacement rather than a collision. A tuple-uniqueness\nguard lost to a concurrent writer is not reported as `TUPLE_CONFLICT` either — the item is\nre-read against current state and re-derived through the same outcome order above, which is\ncontent-aware. `WRITE_CONFLICT` marks the case that is genuinely worth retrying: transient\ncontention on one entity's rows.\n\n**Published ahead of the behaviour.** No handler serves this yet, so a deployed stage answers\n`501`. The examples below are what an importer builds against in the meantime.\n",
5226
+ "description": "Writes up to 100 variants or versions in one call. Each item names its own entity, so one\ncall can span a tariff hierarchy, and addresses a variant by condition tuple rather than by\nid — the id it created or found is on the result entry.\n\nEach item's outcome is derived from what is stored: an unknown tuple is `variant_created`, a\nknown tuple with no version at the item's `valid_from` is `version_created`, an existing\nversion there is `updated`, and a write matching what is stored is `skipped`. An item\nwithout `valid_from` is a last-write-wins write with no `skipped` detection.\n\nItems addressing the same `(entity_id, conditions)` apply in array order; the rest run in\nparallel. No cross-item rollback, and no `_revision` guard.\n",
5071
5227
  "operationId": "$batchUpsertConditionalVariants",
5072
5228
  "summary": "$batchUpsertConditionalVariants",
5073
5229
  "tags": [
@@ -5164,7 +5320,7 @@
5164
5320
  },
5165
5321
  "responses": {
5166
5322
  "200": {
5167
- "description": "What every item did, in request order, and a count per outcome.\n\n`200` whatever the per-item outcomes: a batch that processed 100 items and failed 99 did\nits job, and `counts` says what happened.\n",
5323
+ "description": "What every item did, in request order, and a count per outcome. Always `200`, whatever\nthe per-item outcomes.\n",
5168
5324
  "content": {
5169
5325
  "application/json": {
5170
5326
  "schema": {
@@ -5172,7 +5328,7 @@
5172
5328
  },
5173
5329
  "examples": {
5174
5330
  "Every outcome once": {
5175
- "summary": "The five items above, in order — the created variant's id is the one an order pins, and item five failed on its own without touching the rest",
5331
+ "summary": "The five items above, in order",
5176
5332
  "value": {
5177
5333
  "correlation_id": "tariff-refresh-2027-01",
5178
5334
  "counts": {
@@ -5258,7 +5414,7 @@
5258
5414
  }
5259
5415
  },
5260
5416
  "400": {
5261
- "description": "The envelope cannot be processed at all: more than 100 items, an empty `items`, a slug\nthat names no conditional entity type, or a body this schema rejects outright.\n\nAll of those are request-validation failures, so they carry a message and neither `code`\nnor `details`. Every failure of an individual item is on that item's result entry\ninstead, `ENTITY_NOT_FOUND` included — the entity id is on the item and not in the path,\nso one wrong id in a source file cannot fail the other 99 rows. `ENTITY_TYPE_MISMATCH`\nand `ENTITY_NOT_CONDITIONAL` are per item for the same reason: each item names its own\nentity, and each entity is checked against the slug and for `is_conditional` on its own.\n",
5417
+ "description": "The envelope cannot be processed: more than 100 items, an empty `items`, a slug that\nnames no conditional entity type, or a body this schema rejects. No `code` is carried —\nan individual item's failure is on its own result entry.\n",
5262
5418
  "content": {
5263
5419
  "application/json": {
5264
5420
  "schema": {
@@ -5267,22 +5423,22 @@
5267
5423
  }
5268
5424
  }
5269
5425
  },
5270
- "404": {
5271
- "description": "No such schema (`SCHEMA_NOT_FOUND`) — the one lookup the whole call depends on, since it\nis what the items are validated against.\n",
5426
+ "403": {
5427
+ "description": "The token names no organization this API can act for",
5272
5428
  "content": {
5273
5429
  "application/json": {
5274
5430
  "schema": {
5275
- "$ref": "#/components/schemas/ConditionalPricingError"
5431
+ "$ref": "#/components/schemas/Error"
5276
5432
  }
5277
5433
  }
5278
5434
  }
5279
5435
  },
5280
- "501": {
5281
- "description": "Published ahead of the behaviour. This operation is declared and not yet dispatched, so\nevery request to it is answered here until the batch write behaviour lands — which is how\na deployed stage says \"this exists and does not work yet\" rather than answering with\ncounts a client would read as a completed import.\n\nRead `message`. The body is the shared `Error` shape, as it is on every 501 this API\nanswers, so one \"not built yet\" branch covers all of them.\n",
5436
+ "404": {
5437
+ "description": "`SCHEMA_NOT_FOUND`.",
5282
5438
  "content": {
5283
5439
  "application/json": {
5284
5440
  "schema": {
5285
- "$ref": "#/components/schemas/Error"
5441
+ "$ref": "#/components/schemas/ConditionalPricingError"
5286
5442
  }
5287
5443
  }
5288
5444
  }
@@ -5292,7 +5448,7 @@
5292
5448
  },
5293
5449
  "/v1/conditional-pricing/{slug}/variants:batchDelete": {
5294
5450
  "post": {
5295
- "description": "Removes up to 100 variants or versions in one call — the symmetric bulk withdrawal, so\nretiring a generation of variants, or a scheduled adjustment across many of them, is as\ncheap as creating it was.\n\nThe noun is `variants` on both batch endpoints, although an item carrying `valid_from`\nremoves one **version** rather than the variant: an item without it removes the whole variant\n— its condition tuple, its registration in the index, and every version it accumulated — and\nan item with it removes exactly that version, under the single-item rules. A future version\nand a superseded one are both deletable and both answered with the warnings that say what\nmoved; a variant's last remaining version is refused\n(`LAST_VERSION_UNDELETABLE`), because such a variant would still hold its tuple and still\nresolve to nothing. Delete the variant instead.\n\n**An item addresses its variant one of two ways, and never both**: by `variant_id`, or by the\ncondition tuple it pins. Use ids once the schema has drifted: a tuple naming a condition the\nschema no longer declares cannot be canonicalized, so it addresses nothing. An item naming\nboth fails validation and is an envelope `400`, not a per-item error: the request validator\nrejects the body before any item runs.\n\n**`entity_id` is required beside a `variant_id`, and is not redundant.** A variant id alone\naddresses nothing in this API.\n\n**Items addressing the same variant apply in array order; items addressing different variants\nare processed in parallel.** Because an item addresses its variant two ways, \"the same\nvariant\" is decided after addressing, in three steps: every condition tuple is resolved to a\nvariant id, items are grouped by that id, and each group is applied in array order. So one\ncall may hold an item naming `var-46045` and an item naming the tuple that variant pins, and\nthe guarantee holds across both. There is no cross-item rollback.\n\n**An item that addresses nothing is `skipped` — but only when the variant or the version is\nwhat is missing.** A missing *entity* is a per-item `ENTITY_NOT_FOUND`.\n\nAn interrupted call is safe to send again. A whole-variant delete frees the tuple in its\nfirst phase and removes the version rows afterwards, so a re-run picks up where it stopped\nand reports `skipped` for what has already gone. Nothing is archived: a variant an order or\ncontract pins stops resolving, and a pinned `:resolve` naming it answers `VARIANT_NOT_FOUND`.\n\n**Published ahead of the behaviour.** No handler serves this yet, so a deployed stage answers\n`501`.\n",
5451
+ "description": "Removes up to 100 variants or versions in one call. An item carrying `valid_from` removes\nthat version; one without it removes the whole variant.\n\nEach item addresses its variant by `variant_id` beside its `entity_id`, or by the condition\ntuple it pins, never both. Use ids once a condition has left the schema, since its tuple can\nno longer be canonicalized.\n\nItems addressing the same variant apply in array order, resolved to ids first; the rest run\nin parallel. An item addressing a missing variant or version is `skipped`, and the call is\nsafe to send again.\n",
5296
5452
  "operationId": "$batchDeleteConditionalVariants",
5297
5453
  "summary": "$batchDeleteConditionalVariants",
5298
5454
  "tags": [
@@ -5350,7 +5506,7 @@
5350
5506
  },
5351
5507
  "responses": {
5352
5508
  "200": {
5353
- "description": "What every item did, in request order, and a count per outcome.\n\n`200` whatever the per-item outcomes: the call did its job, and `counts` says what\nhappened.\n",
5509
+ "description": "What every item did, in request order, and a count per outcome. Always `200`, whatever\nthe per-item outcomes.\n",
5354
5510
  "content": {
5355
5511
  "application/json": {
5356
5512
  "schema": {
@@ -5358,7 +5514,7 @@
5358
5514
  },
5359
5515
  "examples": {
5360
5516
  "Every outcome once": {
5361
- "summary": "The three items above, in order — the second addressed a tuple no variant pins, so it names no variant to have skipped",
5517
+ "summary": "The three items above, in order — the second addressed a tuple no variant pins",
5362
5518
  "value": {
5363
5519
  "correlation_id": "postal-code-cleanup-2026-09",
5364
5520
  "counts": {
@@ -5411,7 +5567,7 @@
5411
5567
  }
5412
5568
  },
5413
5569
  "400": {
5414
- "description": "The envelope cannot be processed at all: more than 100 items, an empty `items`, a slug\nthat names no conditional entity type, an item naming both a `variant_id` and a condition\ntuple, or a body this schema rejects outright.\n\nAll of those are request-validation failures, so they carry a message and neither `code`\nnor `details`. Every failure of an individual item is on that item's result entry\ninstead, `ENTITY_NOT_FOUND` included, along with the per-entity `ENTITY_TYPE_MISMATCH`\nand `ENTITY_NOT_CONDITIONAL` — each item names its own entity, so each is checked on its\nown.\n",
5570
+ "description": "The envelope cannot be processed: more than 100 items, an empty `items`, a slug that\nnames no conditional entity type, an item naming both a `variant_id` and a condition\ntuple, or a body this schema rejects. No `code` is carried — an individual item's\nfailure is on its own result entry.\n",
5415
5571
  "content": {
5416
5572
  "application/json": {
5417
5573
  "schema": {
@@ -5420,22 +5576,22 @@
5420
5576
  }
5421
5577
  }
5422
5578
  },
5423
- "404": {
5424
- "description": "No such schema (`SCHEMA_NOT_FOUND`) — the one lookup the whole call depends on.\n",
5579
+ "403": {
5580
+ "description": "The token names no organization this API can act for",
5425
5581
  "content": {
5426
5582
  "application/json": {
5427
5583
  "schema": {
5428
- "$ref": "#/components/schemas/ConditionalPricingError"
5584
+ "$ref": "#/components/schemas/Error"
5429
5585
  }
5430
5586
  }
5431
5587
  }
5432
5588
  },
5433
- "501": {
5434
- "description": "Published ahead of the behaviour. This operation is declared and not yet dispatched, so\nevery request to it is answered here until the batch delete behaviour lands — which is\nhow a deployed stage says \"this exists and does not work yet\" rather than answering with\ncounts a client would read as a completed cleanup.\n\nRead `message`. The body is the shared `Error` shape, as it is on every 501 this API\nanswers, so one \"not built yet\" branch covers all of them.\n",
5589
+ "404": {
5590
+ "description": "`SCHEMA_NOT_FOUND`.",
5435
5591
  "content": {
5436
5592
  "application/json": {
5437
5593
  "schema": {
5438
- "$ref": "#/components/schemas/Error"
5594
+ "$ref": "#/components/schemas/ConditionalPricingError"
5439
5595
  }
5440
5596
  }
5441
5597
  }
@@ -5468,7 +5624,7 @@
5468
5624
  },
5469
5625
  "ConditionalEntitySlug": {
5470
5626
  "type": "string",
5471
- "description": "Schema slug of an entity type that can be conditional — the `{slug}` of every\nconditional-pricing route.\n",
5627
+ "description": "Schema slug of an entity type that can be conditional — the `{slug}` of every conditional-pricing route.",
5472
5628
  "enum": [
5473
5629
  "product",
5474
5630
  "price",
@@ -5477,7 +5633,7 @@
5477
5633
  },
5478
5634
  "ConditionType": {
5479
5635
  "type": "string",
5480
- "description": "The kind of value a condition holds, which decides how a variant's pinned value is matched\nagainst a resolve context.\n\n- `string`: an arbitrary string, matched exactly and case-sensitively\n- `number`: a numeric value\n- `date`: a single date\n- `daterange`: a window with a from and an until timestamp; both ends may be left open\n- `boolean`: a true/false value\n- `select`: one of the values declared in `options`, which is always a closed vocabulary\n- `location`: a geographic value, shaped by `format`\n\nThere is no condition type for the fallback variant. Being the entity's fallback is a\nproperty of the variant, set by the `default` flag on a variant write, and needs nothing\ndeclared in the schema.\n",
5636
+ "description": "The kind of value a condition holds, which decides how a pinned value is matched against a\nresolve context.\n\n- `string`: an arbitrary string, matched exactly and case-sensitively\n- `number`: a numeric value\n- `date`: a single date\n- `daterange`: a window with a from and an until timestamp; either end may be left open\n- `boolean`: a true/false value\n- `select`: one of the values declared in `options`, which is always a closed vocabulary\n- `location`: a geographic value, shaped by `format`\n",
5481
5637
  "enum": [
5482
5638
  "string",
5483
5639
  "number",
@@ -5490,7 +5646,7 @@
5490
5646
  },
5491
5647
  "ConditionDefinition": {
5492
5648
  "type": "object",
5493
- "description": "One condition dimension, in the shape a schema's `conditions` array holds it — copy it in\nverbatim.\n",
5649
+ "description": "One condition dimension, in the shape a schema's `conditions` array holds it — copy it in verbatim.",
5494
5650
  "required": [
5495
5651
  "id",
5496
5652
  "name",
@@ -5501,12 +5657,12 @@
5501
5657
  "id": {
5502
5658
  "type": "string",
5503
5659
  "format": "uuid",
5504
- "description": "Stable identity of the condition, round-tripped unchanged for the lifetime of the\ncondition: it is what tells a rename apart from a remove plus an add. The Entity API\nmints none of its own, so whoever creates a condition supplies one — a catalog condition\narrives with the identity the catalog gives it, the same in every org, and is copied into\nthe schema along with the rest of the object.\n",
5660
+ "description": "Stable identity of the condition, supplied on creation and round-tripped unchanged. A\ncatalog condition keeps the id the catalog gives it.\n",
5505
5661
  "example": "d5839b94-ba20-4225-a78e-76951d352bd6"
5506
5662
  },
5507
5663
  "name": {
5508
5664
  "type": "string",
5509
- "description": "How variants and resolve contexts refer to this condition. Independent of attribute\nnames: a value needed as an attribute too is duplicated onto the variant.\n\n`default`, and any name beginning with `_`, are reserved for the server: a condition\ndeclared under one is ignored, since nothing could pin it and no context could address it.\n",
5665
+ "description": "How variants and resolve contexts refer to this condition, independent of attribute\nnames. `default` and names beginning with `_` are reserved and are ignored here.\n",
5510
5666
  "example": "postal_code"
5511
5667
  },
5512
5668
  "label": {
@@ -5519,7 +5675,7 @@
5519
5675
  },
5520
5676
  "options": {
5521
5677
  "type": "array",
5522
- "description": "The declared vocabulary of a `select` condition. Absent for every other type.\n\nThe same shape a `select` condition's `options` has on the Entity API, item for item: an\nentry is either the value itself or an object carrying that value and an optional display\n`title`. A `title` is never pinned by a variant and never matched — two entries differing\nonly in their title are one vocabulary entry.\n\nThe vocabulary is always closed: a condition carries no flag widening it, so a pinned\nvalue outside a declared vocabulary is rejected with `CONDITION_VALUE_INVALID`. A\nvocabulary that declares nothing is closed too — while `options` is absent or empty, or\nholds nothing this deploy can read, the condition admits no pin at all and the same code\nis returned with an empty `options`. It is *not* enforced on resolve — a vocabulary says\nwhat may be stored, not what may be asked for, so a context value outside it is a query\nthat simply matches nothing.\n",
5678
+ "description": "The vocabulary of a `select` condition, absent for every other type. Each entry is the\nvalue itself or an object carrying that value and a display `title`, which is never\nmatched.\n\nAlways closed: a pinned value outside it is `CONDITION_VALUE_INVALID`, and while\n`options` is absent or empty the condition admits no pin at all. Not enforced on\nresolve, where a context value outside the vocabulary matches nothing.\n",
5523
5679
  "items": {
5524
5680
  "anyOf": [
5525
5681
  {
@@ -5600,7 +5756,7 @@
5600
5756
  "properties": {
5601
5757
  "results": {
5602
5758
  "type": "array",
5603
- "description": "The condition sets built in for the requested entity type, in the order they are offered.\n",
5759
+ "description": "The condition sets built in for the requested entity type, in the order they are offered.",
5604
5760
  "items": {
5605
5761
  "$ref": "#/components/schemas/ConditionSet"
5606
5762
  }
@@ -5609,7 +5765,7 @@
5609
5765
  },
5610
5766
  "ConditionalPricingErrorCode": {
5611
5767
  "type": "string",
5612
- "description": "Machine-readable failure mode of a conditional-pricing operation, allowing clients\nto branch on the kind of failure instead of parsing the error message.\n\n- `SCHEMA_NOT_FOUND` (404): no conditional entity type by that slug\n- `ENTITY_NOT_FOUND` (404): the schema holds no entity with that id\n- `ENTITY_TYPE_MISMATCH` (400): that id belongs to an entity of another type than the slug named\n- `ENTITY_NOT_CONDITIONAL` (400): the entity is of the right type but was not created as a conditional one\n- `VARIANT_NOT_FOUND` (404): the entity has no such variant\n- `VERSION_NOT_FOUND` (404): the variant has no version at that `valid_from`\n- `NO_MATCHES` (404): nothing applied to the context and the entity has no `default` variant\n- `NO_ACTIVE_VERSION` (404): the variant has no version in effect at the instant asked about\n- `AMBIGUOUS_RESOLUTION` (409): several variants match the given context while a single result was requested\n- `TUPLE_CONFLICT` (409): the condition tuple is already claimed by another variant\n- `VERSION_CONFLICT` (409): a version already exists at the given `valid_from` on that variant\n- `CONDITION_UNDEFINED` (400): a resolve context, a listing filter or a variant's pins name a condition the entity's schema does not define\n- `OPERATOR_UNSUPPORTED` (400): the requested operator is not applicable to the condition's type\n- `CONTEXT_FORMAT_INVALID` (400): a resolve context or listing filter value is malformed for its condition type\n- `CONDITION_VALUE_INVALID` (400): a variant write pins a `select` value the condition's `options` do not admit, including every pin on a condition whose `options` are absent, empty or unreadable\n- `TOO_MANY_MATCHES` (400): a multi-match resolve exceeded its result cap\n- `WRITE_CONFLICT` (409): transient write contention, retryable unlike `TUPLE_CONFLICT`\n- `OFFSET_WINDOW_EXCEEDED` (400): a listing's `from` plus `size` reaches past the offset window the search index allows\n- `CURSOR_INVALID` (400): a paging cursor cannot be read, or does not belong to the read it was sent with\n- `VARIANT_LIMIT_REACHED` (400): the entity already holds every variant it may hold\n- `PIN_FORMAT_INVALID` (400): a variant pins a value malformed for its condition's type\n- `VARIANT_UNPINNED` (400): a variant write pins no condition and is not marked `default`, or a batch delete item addresses no variant\n- `LAST_VERSION_UNDELETABLE` (400): the delete would leave the variant with no version at all\n\nIn a batch, the last four are the refusals an importer branches on: `VARIANT_LIMIT_REACHED`\nmeans stop the import, `PIN_FORMAT_INVALID` means one bad row, `LAST_VERSION_UNDELETABLE`\nmeans delete the variant instead.\n\nNot every refusal has a code. These carry a message and neither `code` nor `details`, and\neach operation's `400` names its own: a write pinning the reserved marker (`default` or\n`_default`), a variant marked `default` that also pins a real condition, a pin on a condition\nwhose declared type this deploy cannot read, an id this store cannot key by, a `valid_from`\nthis store cannot sort by, and a version write or delete with no `_revision`. Testing `code`\nfor absence is how a client tells them from the coded failures.\n\nFour of the 404s say that something the request addressed does not exist, and are fixed by\ncorrecting an id or accepting the thing is gone. The other two say the opposite: everything\naddressed exists and there is still nothing to serve — no variant applies to this situation,\nor none of a variant's versions is in effect yet. Those are ordinary business outcomes, told\napart from a wrong id by their code.\n\n`ENTITY_TYPE_MISMATCH` and `ENTITY_NOT_CONDITIONAL` are `400`s, not 404s: the entity the\nrequest addressed **was** found, and the fix is the slug beside it. A slug that names no\nconditional entity type at all is a `400` too.\n\nEach code is emitted with the HTTP status shown above, and only with that status, and each\none is pinned by a member of `ConditionalPricingError` — which is where the structured data\nthat code carries is declared.\n",
5768
+ "description": "Machine-readable failure mode of a conditional-pricing operation, so a client can branch on\nthe kind of failure instead of parsing the message. A `400` is about the request; a `409` is\nabout what is already stored. Refusals raised by request validation carry no `code` at all.\n\n- `SCHEMA_NOT_FOUND` (404): no conditional entity type by that slug\n- `ENTITY_NOT_FOUND` (404): the schema holds no entity with that id\n- `ENTITY_TYPE_MISMATCH` (400): that id belongs to an entity of another type than the slug named\n- `ENTITY_NOT_CONDITIONAL` (409): the entity is of the right type but was not created as a conditional one\n- `VARIANT_NOT_FOUND` (404): the entity has no such variant\n- `VERSION_NOT_FOUND` (404): the variant has no version at that `valid_from`\n- `NO_MATCHES` (404): nothing applied to the context and the entity has no `default` variant\n- `NO_ACTIVE_VERSION` (404): the variant has no version in effect at the instant asked about\n- `AMBIGUOUS_RESOLUTION` (409): several variants match the given context while a single result was requested\n- `TUPLE_CONFLICT` (409): the condition tuple is already claimed by another variant\n- `VERSION_CONFLICT` (409): a version already exists at the given `valid_from` on that variant\n- `CONDITION_UNDEFINED` (400): the request names a condition the entity's schema does not define\n- `VARIANT_PIN_UNDECLARED` (409): a variant a resolve would compose pins a condition the entity's schema no longer declares\n- `OPERATOR_UNSUPPORTED` (400): the requested predicate, or a sort, is not applicable to the condition's type\n- `CONTEXT_FORMAT_INVALID` (400): a resolve context or listing filter value is malformed for its condition type\n- `CONDITION_VALUE_INVALID` (400): a variant write pins a `select` value absent from the condition's declared `options`\n- `CONDITION_UNCONFIGURED` (409): a variant write pins a `select` condition whose `options` are absent or empty\n- `TOO_MANY_MATCHES` (400): a multi-match resolve exceeded its result cap\n- `WRITE_CONFLICT` (409): transient write contention, retryable unlike `TUPLE_CONFLICT`\n- `OFFSET_WINDOW_EXCEEDED` (400): a listing's `from` plus `size` reaches past the offset window the search index allows\n- `CURSOR_INVALID` (400): a paging cursor cannot be read, or does not belong to the read it was sent with\n- `VARIANT_LIMIT_REACHED` (409): the entity already holds every variant it may hold\n- `PIN_FORMAT_INVALID` (400): a variant pins a value malformed for its condition's type\n- `VARIANT_UNPINNED` (400): a variant write pins no condition and is not marked `default`, or a batch delete item addresses no variant\n- `LAST_VERSION_UNDELETABLE` (409): the delete would leave the variant with no version at all\n- `CONDITION_UNREADABLE` (409): the entity's schema declares a condition in a way this deploy cannot read\n- `SORT_INVALID` (400): a listing's `sort` is not `conditions.<name>:asc` or `conditions.<name>:desc`\n- `DEFAULT_MARKER_RESERVED` (400): a variant write's pins, or a resolve context, address the fallback marker — `default` or `_default`\n- `DEFAULT_VARIANT_PINS_CONDITIONS` (400): a variant marked `default` also pins real conditions\n- `VALID_FROM_IMMUTABLE` (400): a version write asks for a different `valid_from` than the version its own address names\n- `VARIANT_CONDITIONS_IMMUTABLE` (409): a version write carries a condition tuple other than the one its variant was created with\n- `IDENTIFIER_INVALID` (400): an id in the request cannot be used as a storage key — empty, carrying an unsupported character, or longer than 128 characters\n- `VALID_FROM_INVALID` (400): a `valid_from` is not one of the timestamp forms a version timeline can be sorted by\n- `VALUE_UNSTORABLE` (400): a write carries a value the store cannot hold, such as a non-finite number or one outside the table's numeric range\n",
5613
5769
  "enum": [
5614
5770
  "SCHEMA_NOT_FOUND",
5615
5771
  "ENTITY_NOT_FOUND",
@@ -5623,9 +5779,11 @@
5623
5779
  "TUPLE_CONFLICT",
5624
5780
  "VERSION_CONFLICT",
5625
5781
  "CONDITION_UNDEFINED",
5782
+ "VARIANT_PIN_UNDECLARED",
5626
5783
  "OPERATOR_UNSUPPORTED",
5627
5784
  "CONTEXT_FORMAT_INVALID",
5628
5785
  "CONDITION_VALUE_INVALID",
5786
+ "CONDITION_UNCONFIGURED",
5629
5787
  "TOO_MANY_MATCHES",
5630
5788
  "WRITE_CONFLICT",
5631
5789
  "OFFSET_WINDOW_EXCEEDED",
@@ -5633,11 +5791,20 @@
5633
5791
  "VARIANT_LIMIT_REACHED",
5634
5792
  "PIN_FORMAT_INVALID",
5635
5793
  "VARIANT_UNPINNED",
5636
- "LAST_VERSION_UNDELETABLE"
5794
+ "LAST_VERSION_UNDELETABLE",
5795
+ "CONDITION_UNREADABLE",
5796
+ "SORT_INVALID",
5797
+ "DEFAULT_MARKER_RESERVED",
5798
+ "DEFAULT_VARIANT_PINS_CONDITIONS",
5799
+ "VALID_FROM_IMMUTABLE",
5800
+ "VARIANT_CONDITIONS_IMMUTABLE",
5801
+ "IDENTIFIER_INVALID",
5802
+ "VALID_FROM_INVALID",
5803
+ "VALUE_UNSTORABLE"
5637
5804
  ]
5638
5805
  },
5639
5806
  "ResolveConditionalEntityRequest": {
5640
- "description": "A resolve names one conditional entity, then says which of its variants it means — one of two\nways, and never both. `context` describes a situation and asks which variants apply to it;\n`variant_id` names one variant and skips matching entirely.\n\nA body carrying both, or neither, is a validation `400`. Asking for the default variant\nwithout knowing its id is `context: {}`, which matches nothing and therefore falls back to it.\n\nEverything below the variant selection is the same on both branches, `as_of` included.\n",
5807
+ "description": "A resolve names one conditional entity and selects its variants either by `context` or by\n`variant_id`, never both. `context: {}` matches nothing and so returns the `default`\nvariant, which is how to ask for it without knowing its id.\n",
5641
5808
  "oneOf": [
5642
5809
  {
5643
5810
  "$ref": "#/components/schemas/ResolveByContextRequest"
@@ -5670,7 +5837,7 @@
5670
5837
  },
5671
5838
  "as_of": {
5672
5839
  "type": "string",
5673
- "description": "The instant the version is selected at — the version with the latest `valid_from` at or\nbefore it. Defaults to now. A variant whose first version is later than this is\nscheduled rather than applicable, and is excluded from resolution entirely.\n\nThat exclusion belongs to context matching only: a set of results may quietly drop a\nmember, where a pin naming one variant cannot answer with silence and is told\n`NO_ACTIVE_VERSION` instead.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time\n(`2026-01-01T00:00:00Z`), to at most millisecond precision.\n",
5840
+ "description": "The instant the version is selected at — the version with the latest `valid_from` at or\nbefore it. Defaults to now. A variant whose first version is later is excluded from\nmatching; a pin naming one is answered `NO_ACTIVE_VERSION` instead.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time, to at most\nmillisecond precision.\n",
5674
5841
  "example": "2027-03-15T00:00:00Z"
5675
5842
  },
5676
5843
  "options": {
@@ -5681,7 +5848,7 @@
5681
5848
  "ResolveByPinRequest": {
5682
5849
  "type": "object",
5683
5850
  "additionalProperties": false,
5684
- "description": "Resolve by naming a variant: compose this one, whatever a context would have matched. What an\norder needs to show the numbers a customer agreed to, and what a contract needs to show what\nis billable now — the two differ only in whether `as_of` is supplied.\n",
5851
+ "description": "Resolve by naming a variant: compose this one, whatever a context would have matched.",
5685
5852
  "required": [
5686
5853
  "schema",
5687
5854
  "entity_id",
@@ -5698,12 +5865,12 @@
5698
5865
  },
5699
5866
  "variant_id": {
5700
5867
  "type": "string",
5701
- "description": "The variant to compose. Condition matching is skipped entirely: no `context` is read, the\n`default` fallback does not apply, and `results` carries exactly one entry — a pin asks\nfor one variant by name.\n\nA `variant_id` this entity has no variant under is `VARIANT_NOT_FOUND`, and so is one\nnaming a variant of a different entity: a variant id alone addresses nothing.\n`SCHEMA_NOT_FOUND` and `ENTITY_NOT_FOUND` are still answered ahead of both.\n\n**Published ahead of the behaviour.** Until the pinned path is built, a body carrying\nthis field is answered `501`, ahead of every check above — the field exists so consumers\ncan build against it, and declining it is how a deployed stage says so rather than\nquietly returning the `default` variant.\n",
5868
+ "description": "The variant to compose. Condition matching is skipped, the `default` fallback does not\napply, and `results` carries exactly one entry. A variant of another entity is\n`VARIANT_NOT_FOUND`.\n",
5702
5869
  "example": "var-46045"
5703
5870
  },
5704
5871
  "as_of": {
5705
5872
  "type": "string",
5706
- "description": "The instant the version is selected at — the version with the latest `valid_from` at or\nbefore it. Defaults to now. The same selector, by the same rule, as on a context resolve:\nhow the variant was chosen is orthogonal to which of its versions applies, so a caller\nreplaying a recorded resolution instant supplies it here.\n\nA pinned variant whose first version is later than this is `NO_ACTIVE_VERSION`, carrying\nthe instant in `details.as_of`, rather than being dropped the way context matching drops\na scheduled variant.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time\n(`2026-01-01T00:00:00Z`), to at most millisecond precision.\n",
5873
+ "description": "The instant the version is selected at — the version with the latest `valid_from` at or\nbefore it. Defaults to now. A pinned variant whose first version is later is\n`NO_ACTIVE_VERSION`, carrying the instant in `details.as_of`.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time, to at most\nmillisecond precision.\n",
5707
5874
  "example": "2027-03-15T00:00:00Z"
5708
5875
  },
5709
5876
  "options": {
@@ -5714,7 +5881,7 @@
5714
5881
  "ResolveContext": {
5715
5882
  "type": "object",
5716
5883
  "additionalProperties": true,
5717
- "description": "The situation to resolve for: a flat map keyed by condition name, as the entity's schema\ndeclares them. A condition left out of the map is not a wildcard — it matches only variants\nthat leave that condition unpinned.\n\nEach value is either an exact value, typed by its condition, or a single-operator predicate\nobject:\n\n- `{ \"lt\": v }`, `{ \"lte\": v }`, `{ \"gt\": v }`, `{ \"gte\": v }` — order against a `number` or\n `date` condition.\n- `{ \"in\": [...] }` — membership, against a `string`, `select` or `number` condition.\n- `{ \"between\": \"2026-03-01\" }` — the explicit spelling of `daterange` containment; a plain\n date supplied for a `daterange` condition means the same thing.\n- `{ \"exists\": true }` — pinned to any value. `{ \"exists\": false }` says what leaving the key\n out says.\n\nAn `in` list carries at most 50,000 values; a longer one is `CONTEXT_FORMAT_INVALID`. To match\na condition whatever its value, send `{ \"exists\": true }` rather than enumerating its\nvocabulary.\n\nExact values are typed by their condition: a `string` or `select` matches exactly and\ncase-sensitively, with no trimming; a `location` of format `zipcode` is the postal code\nitself, and one of format `zipcode_town` an object carrying both, whose town is compared\ncase- and whitespace-insensitively while its postal code is not.\n\n`default`, and any name beginning with `_`, are reserved for the server and cannot be\nsupplied here.\n\nAn empty map is valid and means what it says: it supplies no value, so it matches no variant\nthat pins a condition, and the entity's `default` variant is what comes back. It is the only way to\nask for the default variant without knowing its id.\n",
5884
+ "description": "The situation to resolve for: a flat map keyed by condition name. A condition left out\nmatches only variants that leave it unpinned; an empty map therefore returns the `default`\nvariant.\n\nEach value is an exact value, typed by its condition, or a single-operator predicate:\n\n- `{ \"lt\": v }`, `{ \"lte\": v }`, `{ \"gt\": v }`, `{ \"gte\": v }` — order, against a `number`\n or `date` condition\n- `{ \"in\": [...] }` — membership, against a `string`, `select` or `number` condition\n- `{ \"between\": \"2026-03-01\" }` — `daterange` containment, which a plain date also means\n- `{ \"exists\": true }` — pinned to any value; `{ \"exists\": false }` — left unpinned\n\nAn `in` list carries at most 50,000 values. A `string` or `select` matches exactly and\ncase-sensitively. A `location` of format\n`zipcode` is the postal code itself; one of format `zipcode_town` is an object carrying\nboth, whose town is compared case- and whitespace-insensitively.\n\n`default` and names beginning with `_` are reserved and cannot be supplied.\n",
5718
5885
  "example": {
5719
5886
  "postal_code": "46045",
5720
5887
  "consumption": {
@@ -5730,24 +5897,24 @@
5730
5897
  "resolve_one": {
5731
5898
  "type": "boolean",
5732
5899
  "default": false,
5733
- "description": "Ask for an unambiguous answer. Several applicable variants become `AMBIGUOUS_RESOLUTION`\nrather than a set, and nothing applicable becomes `NO_MATCHES` rather than an empty one.\nThe response shape does not change: `results` simply carries exactly one entry.\n"
5900
+ "description": "Ask for an unambiguous answer: several applicable variants become\n`AMBIGUOUS_RESOLUTION`, and none becomes `NO_MATCHES`.\n"
5734
5901
  },
5735
5902
  "hydrate": {
5736
5903
  "type": "boolean",
5737
5904
  "default": false,
5738
- "description": "Return the entities a relation attribute references in place of the references\nthemselves, one level deep, exactly as an entity read with hydration does.\n\nA fetch, not a second resolution: a referenced entity comes back as it is read, and one\nthat is itself conditional carries its own flag — acting on that is the consumer's\nchoice, and this API does not resolve it on their behalf.\n\nApplied after composition, so a relation attribute whose value this variant's version\nreplaced is hydrated too. That is what makes a composite price work: the override\nreferences different component *entities*, which exist only in the composed payload.\n\nA reference that cannot be fetched comes back exactly as entity hydration returns it —\nno drop, no failure, and no field reporting it. A resolved payload behaves as an entity\nof the same shape would, and the discriminators are the only difference.\n\nCosts one fetch per referenced entity per result, and carries no cap of its own: the\nper-attribute limits are entity hydration's, and the 100-result cap on the resolve itself\nis unchanged.\n\n**Published ahead of the behaviour.** Until hydration is built, `true` is answered `501`\nrather than served as unhydrated references; `false`, which asks for what this path\nalready does, resolves normally.\n"
5905
+ "description": "Return the entities a relation attribute references in place of the references, one\nlevel deep, as an entity read with hydration does. Applied after composition, so a\nrelation this variant's version replaced is hydrated too.\n\nA referenced entity that is itself conditional is returned unresolved, carrying its own\nflag. Costs one fetch per distinct referenced entity, with no per-attribute limit.\n"
5739
5906
  }
5740
5907
  }
5741
5908
  },
5742
5909
  "PinnedResolveOptions": {
5743
5910
  "type": "object",
5744
5911
  "additionalProperties": false,
5745
- "description": "The options a pinned resolve accepts — `hydrate` and nothing else. `resolve_one` has nothing\nto change on this branch, where the answer is exactly one result or a 404, so a body sending\nit is a validation `400`. `hydrate` means what `ResolveOptions.hydrate` means.\n",
5912
+ "description": "The options a pinned resolve accepts — `hydrate` and nothing else. `resolve_one` has nothing\nto change where the answer is one result or a 404, so a body sending it is a `400`.\n",
5746
5913
  "properties": {
5747
5914
  "hydrate": {
5748
5915
  "type": "boolean",
5749
5916
  "default": false,
5750
- "description": "Return the entities a relation attribute references in place of the references\nthemselves, one level deep, exactly as an entity read with hydration does.\n\nA fetch, not a second resolution: a referenced entity comes back as it is read, and one\nthat is itself conditional carries its own flag — acting on that is the consumer's\nchoice, and this API does not resolve it on their behalf.\n\nApplied after composition, so a relation attribute whose value this variant's version\nreplaced is hydrated too. That is what makes a composite price work: the override\nreferences different component *entities*, which exist only in the composed payload.\n\nA reference that cannot be fetched comes back exactly as entity hydration returns it —\nno drop, no failure, and no field reporting it. A resolved payload behaves as an entity\nof the same shape would, and the discriminators are the only difference.\n\nCosts one fetch per referenced entity per result, and carries no cap of its own: the\nper-attribute limits are entity hydration's, and the 100-result cap on the resolve itself\nis unchanged.\n\n**Published ahead of the behaviour.** Until hydration is built, `true` is answered `501`\nrather than served as unhydrated references; `false`, which asks for what this path\nalready does, resolves normally.\n"
5917
+ "description": "Return the entities a relation attribute references in place of the references, one\nlevel deep, as an entity read with hydration does. Applied after composition, so a\nrelation this variant's version replaced is hydrated too.\n\nA referenced entity that is itself conditional is returned unresolved, carrying its own\nflag. Costs one fetch per distinct referenced entity, with no per-attribute limit.\n"
5751
5918
  }
5752
5919
  }
5753
5920
  },
@@ -5759,7 +5926,7 @@
5759
5926
  "properties": {
5760
5927
  "results": {
5761
5928
  "type": "array",
5762
- "description": "One composed payload per applicable variant, capped at 100 — a context selecting more\nthan that is answered with `TOO_MANY_MATCHES` instead. No dominance or specificity\nordering is applied between them.\n",
5929
+ "description": "One composed payload per applicable variant, capped at 100 — a context selecting more is\n`TOO_MANY_MATCHES`. Unordered.\n",
5763
5930
  "items": {
5764
5931
  "$ref": "#/components/schemas/ResolvedVariant"
5765
5932
  }
@@ -5769,7 +5936,7 @@
5769
5936
  "ResolvedVariant": {
5770
5937
  "type": "object",
5771
5938
  "additionalProperties": true,
5772
- "description": "The entity as this variant leaves it — every attribute of a plain entity read, with the\napplicable version's overrides applied — plus the discriminators saying where the numbers\ncame from.\n\nWith `options.hydrate`, a relation attribute holds the entities it references rather than the\nreferences themselves. That changes what an attribute holds, not the payload's shape, so\nnothing is declared here for it.\n",
5939
+ "description": "The entity as this variant leaves it — every attribute of a plain entity read with the\napplicable version's overrides applied — plus the discriminators below.\n",
5773
5940
  "required": [
5774
5941
  "_id",
5775
5942
  "_variant_id",
@@ -5780,12 +5947,12 @@
5780
5947
  "properties": {
5781
5948
  "_id": {
5782
5949
  "type": "string",
5783
- "description": "The logical entity's id — the same one a plain entity read returns. Resolution never\nmints a new identity; a variant is a set of values for *this* entity, not another one.\n",
5950
+ "description": "The logical entity's id, the same one a plain entity read returns.",
5784
5951
  "example": "price-sp26d1yo"
5785
5952
  },
5786
5953
  "_variant_id": {
5787
5954
  "type": "string",
5788
- "description": "The variant these values came from. Durable: this is what an order or a contract pins to\nread the same numbers back later.\n",
5955
+ "description": "The variant these values came from — what an order or contract pins.",
5789
5956
  "example": "var-46045"
5790
5957
  },
5791
5958
  "_version_valid_from": {
@@ -5799,11 +5966,11 @@
5799
5966
  "$ref": "#/components/schemas/VariantConditions"
5800
5967
  }
5801
5968
  ],
5802
- "description": "The conditions this variant pins, plus the boolean `default` discriminator.\n"
5969
+ "description": "The conditions this variant pins, plus the boolean `default` discriminator."
5803
5970
  },
5804
5971
  "_inert_overrides": {
5805
5972
  "type": "array",
5806
- "description": "The variant's stored overrides this payload did not apply, and why. Always present, and\nempty in the ordinary case — a client reads its length rather than branching on its\nabsence, the same way it reads a write's `warnings`.\n\nComputed per read from the schema as it stands, never stored, so granting or withdrawing\n`overridable_attribute` changes what resolves — and this list — without any data being\nrewritten. A version read reports what is stored and carries no such list; this is the\nonly surface that honours the schema.\n",
5973
+ "description": "The variant's stored overrides this payload did not apply, and why. Always present, and\nempty in the ordinary case. Computed per read against the schema as it stands, so\ngranting or withdrawing `overridable_attribute` changes it without any data being\nrewritten.\n",
5807
5974
  "items": {
5808
5975
  "$ref": "#/components/schemas/InertOverride"
5809
5976
  }
@@ -5823,11 +5990,11 @@
5823
5990
  "default": {
5824
5991
  "type": "boolean",
5825
5992
  "default": false,
5826
- "description": "Mark this variant as the entity's fallback: the one served when no other variant applies.\n\nA property of the variant, never an entry in `conditions`. A default variant cannot pin\nanything else, and an entity can have at most one; a second is refused as\n`TUPLE_CONFLICT`.\n\nAvailable to every conditional entity: nothing has to be declared in the schema first.\n"
5993
+ "description": "Mark this variant as the entity's fallback, served when no other variant applies. It can\npin nothing else, and an entity may have one; a second is `TUPLE_CONFLICT`. Available to\nevery conditional entity without anything being declared in the schema.\n"
5827
5994
  },
5828
5995
  "valid_from": {
5829
5996
  "type": "string",
5830
- "description": "When the first version takes effect. Defaults to now.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time\n(`2026-01-01T00:00:00Z`), to at most millisecond precision.\n",
5997
+ "description": "When the first version takes effect. Defaults to now. An RFC 3339 date (`2026-01-01`,\nread as midnight UTC) or date-time, to at most millisecond precision.\n",
5831
5998
  "example": "2027-01-01T00:00:00Z"
5832
5999
  },
5833
6000
  "values": {
@@ -5841,7 +6008,7 @@
5841
6008
  "required": [
5842
6009
  "default"
5843
6010
  ],
5844
- "description": "A variant's pinned conditions as a reader sees them: the pins the schema declares, plus a\nboolean `default` saying whether this is the entity's fallback.\n\n`default` is always present and always a boolean, so a client can branch on \"did I get the\nfallback?\" without knowing how one is stored. The reserved condition a fallback is actually\npinned under never appears here.\n",
6011
+ "description": "A variant's pinned conditions as a reader sees them: the pins the schema declares, plus a\nboolean `default` saying whether this is the entity's fallback.\n",
5845
6012
  "properties": {
5846
6013
  "default": {
5847
6014
  "type": "boolean"
@@ -5855,7 +6022,7 @@
5855
6022
  "PinnedConditions": {
5856
6023
  "type": "object",
5857
6024
  "additionalProperties": true,
5858
- "description": "The situation this variant applies to: a flat map keyed by condition name, as the entity's\nschema declares them. A condition left out is a wildcard — the variant applies whatever the\ncontext says for it, which is what makes adding a condition to a schema non-breaking for the\nvariants that already exist.\n\nExact values only. A predicate is a read-side thing — a resolve context or a listing's\ncondition filter — and is never stored: what a variant applies to is one situation, not a\nrange of them.\n\nValues are typed by their condition and stored canonicalized for that type: a `date` becomes\nmillisecond-precision UTC, a `daterange` an object carrying `from` and `until` where an empty\nstring is an open end, a `location` of format `zipcode` the postal code itself and one of\nformat `zipcode_town` an object carrying both. A `select` value must be a string, and must\nbe one the condition's `options` declare, which is always a closed vocabulary.\n\n`default`, and any name beginning with `_`, are reserved for the server and cannot be pinned\nhere. Whether a variant is the entity's fallback is set through the request's `default` flag.\n",
6025
+ "description": "The situation this variant applies to: a flat map keyed by condition name. A condition left\nout is a wildcard, which is what makes adding a condition to a schema non-breaking for\nexisting variants.\n\nExact values only; predicates belong to reads. Values are stored canonicalized for their\ntype: a `date` becomes millisecond-precision UTC, a `daterange` an object carrying `from`\nand `until` where an empty string is an open end, a `location` of format `zipcode` the\npostal code itself and one of format `zipcode_town` an object carrying both.\n\n`default` and names beginning with `_` are reserved; use the request's `default` flag.\n",
5859
6026
  "example": {
5860
6027
  "postal_code": "46045"
5861
6028
  }
@@ -5863,7 +6030,7 @@
5863
6030
  "VariantValues": {
5864
6031
  "type": "object",
5865
6032
  "additionalProperties": true,
5866
- "description": "The attribute values this version overrides on the base entity, keyed by attribute name.\n\nOnly attributes currently declaring `overridable_attribute` are applied. Metadata fields\n(anything underscore-prefixed), readonly attributes, hidden attributes, computed attributes,\nattributes of a type no variant may override and non-overridable attributes present here are\nnot applied rather than rejected, and every one but the metadata is named in the write's\n`warnings`, so a client working from a slightly stale schema snapshot still succeeds instead\nof failing on fields it could not have known to drop, and still learns which of them did not\nland. Metadata is never named, since a client echoing back a payload it read carries it in\nevery body. An attribute's `render_condition` says when to show it and has no\nbearing on whether a variant may override it.\n\nNot applied means *not updated*, never *removed*: a value already stored for an attribute that\nis not currently overridable is preserved, so removing and restoring the flag deactivates and\nthen reactivates the same override. An append seeds the attributes the variant may not\noverride from the version in effect at its own `valid_from`, so its stored values are not a\npure function of the body that wrote it; a variant's first version, and an append dated before\nthe variant's earliest version, inherit nothing.\n\nA composite price's `price_components` is an ordinary overridable relation attribute. A\ncomposite variant's override references different component *entities*, never a variant or a\nversion of one, and holds whatever a relation attribute ordinarily holds — this API defines no\nreference shape of its own.\n",
6033
+ "description": "The values this version overrides on the base entity, keyed by entity field name.\n\nA field is overridable if its attribute declares `overridable_attribute` — which readonly,\nhidden, computed and metadata fields, and types no variant may override, cannot be given —\nor if a capability declaring `overridable_attribute` names it in `managed_fields`, which\nexcludes only readonly and metadata fields.\n\nFields that are not overridable are reported in the write's `warnings` rather than rejected,\nand keep whatever value they already had. An append seeds them from the version in effect at\nits own `valid_from`.\n\nA composite price's `price_components` is an ordinary overridable relation attribute,\nreferencing component entities rather than variants or versions.\n",
5867
6034
  "example": {
5868
6035
  "unit_amount": 2499,
5869
6036
  "unit_amount_decimal": "24.99"
@@ -5886,7 +6053,7 @@
5886
6053
  "properties": {
5887
6054
  "variant_id": {
5888
6055
  "type": "string",
5889
- "description": "Server-generated, always, and never accepted from a client. This is the durable key orders\nand contracts pin.\n",
6056
+ "description": "Server-generated. The durable key orders and contracts pin.",
5890
6057
  "example": "var-46045"
5891
6058
  },
5892
6059
  "entity_id": {
@@ -5902,7 +6069,7 @@
5902
6069
  "$ref": "#/components/schemas/VariantConditions"
5903
6070
  }
5904
6071
  ],
5905
- "description": "The situation this variant applies to, plus the boolean `default` discriminator — the\nsame shape `_conditions` has on a resolved payload.\n"
6072
+ "description": "The situation this variant applies to, plus the boolean `default` discriminator."
5906
6073
  },
5907
6074
  "valid_from": {
5908
6075
  "type": "string",
@@ -5924,12 +6091,12 @@
5924
6091
  },
5925
6092
  "_revision": {
5926
6093
  "type": "number",
5927
- "description": "The revision a later write to this version must carry to be accepted.\n",
6094
+ "description": "The revision a later write to this version must carry.",
5928
6095
  "readOnly": true
5929
6096
  },
5930
6097
  "warnings": {
5931
6098
  "type": "array",
5932
- "description": "Things worth knowing that did not stop the write. Empty in the ordinary case — a client\nreads its length rather than branching on its absence.\n",
6099
+ "description": "Things worth knowing that did not stop the write. Always present, and empty in the ordinary case.",
5933
6100
  "items": {
5934
6101
  "$ref": "#/components/schemas/WriteWarning"
5935
6102
  }
@@ -5937,12 +6104,12 @@
5937
6104
  }
5938
6105
  },
5939
6106
  "WriteWarning": {
5940
- "description": "Something worth knowing that did not stop a write.\n\nOne vocabulary for every write, so a client branches on what happened rather than on which\nendpoint it called. `code` and `message` are the only two fields every code shares; everything\nelse lives in a `details` object typed per code, so narrowing on `code` yields a payload the\nclient can read rather than an untyped bag. A write raises each code at most once, and in the\nordinary case raises none of them.\n",
6107
+ "description": "Something worth knowing that did not stop a write. One vocabulary for every write; `details`\nis typed per `code`, and a write raises each code at most once.\n",
5941
6108
  "oneOf": [
5942
6109
  {
5943
6110
  "type": "object",
5944
6111
  "additionalProperties": false,
5945
- "description": "This entity is nearing the number of variants it may hold. Surfaced rather than rejected,\nso an importer finds out with a whole run's notice instead of discovering the limit\nhalfway through a refresh.\n",
6112
+ "description": "This entity is nearing the number of variants it may hold.",
5946
6113
  "required": [
5947
6114
  "code",
5948
6115
  "message",
@@ -5972,7 +6139,7 @@
5972
6139
  },
5973
6140
  "cap": {
5974
6141
  "type": "number",
5975
- "description": "Variants this entity may hold. Configurable per deploy, the same value for every\norganization on it.\n"
6142
+ "description": "Variants this entity may hold. Configurable per deploy."
5976
6143
  }
5977
6144
  }
5978
6145
  }
@@ -5981,7 +6148,7 @@
5981
6148
  {
5982
6149
  "type": "object",
5983
6150
  "additionalProperties": false,
5984
- "description": "What resolves **now** changed, other than by a newer version taking effect: the version in\neffect was written behind, or removed.\n",
6151
+ "description": "What resolves now changed, other than by a newer version taking effect: the version in\neffect was written behind, or removed.\n",
5985
6152
  "required": [
5986
6153
  "code",
5987
6154
  "message",
@@ -6005,7 +6172,7 @@
6005
6172
  {
6006
6173
  "type": "object",
6007
6174
  "additionalProperties": false,
6008
- "description": "What a past-dated (`as_of`) read returns changed: the write landed on, or created, a\nversion dated in the past. The version in effect is one of those whenever its own date has\npassed, which is the ordinary case — it covers every instant from that date until now. A\nversion dated now or later covers no past instant and is not reported here.\n",
6175
+ "description": "What a past-dated (`as_of`) read returns changed: the write landed on, or created, a\nversion dated in the past.\n",
6009
6176
  "required": [
6010
6177
  "code",
6011
6178
  "message",
@@ -6029,7 +6196,7 @@
6029
6196
  {
6030
6197
  "type": "object",
6031
6198
  "additionalProperties": false,
6032
- "description": "Attributes named in the request body that the write did not store, whatever the reason.\nThe write itself succeeded: an attribute a variant may not override is left alone rather\nthan making the whole call fail, so a client working from a slightly stale schema snapshot\nstill succeeds instead of failing on fields it could not have known to drop.\n\nOne entry per attribute, each with its own reason, so a client that only cares about typos\nfilters the entries by `reason` rather than branching on a second code.\n",
6199
+ "description": "Attributes named in the request body that the write did not store, one entry each with\nits own reason. The write itself succeeded.\n",
6033
6200
  "required": [
6034
6201
  "code",
6035
6202
  "message",
@@ -6068,7 +6235,7 @@
6068
6235
  "VersionMoved": {
6069
6236
  "type": "object",
6070
6237
  "additionalProperties": false,
6071
- "description": "Which version a write moved, and which one was in effect while it did.\n",
6238
+ "description": "Which version a write moved, and which one was in effect while it did.",
6072
6239
  "required": [
6073
6240
  "valid_from"
6074
6241
  ],
@@ -6080,7 +6247,7 @@
6080
6247
  },
6081
6248
  "active_valid_from": {
6082
6249
  "type": "string",
6083
- "description": "The version in effect when the write landed, before it did. Absent when the variant had\nnone — every version of it still scheduled.\n\nMay lag the variant's timeline by milliseconds, so a version written moments earlier may\nnot be named here. Advisory, like the warning carrying it: nothing branches on it except a\nhuman reading the message.\n",
6250
+ "description": "The version in effect when the write landed, before it did. Absent when the variant had\nnone. Advisory, and may lag the timeline by milliseconds.\n",
6084
6251
  "example": "2026-01-01T00:00:00.000Z"
6085
6252
  }
6086
6253
  }
@@ -6088,7 +6255,7 @@
6088
6255
  "InertOverride": {
6089
6256
  "type": "object",
6090
6257
  "additionalProperties": false,
6091
- "description": "One override that did not apply, and why.\n\nThe same entry on both sides of the feature: a write reports the attributes in its body it did\nnot store, and a resolved payload reports the stored overrides composition did not apply. Those\nare the same fact observed at two moments, so a client learns one shape and reads it in both\nplaces.\n",
6258
+ "description": "One override that did not apply, and why — reported by a write for the attributes in its\nbody, and by a resolved payload for the stored overrides composition passed over.\n",
6092
6259
  "required": [
6093
6260
  "attribute",
6094
6261
  "reason"
@@ -6106,7 +6273,7 @@
6106
6273
  },
6107
6274
  "InertOverrideReason": {
6108
6275
  "type": "string",
6109
- "description": "Why one override did not apply.\n\n- `ATTRIBUTE_NOT_OVERRIDABLE`: the entity's schema declares the attribute but has not granted\n it `overridable_attribute`. Granting the flag is an ordinary schema edit, which makes this\n the reason most often worth acting on.\n- `ATTRIBUTE_READONLY`: the attribute is declared readonly, and a readonly attribute cannot\n be granted the flag.\n- `ATTRIBUTE_HIDDEN`: the attribute is declared hidden, and a hidden attribute cannot be\n granted the flag.\n- `ATTRIBUTE_COMPUTED`: the attribute's value is derived (`type: computed` or\n `computed: true`) rather than stored, so an override would be recomputed away.\n- `ATTRIBUTE_UNDECLARED`: the entity's schema declares no attribute of that name. On a write\n that is usually a typo; on a resolved payload it is a stored override whose attribute has\n since left the schema — a stored value outlives the flag being withdrawn, so it can outlive\n its own attribute too. This API keeps no record of what a schema once declared, so it states\n only the observable fact and does not distinguish the two.\n- `TYPE_NOT_OVERRIDABLE`: the attribute's type is not one a variant may override, whatever\n the schema says about that particular attribute.\n- `CAPABILITY_NOT_OVERRIDABLE`: the attribute is contributed by a capability rather than\n declared on the entity's schema. Published for completeness and not emitted in this version,\n in which no capability attribute can be overridden at all.\n",
6276
+ "description": "Why one override did not apply.\n\n- `ATTRIBUTE_NOT_OVERRIDABLE`: the schema declares the attribute without\n `overridable_attribute`, which is an ordinary schema edit away\n- `ATTRIBUTE_READONLY`: the attribute is readonly, and cannot be granted the flag\n- `ATTRIBUTE_HIDDEN`: the attribute is hidden, and cannot be granted the flag\n- `ATTRIBUTE_COMPUTED`: the attribute's value is derived rather than stored\n- `ATTRIBUTE_UNDECLARED`: the schema declares no attribute of that name\n- `TYPE_NOT_OVERRIDABLE`: the attribute's type is not one a variant may override\n- `CAPABILITY_NOT_OVERRIDABLE`: the field is managed by a capability that does not declare\n `overridable_attribute`\n",
6110
6277
  "enum": [
6111
6278
  "ATTRIBUTE_NOT_OVERRIDABLE",
6112
6279
  "ATTRIBUTE_READONLY",
@@ -6140,7 +6307,7 @@
6140
6307
  },
6141
6308
  "tuple_released": {
6142
6309
  "type": "boolean",
6143
- "description": "Whether this call is the one that freed the variant's combination of condition values.\n`false` where an earlier, interrupted attempt had already freed it — the delete still\nsucceeded, and the combination was already reusable.\n"
6310
+ "description": "Whether this call freed the variant's combination of condition values. `false` where an\nearlier, interrupted attempt had already freed it.\n"
6144
6311
  },
6145
6312
  "versions_deleted": {
6146
6313
  "type": "number",
@@ -6150,7 +6317,7 @@
6150
6317
  },
6151
6318
  "VariantVersion": {
6152
6319
  "type": "object",
6153
- "description": "One version of one variant: the attribute overrides it carries, the instant it takes effect,\nand the variant it belongs to.\n\nThese are the version's **own** overrides, not the base entity overlaid with them — this is\nwhat an editing screen loads and saves, and what it edits is the overrides. Composing them onto\nthe entity is what `:resolve` answers.\n",
6320
+ "description": "One version of one variant: the overrides it carries, the instant it takes effect, and the\nvariant it belongs to. These are the version's own overrides; `:resolve` composes them onto\nthe entity.\n",
6154
6321
  "required": [
6155
6322
  "variant_id",
6156
6323
  "entity_id",
@@ -6180,11 +6347,11 @@
6180
6347
  "$ref": "#/components/schemas/VariantConditions"
6181
6348
  }
6182
6349
  ],
6183
- "description": "The situation the variant applies to, plus the boolean `default` discriminator. A property\nof the variant rather than of this version: every version of a variant carries the same\none, and no version write can change it.\n"
6350
+ "description": "The situation the variant applies to, plus the boolean `default` discriminator. A\nproperty of the variant: every version carries the same one.\n"
6184
6351
  },
6185
6352
  "valid_from": {
6186
6353
  "type": "string",
6187
- "description": "When this version takes effect, canonicalized to millisecond-precision UTC. A version's\nidentity within its variant — it never moves.\n",
6354
+ "description": "When this version takes effect, canonicalized to millisecond-precision UTC. Its identity\nwithin the variant.\n",
6188
6355
  "example": "2027-01-01T00:00:00.000Z"
6189
6356
  },
6190
6357
  "values": {
@@ -6202,14 +6369,14 @@
6202
6369
  },
6203
6370
  "_revision": {
6204
6371
  "type": "integer",
6205
- "description": "The revision a write to this version must carry to be accepted. Always current: every read\nthat returns one is strongly consistent, so it is never a marker a write would be refused\nfor having read too early.\n",
6372
+ "description": "The revision a write to this version must carry. Read from a strongly consistent read.",
6206
6373
  "readOnly": true,
6207
6374
  "example": 3
6208
6375
  }
6209
6376
  }
6210
6377
  },
6211
6378
  "WrittenVariantVersion": {
6212
- "description": "A version as a write left it, together with anything the write moved.\n",
6379
+ "description": "A version as a write left it, together with anything the write moved.",
6213
6380
  "allOf": [
6214
6381
  {
6215
6382
  "$ref": "#/components/schemas/VariantVersion"
@@ -6222,7 +6389,7 @@
6222
6389
  "properties": {
6223
6390
  "warnings": {
6224
6391
  "type": "array",
6225
- "description": "What this write moved, and anything in the body it did not store. Empty in the\nordinary case — a client reads its length rather than branching on its absence.\n",
6392
+ "description": "What this write moved, and anything in the body it did not store. Always present,\nand empty in the ordinary case.\n",
6226
6393
  "items": {
6227
6394
  "$ref": "#/components/schemas/WriteWarning"
6228
6395
  }
@@ -6259,7 +6426,7 @@
6259
6426
  },
6260
6427
  "warnings": {
6261
6428
  "type": "array",
6262
- "description": "What the delete moved, if anything. Empty when a scheduled version was withdrawn — a\nclient reads its length rather than branching on its absence.\n",
6429
+ "description": "What the delete moved. Always present, and empty when a scheduled version was withdrawn.",
6263
6430
  "items": {
6264
6431
  "$ref": "#/components/schemas/WriteWarning"
6265
6432
  }
@@ -6275,7 +6442,7 @@
6275
6442
  "properties": {
6276
6443
  "valid_from": {
6277
6444
  "type": "string",
6278
- "description": "When this version takes effect. Defaults to now.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time\n(`2026-01-01T00:00:00Z`), to at most millisecond precision.\n\nA date in the past is accepted and answered with warnings, never refused. A date the\nvariant already has a version at is refused as `VERSION_CONFLICT`.\n\n**Omit this to mean \"now\"** — that is the only spelling of now that is reliably silent. A\ntimestamp taken from the caller's own clock is already some milliseconds old when the\nserver judges it, which makes it a backdate, however small, and it is answered with the\nwarnings a backdate earns.\n",
6445
+ "description": "When this version takes effect. Omit it to mean now; a timestamp read from the caller's\nown clock is already a backdate by the time the server judges it, and earns a warning.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time, to at most\nmillisecond precision. A date in the past is accepted; one the variant already has a\nversion at is `VERSION_CONFLICT`.\n",
6279
6446
  "example": "2027-01-01T00:00:00Z"
6280
6447
  },
6281
6448
  "values": {
@@ -6284,7 +6451,7 @@
6284
6451
  "$ref": "#/components/schemas/VariantValues"
6285
6452
  }
6286
6453
  ],
6287
- "description": "The attribute overrides this version carries. An append seeds the attributes the variant\nmay not override from the version in effect at this version's own `valid_from` and then\napplies these values over them, so the stored values are not a pure function of this\nbody. An append dated before the variant's earliest version inherits nothing.\n"
6454
+ "description": "The overrides this version carries. Attributes the variant may not override are seeded\nfrom the version in effect at this version's own `valid_from`.\n"
6288
6455
  },
6289
6456
  "conditions": {
6290
6457
  "allOf": [
@@ -6292,7 +6459,7 @@
6292
6459
  "$ref": "#/components/schemas/PinnedConditions"
6293
6460
  }
6294
6461
  ],
6295
- "description": "Optional, and never applied: a variant's conditions are fixed when it is created. Accepted\nonly so that a client building its body from the version it loaded is not forced to strip\nthem out, and refused when they describe a different situation from the stored one.\n"
6462
+ "description": "Accepted only unchanged, so a client can send back the body it loaded. A variant's\nconditions are fixed when it is created.\n"
6296
6463
  }
6297
6464
  }
6298
6465
  },
@@ -6310,17 +6477,17 @@
6310
6477
  "$ref": "#/components/schemas/VariantValues"
6311
6478
  }
6312
6479
  ],
6313
- "description": "The complete set of attribute overrides this version carries. An overridable attribute\nabsent from here stops being overridden.\n\nAttributes the variant may not override are not applied where this carries them, and\ntheir **stored value is kept rather than dropped**.\n"
6480
+ "description": "The complete set of overrides this version carries. An overridable attribute absent from\nhere stops being overridden; one the variant may not override keeps its stored value.\n"
6314
6481
  },
6315
6482
  "_revision": {
6316
6483
  "type": "integer",
6317
6484
  "minimum": 1,
6318
- "description": "The revision marker read from the version being written. The write is refused with\n`WRITE_CONFLICT` if the version has been written since.\n",
6485
+ "description": "The revision read from the version being written. Refused with `WRITE_CONFLICT` if the\nversion has been written since.\n",
6319
6486
  "example": 3
6320
6487
  },
6321
6488
  "valid_from": {
6322
6489
  "type": "string",
6323
- "description": "Optional, and never applied. Accepted when it names the version being addressed — so a\nclient building its body from what it loaded need not strip it out — and refused when it\nnames another: a version's `valid_from` is its identity, and moving it is an append and a\ndelete rather than an edit.\n"
6490
+ "description": "Accepted only when it names the version being addressed. Moving a version is an append\nand a delete.\n"
6324
6491
  },
6325
6492
  "conditions": {
6326
6493
  "allOf": [
@@ -6328,7 +6495,7 @@
6328
6495
  "$ref": "#/components/schemas/PinnedConditions"
6329
6496
  }
6330
6497
  ],
6331
- "description": "Optional, and never applied: a variant's conditions are fixed when it is created. Refused\nwhen they describe a different situation from the stored one.\n"
6498
+ "description": "Accepted only unchanged. A variant's conditions are fixed when it is created."
6332
6499
  }
6333
6500
  }
6334
6501
  },
@@ -6346,17 +6513,17 @@
6346
6513
  "$ref": "#/components/schemas/VariantValues"
6347
6514
  }
6348
6515
  ],
6349
- "description": "Only the attribute overrides to change. Everything not mentioned is left as stored.\n\n`null` is a value like any other here rather than a deletion; to stop overriding an\nattribute, send the complete snapshot without it through the replace operation.\n"
6516
+ "description": "Only the overrides to change; everything not mentioned is left as stored. `null` sets a\nvalue rather than removing an override — use the replace operation to remove one.\n"
6350
6517
  },
6351
6518
  "_revision": {
6352
6519
  "type": "integer",
6353
6520
  "minimum": 1,
6354
- "description": "The revision marker read from the version being written. The write is refused with\n`WRITE_CONFLICT` if the version has been written since.\n",
6521
+ "description": "The revision read from the version being written. Refused with `WRITE_CONFLICT` if the\nversion has been written since.\n",
6355
6522
  "example": 3
6356
6523
  },
6357
6524
  "valid_from": {
6358
6525
  "type": "string",
6359
- "description": "Optional, never applied, and refused when it names a version other than the one addressed."
6526
+ "description": "Accepted only when it names the version being addressed."
6360
6527
  },
6361
6528
  "conditions": {
6362
6529
  "allOf": [
@@ -6364,43 +6531,43 @@
6364
6531
  "$ref": "#/components/schemas/PinnedConditions"
6365
6532
  }
6366
6533
  ],
6367
- "description": "Optional, and never applied. A partial update that tries to change a pinned condition value\nis refused.\n"
6534
+ "description": "Accepted only unchanged. A variant's conditions are fixed when it is created."
6368
6535
  }
6369
6536
  }
6370
6537
  },
6371
6538
  "ListVariantsRequest": {
6372
6539
  "type": "object",
6373
6540
  "additionalProperties": false,
6374
- "description": "How to narrow and page a variant listing. Every property is optional, so `{}` is a valid body\nand asks for the first ten variants of the entity in `variant_id` order — the body itself is\nrequired, and an omitted one is a request-validation `400` rather than an unnarrowed page.\n\n`conditions` and `search` narrow independently and a variant has to satisfy both.\n",
6541
+ "description": "How to narrow and page a variant listing. Every property is optional, so `{}` asks for the\nfirst ten variants in `variant_id` order, but the body itself is required. `conditions` and\n`search` narrow independently and a variant must satisfy both.\n",
6375
6542
  "properties": {
6376
6543
  "conditions": {
6377
6544
  "$ref": "#/components/schemas/VariantConditionFilter"
6378
6545
  },
6379
6546
  "search": {
6380
6547
  "type": "string",
6381
- "description": "Free text matched against the variant's pinned values — how someone finds one postal code\namong 800,000.\n\nMatches `string`, `select` and `number` pins only. A `location` pin is stored as an array\nof its format's parts and a `daterange` pin as an object carrying `from` and `until`, so\nneither is text a user could have typed.\n",
6548
+ "description": "Free text matched against the scalar pins — `string`, `select`, `number` and `date`.\n`location` and `daterange` pins are stored structured and are not matched.\n",
6382
6549
  "example": "460"
6383
6550
  },
6384
6551
  "sort": {
6385
6552
  "type": "string",
6386
- "description": "`conditions.<name>:asc` or `conditions.<name>:desc`, for a `string`, `select`, `number` or\n`date` pin. Anything else — another field, or a pin of another type — is a `400`.\n\n**`variant_id:asc` is appended by the server**, always, so the order is total: many\nvariants can pin one postal code, and without a tiebreaker a cursor would repeat or skip\nrows between pages. Asking for no sort is `variant_id:asc` alone.\n",
6553
+ "description": "`conditions.<name>:asc` or `conditions.<name>:desc`, for a `string`, `select`, `number`\nor `date` pin. `variant_id:asc` is always appended, so the order is total.\n",
6387
6554
  "example": "conditions.postal_code:asc"
6388
6555
  },
6389
6556
  "from": {
6390
6557
  "type": "integer",
6391
6558
  "minimum": 0,
6392
6559
  "default": 0,
6393
- "description": "The offset to read from. Not read when a `cursor` is sent, which carries its own position.\n\nBounded by the search index's offset window, together with `size`: the window bounds the\nlast row a page may contain, so the final servable offset is the window minus the page\nsize. A page reaching past it is `OFFSET_WINDOW_EXCEEDED`, naming all three numbers,\nrather than a page clamped back inside it as entity listing does. The window is the\ndeploy's: read its size from the error, not from here.\n"
6560
+ "description": "The offset to read from, ignored when a `cursor` is sent. Bounded together with `size`\nby the search index's offset window; a page reaching past it is\n`OFFSET_WINDOW_EXCEEDED`, which reports the window.\n"
6394
6561
  },
6395
6562
  "size": {
6396
6563
  "type": "integer",
6397
6564
  "minimum": 1,
6398
6565
  "default": 10,
6399
- "description": "Rows per page. Clamped silently at 1000, as entity listing's is.\n"
6566
+ "description": "Rows per page. Clamped silently at 1000."
6400
6567
  },
6401
6568
  "cursor": {
6402
6569
  "type": "string",
6403
- "description": "Continue from a previous response's `next`, which is where a caller goes when the offset\nwindow runs out. Opaque: it encodes the position and the listing it was issued for, and\nnothing a client should read or construct.\n\n`conditions`, `search` and `sort` must be the ones the cursor was issued with — a cursor\nresumes one listing, and cannot mean anything against a different one. A cursor that is\nmalformed, or does not match the listing it is sent with, is a `400`.\n",
6570
+ "description": "Continue from a previous response's `next`, which is where a caller goes when the offset\nwindow runs out. Opaque, and valid only with the `conditions`, `search` and `sort` it\nwas issued with.\n",
6404
6571
  "example": "eyJmcm9tIjoyNSwibGlzdGluZyI6IjNmOWMxZTJhIn0"
6405
6572
  }
6406
6573
  }
@@ -6408,41 +6575,41 @@
6408
6575
  "VariantTreeRequest": {
6409
6576
  "type": "object",
6410
6577
  "additionalProperties": false,
6411
- "description": "The variants list's request plus `as_of`, the instant each row's version is selected at.\n`size` is clamped at 100 here; every other shared property means what it means on the list.\n",
6578
+ "description": "The variants list's request plus `as_of`, the instant each row's version is selected at.\n`size` is clamped at 100 here; every other property means what it means on the list.\n",
6412
6579
  "properties": {
6413
6580
  "conditions": {
6414
6581
  "$ref": "#/components/schemas/VariantConditionFilter"
6415
6582
  },
6416
6583
  "search": {
6417
6584
  "type": "string",
6418
- "description": "Free text matched against the variant's pinned values — how someone finds one postal code\namong 800,000.\n\nMatches `string`, `select` and `number` pins only. A `location` pin is stored as an array\nof its format's parts and a `daterange` pin as an object carrying `from` and `until`, so\nneither is text a user could have typed.\n",
6585
+ "description": "Free text matched against the scalar pins — `string`, `select`, `number` and `date`.\n`location` and `daterange` pins are stored structured and are not matched.\n",
6419
6586
  "example": "460"
6420
6587
  },
6421
6588
  "sort": {
6422
6589
  "type": "string",
6423
- "description": "`conditions.<name>:asc` or `conditions.<name>:desc`, for a `string`, `select`, `number` or\n`date` pin. Anything else — another field, or a pin of another type — is a `400`.\n\n**`variant_id:asc` is appended by the server**, always, so the order is total: many\nvariants can pin one postal code, and without a tiebreaker a cursor would repeat or skip\nrows between pages. Asking for no sort is `variant_id:asc` alone.\n",
6590
+ "description": "`conditions.<name>:asc` or `conditions.<name>:desc`, for a `string`, `select`, `number`\nor `date` pin. `variant_id:asc` is always appended, so the order is total.\n",
6424
6591
  "example": "conditions.postal_code:asc"
6425
6592
  },
6426
6593
  "from": {
6427
6594
  "type": "integer",
6428
6595
  "minimum": 0,
6429
6596
  "default": 0,
6430
- "description": "The offset to read from. Not read when a `cursor` is sent, which carries its own position.\n\nBounded by the search index's offset window, together with `size`: the window bounds the\nlast row a page may contain, so the final servable offset is the window minus the page\nsize. A page reaching past it is `OFFSET_WINDOW_EXCEEDED`, naming all three numbers,\nrather than a page clamped back inside it as entity listing does. The window is the\ndeploy's: read its size from the error, not from here.\n"
6597
+ "description": "The offset to read from, ignored when a `cursor` is sent. Bounded together with `size`\nby the search index's offset window; a page reaching past it is\n`OFFSET_WINDOW_EXCEEDED`, which reports the window.\n"
6431
6598
  },
6432
6599
  "size": {
6433
6600
  "type": "integer",
6434
6601
  "minimum": 1,
6435
6602
  "default": 10,
6436
- "description": "Rows per page. Clamped silently at 100, a tenth of the variants list's cap: every row here\ncosts its own version lookup.\n"
6603
+ "description": "Rows per page. Clamped silently at 100, since every row costs its own version lookup."
6437
6604
  },
6438
6605
  "cursor": {
6439
6606
  "type": "string",
6440
- "description": "Continue from a previous response's `next`, which is where a caller goes when the offset\nwindow runs out. Opaque: it encodes the position and the listing it was issued for, and\nnothing a client should read or construct.\n\n`conditions`, `search` and `sort` must be the ones the cursor was issued with — a cursor\nresumes one listing, and cannot mean anything against a different one. A cursor that is\nmalformed, or does not match the listing it is sent with, is a `400`.\n",
6607
+ "description": "Continue from a previous response's `next`, which is where a caller goes when the offset\nwindow runs out. Opaque, and valid only with the `conditions`, `search` and `sort` it\nwas issued with.\n",
6441
6608
  "example": "eyJmcm9tIjoyNSwibGlzdGluZyI6IjNmOWMxZTJhIn0"
6442
6609
  },
6443
6610
  "as_of": {
6444
6611
  "type": "string",
6445
- "description": "The instant each row's version is selected at — the version with the latest `valid_from`\nat or before it. Defaults to now. The same selector, by the same rule, as `:resolve`'s.\n\nA variant whose first version is later than this is not dropped the way context matching\ndrops it: it is a row with `status: scheduled` carrying that upcoming first version, which\nis what makes a staged price visible on the editing screen.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time\n(`2026-01-01T00:00:00Z`), to at most millisecond precision.\n",
6612
+ "description": "The instant each row's version is selected at. Defaults to now. A variant whose first\nversion is later is a row with `status: scheduled` carrying that upcoming version.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time, to at most\nmillisecond precision.\n",
6446
6613
  "example": "2027-03-15T00:00:00Z"
6447
6614
  }
6448
6615
  }
@@ -6450,7 +6617,7 @@
6450
6617
  "VariantConditionFilter": {
6451
6618
  "type": "object",
6452
6619
  "additionalProperties": true,
6453
- "description": "Which pins a variant must carry to be listed: a flat map keyed by condition name, as the\nentity's schema declares them. A condition left out of the map is not filtered on at all.\n\nEach value is either an exact value, typed by its condition, or a single-operator predicate\nobject — the same seven a resolve context accepts, because in both cases a predicate is\napplied to the variant's *pinned* value, so nothing about matching moves:\n\n- `{ \"lt\": v }`, `{ \"lte\": v }`, `{ \"gt\": v }`, `{ \"gte\": v }` — order against a `number` or\n `date` condition.\n- `{ \"in\": [...] }` — membership, against a `string`, `select` or `number` condition.\n- `{ \"between\": \"2026-03-01\" }` — the explicit spelling of `daterange` containment; a plain\n date supplied for a `daterange` condition means the same thing.\n- `{ \"exists\": true }` — pinned to any value. `{ \"exists\": false }` — the condition left\n unpinned.\n\nAn `in` list carries at most 50,000 values; a longer one is `CONTEXT_FORMAT_INVALID`. To\nfilter on a condition whatever its pinned value, send `{ \"exists\": true }` rather than\nenumerating its vocabulary.\n\n**A variant matches only where it pins the condition** — the one place a filter and a resolve\ncontext differ. On `:resolve` a condition a variant does not pin matches any value; here,\nasking for postal code 46045 does not return the variants that pin no postal code at all.\n`{ \"exists\": false }` is how those are asked for.\n\nValues are typed and canonicalized exactly as a resolve context's are, by the same code, so\none instant written two ways filters the same way either way. A condition the schema does not\ndeclare is `CONDITION_UNDEFINED`, a predicate its type does not support is\n`OPERATOR_UNSUPPORTED`, and a value malformed for its type is `CONTEXT_FORMAT_INVALID`.\n\n**`default` is accepted here**, as the exact boolean every row reports it as: `true` selects\nthe entity's fallback variant, `false` every variant that is not it.\n\nIt takes no predicate. `default` is not a condition and has no type, so ordering and\nmembership have nothing to apply to. It is also the one key the pinned-only rule above does\nnot describe literally: a variant that is not the fallback does not pin the marker to `false`,\nit does not pin it at all, so `false` selects the variants that leave it unpinned.\n\nNames beginning with `_` stay reserved for the server and cannot be filtered on — `_default`,\nthe marker a fallback is actually stored under, included. `default` is the spelling every read\nreports and the only one this accepts.\n",
6620
+ "description": "Which pins a variant must carry to be listed: a flat map keyed by condition name, taking the\nsame exact values and predicates a resolve context does. A condition left out is not\nfiltered on. An `in` list carries at most 50,000 values.\n\nA variant matches only where it pins the condition — unlike `:resolve`, where an unpinned\ncondition matches any value. `{ \"exists\": false }` selects the variants that leave it\nunpinned.\n\n`default` is accepted as an exact boolean and takes no predicate: `true` selects the\nentity's fallback variant, `false` every variant that is not it. Names beginning with `_`\nare reserved.\n",
6454
6621
  "example": {
6455
6622
  "postal_code": "46045",
6456
6623
  "consumption": {
@@ -6467,7 +6634,7 @@
6467
6634
  "properties": {
6468
6635
  "hits": {
6469
6636
  "type": "integer",
6470
- "description": "How many variants match, exactly, at any depth — not how many this page carries. Exact,\nas entity listing's is.\n",
6637
+ "description": "How many variants match in total, exactly — not how many this page carries.",
6471
6638
  "example": 8128
6472
6639
  },
6473
6640
  "results": {
@@ -6478,14 +6645,14 @@
6478
6645
  },
6479
6646
  "next": {
6480
6647
  "type": "string",
6481
- "description": "The cursor that continues this listing, absent on the last page. Send it back as `cursor`,\nwith the same filter, search and sort.\n",
6648
+ "description": "The cursor that continues this listing, absent on the last page. Send it back as\n`cursor`, with the same filter, search and sort.\n",
6482
6649
  "example": "eyJmcm9tIjoyNSwibGlzdGluZyI6IjNmOWMxZTJhIn0"
6483
6650
  }
6484
6651
  }
6485
6652
  },
6486
6653
  "VariantListRow": {
6487
6654
  "type": "object",
6488
- "description": "One variant as a listing reports it: which variant it is and what it pins.\n\nNo `_revision` — a write re-reads its version through that version's own `GET` — and no\n`_inert_overrides`, since a listing reports what is stored and only `:resolve` honours the\nschema.\n",
6655
+ "description": "One variant as a listing reports it: which variant it is and what it pins.",
6489
6656
  "required": [
6490
6657
  "variant_id",
6491
6658
  "entity_id",
@@ -6510,7 +6677,7 @@
6510
6677
  "$ref": "#/components/schemas/VariantConditions"
6511
6678
  }
6512
6679
  ],
6513
- "description": "The situation this variant applies to, plus the boolean `default` discriminator — the same\nshape a variant write returns.\n\nMay lag: a variant just created can be missing from a page, and one just deleted can still\nbe on it. The pins shown for a variant are never stale, since a variant's conditions are\nimmutable after creation.\n"
6680
+ "description": "The situation this variant applies to, plus the boolean `default` discriminator.\nMembership of a page may lag a write by moments; the pins themselves never do.\n"
6514
6681
  }
6515
6682
  }
6516
6683
  },
@@ -6523,26 +6690,26 @@
6523
6690
  "properties": {
6524
6691
  "hits": {
6525
6692
  "type": "integer",
6526
- "description": "How many variants match, exactly, at any depth — not how many this page carries. Exact,\nas entity listing's is.\n",
6693
+ "description": "How many variants match in total, exactly — not how many this page carries.",
6527
6694
  "example": 8128
6528
6695
  },
6529
6696
  "results": {
6530
6697
  "type": "array",
6531
- "description": "One row per matching variant, in the requested order.\n\nA variant the index still holds but whose versions are already gone — a variant\nmid-delete — is **omitted** rather than returned without a `version`. So `results` can be\nshorter than `hits` implies, for the width of that lag and no longer. Paging still ends\nwhere `next` does.\n",
6698
+ "description": "One row per matching variant, in the requested order. A variant mid-delete is omitted,\nso `results` can be shorter than `hits` implies.\n",
6532
6699
  "items": {
6533
6700
  "$ref": "#/components/schemas/VariantTreeRow"
6534
6701
  }
6535
6702
  },
6536
6703
  "next": {
6537
6704
  "type": "string",
6538
- "description": "The cursor that continues this listing, absent on the last page. Send it back as `cursor`,\nwith the same filter, search and sort.\n\n`as_of` is free to change between pages. It selects which version each row shows and has\nno bearing on which variants match or on the order they come back in, so a screen whose\ndate picker moves mid-listing keeps paging rather than starting over.\n",
6705
+ "description": "The cursor that continues this listing, absent on the last page. Send it back as\n`cursor`, with the same filter, search and sort; `as_of` may change between pages.\n",
6539
6706
  "example": "eyJmcm9tIjoyNSwibGlzdGluZyI6IjNmOWMxZTJhIn0"
6540
6707
  }
6541
6708
  }
6542
6709
  },
6543
6710
  "VariantTreeRow": {
6544
6711
  "type": "object",
6545
- "description": "A listing row plus the one version the tree view shows for it, and the status saying which\nversion that is.\n",
6712
+ "description": "A listing row plus the one version the tree shows for it, and the status saying which.",
6546
6713
  "required": [
6547
6714
  "variant_id",
6548
6715
  "entity_id",
@@ -6569,7 +6736,7 @@
6569
6736
  "$ref": "#/components/schemas/VariantConditions"
6570
6737
  }
6571
6738
  ],
6572
- "description": "The situation this variant applies to, plus the boolean `default` discriminator — the same\nshape a variant write returns.\n\nMay lag: a variant just created can be missing from a page, and one just deleted can still\nbe on it. The pins shown for a variant are never stale, since a variant's conditions are\nimmutable after creation.\n"
6739
+ "description": "The situation this variant applies to, plus the boolean `default` discriminator.\nMembership of a page may lag a write by moments; the pins themselves never do.\n"
6573
6740
  },
6574
6741
  "status": {
6575
6742
  "$ref": "#/components/schemas/VariantTreeRowStatus"
@@ -6580,13 +6747,13 @@
6580
6747
  "$ref": "#/components/schemas/VariantVersionSnapshot"
6581
6748
  }
6582
6749
  ],
6583
- "description": "The version this row shows: the one in effect at `as_of`, or — where every version of the\nvariant is still ahead of it — that upcoming first one. `status` says which of the two it\nis.\n\nAlways present. A variant always has at least one version, and the one case where a row\ncould have none — a variant whose delete has removed its versions but not yet its index\ndocument — is omitted from `results` instead, so a consumer never reads this field\ndefensively.\n"
6750
+ "description": "The version in effect at `as_of`, or the variant's upcoming first one where every\nversion is still ahead of it. `status` says which. Always present.\n"
6584
6751
  }
6585
6752
  }
6586
6753
  },
6587
6754
  "VariantTreeRowStatus": {
6588
6755
  "type": "string",
6589
- "description": "Whether a tree row's version is the one in effect at `as_of`, or one still ahead of it.\n\nExactly two values, and every row has one: a variant always has at least one version, so\neither a version is in effect at `as_of` or every version of that variant is still to come.\n\n- `active`: `version` is the version with the latest `valid_from` at or before `as_of` —\n the same version `active_valid_from` and `NO_ACTIVE_VERSION` speak of.\n- `scheduled`: the variant's first version is later than `as_of`, and `version` is that\n upcoming first version.\n",
6756
+ "description": "Whether a tree row's version is the one in effect at `as_of`, or one still ahead of it.\n\n- `active`: the version with the latest `valid_from` at or before `as_of`\n- `scheduled`: the variant's first version, which is later than `as_of`\n",
6590
6757
  "enum": [
6591
6758
  "active",
6592
6759
  "scheduled"
@@ -6594,7 +6761,7 @@
6594
6761
  },
6595
6762
  "VariantVersionSnapshot": {
6596
6763
  "type": "object",
6597
- "description": "One version of one variant as a listing reports it: `VariantVersion` without `_revision`.\n\nThe revision is missing on purpose. An editing screen re-reads the one version it is about to\nwrite through that version's own `GET`, which is strongly consistent, and writes with the\nrevision it gets back.\n\nEverything else is `VariantVersion` field for field, including the variant's `conditions`,\nwhich every version of a variant repeats.\n",
6764
+ "description": "One version of one variant as a listing reports it: `VariantVersion` without `_revision`.\nRead the version through its own `GET` to get the revision a write must carry.\n",
6598
6765
  "required": [
6599
6766
  "variant_id",
6600
6767
  "entity_id",
@@ -6623,11 +6790,11 @@
6623
6790
  "$ref": "#/components/schemas/VariantConditions"
6624
6791
  }
6625
6792
  ],
6626
- "description": "The situation the variant applies to, plus the boolean `default` discriminator. A property\nof the variant rather than of this version: every version of a variant carries the same\none, and no version write can change it.\n"
6793
+ "description": "The situation the variant applies to, plus the boolean `default` discriminator. A\nproperty of the variant: every version carries the same one.\n"
6627
6794
  },
6628
6795
  "valid_from": {
6629
6796
  "type": "string",
6630
- "description": "When this version takes effect, canonicalized to millisecond-precision UTC. A version's\nidentity within its variant — it never moves.\n",
6797
+ "description": "When this version takes effect, canonicalized to millisecond-precision UTC. Its identity\nwithin the variant.\n",
6631
6798
  "example": "2027-01-01T00:00:00.000Z"
6632
6799
  },
6633
6800
  "values": {
@@ -6653,14 +6820,14 @@
6653
6820
  "properties": {
6654
6821
  "results": {
6655
6822
  "type": "array",
6656
- "description": "A page of the variant's timeline, in the requested `order`.\n",
6823
+ "description": "A page of the variant's timeline, in the requested `order`.",
6657
6824
  "items": {
6658
6825
  "$ref": "#/components/schemas/VariantVersionSnapshot"
6659
6826
  }
6660
6827
  },
6661
6828
  "next": {
6662
6829
  "type": "string",
6663
- "description": "The cursor that continues this timeline, absent only on the last page.\n\nThe only end-of-data signal: a page shorter than `limit`, or an empty one, can still carry\na cursor, so a client pages until this field is absent rather than until a page looks\nshort. Send it back as `cursor`, against the same variant and the same `order`.\n",
6830
+ "description": "The cursor that continues this timeline, absent only on the last page — the only\nend-of-data signal, since a short or empty page can still carry one. Send it back as\n`cursor`, against the same variant and `order`.\n",
6664
6831
  "example": "eyJzayI6IlYjcHJpY2Utc3AyNmQxeW8jdmFyLTQ2MDQ1IzIwMjYtMDEtMDFUMDA6MDA6MDAuMDAwWiIsIm9yZGVyIjoiYXNjIn0"
6665
6832
  }
6666
6833
  }
@@ -6668,21 +6835,21 @@
6668
6835
  "BatchUpsertVariantsRequest": {
6669
6836
  "type": "object",
6670
6837
  "additionalProperties": false,
6671
- "description": "A batch of variant writes under one schema, each item naming the entity it writes to.\n",
6838
+ "description": "A batch of variant writes under one schema, each item naming the entity it writes to.",
6672
6839
  "required": [
6673
6840
  "items"
6674
6841
  ],
6675
6842
  "properties": {
6676
6843
  "correlation_id": {
6677
6844
  "type": "string",
6678
- "description": "An opaque string the caller uses to tie this response to the file and cycle that produced\nit. Echoed back verbatim, only when it was sent, and never interpreted.\n",
6845
+ "description": "An opaque string echoed back verbatim when it was sent, and never interpreted.",
6679
6846
  "example": "tariff-refresh-2027-01"
6680
6847
  },
6681
6848
  "items": {
6682
6849
  "type": "array",
6683
6850
  "minItems": 1,
6684
6851
  "maxItems": 100,
6685
- "description": "The writes to apply, in the order they should apply where two of them address the same\nvariant. At most 100 per call — a limit on one request, distinct from the per-entity\nvariant cap, which limits stored state.\n",
6852
+ "description": "The writes to apply, in the order they should apply where two of them address the same\nvariant. At most 100 per call, which is distinct from the per-entity variant cap.\n",
6686
6853
  "items": {
6687
6854
  "$ref": "#/components/schemas/BatchUpsertItem"
6688
6855
  }
@@ -6692,7 +6859,7 @@
6692
6859
  "BatchUpsertItem": {
6693
6860
  "type": "object",
6694
6861
  "additionalProperties": false,
6695
- "description": "One variant write: the entity it belongs to, the situation it applies to, and the values it\ncarries — the single-item create's body plus `entity_id`. The two differ on `conditions`: on\na create an existing tuple is `TUPLE_CONFLICT`, and here it is a version appended to the\nvariant already holding it.\n\nThere is no `variant_id`. An upsert creates variants that have no id yet.\n",
6862
+ "description": "One variant write: the single-item create's body plus `entity_id`, and no `variant_id`. An\nexisting condition tuple appends a version to the variant holding it rather than conflicting.\n",
6696
6863
  "required": [
6697
6864
  "entity_id",
6698
6865
  "values"
@@ -6700,7 +6867,7 @@
6700
6867
  "properties": {
6701
6868
  "entity_id": {
6702
6869
  "type": "string",
6703
- "description": "The conditional entity this item writes to. On the item rather than in the path, so one\ncall can refresh a whole tariff hierarchy — a composite price and its components\ntogether.\n",
6870
+ "description": "The conditional entity this item writes to.",
6704
6871
  "example": "price-sp26d1yo"
6705
6872
  },
6706
6873
  "conditions": {
@@ -6709,11 +6876,11 @@
6709
6876
  "default": {
6710
6877
  "type": "boolean",
6711
6878
  "default": false,
6712
- "description": "Mark this variant as the entity's fallback, exactly as a create does: a property of the\nvariant, never an entry in `conditions`. An item that pins nothing and is not the default\nis `VARIANT_UNPINNED` — the empty postal-code column in a source file.\n"
6879
+ "description": "Mark this variant as the entity's fallback, as a create does. An item that pins nothing\nand is not the default is `VARIANT_UNPINNED`.\n"
6713
6880
  },
6714
6881
  "valid_from": {
6715
6882
  "type": "string",
6716
- "description": "When the version this item writes takes effect. Omitted, it is a current-state,\nlast-write-wins write with no `skipped` detection.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time\n(`2026-01-01T00:00:00Z`), to at most millisecond precision.\n\nA past instant is written like any other and answered with the timeline warnings on this\nitem, so an importer stamping one `valid_from` across a batch sees them on every item.\n",
6883
+ "description": "When the version this item writes takes effect. Omitted, it is a last-write-wins write\nwith no `skipped` detection; a past instant is accepted and reported in this item's\n`warnings`.\n\nAn RFC 3339 date (`2026-01-01`, read as midnight UTC) or date-time, to at most\nmillisecond precision.\n",
6717
6884
  "example": "2027-01-01T00:00:00Z"
6718
6885
  },
6719
6886
  "values": {
@@ -6724,21 +6891,21 @@
6724
6891
  "BatchDeleteVariantsRequest": {
6725
6892
  "type": "object",
6726
6893
  "additionalProperties": false,
6727
- "description": "A batch of variant and version deletes under one schema, each item naming the entity it\nremoves from.\n",
6894
+ "description": "A batch of variant and version deletes under one schema, each item naming the entity it removes from.",
6728
6895
  "required": [
6729
6896
  "items"
6730
6897
  ],
6731
6898
  "properties": {
6732
6899
  "correlation_id": {
6733
6900
  "type": "string",
6734
- "description": "An opaque string the caller uses to tie this response to the file and cycle that produced\nit. Echoed back verbatim, only when it was sent, and never interpreted.\n",
6901
+ "description": "An opaque string echoed back verbatim when it was sent, and never interpreted.",
6735
6902
  "example": "postal-code-cleanup-2026-09"
6736
6903
  },
6737
6904
  "items": {
6738
6905
  "type": "array",
6739
6906
  "minItems": 1,
6740
6907
  "maxItems": 100,
6741
- "description": "The deletes to apply, in the order they should apply where two of them address the same\nvariant — which is decided after every condition tuple has been resolved to a variant id,\nso the order holds across the two addressing forms. At most 100 per call.\n",
6908
+ "description": "The deletes to apply, in the order they should apply where two of them address the same\nvariant — decided after every condition tuple has been resolved to a variant id. At most\n100 per call.\n",
6742
6909
  "items": {
6743
6910
  "$ref": "#/components/schemas/BatchDeleteItem"
6744
6911
  }
@@ -6746,7 +6913,7 @@
6746
6913
  }
6747
6914
  },
6748
6915
  "BatchDeleteItem": {
6749
- "description": "One delete: the variant, addressed by id or by the condition tuple it pins, and optionally\nthe one version of it to remove.\n\nExactly one of the two forms. An item carrying both a `variant_id` and `conditions` matches\nneither branch and is an envelope `400`, since the request validator rejects the body before\nany item runs.\n",
6916
+ "description": "One delete: the variant, addressed by id or by the condition tuple it pins, and optionally\nthe one version of it to remove. An item carrying both matches neither branch and is an\nenvelope `400`.\n",
6750
6917
  "oneOf": [
6751
6918
  {
6752
6919
  "$ref": "#/components/schemas/BatchDeleteByVariantId"
@@ -6759,7 +6926,7 @@
6759
6926
  "BatchDeleteByVariantId": {
6760
6927
  "type": "object",
6761
6928
  "additionalProperties": false,
6762
- "description": "A delete addressing its variant by id — the form a cleanup pass uses after the schema has\ndrifted, since a tuple naming a condition the schema no longer declares addresses nothing.\n",
6929
+ "description": "A delete addressing its variant by id.",
6763
6930
  "required": [
6764
6931
  "entity_id",
6765
6932
  "variant_id"
@@ -6767,7 +6934,7 @@
6767
6934
  "properties": {
6768
6935
  "entity_id": {
6769
6936
  "type": "string",
6770
- "description": "The conditional entity the variant belongs to. **Required beside `variant_id`, and not\nredundant**: a variant id alone addresses nothing in this API.\n",
6937
+ "description": "The conditional entity the variant belongs to. Required: a variant id alone addresses nothing.",
6771
6938
  "example": "price-sp26d1yo"
6772
6939
  },
6773
6940
  "variant_id": {
@@ -6777,7 +6944,7 @@
6777
6944
  },
6778
6945
  "valid_from": {
6779
6946
  "type": "string",
6780
- "description": "The one version to remove, by the instant it takes effect. Omitted, the whole variant\ngoes — its tuple, its index registration and every version it accumulated.\n\nAn RFC 3339 date or date-time, to at most millisecond precision, canonicalized before it\nis matched.\n",
6947
+ "description": "The one version to remove, by the instant it takes effect. Omitted, the whole variant\ngoes. An RFC 3339 date or date-time, canonicalized before it is matched.\n",
6781
6948
  "example": "2027-01-01T00:00:00Z"
6782
6949
  }
6783
6950
  }
@@ -6785,14 +6952,14 @@
6785
6952
  "BatchDeleteByConditions": {
6786
6953
  "type": "object",
6787
6954
  "additionalProperties": false,
6788
- "description": "A delete addressing its variant by the situation it applies to — the form an importer uses\nwhen it knows the source rows rather than the ids they produced.\n\n`conditions` is optional because the entity's fallback variant pins nothing: an item\naddressing it sends `default: true` and no `conditions`, exactly as a create marks one.\n\n**An item that addresses no variant is a per-item `VARIANT_UNPINNED`, not an envelope\n`400` and not a `skipped`.** Three shapes reach it: no `conditions` and no `default`,\n`conditions: {}`, and `default: false` alone — an empty postal-code column in a source row,\nserialized one way or another.\n\nTwo more shapes validate here and are refused per item rather than described by the schema:\nan item marking `default` while also pinning `conditions` — a fallback variant applies only\nwhen nothing else does, so it cannot also pin — and an item carrying `valid_from` with no\nvariant addressed at all. Both carry a message and no code, as the create path refuses the\nfirst today.\n",
6955
+ "description": "A delete addressing its variant by the situation it applies to.\n\n`conditions` is optional because the fallback variant pins nothing: address it with\n`default: true` and no `conditions`. An item that ends up addressing no variant at all is a\nper-item `VARIANT_UNPINNED`, and one marking `default` beside `conditions` is refused per\nitem with no code.\n",
6789
6956
  "required": [
6790
6957
  "entity_id"
6791
6958
  ],
6792
6959
  "properties": {
6793
6960
  "entity_id": {
6794
6961
  "type": "string",
6795
- "description": "The conditional entity the variant belongs to. Required, as it is beside a `variant_id`.\n",
6962
+ "description": "The conditional entity the variant belongs to.",
6796
6963
  "example": "price-sp26d1yo"
6797
6964
  },
6798
6965
  "conditions": {
@@ -6801,18 +6968,18 @@
6801
6968
  "default": {
6802
6969
  "type": "boolean",
6803
6970
  "default": false,
6804
- "description": "Address the entity's fallback variant, the one it serves when nothing else applies. A\nproperty of the variant, as it is on a write, never an entry in `conditions`.\n"
6971
+ "description": "Address the entity's fallback variant, the one served when nothing else applies."
6805
6972
  },
6806
6973
  "valid_from": {
6807
6974
  "type": "string",
6808
- "description": "The one version to remove, by the instant it takes effect. Omitted, the whole variant\ngoes.\n\nAn RFC 3339 date or date-time, to at most millisecond precision, canonicalized before it\nis matched.\n",
6975
+ "description": "The one version to remove, by the instant it takes effect. Omitted, the whole variant\ngoes. An RFC 3339 date or date-time, canonicalized before it is matched.\n",
6809
6976
  "example": "2027-01-01T00:00:00Z"
6810
6977
  }
6811
6978
  }
6812
6979
  },
6813
6980
  "BatchUpsertResult": {
6814
6981
  "type": "object",
6815
- "description": "What a batch upsert did: one entry per item, in request order, and a count per outcome.\n",
6982
+ "description": "What a batch upsert did: one entry per item, in request order, and a count per outcome.",
6816
6983
  "required": [
6817
6984
  "counts",
6818
6985
  "results"
@@ -6828,7 +6995,7 @@
6828
6995
  },
6829
6996
  "results": {
6830
6997
  "type": "array",
6831
- "description": "One entry per item, **in request order** — position is what maps an outcome back to its\nsource row, and no entry carries an index of its own.\n",
6998
+ "description": "One entry per item, in request order, which is what maps an outcome back to its source row.",
6832
6999
  "items": {
6833
7000
  "$ref": "#/components/schemas/BatchUpsertResultEntry"
6834
7001
  }
@@ -6837,7 +7004,7 @@
6837
7004
  },
6838
7005
  "BatchDeleteResult": {
6839
7006
  "type": "object",
6840
- "description": "What a batch delete did: one entry per item, in request order, and a count per outcome.\n",
7007
+ "description": "What a batch delete did: one entry per item, in request order, and a count per outcome.",
6841
7008
  "required": [
6842
7009
  "counts",
6843
7010
  "results"
@@ -6853,7 +7020,7 @@
6853
7020
  },
6854
7021
  "results": {
6855
7022
  "type": "array",
6856
- "description": "One entry per item, **in request order** — position is what maps an outcome back to its\nsource row.\n",
7023
+ "description": "One entry per item, in request order, which is what maps an outcome back to its source row.",
6857
7024
  "items": {
6858
7025
  "$ref": "#/components/schemas/BatchDeleteResultEntry"
6859
7026
  }
@@ -6862,7 +7029,7 @@
6862
7029
  },
6863
7030
  "BatchUpsertOutcome": {
6864
7031
  "type": "string",
6865
- "description": "What one upsert item did, derived from what was stored rather than from a mode the caller\ndeclared.\n\n- `variant_created`: the condition tuple was unknown, so a variant and its first version were\n created. The entry's `variant_id` is the id an order or contract pins.\n- `version_created`: the tuple was known and had no version at the item's `valid_from`, so\n one was appended. The ordinary monthly-refresh case, and a separate value from\n `variant_created` so an importer's counts can tell \"new postal codes appeared\" from\n \"existing variants got their scheduled adjustment\".\n- `updated`: a version existed at that exact instant and was written in place.\n- `skipped`: reserved for a write whose values are identical to what is stored, so re-running\n an unchanged import reads as a no-op. An item without `valid_from` has no `skipped`\n detection at all.\n- `error`: this item alone failed, and the entry's `error` says why.\n",
7032
+ "description": "What one upsert item did, derived from what was stored.\n\n- `variant_created`: the condition tuple was unknown, so a variant and its first version\n were created\n- `version_created`: the tuple was known and had no version at the item's `valid_from`\n- `updated`: a version existed at that exact instant and was written in place\n- `skipped`: the values are identical to what is stored; not detected for an item without\n `valid_from`\n- `error`: this item alone failed, and the entry's `error` says why\n",
6866
7033
  "enum": [
6867
7034
  "variant_created",
6868
7035
  "version_created",
@@ -6873,7 +7040,7 @@
6873
7040
  },
6874
7041
  "BatchDeleteOutcome": {
6875
7042
  "type": "string",
6876
- "description": "What one delete item did.\n\n- `deleted`: the variant, or the one version the item named, is gone.\n- `skipped`: the item addressed nothing — **the variant or the version**, never the entity. An\n entity that cannot answer the item is an `error` carrying `ENTITY_NOT_FOUND`,\n `ENTITY_TYPE_MISMATCH` or `ENTITY_NOT_CONDITIONAL`.\n- `error`: this item alone failed, and the entry's `error` says why.\n",
7043
+ "description": "What one delete item did.\n\n- `deleted`: the variant, or the one version the item named, is gone\n- `skipped`: the item addressed no such variant or version; a missing entity is an `error`\n- `error`: this item alone failed, and the entry's `error` says why\n",
6877
7044
  "enum": [
6878
7045
  "deleted",
6879
7046
  "skipped",
@@ -6883,7 +7050,7 @@
6883
7050
  "BatchUpsertCounts": {
6884
7051
  "type": "object",
6885
7052
  "additionalProperties": false,
6886
- "description": "How many items reached each outcome. Keyed by exactly the values of `BatchUpsertOutcome`, all\nof them present, so a logger reads a count without `?? 0`.\n\n**They sum to the length of `results`.** There is no `total`.\n",
7053
+ "description": "How many items reached each outcome. Keyed by exactly the values of `BatchUpsertOutcome`,\nall present, and summing to the length of `results`.\n",
6887
7054
  "required": [
6888
7055
  "variant_created",
6889
7056
  "version_created",
@@ -6917,7 +7084,7 @@
6917
7084
  "BatchDeleteCounts": {
6918
7085
  "type": "object",
6919
7086
  "additionalProperties": false,
6920
- "description": "How many items reached each outcome. Keyed by exactly the values of `BatchDeleteOutcome`, all\nof them present, and summing to the length of `results`. No `total`.\n",
7087
+ "description": "How many items reached each outcome. Keyed by exactly the values of `BatchDeleteOutcome`,\nall present, and summing to the length of `results`.\n",
6921
7088
  "required": [
6922
7089
  "deleted",
6923
7090
  "skipped",
@@ -6941,7 +7108,7 @@
6941
7108
  "BatchUpsertResultEntry": {
6942
7109
  "type": "object",
6943
7110
  "additionalProperties": false,
6944
- "description": "What one upsert item did, and anything worth knowing about it.\n\n**It carries nothing else.** Position in `results` is the contract, so no entry carries an\nindex; nothing the caller sent is echoed back beyond `entity_id`; and there is no `_revision`\n— an editing screen re-reads the version it is about to write through its own `GET`.\n",
7111
+ "description": "What one upsert item did. Position in `results` maps it back to its source row.",
6945
7112
  "required": [
6946
7113
  "outcome",
6947
7114
  "entity_id",
@@ -6953,22 +7120,22 @@
6953
7120
  },
6954
7121
  "entity_id": {
6955
7122
  "type": "string",
6956
- "description": "The entity this item wrote to, echoed from the item — present whatever happened.",
7123
+ "description": "The entity this item wrote to, echoed from the item.",
6957
7124
  "example": "price-sp26d1yo"
6958
7125
  },
6959
7126
  "variant_id": {
6960
7127
  "type": "string",
6961
- "description": "The variant this item created or wrote to. Present on every outcome but `error`: for a\n`variant_created` item it is the id an importer needs to pin, and for the rest it is the\nvariant the item's condition tuple resolved to.\n",
7128
+ "description": "The variant this item created or wrote to. Present on every outcome but `error`.",
6962
7129
  "example": "var-46045"
6963
7130
  },
6964
7131
  "valid_from": {
6965
7132
  "type": "string",
6966
- "description": "The version this item wrote, canonicalized to millisecond-precision UTC. Present on every\noutcome but `error`, including for an item that sent none — the server stamps the instant\na current-state write takes effect, and this is where the caller reads it back.\n",
7133
+ "description": "The version this item wrote, canonicalized to millisecond-precision UTC. Present on\nevery outcome but `error`, including for an item that sent none.\n",
6967
7134
  "example": "2027-01-01T00:00:00.000Z"
6968
7135
  },
6969
7136
  "warnings": {
6970
7137
  "type": "array",
6971
- "description": "Things worth knowing that did not stop this item's write. **Always present, and possibly\nempty** — on a `skipped` and an `error` entry too — so a client reads its length rather\nthan branching on its absence, as every other write in this document already asks.\n`skipped` describes what storage did; a warning describes what the request asked for, and\nthe two are not the same fact.\n\nEvery warning fires per item, with no batch-level suppression:\n`VARIANT_COUNT_APPROACHING_CAP` included, even where an entity past its threshold\nproduces it on all 100 entries. A logger dedupes by code.\n",
7138
+ "description": "Things worth knowing that did not stop this item's write. Always present and possibly\nempty, on every outcome. Fires per item, with no batch-level deduplication.\n",
6972
7139
  "items": {
6973
7140
  "$ref": "#/components/schemas/WriteWarning"
6974
7141
  }
@@ -6979,14 +7146,14 @@
6979
7146
  "$ref": "#/components/schemas/ConditionalPricingError"
6980
7147
  }
6981
7148
  ],
6982
- "description": "Why this item failed, present only with `outcome: error`. The same typed shape a\nsingle-item write is refused with, so a per-item failure and a single-item failure are\nread by one client type.\n\nAn item carries the codes variant create raises — `VARIANT_UNPINNED`,\n`CONDITION_UNDEFINED`, `CONDITION_VALUE_INVALID`, `PIN_FORMAT_INVALID`,\n`VARIANT_LIMIT_REACHED`, `WRITE_CONFLICT` for transient contention, and the three the\naddressed entity answers with: `ENTITY_NOT_FOUND`, `ENTITY_TYPE_MISMATCH` and\n`ENTITY_NOT_CONDITIONAL` — less two. Those three are per item because each item names its\nown entity, while `SCHEMA_NOT_FOUND` is the envelope's, since the slug is in the path. `TUPLE_CONFLICT` never appears on an item, and neither does `VERSION_CONFLICT`: a\nguard failure on a brand-new tuple is re-read and re-derived, and an existing\n`valid_from` is a replacement.\n"
7149
+ "description": "Why this item failed, present only with `outcome: error`: the codes a variant create\nraises, plus `ENTITY_NOT_FOUND`, `ENTITY_TYPE_MISMATCH` and `ENTITY_NOT_CONDITIONAL`,\nwhich are per item because each item names its own entity. Never `TUPLE_CONFLICT` or\n`VERSION_CONFLICT`.\n"
6983
7150
  }
6984
7151
  }
6985
7152
  },
6986
7153
  "BatchDeleteResultEntry": {
6987
7154
  "type": "object",
6988
7155
  "additionalProperties": false,
6989
- "description": "What one delete item did, and anything worth knowing about it.\n\nThe same six properties as a batch upsert entry, and it carries nothing else.\n",
7156
+ "description": "What one delete item did. Position in `results` maps it back to its source row.",
6990
7157
  "required": [
6991
7158
  "outcome",
6992
7159
  "entity_id",
@@ -6998,22 +7165,22 @@
6998
7165
  },
6999
7166
  "entity_id": {
7000
7167
  "type": "string",
7001
- "description": "The entity this item removed from, echoed from the item — present whatever happened.",
7168
+ "description": "The entity this item removed from, echoed from the item.",
7002
7169
  "example": "price-sp26d1yo"
7003
7170
  },
7004
7171
  "variant_id": {
7005
7172
  "type": "string",
7006
- "description": "The variant this item removed, or whose version it removed. Present wherever it is known:\nalways for an item that named one, and for an item addressing a condition tuple only once\nthat tuple resolved. **A `skipped` entry for a tuple no variant pins therefore names no\nvariant.**\n",
7173
+ "description": "The variant this item removed, or whose version it removed. Present wherever it is\nknown, so a `skipped` entry for a tuple no variant pins names none.\n",
7007
7174
  "example": "var-46045"
7008
7175
  },
7009
7176
  "valid_from": {
7010
7177
  "type": "string",
7011
- "description": "The version this item removed, canonicalized to millisecond-precision UTC. Absent where\nthe item removed the whole variant, which is what distinguishes the two deletes this one\nendpoint performs.\n",
7178
+ "description": "The version this item removed, canonicalized to millisecond-precision UTC. Absent where\nthe item removed the whole variant.\n",
7012
7179
  "example": "2027-01-01T00:00:00.000Z"
7013
7180
  },
7014
7181
  "warnings": {
7015
7182
  "type": "array",
7016
- "description": "Things worth knowing that did not stop this item's delete — chiefly which reads the\nremoval moved: `ACTIVE_VERSION_CHANGED` where what resolves now changed, and\n`SUPERSEDED_VERSION_WRITTEN` where a past-dated read did. Always present and possibly\nempty, on every outcome, as batch upsert's is.\n",
7183
+ "description": "Things worth knowing that did not stop this item's delete, chiefly which reads the\nremoval moved. Always present and possibly empty, on every outcome.\n",
7017
7184
  "items": {
7018
7185
  "$ref": "#/components/schemas/WriteWarning"
7019
7186
  }
@@ -7024,7 +7191,7 @@
7024
7191
  "$ref": "#/components/schemas/ConditionalPricingError"
7025
7192
  }
7026
7193
  ],
7027
- "description": "Why this item failed, present only with `outcome: error`. The same typed shape a\nsingle-item delete is refused with.\n\n`LAST_VERSION_UNDELETABLE` is the refusal specific to this endpoint's dated form;\n`VARIANT_UNPINNED` is an item that addresses no variant — no `variant_id`, no\n`default`, and no or empty `conditions`; `ENTITY_NOT_FOUND`, `ENTITY_TYPE_MISMATCH` and\n`ENTITY_NOT_CONDITIONAL` are per item, since each item names its own entity;\n`WRITE_CONFLICT` is transient contention.\nA missing variant or version is not here at all — that is `skipped`.\n"
7194
+ "description": "Why this item failed, present only with `outcome: error`:\n`LAST_VERSION_UNDELETABLE`, `VARIANT_UNPINNED`, the codes a condition tuple that cannot\nbe canonicalized raises, `IDENTIFIER_INVALID`, `VALID_FROM_INVALID`, `WRITE_CONFLICT`,\nand the per-item `ENTITY_NOT_FOUND`, `ENTITY_TYPE_MISMATCH` and\n`ENTITY_NOT_CONDITIONAL`. A missing variant or version is `skipped` instead.\n"
7028
7195
  }
7029
7196
  }
7030
7197
  },
@@ -7048,7 +7215,7 @@
7048
7215
  }
7049
7216
  },
7050
7217
  "ReportedError": {
7051
- "description": "The `error` field of an error response: the message, or — where the request itself failed\nvalidation before any handler ran — the validation errors themselves, which those 400s put\nhere in place of a string.\n\nA conditional-pricing operation answers a body its schema rejects with the list, and\neverything else it refuses with the message.\n",
7218
+ "description": "The `error` field of an error response: the message, or — where the request failed\nvalidation before any handler ran — the validation errors themselves.\n",
7052
7219
  "oneOf": [
7053
7220
  {
7054
7221
  "type": "string",
@@ -7066,7 +7233,7 @@
7066
7233
  ]
7067
7234
  },
7068
7235
  "ConditionalPricingError": {
7069
- "description": "An error from a conditional-pricing operation, carrying a machine-readable `code` from the\nconditional-pricing vocabulary plus the structured data that code explains, so a client can\nbranch on the kind of failure rather than parse the message.\nReferenced only by the operations that emit these codes; every other operation\nkeeps the plain `Error` shape.\n\n`details` is typed per code. Narrow on `code` and the object under it declares exactly the\nfields that code sends — never a field it does not send, and nothing beyond the declaration —\nso the conflicting variant id, or the value and vocabulary behind a rejected pin, is read\ndirectly.\n\nNot every failure these operations raise is in the vocabulary. A request body that is simply\nmalformed earns a message and nothing to branch on, and is answered with neither `code` nor\n`details` — the last member of the union, so testing `code` for absence is how a client tells\none of those from the twenty-three coded failures.\n",
7236
+ "description": "An error from a conditional-pricing operation, carrying a `code` plus the structured data\nthat code explains. `details` is typed per code: narrow on `code` and the object under it\ndeclares exactly the fields that code sends.\n\nA request these schemas reject is answered by the request validator with a message and\ncarries neither `code` nor `details` — the last member of the union.\n",
7070
7237
  "allOf": [
7071
7238
  {
7072
7239
  "$ref": "#/components/schemas/Error"
@@ -7080,7 +7247,7 @@
7080
7247
  "$ref": "#/components/schemas/ReportedError"
7081
7248
  }
7082
7249
  ],
7083
- "description": "What went wrong, in the field responses have always used and every caller to date\nreads. Carries the same string as `message` — which the shared `Error` schema\nrequires — except on a request-validation failure, which puts the list of validation\nerrors here instead.\n"
7250
+ "description": "What went wrong. The same string as `message`, except on a request-validation\nfailure, which puts the list of validation errors here instead.\n"
7084
7251
  }
7085
7252
  }
7086
7253
  },
@@ -7088,7 +7255,7 @@
7088
7255
  "oneOf": [
7089
7256
  {
7090
7257
  "type": "object",
7091
- "description": "No conditional entity type by that slug — the organization has no schema under it, or\nthe schema it has is not a conditional-pricing one.\n\nAddressed to the caller's own path parameter, and the same answer for every operation:\nnothing below a schema can be looked up until the schema itself is known.\n",
7258
+ "description": "No conditional entity type by that slug.",
7092
7259
  "required": [
7093
7260
  "code",
7094
7261
  "details"
@@ -7118,7 +7285,7 @@
7118
7285
  },
7119
7286
  {
7120
7287
  "type": "object",
7121
- "description": "The schema exists and holds no entity with that id.\n\nA wrong entity id is answered here, never as a variant that was never there. An id\nthat *does* exist, under another type, is not this code — the entity was found, and\n`ENTITY_TYPE_MISMATCH` is what says so.\n",
7288
+ "description": "The schema holds no entity with that id.",
7122
7289
  "required": [
7123
7290
  "code",
7124
7291
  "details"
@@ -7154,7 +7321,7 @@
7154
7321
  },
7155
7322
  {
7156
7323
  "type": "object",
7157
- "description": "That entity id belongs to an entity of a different type than the `{slug}` segment\nnamed.\n\nA `400`, not a `404`: the entity **was** found. The fix is to correct the slug and\nsend the request again; the entity id and the variant id were right. `actual_schema`\nnames the type the id belongs to, and where that is a conditional entity type it is\nthe slug to send.\n",
7324
+ "description": "That entity id belongs to an entity of a different type than the `{slug}` segment\nnamed. Correct the slug and send the request again.\n",
7158
7325
  "required": [
7159
7326
  "code",
7160
7327
  "details"
@@ -7187,7 +7354,7 @@
7187
7354
  },
7188
7355
  "actual_schema": {
7189
7356
  "type": "string",
7190
- "description": "The entity type that id belongs to. Where it is a conditional entity type, it\nis the slug to send instead.\n",
7357
+ "description": "The entity type that id belongs to. Where it is a conditional entity type,\nit is the slug to send instead.\n",
7191
7358
  "example": "product"
7192
7359
  }
7193
7360
  }
@@ -7196,7 +7363,7 @@
7196
7363
  },
7197
7364
  {
7198
7365
  "type": "object",
7199
- "description": "The entity is of the type the slug named, and is not a conditional one.\n\nA Product, Price or Coupon carries variants only if it was created with\n`is_conditional` set, and that flag is fixed at creation. So this is not a refusal\nanother request can get past: the entity has no conditional capability to address,\nand one that needs it has to be created as such.\n\nA `400`, as `ENTITY_TYPE_MISMATCH` is: the entity was found. Reading and writing the\nentity itself are unaffected; it is these endpoints that do not apply to it.\n",
7366
+ "description": "The entity is of the type the slug named and was not created with `is_conditional`,\nwhich is fixed at creation. Reading and writing the entity itself are unaffected.\n",
7200
7367
  "required": [
7201
7368
  "code",
7202
7369
  "details"
@@ -7232,7 +7399,7 @@
7232
7399
  },
7233
7400
  {
7234
7401
  "type": "object",
7235
- "description": "This entity has no such variant — nothing to read, write or delete, and nothing that\nwas ever there to have deleted.\n\nRaised only once the entity itself has been established, so it never stands in for a\nwrong entity id or a wrong slug.\n",
7402
+ "description": "This entity has no such variant.",
7236
7403
  "required": [
7237
7404
  "code",
7238
7405
  "details"
@@ -7268,7 +7435,7 @@
7268
7435
  },
7269
7436
  {
7270
7437
  "type": "object",
7271
- "description": "No version at that `valid_from` — never written, or deleted since.\n\nA version is addressed by the exact instant it takes effect from, not by the instant\na read happens to fall in, so this is not \"no version applies then\"; that case is\n`NO_ACTIVE_VERSION`.\n\nResolving a dated address tells an absent version from an absent variant, so a\nvariant that does not exist at all answers `VARIANT_NOT_FOUND` — the same answer the\npaths that name no date give.\n",
7438
+ "description": "No version at that `valid_from` — never written, or deleted since. A version is\naddressed by the exact instant it takes effect from; for the version in effect at an\ninstant, see `NO_ACTIVE_VERSION`.\n",
7272
7439
  "required": [
7273
7440
  "code",
7274
7441
  "details"
@@ -7304,7 +7471,7 @@
7304
7471
  },
7305
7472
  {
7306
7473
  "type": "object",
7307
- "description": "Nothing applied to the given context, and the entity has no `default` variant to fall\nback to.\n\nEverything the request addressed exists: this is an answer about the organization's\nown data, not a defect to fix — \"we do not serve this situation\".\n\nOnly reachable with `resolve_one`. Without it the same situation is a `200` carrying\nan empty `results`, since a set of applicable variants can legitimately be empty; it\nis asking for exactly one answer that turns having none into a failure.\n",
7474
+ "description": "Nothing applied to the given context and the entity has no `default` variant. Only\nreachable with `resolve_one`; without it the same situation is a `200` carrying an\nempty `results`.\n",
7308
7475
  "required": [
7309
7476
  "code",
7310
7477
  "details"
@@ -7340,7 +7507,7 @@
7340
7507
  },
7341
7508
  {
7342
7509
  "type": "object",
7343
- "description": "The variant has no version in effect at the instant asked about — raised by the\nshorthand reads and writes that address \"the version in effect\" without naming a\ndate, and by a pinned `:resolve` whose variant has no version in effect at `as_of`.\n\nRelative to `as_of`, and not a claim that every version is scheduled: a variant that\nis live today has none in effect at an instant before its first `valid_from` either.\nThe ordinary case is a variant staged ahead of its launch, which is a variant waiting\nrather than a variant broken — address one of its versions by `valid_from` to read or\nedit it before it takes effect.\n",
7510
+ "description": "The variant has no version in effect at the instant asked about — typically a\nvariant staged ahead of its launch. Address one of its versions by `valid_from` to\nread or edit it before it takes effect.\n",
7344
7511
  "required": [
7345
7512
  "code",
7346
7513
  "details"
@@ -7376,7 +7543,7 @@
7376
7543
  },
7377
7544
  {
7378
7545
  "type": "object",
7379
- "description": "Several variants apply to the given context while a single result was requested.\n\nThe candidates are named, and none of them is served over the others: which is right\nis a question about the organization's own data.\n",
7546
+ "description": "Several variants apply to the given context while a single result was requested.",
7380
7547
  "required": [
7381
7548
  "code",
7382
7549
  "details"
@@ -7398,7 +7565,7 @@
7398
7565
  "candidates": {
7399
7566
  "type": "array",
7400
7567
  "minItems": 2,
7401
- "description": "Every variant that applied, each with the conditions it pins — which is what\nmakes the overlap actionable: two variants both apply because their pins do\nnot distinguish the context they were both asked about.\n\nBounded by the same cap `TOO_MANY_MATCHES` reports, which is checked first,\nso this list is never longer than one response may carry.\n",
7568
+ "description": "Every variant that applied, each with the conditions it pins.",
7402
7569
  "items": {
7403
7570
  "type": "object",
7404
7571
  "additionalProperties": false,
@@ -7424,7 +7591,7 @@
7424
7591
  },
7425
7592
  {
7426
7593
  "type": "object",
7427
- "description": "The condition tuple this write claims is already held.\n\nPersistent, unlike `WRITE_CONFLICT`: the same request fails the same way until the\nholder changes, so a bulk importer can tell \"send this again\" from \"this combination\nis taken and always will be\".\n",
7594
+ "description": "The condition tuple this write claims is already held. Persistent, unlike\n`WRITE_CONFLICT`: the same request fails the same way until the holder changes.\n",
7428
7595
  "required": [
7429
7596
  "code",
7430
7597
  "details"
@@ -7450,7 +7617,7 @@
7450
7617
  },
7451
7618
  "conflicting_variant_id": {
7452
7619
  "type": "string",
7453
- "description": "The variant already holding the tuple, where the write read it back.\n",
7620
+ "description": "The variant already holding the tuple, where the write read it back.",
7454
7621
  "example": "var-50667"
7455
7622
  }
7456
7623
  }
@@ -7479,15 +7646,417 @@
7479
7646
  "valid_from"
7480
7647
  ],
7481
7648
  "properties": {
7482
- "variant_id": {
7483
- "type": "string",
7484
- "description": "The variant the write addressed.",
7485
- "example": "var-46045"
7649
+ "variant_id": {
7650
+ "type": "string",
7651
+ "description": "The variant the write addressed.",
7652
+ "example": "var-46045"
7653
+ },
7654
+ "valid_from": {
7655
+ "type": "string",
7656
+ "description": "The instant already claimed by a version of that variant.",
7657
+ "example": "2027-01-01T00:00:00.000Z"
7658
+ }
7659
+ }
7660
+ }
7661
+ }
7662
+ },
7663
+ {
7664
+ "type": "object",
7665
+ "description": "The request names a condition the entity's schema does not define. A *stored* pin on\na condition the schema no longer declares is `VARIANT_PIN_UNDECLARED`.\n",
7666
+ "required": [
7667
+ "code",
7668
+ "details"
7669
+ ],
7670
+ "properties": {
7671
+ "code": {
7672
+ "type": "string",
7673
+ "enum": [
7674
+ "CONDITION_UNDEFINED"
7675
+ ]
7676
+ },
7677
+ "details": {
7678
+ "type": "object",
7679
+ "additionalProperties": false,
7680
+ "required": [
7681
+ "condition_name"
7682
+ ],
7683
+ "properties": {
7684
+ "condition_name": {
7685
+ "type": "string",
7686
+ "description": "The condition the request named and the schema does not define.",
7687
+ "example": "postal_code"
7688
+ }
7689
+ }
7690
+ }
7691
+ }
7692
+ },
7693
+ {
7694
+ "type": "object",
7695
+ "description": "A variant a resolve would compose pins a condition the entity's schema no longer\ndeclares, which would make it match every context. Declare the condition again, or\nremove the variants pinning it — found with `variants:list` or the tree, and removed\nby id.\n",
7696
+ "required": [
7697
+ "code",
7698
+ "details"
7699
+ ],
7700
+ "properties": {
7701
+ "code": {
7702
+ "type": "string",
7703
+ "enum": [
7704
+ "VARIANT_PIN_UNDECLARED"
7705
+ ]
7706
+ },
7707
+ "details": {
7708
+ "type": "object",
7709
+ "additionalProperties": false,
7710
+ "required": [
7711
+ "condition_name",
7712
+ "variant_id"
7713
+ ],
7714
+ "properties": {
7715
+ "condition_name": {
7716
+ "type": "string",
7717
+ "description": "The condition the variant pins and the schema no longer declares.",
7718
+ "example": "postal_code"
7719
+ },
7720
+ "variant_id": {
7721
+ "type": "string",
7722
+ "description": "One variant carrying such a pin.",
7723
+ "example": "var-46045"
7724
+ }
7725
+ }
7726
+ }
7727
+ }
7728
+ },
7729
+ {
7730
+ "type": "object",
7731
+ "description": "The requested operator is not applicable to the condition's type.",
7732
+ "required": [
7733
+ "code",
7734
+ "details"
7735
+ ],
7736
+ "properties": {
7737
+ "code": {
7738
+ "type": "string",
7739
+ "enum": [
7740
+ "OPERATOR_UNSUPPORTED"
7741
+ ]
7742
+ },
7743
+ "details": {
7744
+ "type": "object",
7745
+ "additionalProperties": false,
7746
+ "required": [
7747
+ "condition_name",
7748
+ "condition_type",
7749
+ "operator"
7750
+ ],
7751
+ "properties": {
7752
+ "condition_name": {
7753
+ "type": "string",
7754
+ "example": "postal_code"
7755
+ },
7756
+ "condition_type": {
7757
+ "type": "string",
7758
+ "description": "The type the schema declares that condition with.",
7759
+ "example": "location"
7760
+ },
7761
+ "operator": {
7762
+ "type": "string",
7763
+ "description": "The predicate the context or filter asked for, or `sort` where a listing\nasked to order by a condition whose type has no order.\n",
7764
+ "example": "between"
7765
+ }
7766
+ }
7767
+ }
7768
+ }
7769
+ },
7770
+ {
7771
+ "type": "object",
7772
+ "description": "A resolve context or listing filter value that is malformed for its condition's type.",
7773
+ "required": [
7774
+ "code",
7775
+ "details"
7776
+ ],
7777
+ "properties": {
7778
+ "code": {
7779
+ "type": "string",
7780
+ "enum": [
7781
+ "CONTEXT_FORMAT_INVALID"
7782
+ ]
7783
+ },
7784
+ "details": {
7785
+ "type": "object",
7786
+ "additionalProperties": false,
7787
+ "required": [
7788
+ "condition_name",
7789
+ "expected"
7790
+ ],
7791
+ "properties": {
7792
+ "condition_name": {
7793
+ "type": "string",
7794
+ "example": "postal_code"
7795
+ },
7796
+ "expected": {
7797
+ "type": "string",
7798
+ "description": "What a value for that condition has to be, in prose.",
7799
+ "example": "a postal code"
7800
+ }
7801
+ }
7802
+ }
7803
+ }
7804
+ },
7805
+ {
7806
+ "type": "object",
7807
+ "description": "A variant write pins a `select` value absent from its condition's vocabulary. A\ncondition declaring no vocabulary at all is `CONDITION_UNCONFIGURED`, and one whose\n`options` hold nothing readable is `CONDITION_UNREADABLE`.\n",
7808
+ "required": [
7809
+ "code",
7810
+ "details"
7811
+ ],
7812
+ "properties": {
7813
+ "code": {
7814
+ "type": "string",
7815
+ "enum": [
7816
+ "CONDITION_VALUE_INVALID"
7817
+ ]
7818
+ },
7819
+ "details": {
7820
+ "type": "object",
7821
+ "additionalProperties": false,
7822
+ "required": [
7823
+ "condition_name",
7824
+ "value",
7825
+ "options"
7826
+ ],
7827
+ "properties": {
7828
+ "condition_name": {
7829
+ "type": "string",
7830
+ "example": "segment"
7831
+ },
7832
+ "value": {
7833
+ "description": "The value the write pinned, as it arrived.",
7834
+ "example": "industrial"
7835
+ },
7836
+ "options": {
7837
+ "type": "array",
7838
+ "minItems": 1,
7839
+ "description": "The vocabulary as enforced, after any entries this deploy cannot read have\nbeen dropped.\n",
7840
+ "items": {
7841
+ "type": "string"
7842
+ },
7843
+ "example": [
7844
+ "private",
7845
+ "commercial"
7846
+ ]
7847
+ }
7848
+ }
7849
+ }
7850
+ }
7851
+ },
7852
+ {
7853
+ "type": "object",
7854
+ "description": "A variant write pins a `select` condition whose `options` are absent or empty. A\n`select` vocabulary is always closed, so one declaring nothing admits nothing; fill\nthe `options` in.\n",
7855
+ "required": [
7856
+ "code",
7857
+ "details"
7858
+ ],
7859
+ "properties": {
7860
+ "code": {
7861
+ "type": "string",
7862
+ "enum": [
7863
+ "CONDITION_UNCONFIGURED"
7864
+ ]
7865
+ },
7866
+ "details": {
7867
+ "type": "object",
7868
+ "additionalProperties": false,
7869
+ "required": [
7870
+ "condition_name"
7871
+ ],
7872
+ "properties": {
7873
+ "condition_name": {
7874
+ "type": "string",
7875
+ "description": "The condition whose vocabulary is not configured yet.",
7876
+ "example": "segment"
7877
+ }
7878
+ }
7879
+ }
7880
+ }
7881
+ },
7882
+ {
7883
+ "type": "object",
7884
+ "description": "A multi-match resolve found more variants than one response may carry. Narrow the\ncontext; the matches are not reported.\n",
7885
+ "required": [
7886
+ "code",
7887
+ "details"
7888
+ ],
7889
+ "properties": {
7890
+ "code": {
7891
+ "type": "string",
7892
+ "enum": [
7893
+ "TOO_MANY_MATCHES"
7894
+ ]
7895
+ },
7896
+ "details": {
7897
+ "type": "object",
7898
+ "additionalProperties": false,
7899
+ "required": [
7900
+ "limit"
7901
+ ],
7902
+ "properties": {
7903
+ "limit": {
7904
+ "type": "number",
7905
+ "description": "The most variants one resolve may compose.",
7906
+ "example": 100
7907
+ }
7908
+ }
7909
+ }
7910
+ }
7911
+ },
7912
+ {
7913
+ "type": "object",
7914
+ "description": "Transient write contention. Retryable, unlike `TUPLE_CONFLICT`. The revisions are\npresent where the contention was detected on a specific version.\n",
7915
+ "required": [
7916
+ "code",
7917
+ "details"
7918
+ ],
7919
+ "properties": {
7920
+ "code": {
7921
+ "type": "string",
7922
+ "enum": [
7923
+ "WRITE_CONFLICT"
7924
+ ]
7925
+ },
7926
+ "details": {
7927
+ "type": "object",
7928
+ "additionalProperties": false,
7929
+ "required": [
7930
+ "variant_id"
7931
+ ],
7932
+ "properties": {
7933
+ "variant_id": {
7934
+ "type": "string",
7935
+ "description": "The variant the write addressed.",
7936
+ "example": "var-46045"
7937
+ },
7938
+ "valid_from": {
7939
+ "type": "string",
7940
+ "description": "The version the write addressed, where one was addressed.",
7941
+ "example": "2027-01-01T00:00:00.000Z"
7942
+ },
7943
+ "expected_revision": {
7944
+ "type": "number",
7945
+ "description": "The revision the write required the stored version to still be at.",
7946
+ "example": 3
7947
+ },
7948
+ "current_revision": {
7949
+ "type": "number",
7950
+ "description": "The revision the version is actually at, where the failed write read it back.",
7951
+ "example": 4
7952
+ }
7953
+ }
7954
+ }
7955
+ }
7956
+ },
7957
+ {
7958
+ "type": "object",
7959
+ "description": "A listing's `from` plus `size` reaches past the offset window the search index\nallows — the window bounds the last row a page may contain. The page is not clamped;\npage on with the last response's `next` instead.\n",
7960
+ "required": [
7961
+ "code",
7962
+ "details"
7963
+ ],
7964
+ "properties": {
7965
+ "code": {
7966
+ "type": "string",
7967
+ "enum": [
7968
+ "OFFSET_WINDOW_EXCEEDED"
7969
+ ]
7970
+ },
7971
+ "details": {
7972
+ "type": "object",
7973
+ "additionalProperties": false,
7974
+ "required": [
7975
+ "from",
7976
+ "size",
7977
+ "window"
7978
+ ],
7979
+ "properties": {
7980
+ "from": {
7981
+ "type": "integer",
7982
+ "description": "The offset the request asked for.",
7983
+ "example": 24990
7984
+ },
7985
+ "size": {
7986
+ "type": "integer",
7987
+ "description": "The page size the request asked for, after clamping.",
7988
+ "example": 25
7989
+ },
7990
+ "window": {
7991
+ "type": "integer",
7992
+ "description": "The last row this deploy's index will serve from an offset.",
7993
+ "example": 25000
7994
+ }
7995
+ }
7996
+ }
7997
+ }
7998
+ },
7999
+ {
8000
+ "type": "object",
8001
+ "description": "A paging cursor could not be used for the read it arrived on. Start the read again\nwithout one; `details.reason` says which check failed.\n",
8002
+ "required": [
8003
+ "code",
8004
+ "details"
8005
+ ],
8006
+ "properties": {
8007
+ "code": {
8008
+ "type": "string",
8009
+ "enum": [
8010
+ "CURSOR_INVALID"
8011
+ ]
8012
+ },
8013
+ "details": {
8014
+ "type": "object",
8015
+ "additionalProperties": false,
8016
+ "required": [
8017
+ "reason"
8018
+ ],
8019
+ "properties": {
8020
+ "reason": {
8021
+ "type": "string",
8022
+ "description": "Which check the cursor failed, in prose.",
8023
+ "example": "The cursor was issued for a different sort order"
8024
+ }
8025
+ }
8026
+ }
8027
+ }
8028
+ },
8029
+ {
8030
+ "type": "object",
8031
+ "description": "The entity already holds every variant it may hold. In a batch, every remaining item\nfor that entity will be refused the same way.\n",
8032
+ "required": [
8033
+ "code",
8034
+ "details"
8035
+ ],
8036
+ "properties": {
8037
+ "code": {
8038
+ "type": "string",
8039
+ "enum": [
8040
+ "VARIANT_LIMIT_REACHED"
8041
+ ]
8042
+ },
8043
+ "details": {
8044
+ "type": "object",
8045
+ "additionalProperties": false,
8046
+ "required": [
8047
+ "variant_count",
8048
+ "cap"
8049
+ ],
8050
+ "properties": {
8051
+ "variant_count": {
8052
+ "type": "number",
8053
+ "description": "Variants this entity already holds.",
8054
+ "example": 5000
7486
8055
  },
7487
- "valid_from": {
7488
- "type": "string",
7489
- "description": "The instant already claimed by a version of that variant.",
7490
- "example": "2027-01-01T00:00:00.000Z"
8056
+ "cap": {
8057
+ "type": "number",
8058
+ "description": "Variants this entity may hold. Configurable per deploy.",
8059
+ "example": 5000
7491
8060
  }
7492
8061
  }
7493
8062
  }
@@ -7495,7 +8064,7 @@
7495
8064
  },
7496
8065
  {
7497
8066
  "type": "object",
7498
- "description": "A condition the entity's schema does not define, named by a resolve context, by a\nlisting's condition filter, or by a variant's pins.\n",
8067
+ "description": "A variant pins a value malformed for its condition's type. `condition_type` is what\na client branches on; `expected` says what the value had to be, which the type alone\ndoes not — a `location` is `location` whichever `format` it declares.\n",
7499
8068
  "required": [
7500
8069
  "code",
7501
8070
  "details"
@@ -7504,20 +8073,36 @@
7504
8073
  "code": {
7505
8074
  "type": "string",
7506
8075
  "enum": [
7507
- "CONDITION_UNDEFINED"
8076
+ "PIN_FORMAT_INVALID"
7508
8077
  ]
7509
8078
  },
7510
8079
  "details": {
7511
8080
  "type": "object",
7512
8081
  "additionalProperties": false,
7513
8082
  "required": [
7514
- "condition_name"
8083
+ "condition_name",
8084
+ "condition_type",
8085
+ "expected",
8086
+ "value"
7515
8087
  ],
7516
8088
  "properties": {
7517
8089
  "condition_name": {
7518
8090
  "type": "string",
7519
- "description": "The condition named by the request and absent from the schema.",
7520
- "example": "postal_code"
8091
+ "example": "valid_period"
8092
+ },
8093
+ "condition_type": {
8094
+ "type": "string",
8095
+ "description": "The type the schema declares that condition with.",
8096
+ "example": "daterange"
8097
+ },
8098
+ "expected": {
8099
+ "type": "string",
8100
+ "description": "What a pin for that condition has to be, in prose.",
8101
+ "example": "an object carrying a from and an until date, either may be open"
8102
+ },
8103
+ "value": {
8104
+ "description": "The value the write pinned, as it arrived.",
8105
+ "example": "2027-01-01/2027-12-31"
7521
8106
  }
7522
8107
  }
7523
8108
  }
@@ -7525,7 +8110,7 @@
7525
8110
  },
7526
8111
  {
7527
8112
  "type": "object",
7528
- "description": "The requested operator is not applicable to the condition's type.",
8113
+ "description": "The write pins no condition and is not marked `default`, so it would match every\nresolve. On a delete, the item addresses no variant at all.\n",
7529
8114
  "required": [
7530
8115
  "code",
7531
8116
  "details"
@@ -7534,31 +8119,20 @@
7534
8119
  "code": {
7535
8120
  "type": "string",
7536
8121
  "enum": [
7537
- "OPERATOR_UNSUPPORTED"
8122
+ "VARIANT_UNPINNED"
7538
8123
  ]
7539
8124
  },
7540
8125
  "details": {
7541
8126
  "type": "object",
7542
8127
  "additionalProperties": false,
7543
8128
  "required": [
7544
- "condition_name",
7545
- "condition_type",
7546
- "operator"
8129
+ "entity_id"
7547
8130
  ],
7548
8131
  "properties": {
7549
- "condition_name": {
7550
- "type": "string",
7551
- "example": "postal_code"
7552
- },
7553
- "condition_type": {
7554
- "type": "string",
7555
- "description": "The type the schema declares that condition with, which is what decides the\noperators it accepts.\n",
7556
- "example": "location"
7557
- },
7558
- "operator": {
8132
+ "entity_id": {
7559
8133
  "type": "string",
7560
- "description": "The operator the context or filter asked for.",
7561
- "example": "between"
8134
+ "description": "The conditional entity the item addressed.",
8135
+ "example": "price-sp26d1yo"
7562
8136
  }
7563
8137
  }
7564
8138
  }
@@ -7566,7 +8140,7 @@
7566
8140
  },
7567
8141
  {
7568
8142
  "type": "object",
7569
- "description": "A resolve context or listing filter value that is malformed for its condition's type.\n\n`details` says what the type requires, never what arrived: a resolve context value is\nnot quoted back.\n",
8143
+ "description": "The delete would leave the variant with no version at all, which would keep its\ncondition tuple claimed while resolving to nothing. Delete the variant instead.\n",
7570
8144
  "required": [
7571
8145
  "code",
7572
8146
  "details"
@@ -7575,25 +8149,26 @@
7575
8149
  "code": {
7576
8150
  "type": "string",
7577
8151
  "enum": [
7578
- "CONTEXT_FORMAT_INVALID"
8152
+ "LAST_VERSION_UNDELETABLE"
7579
8153
  ]
7580
8154
  },
7581
8155
  "details": {
7582
8156
  "type": "object",
7583
8157
  "additionalProperties": false,
7584
8158
  "required": [
7585
- "condition_name",
7586
- "expected"
8159
+ "variant_id",
8160
+ "valid_from"
7587
8161
  ],
7588
8162
  "properties": {
7589
- "condition_name": {
8163
+ "variant_id": {
7590
8164
  "type": "string",
7591
- "example": "postal_code"
8165
+ "description": "The variant whose last version the delete addressed.",
8166
+ "example": "var-46045"
7592
8167
  },
7593
- "expected": {
8168
+ "valid_from": {
7594
8169
  "type": "string",
7595
- "description": "What a value for that condition has to be, in prose.",
7596
- "example": "a postal code"
8170
+ "description": "The version the delete addressed, by the instant it takes effect from.",
8171
+ "example": "2027-01-01T00:00:00.000Z"
7597
8172
  }
7598
8173
  }
7599
8174
  }
@@ -7601,7 +8176,7 @@
7601
8176
  },
7602
8177
  {
7603
8178
  "type": "object",
7604
- "description": "A variant write pins a `select` value the condition's `options` do not admit — either\na value a declared vocabulary does not contain, or any value at all where the\ncondition declares no vocabulary for it to be in.\n\nOne of the two codes that report the submitted value back — `PIN_FORMAT_INVALID` is\nthe other. A resolve context value is never quoted back.\n",
8179
+ "description": "A condition the entity's schema declares in a way this deploy cannot read: a\n`location` whose `format` is unrecognized or absent, or a `select` whose `options`\nhold nothing readable. Repair the definition on the schema.\n\nReads that do not interpret the condition — an unfiltered variants list, the tree, a\nvariant by id, a version timeline — keep working.\n",
7605
8180
  "required": [
7606
8181
  "code",
7607
8182
  "details"
@@ -7610,7 +8185,7 @@
7610
8185
  "code": {
7611
8186
  "type": "string",
7612
8187
  "enum": [
7613
- "CONDITION_VALUE_INVALID"
8188
+ "CONDITION_UNREADABLE"
7614
8189
  ]
7615
8190
  },
7616
8191
  "details": {
@@ -7618,28 +8193,22 @@
7618
8193
  "additionalProperties": false,
7619
8194
  "required": [
7620
8195
  "condition_name",
7621
- "value",
7622
- "options"
8196
+ "unreadable"
7623
8197
  ],
7624
8198
  "properties": {
7625
8199
  "condition_name": {
7626
8200
  "type": "string",
7627
- "example": "segment"
8201
+ "description": "The condition whose definition this deploy cannot read.",
8202
+ "example": "delivery_area"
7628
8203
  },
7629
- "value": {
7630
- "description": "The value the write pinned, as it arrived. Declared without a type: the\nvocabulary holds strings, so anything else is out of it by definition and is\nreported as sent.\n",
7631
- "example": "industrial"
7632
- },
7633
- "options": {
7634
- "type": "array",
7635
- "description": "The vocabulary *as enforced* — after the entries this deploy cannot read have\nbeen dropped, so a tenant whose `options` holds a title-only entry is told\nwhat the API actually checked against rather than what they believe they\nwrote. Empty when the condition declares no vocabulary at all, which is\nitself the reason the pin was refused; the message says which of the two\n(unconfigured, or unreadable) applies.\n",
7636
- "items": {
7637
- "type": "string"
7638
- },
7639
- "example": [
7640
- "private",
7641
- "commercial"
7642
- ]
8204
+ "unreadable": {
8205
+ "type": "string",
8206
+ "description": "Which field of the definition cannot be read, named as the schema spells it.",
8207
+ "enum": [
8208
+ "format",
8209
+ "options"
8210
+ ],
8211
+ "example": "format"
7643
8212
  }
7644
8213
  }
7645
8214
  }
@@ -7647,7 +8216,7 @@
7647
8216
  },
7648
8217
  {
7649
8218
  "type": "object",
7650
- "description": "A multi-match resolve found more variants than one response may carry. Narrowing the\ncontext is the only fix; the matches are not reported.\n",
8219
+ "description": "A listing's `sort` is not a field and direction this API can read. A well-formed\n`sort` naming a condition is refused under that condition's own code instead.\n",
7651
8220
  "required": [
7652
8221
  "code",
7653
8222
  "details"
@@ -7656,20 +8225,20 @@
7656
8225
  "code": {
7657
8226
  "type": "string",
7658
8227
  "enum": [
7659
- "TOO_MANY_MATCHES"
8228
+ "SORT_INVALID"
7660
8229
  ]
7661
8230
  },
7662
8231
  "details": {
7663
8232
  "type": "object",
7664
8233
  "additionalProperties": false,
7665
8234
  "required": [
7666
- "limit"
8235
+ "expected"
7667
8236
  ],
7668
8237
  "properties": {
7669
- "limit": {
7670
- "type": "number",
7671
- "description": "The most variants one resolve may compose.",
7672
- "example": 100
8238
+ "expected": {
8239
+ "type": "string",
8240
+ "description": "What a `sort` has to be, in prose.",
8241
+ "example": "conditions.<name>:asc or conditions.<name>:desc, naming a string, select, number or date condition"
7673
8242
  }
7674
8243
  }
7675
8244
  }
@@ -7677,7 +8246,7 @@
7677
8246
  },
7678
8247
  {
7679
8248
  "type": "object",
7680
- "description": "Transient write contention — concurrent writers, or throughput pressure on the\nentity's own rows. Retryable, unlike `TUPLE_CONFLICT`.\n\nThe revisions are present where the contention was detected on a specific version: a\nwrite carrying `_revision` lost to another that landed first, and a client that read\nthe version again would see `current_revision`. A variant-level refusal carries the\nvariant alone.\n",
8249
+ "description": "The fallback marker — `default` or `_default` — used as though it were a condition.\nMark the variant `default` instead, or leave the marker out of the context.\n",
7681
8250
  "required": [
7682
8251
  "code",
7683
8252
  "details"
@@ -7686,35 +8255,20 @@
7686
8255
  "code": {
7687
8256
  "type": "string",
7688
8257
  "enum": [
7689
- "WRITE_CONFLICT"
8258
+ "DEFAULT_MARKER_RESERVED"
7690
8259
  ]
7691
8260
  },
7692
8261
  "details": {
7693
8262
  "type": "object",
7694
8263
  "additionalProperties": false,
7695
8264
  "required": [
7696
- "variant_id"
8265
+ "condition_name"
7697
8266
  ],
7698
8267
  "properties": {
7699
- "variant_id": {
7700
- "type": "string",
7701
- "description": "The variant the write addressed.",
7702
- "example": "var-46045"
7703
- },
7704
- "valid_from": {
8268
+ "condition_name": {
7705
8269
  "type": "string",
7706
- "description": "The version the write addressed, where one was addressed.",
7707
- "example": "2027-01-01T00:00:00.000Z"
7708
- },
7709
- "expected_revision": {
7710
- "type": "number",
7711
- "description": "The revision the write required the stored version to still be at.",
7712
- "example": 3
7713
- },
7714
- "current_revision": {
7715
- "type": "number",
7716
- "description": "The revision the version is actually at, where the failed write read it back.\nAbsent when it could not be.\n",
7717
- "example": 4
8270
+ "description": "The marker, spelled as the request spelled it.",
8271
+ "example": "default"
7718
8272
  }
7719
8273
  }
7720
8274
  }
@@ -7722,7 +8276,7 @@
7722
8276
  },
7723
8277
  {
7724
8278
  "type": "object",
7725
- "description": "A listing asked for a page reaching past the window the search index allows.\n\n**`from` plus `size`**, not `from` alone: the window bounds the last row a page may\ncontain, so the final servable offset is `window` minus the page size. All three\nnumbers are in `details`, because a refusal quoting only an offset below the window\nreads like a mistake.\n\nNot a page clamped back inside the window, as entity listing does. The fix is a\ndifferent request: page on with the last response's `next`.\n\nThe window belongs to the deploy's search index, so it is reported and never\npublished.\n\nRaised by the paginated variant reads, which answer `501` until their behaviour\nlands.\n",
8279
+ "description": "A variant marked `default` that also pins real conditions. Drop `default` to keep\nthe pins, or drop the pins to keep the fallback.\n",
7726
8280
  "required": [
7727
8281
  "code",
7728
8282
  "details"
@@ -7731,32 +8285,25 @@
7731
8285
  "code": {
7732
8286
  "type": "string",
7733
8287
  "enum": [
7734
- "OFFSET_WINDOW_EXCEEDED"
8288
+ "DEFAULT_VARIANT_PINS_CONDITIONS"
7735
8289
  ]
7736
8290
  },
7737
8291
  "details": {
7738
8292
  "type": "object",
7739
8293
  "additionalProperties": false,
7740
8294
  "required": [
7741
- "from",
7742
- "size",
7743
- "window"
8295
+ "condition_names"
7744
8296
  ],
7745
8297
  "properties": {
7746
- "from": {
7747
- "type": "integer",
7748
- "description": "The offset the request asked for.",
7749
- "example": 24990
7750
- },
7751
- "size": {
7752
- "type": "integer",
7753
- "description": "The page size the request asked for, after clamping. Present because the two\ntogether are what exceeded the window — an offset inside it can still be\nrefused for the page it would have to read.\n",
7754
- "example": 25
7755
- },
7756
- "window": {
7757
- "type": "integer",
7758
- "description": "The last row this deploy's index will serve from an offset. Read it to size a\npage control, never to decide when to switch to the cursor — a caller can page\non with `next` from any page.\n",
7759
- "example": 25000
8298
+ "condition_names": {
8299
+ "type": "array",
8300
+ "description": "The conditions the write pinned beside the marker.",
8301
+ "items": {
8302
+ "type": "string"
8303
+ },
8304
+ "example": [
8305
+ "postal_code"
8306
+ ]
7760
8307
  }
7761
8308
  }
7762
8309
  }
@@ -7764,7 +8311,7 @@
7764
8311
  },
7765
8312
  {
7766
8313
  "type": "object",
7767
- "description": "A paging cursor could not be used for the read it arrived on.\n\nOne code for every way that happens, because the caller's fix is the same for all of\nthem: start the read again without a cursor. `details.reason` says which check failed,\nfor a human reading a log rather than for a client to branch on — a cursor this API\nminted and a caller stored can go stale, be truncated in transit, be replayed against\na different filter or sort, or be replayed against another variant or the opposite\norder on a versions read.\n\nRaised by the paginated variant and version reads, which answer `501` until their\nbehaviour lands.\n",
8314
+ "description": "A version write asking for a different `valid_from` than the version its own address\nnames. Moving a version is an append and a delete; a body repeating the `valid_from`\nit was read with is accepted.\n",
7768
8315
  "required": [
7769
8316
  "code",
7770
8317
  "details"
@@ -7773,20 +8320,26 @@
7773
8320
  "code": {
7774
8321
  "type": "string",
7775
8322
  "enum": [
7776
- "CURSOR_INVALID"
8323
+ "VALID_FROM_IMMUTABLE"
7777
8324
  ]
7778
8325
  },
7779
8326
  "details": {
7780
8327
  "type": "object",
7781
8328
  "additionalProperties": false,
7782
8329
  "required": [
7783
- "reason"
8330
+ "addressed",
8331
+ "requested"
7784
8332
  ],
7785
8333
  "properties": {
7786
- "reason": {
8334
+ "addressed": {
7787
8335
  "type": "string",
7788
- "description": "Which check the cursor failed, in prose.",
7789
- "example": "The cursor was issued for a different sort order"
8336
+ "description": "The version the request addressed, by the instant it takes effect from.",
8337
+ "example": "2027-01-01T00:00:00.000Z"
8338
+ },
8339
+ "requested": {
8340
+ "type": "string",
8341
+ "description": "The instant the body asked for instead, canonicalized.",
8342
+ "example": "2027-04-01T00:00:00.000Z"
7790
8343
  }
7791
8344
  }
7792
8345
  }
@@ -7794,7 +8347,7 @@
7794
8347
  },
7795
8348
  {
7796
8349
  "type": "object",
7797
- "description": "The entity already holds every variant it may hold.\n\nA hard refusal, unlike the `VARIANT_COUNT_APPROACHING_CAP` warning that precedes it,\ncarrying the warning's two keys. In a batch it means stop the import rather than fix\na row: every remaining item for that entity will be refused the same way.\n\nEmitted by `$createConditionalVariant`. The batch writes that will also raise it\nanswer `501` until their handlers land.\n",
8350
+ "description": "A version write carrying a condition tuple other than the one its variant was\ncreated with. Create a second variant for the other situation.\n",
7798
8351
  "required": [
7799
8352
  "code",
7800
8353
  "details"
@@ -7803,26 +8356,20 @@
7803
8356
  "code": {
7804
8357
  "type": "string",
7805
8358
  "enum": [
7806
- "VARIANT_LIMIT_REACHED"
8359
+ "VARIANT_CONDITIONS_IMMUTABLE"
7807
8360
  ]
7808
8361
  },
7809
8362
  "details": {
7810
8363
  "type": "object",
7811
8364
  "additionalProperties": false,
7812
8365
  "required": [
7813
- "variant_count",
7814
- "cap"
8366
+ "variant_id"
7815
8367
  ],
7816
8368
  "properties": {
7817
- "variant_count": {
7818
- "type": "number",
7819
- "description": "Variants this entity already holds.",
7820
- "example": 5000
7821
- },
7822
- "cap": {
7823
- "type": "number",
7824
- "description": "Variants this entity may hold. Configurable per deploy, the same value for\nevery organization on it.\n",
7825
- "example": 5000
8369
+ "variant_id": {
8370
+ "type": "string",
8371
+ "description": "The variant whose conditions the write would have changed.",
8372
+ "example": "var-46045"
7826
8373
  }
7827
8374
  }
7828
8375
  }
@@ -7830,7 +8377,7 @@
7830
8377
  },
7831
8378
  {
7832
8379
  "type": "object",
7833
- "description": "A variant pins a value that is malformed for its condition's type.\n\nThe write-side mirror of `CONTEXT_FORMAT_INVALID`: a context value is *matched*, a\nvariant's value is *pinned*. Unlike that one it reports the value back, as\n`CONDITION_VALUE_INVALID` does.\n\nDistinct from a condition whose *declared type* this deploy cannot read at all: that\nis a schema problem rather than a bad row, every item naming the condition fails\nidentically, and it stays uncoded.\n\nIt carries `expected` as well as `condition_type`, and the two are not the same fact:\na `location` condition is `condition_type: location` whichever format it declares,\nand the two formats want different values — a postal code, or an object carrying a\npostal code and a town. The type is what a client branches on; `expected` is what\nsays what the value had to be.\n\nEmitted by `$createConditionalVariant`. The batch writes that will also raise it\nanswer `501` until their handlers land.\n",
8380
+ "description": "An id in the request that cannot be used as a storage key: empty, longer than 128\ncharacters, or carrying a character outside letters, digits, `_`, `.`, `:` and `-`.\nEvery id the platform issues qualifies.\n",
7834
8381
  "required": [
7835
8382
  "code",
7836
8383
  "details"
@@ -7839,36 +8386,30 @@
7839
8386
  "code": {
7840
8387
  "type": "string",
7841
8388
  "enum": [
7842
- "PIN_FORMAT_INVALID"
8389
+ "IDENTIFIER_INVALID"
7843
8390
  ]
7844
8391
  },
7845
8392
  "details": {
7846
8393
  "type": "object",
7847
8394
  "additionalProperties": false,
7848
8395
  "required": [
7849
- "condition_name",
7850
- "condition_type",
7851
- "expected",
7852
- "value"
8396
+ "field",
8397
+ "reason"
7853
8398
  ],
7854
8399
  "properties": {
7855
- "condition_name": {
7856
- "type": "string",
7857
- "example": "valid_period"
7858
- },
7859
- "condition_type": {
8400
+ "field": {
7860
8401
  "type": "string",
7861
- "description": "The type the schema declares that condition with, which is what decides the\nvalues it accepts.\n",
7862
- "example": "daterange"
8402
+ "description": "Which id could not be keyed by, named as the request names it.",
8403
+ "enum": [
8404
+ "entity_id",
8405
+ "variant_id"
8406
+ ],
8407
+ "example": "entity_id"
7863
8408
  },
7864
- "expected": {
8409
+ "reason": {
7865
8410
  "type": "string",
7866
- "description": "What a pin for that condition has to be, in prose — the same field\n`CONTEXT_FORMAT_INVALID` carries, worded for the write side. It says what\n`condition_type` cannot: a `location` of format `zipcode` wants a postal code\nand one of format `zipcode_town` wants an object carrying both, and the type\nis `location` either way.\n",
7867
- "example": "an object carrying a from and an until date, either may be open"
7868
- },
7869
- "value": {
7870
- "description": "The value the write pinned, as it arrived. Declared without a type, since\nwhat makes it invalid is that it is not of the condition's type.\n",
7871
- "example": "2027-01-01/2027-12-31"
8411
+ "description": "Which of the three checks the id failed, in prose.",
8412
+ "example": "it carries a character this scheme does not admit"
7872
8413
  }
7873
8414
  }
7874
8415
  }
@@ -7876,7 +8417,7 @@
7876
8417
  },
7877
8418
  {
7878
8419
  "type": "object",
7879
- "description": "The write pins no condition and is not marked `default`.\n\nSuch a variant would be a universal wildcard matching every resolve, which is a far\nmore dangerous thing than a fallback and far easier to create by accident — an empty\npostal-code column in a source file produces exactly this.\n\nRaised on a delete too, where an item addresses no variant at all — no `variant_id`,\nno `default`, and either no `conditions` or an empty one: the item names no target.\nIt is an `error`, not a `skipped`.\n\nEmitted by `$createConditionalVariant`. Batch upsert and batch delete, which raise\nit too, answer `501` until their handlers land.\n",
8420
+ "description": "A `valid_from` that is not one of the timestamp forms a version timeline can be\nsorted by. Wider ISO 8601 forms — a bare year, an ordinal date, `T24:00:00Z` — are\nrefused rather than interpreted.\n",
7880
8421
  "required": [
7881
8422
  "code",
7882
8423
  "details"
@@ -7885,20 +8426,20 @@
7885
8426
  "code": {
7886
8427
  "type": "string",
7887
8428
  "enum": [
7888
- "VARIANT_UNPINNED"
8429
+ "VALID_FROM_INVALID"
7889
8430
  ]
7890
8431
  },
7891
8432
  "details": {
7892
8433
  "type": "object",
7893
8434
  "additionalProperties": false,
7894
8435
  "required": [
7895
- "entity_id"
8436
+ "expected"
7896
8437
  ],
7897
8438
  "properties": {
7898
- "entity_id": {
8439
+ "expected": {
7899
8440
  "type": "string",
7900
- "description": "The conditional entity the item addressed.",
7901
- "example": "price-sp26d1yo"
8441
+ "description": "What a `valid_from` has to be, in prose.",
8442
+ "example": "an RFC 3339 date, optionally with a time to at most millisecond precision and an optional UTC offset"
7902
8443
  }
7903
8444
  }
7904
8445
  }
@@ -7906,7 +8447,7 @@
7906
8447
  },
7907
8448
  {
7908
8449
  "type": "object",
7909
- "description": "The delete would leave the variant with no version at all.\n\nSuch a variant would still hold its condition tuple and still be selectable, and\nwould then resolve to nothing — a variant delete wearing a version delete's clothes.\nDelete the variant instead; that frees the tuple too.\n\nEmitted by `$deleteConditionalVariantVersion`. Batch delete, which raises it too,\nanswers `501` until its handler lands.\n",
8450
+ "description": "A value the store cannot hold: a non-finite number, one outside the table's numeric\nrange, or a key with no value. Send a value needing more precision than a JSON\nnumber carries as a decimal string, as `unit_amount_decimal` does.\n",
7910
8451
  "required": [
7911
8452
  "code",
7912
8453
  "details"
@@ -7915,26 +8456,26 @@
7915
8456
  "code": {
7916
8457
  "type": "string",
7917
8458
  "enum": [
7918
- "LAST_VERSION_UNDELETABLE"
8459
+ "VALUE_UNSTORABLE"
7919
8460
  ]
7920
8461
  },
7921
8462
  "details": {
7922
8463
  "type": "object",
7923
8464
  "additionalProperties": false,
7924
8465
  "required": [
7925
- "variant_id",
7926
- "valid_from"
8466
+ "path",
8467
+ "reason"
7927
8468
  ],
7928
8469
  "properties": {
7929
- "variant_id": {
8470
+ "path": {
7930
8471
  "type": "string",
7931
- "description": "The variant whose last version the delete addressed.",
7932
- "example": "var-46045"
8472
+ "description": "Where the value sits, as a dotted path of the request's own keys, with array\nentries by index.\n",
8473
+ "example": "values.tiers.0.unit_amount"
7933
8474
  },
7934
- "valid_from": {
8475
+ "reason": {
7935
8476
  "type": "string",
7936
- "description": "The version the delete addressed, by the instant it takes effect from.",
7937
- "example": "2027-01-01T00:00:00.000Z"
8477
+ "description": "What about the value cannot be stored, in prose.",
8478
+ "example": "the non-finite number Infinity"
7938
8479
  }
7939
8480
  }
7940
8481
  }
@@ -7942,7 +8483,7 @@
7942
8483
  },
7943
8484
  {
7944
8485
  "type": "object",
7945
- "description": "A failure the vocabulary has no entry for: a malformed request body, a value that is\nnot readable as its condition's type, a rule refusing a write for a reason a client\ncannot branch on. The message says what to fix, and no code is sent.\n\nCarries what every response carries and nothing else — reading `code` on this member\nis how a client tells it from the coded members above.\n",
8486
+ "description": "A refusal the request validator raises, answered before any handler runs. The\nmessage says what to fix, and no code is sent — which is how a client tells this\nmember from the coded ones.\n",
7946
8487
  "additionalProperties": false,
7947
8488
  "required": [
7948
8489
  "message"
@@ -10373,6 +10914,10 @@
10373
10914
  "description": "The optional reference date for the price computation (ISO 8601 format)",
10374
10915
  "type": "string",
10375
10916
  "format": "date"
10917
+ },
10918
+ "city": {
10919
+ "description": "The city the postal code belongs to. Not used for price computation,\nonly echoed back in `inputs` for display purposes.\n",
10920
+ "type": "string"
10376
10921
  }
10377
10922
  },
10378
10923
  "required": [
@@ -10612,6 +11157,14 @@
10612
11157
  "breakdown": {
10613
11158
  "$ref": "#/components/schemas/ComputedPriceBreakdown"
10614
11159
  },
11160
+ "inputs": {
11161
+ "description": "A snapshot of the parameters this price was computed from, for display purposes\n(e.g. showing \"computed for 3,500 kWh/year\"). Included in the `_meta` signature.\n",
11162
+ "allOf": [
11163
+ {
11164
+ "$ref": "#/components/schemas/ComputePriceInputs"
11165
+ }
11166
+ ]
11167
+ },
10615
11168
  "_meta": {
10616
11169
  "$ref": "#/components/schemas/SignatureMeta"
10617
11170
  }
@@ -10624,6 +11177,49 @@
10624
11177
  "breakdown"
10625
11178
  ]
10626
11179
  },
11180
+ "ComputePriceInputs": {
11181
+ "type": "object",
11182
+ "description": "Echo of the request parameters used to compute the price, in the caller-facing shape.",
11183
+ "properties": {
11184
+ "type": {
11185
+ "$ref": "#/components/schemas/ProductCategory"
11186
+ },
11187
+ "consumptionHT": {
11188
+ "type": "number"
11189
+ },
11190
+ "consumptionNT": {
11191
+ "type": "number"
11192
+ },
11193
+ "consumptionType": {
11194
+ "$ref": "#/components/schemas/ConsumptionTypeGetAg"
11195
+ },
11196
+ "zipCode": {
11197
+ "type": "string"
11198
+ },
11199
+ "city": {
11200
+ "type": "string"
11201
+ },
11202
+ "providerId": {
11203
+ "type": "string"
11204
+ },
11205
+ "billingPeriod": {
11206
+ "type": "string",
11207
+ "enum": [
11208
+ "weekly",
11209
+ "monthly",
11210
+ "every_quarter",
11211
+ "every_6_months",
11212
+ "yearly",
11213
+ "one_time"
11214
+ ]
11215
+ },
11216
+ "referenceDate": {
11217
+ "type": "string",
11218
+ "format": "date"
11219
+ }
11220
+ },
11221
+ "additionalProperties": true
11222
+ },
10627
11223
  "SpotMarketBiddingZone": {
10628
11224
  "description": "The bidding zone for a spot market price.",
10629
11225
  "type": "string",
@@ -14057,6 +14653,23 @@
14057
14653
  "items": {
14058
14654
  "$ref": "#/components/schemas/ProductRecommendation"
14059
14655
  }
14656
+ },
14657
+ "source": {
14658
+ "type": "object",
14659
+ "description": "Context about what the recommendations were searched against.",
14660
+ "properties": {
14661
+ "item": {
14662
+ "description": "The first line item of the contract used as source for the recommendation.\nCarries the amounts the customer currently pays; only present when searching by contract_id.\n",
14663
+ "anyOf": [
14664
+ {
14665
+ "$ref": "#/components/schemas/PriceItem"
14666
+ },
14667
+ {
14668
+ "$ref": "#/components/schemas/CompositePriceItem"
14669
+ }
14670
+ ]
14671
+ }
14672
+ }
14060
14673
  }
14061
14674
  },
14062
14675
  "required": [