API referenceconfig
Publish rule set
PUT
/v1/config/rulesIdempotency-Key requiredAccepts X-Api-Key · oauthClientCredentials · bearer
Publish a new immutable rule-set version
Mints version baseVersion+1 from the full rule list in the body; prior versions stay readable forever.Show more
Mints version baseVersion+1 from the full rule list in the body; prior versions stay readable forever. Optimistic concurrency: `baseVersion` must equal the current published version, otherwise 409 CONFLICT with details.currentVersion — re-read, re-simulate, re-publish. Two rules tying at the same layer/priority/specificity, or a lower layer widening a higher layer's cap, are 400 VALIDATION at publish time — never a runtime coin-flip. Negative-margin guard: when the quoted simulation priced any line at a negative margin, publish is 409 CONFLICT (details.reason NEGATIVE_MARGIN_OVERRIDE_REQUIRED, with details.simulationId and details.negativeMarginLines) unless the body sets negativeMarginOverride true AND gives a reason; an override with no reason (and no note) is 400 VALIDATION details.reason REASON_REQUIRED. The reason and the override are recorded on the version and on config.rules_published, not echoed on RuleSet. Emits config.rules_published on the event envelope. Requires rules:manage.Headers
- Idempotency-KeystringRequired
Mandatory on mutations: **48h TTL**; replay with same body-hash returns the original response with Idempotency-Replayed true; same key + different body is 409 IDEMPOTENCY_CONFLICT; in-flight duplicate…Show more
Mandatory on mutations: **48h TTL**; replay with same body-hash returns the original response with Idempotency-Replayed true; same key + different body is 409 IDEMPOTENCY_CONFLICT; in-flight duplicate is 409 IDEMPOTENCY_IN_PROGRESS.
Request body
- baseVersionintegerRequiredMust equal the current published version, else 409 CONFLICT (details.currentVersion)
- simulationIdstringOptional
- notestringOptionalChange note shown in the console version history
- reasonstringOptional
Why this version is published. Mandatory with negativeMarginOverride — an override with no reason is 400 VALIDATION details.reason REASON_REQUIRED (a note stands in for it on an ordinary publish);…Show more
Why this version is published. Mandatory with negativeMarginOverride — an override with no reason is 400 VALIDATION details.reason REASON_REQUIRED (a note stands in for it on an ordinary publish); longer than 500 characters is 400 VALIDATION INVALID_RULE. Recorded on the version and on config.rules_published; not echoed on RuleSet. - negativeMarginOverridebooleanOptionalDefault false
Explicit override of the negative-margin guard: true publishes a rule list whose quoted simulation priced lines below their base.Show more
Explicit override of the negative-margin guard: true publishes a rule list whose quoted simulation priced lines below their base. Without it such a publish is 409 CONFLICT details.reason NEGATIVE_MARGIN_OVERRIDE_REQUIRED. - rulesarray of MarkupRuleRequired
6 child attributes
- ruleIdstringRequiredStable across versions; the id appliedRules[] quotes
- layerstringRequiredWhere the rule sits; lower layers (ORG, SITE) can narrow a higher layer's rule, never widen it.
- PLATFORM
- TENANT
- ORG
- SITE
- scopeMarkupRuleScopeRequired
Where a rule applies. Every field optional; an absent field matches everything; more fields = more specific.Show more
Where a rule applies. Every field optional; an absent field matches everything; more fields = more specific. Tenant is implied by the token, never sent.9 child attributes
- domainstringOptionalCanonical product code
- supplierstringOptionalOnly offers of this supplier.
- channelstringOptionalOnly sales through this channel, for example API, CONSOLE or AI.
- siteIdstringOptionalOnly sales on this site.
- posstringOptionalPoint-of-sale market
- currencystringOptionalISO-4217 of the resolved display currency
- productClassstringOptionalOnly offers of this product class.
- validFromstring · date-timeOptionalThe rule applies from this time.
- validTostring · date-timeOptionalThe rule applies until this time.
- actionMarkupRuleActionRequired
What a rule does. `value` is always an integer — no floats anywhere near money: basis points for the *_PCT kinds (1250 = 12.50%); minor units of `currency` for MARKUP_ABS and FEE_LINE; the rounding…Show more
What a rule does. `value` is always an integer — no floats anywhere near money: basis points for the *_PCT kinds (1250 = 12.50%); minor units of `currency` for MARKUP_ABS and FEE_LINE; the rounding increment in minor units for ROUND.5 child attributes
- kindstringRequiredWhat the rule does to the price, a percentage or absolute markup, a commission, a fee line or rounding.
- MARKUP_PCT
- MARKUP_ABS
- COMMISSION_PCT
- FEE_LINE
- ROUND
- valueintegerRequiredAn integer, in basis points for the percentage kinds (1250 is 12.50%) and in minor units of the currency otherwise.
- currencystringOptionalISO-4217; required for MARKUP_ABS and FEE_LINE when scope.currency is absent
- feeTypestringOptionalFEE_LINE only — the breakdown label the fee renders under
- roundingModestringOptionalROUND only; default UP
- UP
- DOWN
- HALF_UP
- floorMarkupRuleFloorOptional
The floor primitive resolving conflict C-5: after rule application sell:= max(sell, floor).Show more
The floor primitive resolving conflict C-5: after rule application sell:= max(sell, floor). SUPPLIER_SELL_FLOOR takes the offer's supplier-imposed floor; ABS is a tenant-policy floor in `value`. Floor enforcement is law — there is no flag to disable it.2 child attributes
- kindstringRequiredThe floor to keep, the supplier's own sell floor or an absolute amount.
- SUPPLIER_SELL_FLOOR
- ABS
- valueMoneyOptionalInteger minor units + ISO-4217, never floats (platform law)
5 child attributes
- amountintegerRequiredMinor units
- currencystringRequiredISO-4217
- fxAppliedbooleanOptionalTrue when the amount was converted from the supplier's currency.
- fxSnapshotIdstringOptionalThe exchange-rate snapshot the amount was converted with, fixed when the price was quoted.
- breakdownarray of PriceBreakdownLineOptional
Sell-side component lines summing to amount; the platform may send it empty.Show more
Sell-side component lines summing to amount; the platform may send it empty. One line shape everywhere — the same PriceBreakdownLine the rule simulation answers. Never carries a supplier net.
- priorityintegerRequiredHigher wins within a layer at equal specificity
Responses
- 200Published; the body is the new versionRuleSet
- 400VALIDATION. details.reason names the rule that failed: INVALID_RULE, RULE_TIE, LAYER_AUTHORITY, CAP_EXCEEDED, or REASON_REQUIRED when negativeMarginOverride is true with no reason.ApiErrorResponse
- 401ApiErrorResponse
UNAUTHENTICATED — missing or expired token.Show more
UNAUTHENTICATED — missing or expired token. Declared on every operation because `security` is declared globally at the document root: every route is authenticated, so every route can answer 401. - 403ApiErrorResponse
AGENT_TIER_INSUFFICIENT, also 403, is a request whose agent tier does not allow the tool class; it carries nextAction verify_agent and details.tier, details.required, details.toolClass and…Show more
AGENT_TIER_INSUFFICIENT, also 403, is a request whose agent tier does not allow the tool class; it carries nextAction verify_agent and details.tier, details.required, details.toolClass and details.operationId. A ceiling only narrows an unverified caller and never widens a scope. - 405ApiErrorResponse
VALIDATION — the public edge serves GET, POST, PUT, PATCH and DELETE only; any other method is refused before the platform sees it (details.reason METHOD_NOT_ALLOWED, details.source edge).Show more
VALIDATION — the public edge serves GET, POST, PUT, PATCH and DELETE only; any other method is refused before the platform sees it (details.reason METHOD_NOT_ALLOWED, details.source edge). Not retryable as sent. - 409ApiErrorResponse
CONFLICT (stale baseVersion, no matching simulation, or a negative-margin simulation without an override — details.reason STALE_VERSION | SIMULATION_REQUIRED | NEGATIVE_MARGIN_OVERRIDE_REQUIRED) |…Show more
CONFLICT (stale baseVersion, no matching simulation, or a negative-margin simulation without an override — details.reason STALE_VERSION | SIMULATION_REQUIRED | NEGATIVE_MARGIN_OVERRIDE_REQUIRED) | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS - 413ApiErrorResponse
VALIDATION — the request body is larger than the surface's limit.Show more
VALIDATION — the request body is larger than the surface's limit. Not retryable as sent: shrink the body. Declared on every operation that takes a request body. details carries exactly one key, maxBodyBytes (integer): the limit that applies to this surface, in bytes. - 414ApiErrorResponse
VALIDATION — the request URI (path and query string) is longer than 8 KiB; the public edge refuses it before the platform sees it (details.reason URI_TOO_LONG, details.source edge).Show more
VALIDATION — the request URI (path and query string) is longer than 8 KiB; the public edge refuses it before the platform sees it (details.reason URI_TOO_LONG, details.source edge). Not retryable as sent: send long input in the body of a POST. - 415ApiErrorResponse
VALIDATION — a request body that is not JSON (application/json or application/*+json; a form body only on the OAuth token routes).Show more
VALIDATION — a request body that is not JSON (application/json or application/*+json; a form body only on the OAuth token routes). The public edge refuses it before the platform sees it (details.reason UNSUPPORTED_MEDIA_TYPE, details.source edge). Not retryable as sent: send Content-Type application/json. Declared on every operation that takes a JSON request body. - 500INTERNAL — a bug, and traceId is the support handle.ApiErrorResponse
Errors you should handle
Branch on code and honour retryable — never on the HTTP status alone.
| 400 | VALIDATION Rules rule_tnt_htl_10pct and rule_tnt_htl_12pct tie at TENANT priority 100 with the same scope | not retryable |
| 401 | UNAUTHENTICATED The bearer token is expired. Re-authenticate and retry. | not retryable |
| 403 | PERMISSION_DENIED Role lacks the required scope: booking:write | not retryable |
| 403 | TAB_DISABLED Tenant acme-travel is not entitled to sell CRU | not retryable |
| 403 | OUT_OF_SCOPE The token's scope ladder does not reach this customer subtree | not retryable |
| 403 | TENANT_MISMATCH orderRef MO-8KD-2FQ-9XT does not belong to tenant acme-travel | not retryable |
| 405 | VALIDATION This method is not served. | not retryable |
| 409 | CONFLICT Simulation sim_9e31b4 priced 2 lines at a negative margin. Set negativeMarginOverride and give a reason to publish. | not retryable |
| 409 | IDEMPOTENCY_CONFLICT Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed | not retryable |
| 409 | IDEMPOTENCY_IN_PROGRESS A request with this Idempotency-Key is still executing | retryable |
| 413 | VALIDATION The request body is larger than this API accepts (262144 bytes). | not retryable |
| 414 | VALIDATION The request URI is longer than 8 KiB. | not retryable |
| 415 | VALIDATION Send application/json. | not retryable |
| 500 | INTERNAL An unexpected error occurred. Quote the traceId to support. | retryable |