Updates description and/or rules on a DRAFT ruleset. Only DRAFT rulesets are editable. context and schema are identity fields fixed at creation — submitting a changed value rejects with 422 naming the field; resubmitting the current value unchanged is a no-op. Rebinding a context is a new ruleset, not an update.
{
"classifier": "UNAUTHORIZED",
"code": 401,
"message": "No valid means of authentication was provided"
}
{
"classifier": "FORBIDDEN",
"code": 403,
"message": "Not allowed to access this resource or feature"
}
{
"classifier": "NOT_FOUND",
"code": 404,
"message": "The requested resource does not exist"
}
{
"classifier": "VALIDATION_ERROR",
"code": 422,
"validation_errors": [
{
"message": "must be a positive integer",
"path": "limit"
}
]
}
Authorizations
Authorization
string
header
required
RS256-signed JWT bearer token, obtained via the OAuth2 client-credentials grant and verifiable against the issuer's JWKS (see Authentication).
Path Parameters
id
string
required
Ruleset ID
Body
application/json
Update attributes
Only DRAFT rulesets are editable. Omit a field to leave it unchanged. The identity fields context and schema are not updatable — a request carrying a changed value rejects with 422 naming the field, while carrying the current value unchanged is a no-op, so read-modify-write clients may echo the full object back.
description
string
Example:
"Block USD transactions over limit"
rules
RuleInput · object[]
Replaces the full rules array
Show child attributes
Response
Updated ruleset
id
string<uuid>
Example:
"6c1f4e8b-9a2d-4c7e-b5f3-8d1a6e9c4b2f"
activated_at
string<date-time> | null
Example:
"2026-05-15T08:00:00Z"
context
string
Example:
"checkout"
created_at
string<date-time>
Example:
"2026-05-14T16:20:00Z"
created_by
string | null
Example:
"ops@example.com"
description
string | null
Example:
"Block USD transactions over limit"
rules
Rule · object[]
Rule definitions evaluated in array order
Show child attributes
schema
SchemaBinding · object
A ruleset's declared (schema, version) pair. name references a registry entry; version is required for custom schemas and absent for built-ins, whose version is implicit and advances only via Specter releases. Set at ruleset creation and immutable for the life of that ruleset version — rebinding a context means publishing a new ruleset version with the new binding and activating it. Rulesets that predate the registry surface the launch-backfilled transaction binding.
{
"classifier": "UNAUTHORIZED",
"code": 401,
"message": "No valid means of authentication was provided"
}
{
"classifier": "FORBIDDEN",
"code": 403,
"message": "Not allowed to access this resource or feature"
}
{
"classifier": "NOT_FOUND",
"code": 404,
"message": "The requested resource does not exist"
}
{
"classifier": "VALIDATION_ERROR",
"code": 422,
"validation_errors": [
{
"message": "must be a positive integer",
"path": "limit"
}
]
}
{"docsConfig":{"theme":"maple","$schema":"https://mintlify.com/docs.json","name":"Hellgate Cloud Platform","colors":{"primary":"#16A34A","light":"#07C983","dark":"#15803D"},"logo":{"light":"https://mintcdn.com/starfishgmbhcokg/fao_mR6VDpBCxc3o/logo/light.svg?fit=max\u0026auto=format\u0026n=fao_mR6VDpBCxc3o\u0026q=85\u0026s=1f2caba60febae2b0dcaddf3e196a132","dark":"https://mintcdn.com/starfishgmbhcokg/fao_mR6VDpBCxc3o/logo/dark.svg?fit=max\u0026auto=format\u0026n=fao_mR6VDpBCxc3o\u0026q=85\u0026s=a89c19b7c5f7607499a161a2db5b0f74","href":"https://developer.hellgate.io"},"favicon":"https://mintcdn.com/starfishgmbhcokg/dfhj2Hm84A9ik5S0/favicon.svg?fit=max\u0026auto=format\u0026n=dfhj2Hm84A9ik5S0\u0026q=85\u0026s=d897b485a1cd938d6af772ddd4885368","api":{"params":{"post":["x-data-classification"]}},"background":{"decoration":"gradient"},"navbar":{"links":[{"href":"https://hellgate.io","label":"Visit hellgate.io"}],"primary":{"type":"button","label":"Write our Support","href":"mailto:support@hellgate.io"}},"footer":{"socials":{"website":"https://hellgate.io","github":"https://github.com/starfish-codes","linkedin":"https://www.linkedin.com/company/hellgate-io","youtube":"https://www.youtube.com/@hellgate-io"}},"seo":{"metatags":{"google-site-verification":"OJ_nWFren-zsecLNqkmvOGrnXjmCZ_fPg5ov1mM8Tno"}},"contextual":{"options":["copy","view","download-spec","chatgpt","claude","perplexity","mcp","cursor","vscode"],"display":"header"},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"starfishgmbhcokg","hasAudioNarration":false,"actualSubdomain":"starfishgmbhcokg","customDomains":["developer.hellgate.io"],"gitSource":{"type":"github","owner":"starfish-codes","repo":"cpa-docs","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6af75b166809715f2d560c3b","clientVersion":"0.0.4128","searchFilterCounts":[{"language":"default","tag":"API Reference","count":244},{"language":"default","tag":"Commerce","count":181},{"language":"default","tag":"Documentation","count":82},{"language":"default","tag":"Guardian","count":77},{"language":"default","tag":"Tokens","count":67},{"language":"default","tag":"Specter","count":60},{"language":"default","tag":"Admin","count":50},{"language":"default","tag":"Payments","count":36},{"language":"default","tag":"Configuration","count":29},{"language":"default","tag":"Wallet Tokens","count":28}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-rbac-roles","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":":root{--hg-classification-bg: #fef3c7;--hg-classification-fg: #92400e}.dark{--hg-classification-bg: rgb(245 158 11 / 0.15);--hg-classification-fg: #fcd34d}div[data-component-part=\"field-meta-post\"]{background-color:var(--hg-classification-bg);color:var(--hg-classification-fg)}.dark div[data-component-part=\"field-meta-post\"]{background-color:var(--hg-classification-bg);color:var(--hg-classification-fg)}.classification-badge{display:inline-block;padding:0.125rem 0.5rem;border-radius:0.375rem;font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;font-size:0.875em;white-space:nowrap;background-color:var(--hg-classification-bg);color:var(--hg-classification-fg)}"}],"page":{"pagePath":"products/specter/api-reference/rulesets/update-ruleset","slug":"/products/specter/api-reference/rulesets/update-ruleset","pageMetadata":{"title":"Update ruleset","description":"Updates `description` and/or `rules` on a DRAFT ruleset. Only DRAFT rulesets are editable. context and schema are identity fields fixed at creation — submitting a changed value rejects with 422 naming the field; resubmitting the current value unchanged is a no-op. Rebinding a context is a new ruleset, not an update.","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml patch /api/admin/rulesets/{id}","href":"/products/specter/api-reference/rulesets/update-ruleset","autogeneratedByOpenApi":true},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n code: \"code\",\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_components.p, {\n children: [\"Updates \", _jsx(_components.code, {\n children: \"description\"\n }), \" and/or \", _jsx(_components.code, {\n children: \"rules\"\n }), \" on a DRAFT ruleset. Only DRAFT rulesets are editable. context and schema are identity fields fixed at creation — submitting a changed value rejects with 422 naming the field; resubmitting the current value unchanged is a no-op. Rebinding a context is a new ruleset, not an update.\"]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){return _jsx(_Fragment,{})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Update ruleset","description":"Updates `description` and/or `rules` on a DRAFT ruleset. Only DRAFT rulesets are editable. context and schema are identity fields fixed at creation — submitting a changed value rejects with 422 naming the field; resubmitting the current value unchanged is a no-op. Rebinding a context is a new ruleset, not an update.","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml patch /api/admin/rulesets/{id}","href":"/products/specter/api-reference/rulesets/update-ruleset","autogeneratedByOpenApi":true}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":[],"openApiReferenceData":{"operation":{"callbacks":{},"description":"Updates `description` and/or `rules` on a DRAFT ruleset. Only DRAFT rulesets are editable. context and schema are identity fields fixed at creation — submitting a changed value rejects with 422 naming the field; resubmitting the current value unchanged is a no-op. Rebinding a context is a new ruleset, not an update.","operationId":"admin_rulesets_update","parameters":["6f380d6e-3633-4a65-86eb-0fc60f156d32"],"requestBody":"5e5d97c9-cfca-4c90-b888-7a7ddb28ea3b","responses":{"200":"8aa37614-19b2-4979-9da6-b67f41a45d86","401":"9fbe4ab6-2c43-4b75-a630-aa93c4dd878a","403":"89b2b52f-a56c-4cc5-a7a0-c6d45adfd8ce","404":"f82d251c-5da4-4cd2-b434-7a8c204ec620","422":"6896d84b-5e7d-43a8-9c4a-48ace3d887ee"},"security":["7e9bff80-731a-401e-a5ab-9812892bff7b"],"summary":"Update ruleset","tags":["Rulesets"],"method":"patch","type":"path","servers":["9c71d782-c2bc-48a3-b97e-c9127bcf9c5d"],"path":"/api/admin/rulesets/{id}","baseUrlOptions":["https://{instance}.{env}.on-hellgate.cloud"],"title":"Update ruleset","codeSamples":[],"requiredOnlyExamples":false,"requestExampleType":"generated","responseExampleType":"generated"},"dependencies":{"parameters":{"6f380d6e-3633-4a65-86eb-0fc60f156d32":{"description":"Ruleset ID","in":"path","name":"id","required":true,"schema":{"type":"string","isRequired":true,"description":"Ruleset ID","uniqueKey":"","typeLabel":"string"}}},"requestBody":{"content":{"application/json":{"schema":{"description":"Only DRAFT rulesets are editable. Omit a field to leave it unchanged. The identity fields context and schema are not updatable — a request carrying a changed value rejects with 422 naming the field, while carrying the current value unchanged is a no-op, so read-modify-write clients may echo the full object back.","properties":{"description":{"example":"Block USD transactions over limit","type":"string","uniqueKey":"description","typeLabel":"string"},"rules":{"description":"Replaces the full rules array","items":{"properties":{"id":{"description":"Client-assigned rule identifier. Used in triggered-rule and backend-result entries.","example":"r1","type":"string","isRequired":true,"uniqueKey":"rules.items.[INDEX].id","typeLabel":"string"},"name":{"example":"Block USD","type":"string","isRequired":true,"uniqueKey":"rules.items.[INDEX].name","typeLabel":"string"},"type":{"description":"`condition` — evaluated locally. `backend` — delegates to an external adapter. `backend_group` — runs several backends together under a strategy. `blacklist` — blocks when every listed field matches an active blacklist entry. Each type has its own required fields: condition needs `action` and `condition`; backend needs `backend`; backend_group needs `strategy` and `members`; blacklist needs `fields`.","enum":["condition","backend","backend_group","blacklist"],"type":"string","isRequired":true,"uniqueKey":"rules.items.[INDEX].type","typeLabel":"enum\u003cstring>"},"action":{"description":"Required for condition and blacklist rules. Backend rules derive their action from the adapter response.","enum":["BLOCK","REVIEW"],"type":"string","uniqueKey":"rules.items.[INDEX].action","typeLabel":"enum\u003cstring>"},"async":{"oneOf":[{"example":true,"description":"When `true`, this backend or backend_group rule runs asynchronously after the synchronous response is returned. Only valid on `backend` and `backend_group` rules. A sync-phase ALLOW with async rules becomes PROVISIONAL until all async rules complete.","type":"boolean","uniqueKey":"rules.items.[INDEX].async.oneOf.0","typeLabel":"boolean"},{"example":true,"description":"When `true`, this backend or backend_group rule runs asynchronously after the synchronous response is returned. Only valid on `backend` and `backend_group` rules. A sync-phase ALLOW with async rules becomes PROVISIONAL until all async rules complete.","type":"null","uniqueKey":"rules.items.[INDEX].async.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].async","typeLabel":"boolean | null"},"backend":{"description":"Required for backend rules. Backend adapter key; must match a configured adapter.","example":"vdm","type":"string","uniqueKey":"rules.items.[INDEX].backend","typeLabel":"string"},"condition":{"description":"Condition expression tree. Required for condition rules; optional on backend and backend_group rules, where it guards the call — when false or unevaluable the backend is skipped with a `guard_not_met` result and evaluation continues. Logical: `{op: and|or, exprs: [...]}`, `{op: not, expr: {...}}`. Comparison: `{op: eq|neq|lt|lte|gt|gte, left: operand, right: operand}`. Set: `{op: in|not_in, left: operand, right: operand}`. Operands are objects, never bare strings — a field reference `{path: \"$.…\"}`, a filtered reference `{expr: \"$.… | downcase() | …\"}`, a literal `{value: …}`, or an HMAC composition `{op: hmac, fields: [\"$.… | filter()\", {type: const, value: \"…\"}]}` (1..16 inputs, at least one a field reference; emits `hmc_` + 64 hex). Any operand form is valid on either side. A pipe chain inside `path` is not parsed; use `expr`. Filters: `downcase()`, `upcase()`, `trim()`, `idn_ascii()`, `domain()` — type-checked against the bound schema at submission. Field paths must be declared by the bound schema; `pci-pan` / `pci-sad` fields cannot be compared against a literal, and a `pci-sad` field cannot enter an HMAC or appear anywhere in a velocity condition (a `pci-pan`-scoped partition or `distinct_on` value is stored as its HMAC fingerprint, never raw); array- and object-typed fields are usable only as the right operand of `in` / `not_in`. Violations reject with `422` naming the operand. Absent values make the predicate abstain. Velocity: `{op: velocity, aggregate: count|sum|distinct_count, window_type: rolling_seconds|rolling_days|day|week|month|quarter|year|bounded_long, partition_by: \"$.path | filter()\", threshold: N, window_seconds: N, window_days: N, field: \"$.path\", distinct_on: \"$.path | filter()\", fail_posture: open|closed}` — fires when an accumulated value per partition within the window exceeds threshold (strict greater-than, the only comparison in v1, matching the legacy rolling-count behavior). count counts decisions observed; sum accumulates the value of the amount-typed field named by `field` across the same partition-and-window; distinct_count counts the unique values of `distinct_on` observed within the partition-and-window — exact, never approximate. distinct_count requires both `distinct_on` and `partition_by`; `distinct_on` takes the same forms as partition_by (filtered field reference or hmac composition) under the same scope rules — a pci-pan-scoped distinct value joins the member set as its HMAC fingerprint, pii- and any-scoped values join raw. The member set is opaque: every surface reports the count only, no surface lists the members. Member expiry follows the window shape: under bounded_long (and rolling_seconds), each distinct value's membership expires window-length after its first sighting (member-anchored TTL, not refreshed by re-sighting, lazily evicted); under a calendar window the member set is period-keyed and expires whole at the period boundary. The live count covers unexpired members. window_type bounded_long is the long-lifetime shape for distinct_count and is valid with no other aggregate, while distinct_count itself composes with any window_type except rolling_days (a sum of daily distinct counts is not a distinct count, so distinct_count with rolling_days rejects with 422 naming the offending axes); count and sum reject bounded_long with 422 naming the offending axes. The member count per counter is capped per the platform resource-caps policy; cap saturation at decision time resolves like a counter-operation failure, per the rule's fail_posture. With sum, `field` is required, must be a plain path (no filter pipes — no v1 filter emits an amount) and must resolve to an amount-typed declaration on the bound schema; violations reject with 422 naming the path and its declared type. With count or distinct_count, supplying `field` in the extended form rejects with 422. Sum counters auto-partition by the ISO currency code adjacent to the summed amount field per the type vocabulary: each currency accumulates independently under the same logical partition (no FX conversion is ever consulted), and the simulation trace surfaces which currency counter a test payload addressed. Sums accumulate in the field's minor units in a signed 64-bit accumulator — a negative decision-request amount decrements, which is the only way a refund reaches a counter (lifecycle events never do) — and the threshold compares in the same minor units. Outside sum, `field` survives only as the legacy compatibility spelling of the partition (below) — write new rules with partition_by. partition_by is a filtered field reference or an hmac composition (`{op: hmac, fields: [...]}`) — the identity-fingerprint shape: the computed fingerprint is the partition key, so \"this PII tuple seen N times in window W\" is expressible without any schema-level derived field. Either form resolves against the bound (schema, version); pci-sad-scoped fields reject with 422, a pci-pan-scoped partition value addresses counter storage via its HMAC fingerprint, pii- and any-scoped values address it raw, and an hmac-composed partition is a fingerprint by construction (raw inputs never persist). Two rules share an hmac-partitioned counter iff their compositions' post-parse normalized forms are identical — same operator, same ordered field list, same filter chain per input; whitespace-only differences share, reordered inputs or differing chains produce independent counters. An hmac composition, as partition_by or as distinct_on, is valid only with window_type rolling_seconds (window_seconds at most 172800, 48 hours, regardless of aggregate) and the calendar types day, week and month; combining a composition with bounded_long, rolling_days, quarter or year, or with rolling_seconds above 172800, rejects with 422 naming the offending axes (the composition window guard: composition-keyed counter state stays short-lived until HMAC key rotation semantics exist). The guard does not apply to plain field-path partitions, including pci-pan-scoped ones. Canonicalization is the author's explicit choice per input via filter pipes; a composition with a string-typed pii-scoped input whose chain has none of downcase() / upcase() / trim() submits successfully; Specter records a weak-canonicalization soft-warn naming the input path in the audit trail, and the response carries no warning (unnormalized inputs split case / whitespace variants into separate counters). The distinct partition count per rule is capped per the platform resource-caps policy; cap saturation at decision time resolves like a counter-operation failure, per the rule's fail_posture. The legacy short form `{op: velocity, field: \"$.path\", threshold: N, window_seconds: N}` — field naming the counted partition, no aggregate / window_type / partition_by — stays valid and normalizes on read to the explicit form (aggregate: count, window_type: rolling_seconds, partition_by taking the legacy field); combining the legacy field-as-partition reading with any extended axis rejects with 422. In the extended form partition_by is required for every aggregate, an omitted aggregate defaults to count, an omitted window_type defaults to rolling_seconds (both normalize to the explicit form on read), and supplying distinct_on with count or sum rejects with 422. window_seconds is required when window_type is rolling_seconds, rejects with 422 for calendar window types and for rolling_days, and for bounded_long is the optional member lifetime — default 31536000 (365 days), permitted range 86400 to 157680000 (1 day to 5 years), outside which submission rejects with 422. A rolling_seconds window slides continuously and is exact to the second. With aggregate count or distinct_count on a plain field-path partition it carries no upper bound (existing-rule compatibility). With aggregate sum it is valid up to 172800 (48 hours); a sum with window_seconds above 172800 rejects with 422 naming rolling_days as the long-horizon shape. Sub-day sum windows, including a true sliding 24 hours, are valid. window_type rolling_days is the day-quantized long rolling shape: it requires window_days, an integer in the range 2 to 366, rejects window_seconds with 422, and is valid with aggregate count and sum only. Its window is the current UTC day so far plus the N-1 preceding closed UTC days, so the effective length varies between N-1 and N days by time of day and the window edge advances at 00:00:00 UTC, not continuously; whole-day exactness only, the oldest day is never interpolated. Day identifiers follow the calendar period-key convention (\"2026-08-21\"). A rolling_seconds sum holds per-event contributions internally to age them out, never exposed on any surface, and the per-counter event count is capped per the platform resource-caps policy, with cap saturation at decision time resolving like a counter-operation failure, per the rule's fail_posture. Calendar windows are UTC service-side: day rolls at 00:00:00 UTC, week is ISO-8601 (Monday start, week 1 holds January 4, keyed by ISO year — \"2025-W01\" can hold a December 2024 event), month rolls on the 1st, quarter on Jan 1 / Apr 1 / Jul 1 / Oct 1 and year on Jan 1 (both keyed by Gregorian year). Each period instance has a stable period key — \"2026-05-28\", \"2026-W22\", \"2026-05\", \"2026-Q2\", \"2026\" — surfaced in the simulation trace. Increments and reads attribute to the period containing decision_time (instance wall clock at evaluation); the decision path always increments and reads the current period as one atomic operation. A closed period's counter is retained for 5 minutes past its boundary (a read-TTL grace), but no API surface addresses it — simulation reads the current period — and no earlier period is queryable. Counters are shared across rules whose canonical counter definition matches — same schema and carry-over generation (the raw version number is not part of the key: counters survive a schema revision when the referenced paths' declarations are unchanged), partition_by canonical form, aggregate, window_type and its window length (rolling_seconds, rolling_days, bounded_long) or period instance (calendar), plus the summed field and its currency where aggregate is sum and the distinct_on canonical form where aggregate is distinct_count — with increments firing once per decision per counter, on the decision path only (lifecycle events never touch a counter; a retried request increments again); a rule cannot demand a private counter, and the trace's counter_id confirms sharing intent. A counter never written reads 0 (an empty member set for distinct_count) — a normal cold-start value, not a failure; a genuine counter read / write failure resolves per the rule-level fail_posture declaration (open by default). An absent partition_by, distinct_on or summed field value at decision time abstains the rule and skips the increment. Unknown aggregate or window_type values reject at submission with 422 naming the offending axis. Semantics and examples: see the [rule engine](/products/specter/concepts/rule-engine).","type":"object","uniqueKey":"rules.items.[INDEX].condition","typeLabel":"object"},"description":{"oneOf":[{"example":"Escalate to the risk backend","type":"string","uniqueKey":"rules.items.[INDEX].description.oneOf.0","typeLabel":"string"},{"example":"Escalate to the risk backend","type":"null","uniqueKey":"rules.items.[INDEX].description.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].description","typeLabel":"string | null"},"enabled":{"default":true,"example":true,"type":"boolean","uniqueKey":"rules.items.[INDEX].enabled","typeLabel":"boolean"},"fail_posture":{"oneOf":[{"description":"Velocity rules only — condition rules whose expression contains a velocity op; declaring it elsewhere rejects with 422. Governs the rule's outcome when its counter cannot be read or written on the decision path (cold-start reads are not failures). open — the default — the rule abstains and evaluation proceeds to the next rule. closed — the rule fires its declared action; a BLOCK short-circuits per evaluation order (later rules are not evaluated and their counters are not incremented), a REVIEW accumulates and evaluation proceeds. The posture applies to every velocity op in the rule's condition. It is also accepted, and read back, on each velocity op inside condition (the spelling the management UI writes); a value there that differs from the rule's rejects with 422. On a shared counter each rule resolves its own posture independently — one physical failure can abstain a fail-open rule and fire a fail-closed one within the same decision. The decision response carries only the posture outcome, never a counter-store error.","enum":["open","closed"],"type":"string","uniqueKey":"rules.items.[INDEX].fail_posture.oneOf.0","typeLabel":"enum\u003cstring>"},{"description":"Velocity rules only — condition rules whose expression contains a velocity op; declaring it elsewhere rejects with 422. Governs the rule's outcome when its counter cannot be read or written on the decision path (cold-start reads are not failures). open — the default — the rule abstains and evaluation proceeds to the next rule. closed — the rule fires its declared action; a BLOCK short-circuits per evaluation order (later rules are not evaluated and their counters are not incremented), a REVIEW accumulates and evaluation proceeds. The posture applies to every velocity op in the rule's condition. It is also accepted, and read back, on each velocity op inside condition (the spelling the management UI writes); a value there that differs from the rule's rejects with 422. On a shared counter each rule resolves its own posture independently — one physical failure can abstain a fail-open rule and fire a fail-closed one within the same decision. The decision response carries only the posture outcome, never a counter-store error.","enum":["open","closed"],"type":"null","uniqueKey":"rules.items.[INDEX].fail_posture.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].fail_posture","typeLabel":"enum\u003cstring> | null"},"fields":{"description":"Required for blacklist rules. JSON path fields checked against the blacklist; all must match for the rule to fire. Each may carry an inline filter pipe chain (`$.customer.email | downcase()`). Array- or object-typed and `pci-pan` / `pci-sad`-scoped fields reject with `422`.","example":["$.credential_fingerprint","$.device.ip"],"items":{"type":"string","uniqueKey":"rules.items.[INDEX].fields.items.[INDEX]","typeLabel":"string"},"type":"array","uniqueKey":"rules.items.[INDEX].fields","typeLabel":"string[]"},"live":{"default":true,"description":"Backend rules only. `false` runs the backend in shadow mode — it is called and its result recorded, but it never affects the verdict.","example":true,"type":"boolean","uniqueKey":"rules.items.[INDEX].live","typeLabel":"boolean"},"members":{"description":"Required for backend_group rules. The backends the group runs — at least 2.","minItems":2,"items":{"properties":{"backend":{"description":"Backend adapter key.","example":"vdm","type":"string","isRequired":true,"uniqueKey":"rules.items.[INDEX].members.items.[INDEX].backend","typeLabel":"string"},"live":{"default":true,"example":true,"type":"boolean","uniqueKey":"rules.items.[INDEX].members.items.[INDEX].live","typeLabel":"boolean"},"weight":{"description":"Relative weight for the `split` strategy — required and at least 1 on every member when the strategy is `split`; ignored for `fallback` and `parallel`.","example":60,"minimum":1,"type":"integer","uniqueKey":"rules.items.[INDEX].members.items.[INDEX].weight","typeLabel":"integer"}},"required":["backend"],"type":"object","uniqueKey":"rules.items.[INDEX].members.items.[INDEX]","typeLabel":"object"},"type":"array","uniqueKey":"rules.items.[INDEX].members","typeLabel":"object[]"},"merge":{"description":"How member decisions are merged (backend_group rules with the `parallel` strategy). Required for `parallel`; on any other strategy it is rejected, not ignored.","enum":["strictest","most_lenient","consensus"],"type":"string","uniqueKey":"rules.items.[INDEX].merge","typeLabel":"enum\u003cstring>"},"on_error":{"description":"Required for backend and backend_group rules — the fail posture is always declared explicitly, never defaulted. `allow` — fail-open (continue evaluation). `block` — fail-closed (BLOCK on adapter error). `review` — REVIEW on adapter error.","enum":["allow","block","review"],"type":"string","uniqueKey":"rules.items.[INDEX].on_error","typeLabel":"enum\u003cstring>"},"populate_on":{"description":"Blacklist rules only. Event types that auto-populate the blacklist with this rule's field values. Defaults to `[\"fraud_report\"]`.","example":["fraud_report","chargeback"],"items":{"enum":["fraud_report","chargeback","failed"],"type":"string","uniqueKey":"rules.items.[INDEX].populate_on.items.[INDEX]","typeLabel":"enum\u003cstring>"},"type":"array","uniqueKey":"rules.items.[INDEX].populate_on","typeLabel":"enum\u003cstring>[]"},"strategy":{"description":"Required for backend_group rules. `fallback` — try members in order until one succeeds. `parallel` — run all members and merge (`merge` required). `split` — weighted routing by member `weight`.","enum":["fallback","parallel","split"],"type":"string","uniqueKey":"rules.items.[INDEX].strategy","typeLabel":"enum\u003cstring>"},"ttl_seconds":{"minimum":1,"description":"Blacklist rules only. TTL for entries this rule auto-populates; omit for entries that never expire.","example":86400,"type":"integer","uniqueKey":"rules.items.[INDEX].ttl_seconds","typeLabel":"integer"}},"required":["id","name","type"],"title":"RuleInput","type":"object","uniqueKey":"rules.items.[INDEX]","typeLabel":"RuleInput · object"},"type":"array","uniqueKey":"rules","typeLabel":"RuleInput · object[]"}},"title":"UpdateRulesetRequest","type":"object","uniqueKey":"","typeLabel":"UpdateRulesetRequest · object"},"examples":{"Example":{"title":"Example","value":{"description":"Block USD transactions over limit","rules":[{"id":"r1","name":"Block USD","async":true,"backend":"vdm","condition":{},"description":"Escalate to the risk backend","enabled":true,"fields":["$.credential_fingerprint","$.device.ip"],"live":true,"members":[{"backend":"vdm","live":true,"weight":60}],"populate_on":["fraud_report","chargeback"],"ttl_seconds":86400}]}}},"exampleType":"generated"}},"description":"Update attributes","required":true,"exampleType":"generated"},"responses":{"8aa37614-19b2-4979-9da6-b67f41a45d86":{"content":{"application/json":{"schema":{"properties":{"id":{"example":"6c1f4e8b-9a2d-4c7e-b5f3-8d1a6e9c4b2f","format":"uuid","type":"string","uniqueKey":"id","typeLabel":"string\u003cuuid>"},"activated_at":{"oneOf":[{"example":"2026-05-15T08:00:00Z","format":"date-time","type":"string","uniqueKey":"activated_at.oneOf.0","typeLabel":"string\u003cdate-time>"},{"example":"2026-05-15T08:00:00Z","format":"date-time","type":"null","uniqueKey":"activated_at.oneOf.1","typeLabel":"null"}],"uniqueKey":"activated_at","typeLabel":"string\u003cdate-time> | null"},"context":{"example":"checkout","type":"string","uniqueKey":"context","typeLabel":"string"},"created_at":{"example":"2026-05-14T16:20:00Z","format":"date-time","type":"string","uniqueKey":"created_at","typeLabel":"string\u003cdate-time>"},"created_by":{"oneOf":[{"example":"ops@example.com","type":"string","uniqueKey":"created_by.oneOf.0","typeLabel":"string"},{"example":"ops@example.com","type":"null","uniqueKey":"created_by.oneOf.1","typeLabel":"null"}],"uniqueKey":"created_by","typeLabel":"string | null"},"description":{"oneOf":[{"example":"Block USD transactions over limit","type":"string","uniqueKey":"description.oneOf.0","typeLabel":"string"},{"example":"Block USD transactions over limit","type":"null","uniqueKey":"description.oneOf.1","typeLabel":"null"}],"uniqueKey":"description","typeLabel":"string | null"},"rules":{"description":"Rule definitions evaluated in array order","items":{"properties":{"enabled":{"example":true,"type":"boolean","isRequired":true,"uniqueKey":"rules.items.[INDEX].enabled","typeLabel":"boolean"},"type":{"description":"`condition` — evaluated locally. `backend` — delegates to an external adapter. `blacklist` — blocks if all specified fields match an active blacklist entry. `backend_group` — runs several backends together under a strategy.","enum":["condition","backend","blacklist","backend_group"],"type":"string","isRequired":true,"uniqueKey":"rules.items.[INDEX].type","typeLabel":"enum\u003cstring>"},"id":{"oneOf":[{"example":"r1","type":"string","uniqueKey":"rules.items.[INDEX].id.oneOf.0","typeLabel":"string"},{"example":"r1","type":"null","uniqueKey":"rules.items.[INDEX].id.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].id","typeLabel":"string | null"},"action":{"description":"Required for condition rules. Backend rules derive their action from the adapter response.","enum":["BLOCK","REVIEW"],"type":"string","uniqueKey":"rules.items.[INDEX].action","typeLabel":"enum\u003cstring>"},"async":{"oneOf":[{"example":true,"description":"When `true`, this backend or backend_group rule is executed asynchronously after the synchronous response is returned. Only applies to `backend` and `backend_group` rule types. A sync-phase ALLOW with async rules present is returned as PROVISIONAL.","type":"boolean","uniqueKey":"rules.items.[INDEX].async.oneOf.0","typeLabel":"boolean"},{"example":true,"description":"When `true`, this backend or backend_group rule is executed asynchronously after the synchronous response is returned. Only applies to `backend` and `backend_group` rule types. A sync-phase ALLOW with async rules present is returned as PROVISIONAL.","type":"null","uniqueKey":"rules.items.[INDEX].async.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].async","typeLabel":"boolean | null"},"backend":{"description":"Required for backend rules. Backend adapter key; must match a configured adapter.","example":"vdm","type":"string","uniqueKey":"rules.items.[INDEX].backend","typeLabel":"string"},"condition":{"description":"Condition expression tree. Required for condition rules; optional on backend and backend_group rules, where it guards the call — when false or unevaluable the backend is skipped with a `guard_not_met` result and evaluation continues. Logical: `{op: and|or, exprs: [...]}`, `{op: not, expr: {...}}`. Comparison: `{op: eq|neq|lt|lte|gt|gte, left: operand, right: operand}`. Set: `{op: in|not_in, left: operand, right: operand}`. Operands are objects, never bare strings — a field reference `{path: \"$.…\"}`, a filtered reference `{expr: \"$.… | downcase() | …\"}`, a literal `{value: …}`, or an HMAC composition `{op: hmac, fields: [\"$.… | filter()\", {type: const, value: \"…\"}]}` (1..16 inputs, at least one a field reference; emits `hmc_` + 64 hex). Any operand form is valid on either side. A pipe chain inside `path` is not parsed; use `expr`. Filters: `downcase()`, `upcase()`, `trim()`, `idn_ascii()`, `domain()` — type-checked against the bound schema at submission. Field paths must be declared by the bound schema; `pci-pan` / `pci-sad` fields cannot be compared against a literal, and a `pci-sad` field cannot enter an HMAC or appear anywhere in a velocity condition (a `pci-pan`-scoped partition or `distinct_on` value is stored as its HMAC fingerprint, never raw); array- and object-typed fields are usable only as the right operand of `in` / `not_in`. Violations reject with `422` naming the operand. Absent values make the predicate abstain. Velocity: `{op: velocity, aggregate: count|sum|distinct_count, window_type: rolling_seconds|rolling_days|day|week|month|quarter|year|bounded_long, partition_by: \"$.path | filter()\", threshold: N, window_seconds: N, window_days: N, field: \"$.path\", distinct_on: \"$.path | filter()\", fail_posture: open|closed}` — fires when an accumulated value per partition within the window exceeds threshold (strict greater-than, the only comparison in v1, matching the legacy rolling-count behavior). count counts decisions observed; sum accumulates the value of the amount-typed field named by `field` across the same partition-and-window; distinct_count counts the unique values of `distinct_on` observed within the partition-and-window — exact, never approximate. distinct_count requires both `distinct_on` and `partition_by`; `distinct_on` takes the same forms as partition_by (filtered field reference or hmac composition) under the same scope rules — a pci-pan-scoped distinct value joins the member set as its HMAC fingerprint, pii- and any-scoped values join raw. The member set is opaque: every surface reports the count only, no surface lists the members. Member expiry follows the window shape: under bounded_long (and rolling_seconds), each distinct value's membership expires window-length after its first sighting (member-anchored TTL, not refreshed by re-sighting, lazily evicted); under a calendar window the member set is period-keyed and expires whole at the period boundary. The live count covers unexpired members. window_type bounded_long is the long-lifetime shape for distinct_count and is valid with no other aggregate, while distinct_count itself composes with any window_type except rolling_days (a sum of daily distinct counts is not a distinct count, so distinct_count with rolling_days rejects with 422 naming the offending axes); count and sum reject bounded_long with 422 naming the offending axes. The member count per counter is capped per the platform resource-caps policy; cap saturation at decision time resolves like a counter-operation failure, per the rule's fail_posture. With sum, `field` is required, must be a plain path (no filter pipes — no v1 filter emits an amount) and must resolve to an amount-typed declaration on the bound schema; violations reject with 422 naming the path and its declared type. With count or distinct_count, supplying `field` in the extended form rejects with 422. Sum counters auto-partition by the ISO currency code adjacent to the summed amount field per the type vocabulary: each currency accumulates independently under the same logical partition (no FX conversion is ever consulted), and the simulation trace surfaces which currency counter a test payload addressed. Sums accumulate in the field's minor units in a signed 64-bit accumulator — a negative decision-request amount decrements, which is the only way a refund reaches a counter (lifecycle events never do) — and the threshold compares in the same minor units. Outside sum, `field` survives only as the legacy compatibility spelling of the partition (below) — write new rules with partition_by. partition_by is a filtered field reference or an hmac composition (`{op: hmac, fields: [...]}`) — the identity-fingerprint shape: the computed fingerprint is the partition key, so \"this PII tuple seen N times in window W\" is expressible without any schema-level derived field. Either form resolves against the bound (schema, version); pci-sad-scoped fields reject with 422, a pci-pan-scoped partition value addresses counter storage via its HMAC fingerprint, pii- and any-scoped values address it raw, and an hmac-composed partition is a fingerprint by construction (raw inputs never persist). Two rules share an hmac-partitioned counter iff their compositions' post-parse normalized forms are identical — same operator, same ordered field list, same filter chain per input; whitespace-only differences share, reordered inputs or differing chains produce independent counters. An hmac composition, as partition_by or as distinct_on, is valid only with window_type rolling_seconds (window_seconds at most 172800, 48 hours, regardless of aggregate) and the calendar types day, week and month; combining a composition with bounded_long, rolling_days, quarter or year, or with rolling_seconds above 172800, rejects with 422 naming the offending axes (the composition window guard: composition-keyed counter state stays short-lived until HMAC key rotation semantics exist). The guard does not apply to plain field-path partitions, including pci-pan-scoped ones. Canonicalization is the author's explicit choice per input via filter pipes; a composition with a string-typed pii-scoped input whose chain has none of downcase() / upcase() / trim() submits successfully; Specter records a weak-canonicalization soft-warn naming the input path in the audit trail, and the response carries no warning (unnormalized inputs split case / whitespace variants into separate counters). The distinct partition count per rule is capped per the platform resource-caps policy; cap saturation at decision time resolves like a counter-operation failure, per the rule's fail_posture. The legacy short form `{op: velocity, field: \"$.path\", threshold: N, window_seconds: N}` — field naming the counted partition, no aggregate / window_type / partition_by — stays valid and normalizes on read to the explicit form (aggregate: count, window_type: rolling_seconds, partition_by taking the legacy field); combining the legacy field-as-partition reading with any extended axis rejects with 422. In the extended form partition_by is required for every aggregate, an omitted aggregate defaults to count, an omitted window_type defaults to rolling_seconds (both normalize to the explicit form on read), and supplying distinct_on with count or sum rejects with 422. window_seconds is required when window_type is rolling_seconds, rejects with 422 for calendar window types and for rolling_days, and for bounded_long is the optional member lifetime — default 31536000 (365 days), permitted range 86400 to 157680000 (1 day to 5 years), outside which submission rejects with 422. A rolling_seconds window slides continuously and is exact to the second. With aggregate count or distinct_count on a plain field-path partition it carries no upper bound (existing-rule compatibility). With aggregate sum it is valid up to 172800 (48 hours); a sum with window_seconds above 172800 rejects with 422 naming rolling_days as the long-horizon shape. Sub-day sum windows, including a true sliding 24 hours, are valid. window_type rolling_days is the day-quantized long rolling shape: it requires window_days, an integer in the range 2 to 366, rejects window_seconds with 422, and is valid with aggregate count and sum only. Its window is the current UTC day so far plus the N-1 preceding closed UTC days, so the effective length varies between N-1 and N days by time of day and the window edge advances at 00:00:00 UTC, not continuously; whole-day exactness only, the oldest day is never interpolated. Day identifiers follow the calendar period-key convention (\"2026-08-21\"). A rolling_seconds sum holds per-event contributions internally to age them out, never exposed on any surface, and the per-counter event count is capped per the platform resource-caps policy, with cap saturation at decision time resolving like a counter-operation failure, per the rule's fail_posture. Calendar windows are UTC service-side: day rolls at 00:00:00 UTC, week is ISO-8601 (Monday start, week 1 holds January 4, keyed by ISO year — \"2025-W01\" can hold a December 2024 event), month rolls on the 1st, quarter on Jan 1 / Apr 1 / Jul 1 / Oct 1 and year on Jan 1 (both keyed by Gregorian year). Each period instance has a stable period key — \"2026-05-28\", \"2026-W22\", \"2026-05\", \"2026-Q2\", \"2026\" — surfaced in the simulation trace. Increments and reads attribute to the period containing decision_time (instance wall clock at evaluation); the decision path always increments and reads the current period as one atomic operation. A closed period's counter is retained for 5 minutes past its boundary (a read-TTL grace), but no API surface addresses it — simulation reads the current period — and no earlier period is queryable. Counters are shared across rules whose canonical counter definition matches — same schema and carry-over generation (the raw version number is not part of the key: counters survive a schema revision when the referenced paths' declarations are unchanged), partition_by canonical form, aggregate, window_type and its window length (rolling_seconds, rolling_days, bounded_long) or period instance (calendar), plus the summed field and its currency where aggregate is sum and the distinct_on canonical form where aggregate is distinct_count — with increments firing once per decision per counter, on the decision path only (lifecycle events never touch a counter; a retried request increments again); a rule cannot demand a private counter, and the trace's counter_id confirms sharing intent. A counter never written reads 0 (an empty member set for distinct_count) — a normal cold-start value, not a failure; a genuine counter read / write failure resolves per the rule-level fail_posture declaration (open by default). An absent partition_by, distinct_on or summed field value at decision time abstains the rule and skips the increment. Unknown aggregate or window_type values reject at submission with 422 naming the offending axis. Semantics and examples: see the [rule engine](/products/specter/concepts/rule-engine).","type":"object","uniqueKey":"rules.items.[INDEX].condition","typeLabel":"object"},"description":{"oneOf":[{"example":"Escalate to the risk backend","type":"string","uniqueKey":"rules.items.[INDEX].description.oneOf.0","typeLabel":"string"},{"example":"Escalate to the risk backend","type":"null","uniqueKey":"rules.items.[INDEX].description.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].description","typeLabel":"string | null"},"fail_posture":{"oneOf":[{"description":"Velocity rules only — condition rules whose expression contains a velocity op. Governs the rule's outcome when its counter cannot be read or written on the decision path (cold-start reads are not failures). open — the default — the rule abstains and evaluation proceeds to the next rule. closed — the rule fires its declared action; a BLOCK short-circuits per evaluation order, a REVIEW accumulates and evaluation proceeds. Reads back as the posture the rule's velocity ops agree on (open when none declares one), null when they differ (possible only with the rule-level value omitted) or on a rule without a velocity op; each velocity op inside condition also reads back its own. On a shared counter each rule resolves its own posture independently — one physical failure can abstain a fail-open rule and fire a fail-closed one within the same decision. The decision response carries only the posture outcome, never a counter-store error.","enum":["open","closed"],"type":"string","uniqueKey":"rules.items.[INDEX].fail_posture.oneOf.0","typeLabel":"enum\u003cstring>"},{"description":"Velocity rules only — condition rules whose expression contains a velocity op. Governs the rule's outcome when its counter cannot be read or written on the decision path (cold-start reads are not failures). open — the default — the rule abstains and evaluation proceeds to the next rule. closed — the rule fires its declared action; a BLOCK short-circuits per evaluation order, a REVIEW accumulates and evaluation proceeds. Reads back as the posture the rule's velocity ops agree on (open when none declares one), null when they differ (possible only with the rule-level value omitted) or on a rule without a velocity op; each velocity op inside condition also reads back its own. On a shared counter each rule resolves its own posture independently — one physical failure can abstain a fail-open rule and fire a fail-closed one within the same decision. The decision response carries only the posture outcome, never a counter-store error.","enum":["open","closed"],"type":"null","uniqueKey":"rules.items.[INDEX].fail_posture.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].fail_posture","typeLabel":"enum\u003cstring> | null"},"fields":{"oneOf":[{"example":["$.device.ip"],"description":"JSON path fields to match against the blacklist (blacklist rules only). All fields must match for a BLOCK to be triggered. Each field may carry an inline filter pipe chain (`$.customer.email | downcase()`), subject to the same type and scope gates as a condition operand; pci-pan- and pci-sad-scoped fields and array- or object-typed fields reject at submission with 422.","items":{"type":"string","uniqueKey":"rules.items.[INDEX].fields.oneOf.0.items.[INDEX]","typeLabel":"string"},"type":"array","uniqueKey":"rules.items.[INDEX].fields.oneOf.0","typeLabel":"string[]"},{"example":["$.device.ip"],"description":"JSON path fields to match against the blacklist (blacklist rules only). All fields must match for a BLOCK to be triggered. Each field may carry an inline filter pipe chain (`$.customer.email | downcase()`), subject to the same type and scope gates as a condition operand; pci-pan- and pci-sad-scoped fields and array- or object-typed fields reject at submission with 422.","items":{"type":"string"},"type":"null","uniqueKey":"rules.items.[INDEX].fields.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].fields","typeLabel":"string[] | null"},"live":{"oneOf":[{"example":true,"description":"Whether the backend runs live (true) or in shadow mode (false). Present on backend rules and on backend_group members.","type":"boolean","uniqueKey":"rules.items.[INDEX].live.oneOf.0","typeLabel":"boolean"},{"example":true,"description":"Whether the backend runs live (true) or in shadow mode (false). Present on backend rules and on backend_group members.","type":"null","uniqueKey":"rules.items.[INDEX].live.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].live","typeLabel":"boolean | null"},"members":{"oneOf":[{"example":["vdm"],"description":"Backend members of the group (backend_group rules only).","items":{"properties":{"backend":{"example":"vdm","type":"string","uniqueKey":"rules.items.[INDEX].members.oneOf.0.items.[INDEX].backend","typeLabel":"string"},"live":{"example":true,"type":"boolean","uniqueKey":"rules.items.[INDEX].members.oneOf.0.items.[INDEX].live","typeLabel":"boolean"},"weight":{"description":"Relative weight for the split strategy.","example":60,"type":"integer","uniqueKey":"rules.items.[INDEX].members.oneOf.0.items.[INDEX].weight","typeLabel":"integer"}},"type":"object","uniqueKey":"rules.items.[INDEX].members.oneOf.0.items.[INDEX]","typeLabel":"object"},"type":"array","uniqueKey":"rules.items.[INDEX].members.oneOf.0","typeLabel":"object[]"},{"example":["vdm"],"description":"Backend members of the group (backend_group rules only).","items":{"properties":{"backend":{"example":"vdm","type":"string"},"live":{"example":true,"type":"boolean"},"weight":{"description":"Relative weight for the split strategy.","example":60,"type":"integer"}},"type":"object"},"type":"null","uniqueKey":"rules.items.[INDEX].members.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].members","typeLabel":"object[] | null"},"merge":{"oneOf":[{"description":"How parallel member decisions are merged (backend_group rules only).","enum":["strictest","most_lenient","consensus"],"type":"string","uniqueKey":"rules.items.[INDEX].merge.oneOf.0","typeLabel":"enum\u003cstring>"},{"description":"How parallel member decisions are merged (backend_group rules only).","enum":["strictest","most_lenient","consensus"],"type":"null","uniqueKey":"rules.items.[INDEX].merge.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].merge","typeLabel":"enum\u003cstring> | null"},"name":{"oneOf":[{"example":"Block USD","type":"string","uniqueKey":"rules.items.[INDEX].name.oneOf.0","typeLabel":"string"},{"example":"Block USD","type":"null","uniqueKey":"rules.items.[INDEX].name.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].name","typeLabel":"string | null"},"on_error":{"description":"Error policy for backend and backend_group rules. `allow` — fail-open (continue evaluation). `block` — fail-closed (BLOCK on error). `review` — REVIEW on error.","enum":["allow","block","review"],"type":"string","uniqueKey":"rules.items.[INDEX].on_error","typeLabel":"enum\u003cstring>"},"populate_on":{"oneOf":[{"example":["fraud_report","chargeback"],"description":"Event types that trigger blacklist population for this rule. Defaults to `[\"fraud_report\"]` if omitted.","items":{"enum":["fraud_report","chargeback","failed"],"type":"string","uniqueKey":"rules.items.[INDEX].populate_on.oneOf.0.items.[INDEX]","typeLabel":"enum\u003cstring>"},"type":"array","uniqueKey":"rules.items.[INDEX].populate_on.oneOf.0","typeLabel":"enum\u003cstring>[]"},{"example":["fraud_report","chargeback"],"description":"Event types that trigger blacklist population for this rule. Defaults to `[\"fraud_report\"]` if omitted.","items":{"enum":["fraud_report","chargeback","failed"],"type":"string"},"type":"null","uniqueKey":"rules.items.[INDEX].populate_on.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].populate_on","typeLabel":"enum\u003cstring>[] | null"},"strategy":{"oneOf":[{"description":"Member execution strategy (backend_group rules only). `fallback` — try members in order until one succeeds. `parallel` — run all members and merge. `split` — weighted A/B routing.","enum":["fallback","parallel","split"],"type":"string","uniqueKey":"rules.items.[INDEX].strategy.oneOf.0","typeLabel":"enum\u003cstring>"},{"description":"Member execution strategy (backend_group rules only). `fallback` — try members in order until one succeeds. `parallel` — run all members and merge. `split` — weighted A/B routing.","enum":["fallback","parallel","split"],"type":"null","uniqueKey":"rules.items.[INDEX].strategy.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].strategy","typeLabel":"enum\u003cstring> | null"},"ttl_seconds":{"oneOf":[{"minimum":1,"example":86400,"description":"TTL in seconds for blacklist entries created by this rule. If omitted, entries never expire.","type":"integer","uniqueKey":"rules.items.[INDEX].ttl_seconds.oneOf.0","typeLabel":"integer"},{"minimum":1,"example":86400,"description":"TTL in seconds for blacklist entries created by this rule. If omitted, entries never expire.","type":"null","uniqueKey":"rules.items.[INDEX].ttl_seconds.oneOf.1","typeLabel":"null"}],"uniqueKey":"rules.items.[INDEX].ttl_seconds","typeLabel":"integer | null"}},"required":["enabled","type"],"title":"Rule","type":"object","uniqueKey":"rules.items.[INDEX]","typeLabel":"Rule · object"},"type":"array","uniqueKey":"rules","typeLabel":"Rule · object[]"},"schema":{"description":"A ruleset's declared (schema, version) pair. name references a registry entry; version is required for custom schemas and absent for built-ins, whose version is implicit and advances only via Specter releases. Set at ruleset creation and immutable for the life of that ruleset version — rebinding a context means publishing a new ruleset version with the new binding and activating it. Rulesets that predate the registry surface the launch-backfilled transaction binding.","properties":{"name":{"example":"transaction","type":"string","isRequired":true,"uniqueKey":"schema.name","typeLabel":"string"},"version":{"oneOf":[{"description":"Registry version for custom schemas. Absent for built-in schemas.","example":1,"type":"integer","uniqueKey":"schema.version.oneOf.0","typeLabel":"integer"},{"description":"Registry version for custom schemas. Absent for built-in schemas.","example":1,"type":"null","uniqueKey":"schema.version.oneOf.1","typeLabel":"null"}],"uniqueKey":"schema.version","typeLabel":"integer | null"}},"required":["name"],"title":"SchemaBinding","type":"object","uniqueKey":"schema","typeLabel":"SchemaBinding · object"},"status":{"enum":["DRAFT","ACTIVE","INACTIVE"],"type":"string","uniqueKey":"status","typeLabel":"enum\u003cstring>"},"version":{"example":1,"type":"integer","uniqueKey":"version","typeLabel":"integer"}},"title":"Ruleset","type":"object","uniqueKey":"","typeLabel":"Ruleset · object"},"examples":{"Example":{"title":"Example","value":{"id":"6c1f4e8b-9a2d-4c7e-b5f3-8d1a6e9c4b2f","activated_at":"2026-05-15T08:00:00Z","context":"checkout","created_at":"2026-05-14T16:20:00Z","created_by":"ops@example.com","description":"Block USD transactions over limit","rules":[{"enabled":true,"type":"condition","id":"r1","action":"BLOCK","async":true,"backend":"vdm","condition":{},"description":"Escalate to the risk backend","fail_posture":"open","fields":["$.device.ip"],"live":true,"members":["vdm"],"merge":"strictest","name":"Block USD","on_error":"allow","populate_on":["fraud_report","chargeback"],"strategy":"fallback","ttl_seconds":86400}],"schema":{"name":"transaction","version":1},"status":"DRAFT","version":1}}},"exampleType":"generated"}},"description":"Updated ruleset","exampleType":"generated"},"9fbe4ab6-2c43-4b75-a630-aa93c4dd878a":{"content":{"application/json":{"schema":{"description":"No valid authentication was provided.","example":{"classifier":"UNAUTHORIZED","code":401,"message":"No valid means of authentication was provided"},"properties":{"classifier":{"enum":["UNAUTHORIZED"],"type":"string","isRequired":true,"uniqueKey":"classifier","typeLabel":"enum\u003cstring>"},"code":{"enum":[401],"type":"integer","isRequired":true,"uniqueKey":"code","typeLabel":"enum\u003cinteger>"},"message":{"type":"string","isRequired":true,"uniqueKey":"message","typeLabel":"string"}},"required":["classifier","code","message"],"title":"Unauthorized","type":"object","uniqueKey":"","typeLabel":"Unauthorized · object"},"examples":{"Example":{"title":"Example","value":{"classifier":"UNAUTHORIZED","code":401,"message":"No valid means of authentication was provided"}}},"exampleType":"generated"}},"description":"Unauthorized","exampleType":"generated"},"89b2b52f-a56c-4cc5-a7a0-c6d45adfd8ce":{"content":{"application/json":{"schema":{"description":"The token lacks the scope required for this operation.","example":{"classifier":"FORBIDDEN","code":403,"message":"Not allowed to access this resource or feature"},"properties":{"classifier":{"enum":["FORBIDDEN"],"type":"string","isRequired":true,"uniqueKey":"classifier","typeLabel":"enum\u003cstring>"},"code":{"enum":[403],"type":"integer","isRequired":true,"uniqueKey":"code","typeLabel":"enum\u003cinteger>"},"message":{"type":"string","isRequired":true,"uniqueKey":"message","typeLabel":"string"}},"required":["classifier","code","message"],"title":"Forbidden","type":"object","uniqueKey":"","typeLabel":"Forbidden · object"},"examples":{"Example":{"title":"Example","value":{"classifier":"FORBIDDEN","code":403,"message":"Not allowed to access this resource or feature"}}},"exampleType":"generated"}},"description":"Forbidden","exampleType":"generated"},"f82d251c-5da4-4cd2-b434-7a8c204ec620":{"content":{"application/json":{"schema":{"description":"The requested resource does not exist.","example":{"classifier":"NOT_FOUND","code":404,"message":"The requested resource does not exist"},"properties":{"classifier":{"enum":["NOT_FOUND"],"type":"string","isRequired":true,"uniqueKey":"classifier","typeLabel":"enum\u003cstring>"},"code":{"enum":[404],"type":"integer","isRequired":true,"uniqueKey":"code","typeLabel":"enum\u003cinteger>"},"message":{"type":"string","isRequired":true,"uniqueKey":"message","typeLabel":"string"}},"required":["classifier","code","message"],"title":"NotFound","type":"object","uniqueKey":"","typeLabel":"NotFound · object"},"examples":{"Example":{"title":"Example","value":{"classifier":"NOT_FOUND","code":404,"message":"The requested resource does not exist"}}},"exampleType":"generated"}},"description":"Not found","exampleType":"generated"},"6896d84b-5e7d-43a8-9c4a-48ace3d887ee":{"content":{"application/json":{"schema":{"description":"Response shape for request validation failures","example":{"classifier":"VALIDATION_ERROR","code":422,"validation_errors":[{"message":"must be a positive integer","path":"limit"}]},"properties":{"classifier":{"example":"VALIDATION_ERROR","type":"string","isRequired":true,"uniqueKey":"classifier","typeLabel":"string"},"code":{"example":422,"type":"integer","isRequired":true,"uniqueKey":"code","typeLabel":"integer"},"validation_errors":{"items":{"properties":{"message":{"example":"must be a positive integer","type":"string","uniqueKey":"validation_errors.items.[INDEX].message","typeLabel":"string"},"path":{"example":"limit","type":"string","uniqueKey":"validation_errors.items.[INDEX].path","typeLabel":"string"}},"type":"object","uniqueKey":"validation_errors.items.[INDEX]","typeLabel":"object"},"type":"array","isRequired":true,"uniqueKey":"validation_errors","typeLabel":"object[]"}},"required":["classifier","code","validation_errors"],"title":"ValidationErrors","type":"object","uniqueKey":"","typeLabel":"ValidationErrors · object"},"examples":{"Example":{"title":"Example","value":{"classifier":"VALIDATION_ERROR","code":422,"validation_errors":[{"message":"must be a positive integer","path":"limit"}]}}},"exampleType":"generated"}},"description":"Validation errors","exampleType":"generated"}},"security":{"7e9bff80-731a-401e-a5ab-9812892bff7b":{"bearerAuth":{"bearerFormat":"JWT","description":"RS256-signed JWT bearer token, obtained via the OAuth2 client-credentials grant and verifiable against the issuer's JWKS (see Authentication).","scheme":"bearer","type":"http","scopes":[],"schemeName":"bearerAuth"}}},"servers":{"9c71d782-c2bc-48a3-b97e-c9127bcf9c5d":{"description":"Managed instance of Specter","url":"https://{instance}.{env}.on-hellgate.cloud","variables":{"instance":{"default":"my-instance","description":"Your unique instance slug, provided during onboarding."},"env":{"default":"eu1","description":"Deployment environment (currently eu1)."}}}},"processedSecurityOptions":[{"schemes":[{"scheme":{"bearerFormat":"JWT","description":"RS256-signed JWT bearer token, obtained via the OAuth2 client-credentials grant and verifiable against the issuer's JWKS (see Authentication).","scheme":"bearer","type":"http","scopes":[],"schemeName":"bearerAuth"},"schema":{"title":"Bearer Auth","type":"string","isRequired":true,"description":"RS256-signed JWT bearer token, obtained via the OAuth2 client-credentials grant and verifiable against the issuer's JWKS (see Authentication).","format":"bearer","typeLabel":"string\u003cbearer>","deprecated":false},"schemeName":"bearerAuth","name":"Authorization"}],"title":"bearerAuth"}]},"originalFileLocation":"products/specter/openapi.yaml"},"subdomain":"starfishgmbhcokg","siteName":"Hellgate Cloud Platform","title":"Update ruleset - Hellgate Cloud Platform","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-11-12T12:24:55.053Z","originalFileLocation":"products/specter/api-reference/rulesets/update-ruleset.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Updates description and/or rules on a DRAFT ruleset. Only DRAFT rulesets are editable. context and schema are identity fields fixed at creation — submitting a changed value rejects with 422 naming the field; resubmitting the current value unchanged is a no-op. Rebinding a context is a new ruleset, not an update.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Hellgate Cloud Platform\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/starfishgmbhcokg/ehX6KGXou7_zUBXs/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/starfishgmbhcokg/ehX6KGXou7_zUBXs/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/starfishgmbhcokg/ehX6KGXou7_zUBXs/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/starfishgmbhcokg/ehX6KGXou7_zUBXs/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/starfishgmbhcokg/ehX6KGXou7_zUBXs/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/starfishgmbhcokg/ehX6KGXou7_zUBXs/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/starfishgmbhcokg/ehX6KGXou7_zUBXs/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/starfishgmbhcokg/ehX6KGXou7_zUBXs/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/starfishgmbhcokg/ehX6KGXou7_zUBXs/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Update ruleset - Hellgate Cloud Platform\">\u003cmeta data-mint-head property=\"og:description\" content=\"Updates description and/or rules on a DRAFT ruleset. Only DRAFT rulesets are editable. context and schema are identity fields fixed at creation — submitting a changed value rejects with 422 naming the field; resubmitting the current value unchanged is a no-op. Rebinding a context is a new ruleset, not an update.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Hellgate Cloud Platform\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://developer.hellgate.io/products/specter/api-reference/rulesets/update-ruleset\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://starfishgmbhcokg.mintlify.site/_mintlify/api/og/v2?division=Rulesets\u0026amp;title=Update+ruleset\u0026amp;description=Updates+%60description%60+and%2For+%60rules%60+on+a+DRAFT+ruleset.+Only+DRAFT+rulesets+are+editable.+context+and+schema+are+identity+fields+fixed+at+creation+%E2%80%94+submitting\u0026amp;theme=4a79c05b20e2f91a365b752c\u0026amp;signature=fbf65fd5b76bc9eb09c0abdf40fc6f5c4fca0fcf69c20a077f7ea24787bfd17c\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Update ruleset - Hellgate Cloud Platform\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Updates description and/or rules on a DRAFT ruleset. Only DRAFT rulesets are editable. context and schema are identity fields fixed at creation — submitting a changed value rejects with 422 naming the field; resubmitting the current value unchanged is a no-op. Rebinding a context is a new ruleset, not an update.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://starfishgmbhcokg.mintlify.site/_mintlify/api/og/v2?division=Rulesets\u0026amp;title=Update+ruleset\u0026amp;description=Updates+%60description%60+and%2For+%60rules%60+on+a+DRAFT+ruleset.+Only+DRAFT+rulesets+are+editable.+context+and+schema+are+identity+fields+fixed+at+creation+%E2%80%94+submitting\u0026amp;theme=4a79c05b20e2f91a365b752c\u0026amp;signature=fbf65fd5b76bc9eb09c0abdf40fc6f5c4fca0fcf69c20a077f7ea24787bfd17c\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/starfishgmbhcokg/ehX6KGXou7_zUBXs/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Hellgate Cloud Platform\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#16A34A\">\u003cmeta data-mint-head name=\"google-site-verification\" content=\"OJ_nWFren-zsecLNqkmvOGrnXjmCZ_fPg5ov1mM8Tno\">\u003clink data-mint-head rel=\"canonical\" href=\"https://developer.hellgate.io/products/specter/api-reference/rulesets/update-ruleset\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/products/specter/api-reference/rulesets/update-ruleset.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://developer.hellgate.io/#organization\",\"name\":\"Hellgate Cloud Platform\",\"url\":\"https://developer.hellgate.io\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/starfishgmbhcokg/fao_mR6VDpBCxc3o/logo/light.svg?fit=max\u0026auto=format\u0026n=fao_mR6VDpBCxc3o\u0026q=85\u0026s=1f2caba60febae2b0dcaddf3e196a132\"}},{\"@type\":\"WebSite\",\"@id\":\"https://developer.hellgate.io#website\",\"name\":\"Hellgate Cloud Platform\",\"url\":\"https://developer.hellgate.io\",\"publisher\":{\"@id\":\"https://developer.hellgate.io/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://developer.hellgate.io/products/specter/api-reference/rulesets/update-ruleset#webpage\",\"url\":\"https://developer.hellgate.io/products/specter/api-reference/rulesets/update-ruleset\",\"name\":\"Update ruleset\",\"description\":\"Updates description and/or rules on a DRAFT ruleset. Only DRAFT rulesets are editable. context and schema are identity fields fixed at creation — submitting a changed value rejects with 422 naming the field; resubmitting the current value unchanged is a no-op. Rebinding a context is a new ruleset, not an update.\",\"dateModified\":\"2026-11-12T12:24:55.053Z\",\"isPartOf\":{\"@id\":\"https://developer.hellgate.io#website\"},\"breadcrumb\":{\"@id\":\"https://developer.hellgate.io/products/specter/api-reference/rulesets/update-ruleset#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://developer.hellgate.io/products/specter/api-reference/rulesets/update-ruleset#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Admin\",\"item\":\"https://developer.hellgate.io/products/specter/api-reference/rulesets/create-ruleset\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Rulesets\",\"item\":\"https://developer.hellgate.io/products/specter/api-reference/rulesets/create-ruleset\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Update ruleset\",\"item\":\"https://developer.hellgate.io/products/specter/api-reference/rulesets/update-ruleset\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://developer.hellgate.io/products/specter/api-reference/rulesets/update-ruleset#apireference\",\"headline\":\"Update ruleset\",\"name\":\"Update ruleset\",\"description\":\"Updates description and/or rules on a DRAFT ruleset. Only DRAFT rulesets are editable. context and schema are identity fields fixed at creation — submitting a changed value rejects with 422 naming the field; resubmitting the current value unchanged is a no-op. Rebinding a context is a new ruleset, not an update.\",\"url\":\"https://developer.hellgate.io/products/specter/api-reference/rulesets/update-ruleset\",\"mainEntityOfPage\":{\"@id\":\"https://developer.hellgate.io/products/specter/api-reference/rulesets/update-ruleset#webpage\"},\"dateModified\":\"2026-11-12T12:24:55.053Z\",\"publisher\":{\"@id\":\"https://developer.hellgate.io/#organization\"},\"isPartOf\":{\"@id\":\"https://developer.hellgate.io#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Hellgate Cloud Platform\",\"publisher\":{\"@type\":\"Organization\",\"name\":\"Hellgate Cloud Platform\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","agentPointerHref":"/products/specter/api-reference/rulesets/update-ruleset.md","siteName":"Hellgate Cloud Platform","nav":{"global":null,"products":[{"product":"Cloud Platform","icon":"cloud","pages":[{"title":"Cloud Platform","href":"/platform"}]},{"product":"Guardian","icon":"vault","pages":[{"title":"Guardian","href":"/products/guardian/overview"}]},{"product":"Specter","icon":"shield","tabs":[{"tab":"Documentation","pages":[{"title":"Documentation","href":"/products/specter/overview"}]},{"tab":"API Reference","groups":[{"group":"Getting Started","pages":[{"title":"API Information","href":"/products/specter/api-reference/getting-started"}]},{"group":"Decisions","pages":[{"title":"Create decision","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/decisions","href":"/products/specter/api-reference/decisions/create-decision"},{"title":"Get decision details","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/decisions/{id}","href":"/products/specter/api-reference/decisions/get-decision-details"},{"title":"Resolve review","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/decisions/{id}/resolve","href":"/products/specter/api-reference/resolutions/resolve-review"},{"title":"Get decision payload","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/decisions/{id}/payload","href":"/products/specter/api-reference/decisions/get-decision-payload"},{"title":"Get decision evidence","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/decisions/{id}/evidence","href":"/products/specter/api-reference/decisions/get-decision-evidence"},{"title":"Execute interceptor","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/interceptors/{ref}","href":"/products/specter/api-reference/interceptors/execute-interceptor"}]},{"group":"Lifecycle Events","pages":[{"title":"Ingest lifecycle event","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/events","href":"/products/specter/api-reference/lifecycle-events/ingest-lifecycle-event"},{"title":"Get event details","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/events/{id}","href":"/products/specter/api-reference/lifecycle-events/get-event-details"}]},{"group":"Admin","pages":[{"group":"Rulesets","pages":[{"title":"Create ruleset","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/rulesets","href":"/products/specter/api-reference/rulesets/create-ruleset"},{"title":"Get rulesets","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/rulesets","href":"/products/specter/api-reference/rulesets/get-rulesets"},{"title":"Get ruleset details","description":"Retrieves a ruleset with its rules and lifecycle state.","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/rulesets/{id}","href":"/products/specter/api-reference/rulesets/get-ruleset-details"},{"title":"Update ruleset","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml patch /api/admin/rulesets/{id}","href":"/products/specter/api-reference/rulesets/update-ruleset"},{"title":"Activate ruleset","description":"Transitions a ruleset from DRAFT → ACTIVE. Any previously ACTIVE ruleset for the same context is demoted to INACTIVE. Activation rejects with 422 when the ruleset's schema binding is deprecated.","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/rulesets/{id}/activate","href":"/products/specter/api-reference/rulesets/activate-ruleset"},{"title":"Rollback ruleset","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/rulesets/{id}/rollback","href":"/products/specter/api-reference/rulesets/rollback-ruleset"},{"title":"Simulate ruleset","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/rulesets/{id}/simulate","href":"/products/specter/api-reference/rulesets/simulate-ruleset"}]},{"group":"Schemas","pages":[{"title":"List schemas","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/schemas","href":"/products/specter/api-reference/schemas/list-schemas"},{"title":"Register schema","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/schemas","href":"/products/specter/api-reference/schemas/register-schema"},{"title":"Get schema","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/schemas/{name}","href":"/products/specter/api-reference/schemas/get-schema"},{"title":"List versions","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/schemas/{name}/versions","href":"/products/specter/api-reference/schemas/list-versions"},{"title":"Publish version","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/schemas/{name}/versions","href":"/products/specter/api-reference/schemas/publish-version"},{"title":"Get version","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/schemas/{name}/versions/{version}","href":"/products/specter/api-reference/schemas/get-version"},{"title":"Deprecate version","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/schemas/{name}/versions/{version}/deprecate","href":"/products/specter/api-reference/schemas/deprecate-version"}]},{"group":"Integrations","pages":[{"title":"Create integration","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/integrations","href":"/products/specter/api-reference/integrations/create-integration"},{"title":"Get integrations","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/integrations","href":"/products/specter/api-reference/integrations/get-integrations"},{"title":"Get integration details","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/integrations/{id}","href":"/products/specter/api-reference/integrations/get-integration-details"},{"title":"Update integration","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml patch /api/admin/integrations/{id}","href":"/products/specter/api-reference/integrations/update-integration"},{"title":"Delete integration","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml delete /api/admin/integrations/{id}","href":"/products/specter/api-reference/integrations/delete-integration"}]},{"group":"Interceptors","pages":[{"title":"Create interceptor","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/interceptors","href":"/products/specter/api-reference/interceptors/create-interceptor"},{"title":"Get interceptors","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/interceptors","href":"/products/specter/api-reference/interceptors/get-interceptors"},{"title":"Get interceptor details","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/interceptors/{id}","href":"/products/specter/api-reference/interceptors/get-interceptor-details"},{"title":"Update interceptor","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml patch /api/admin/interceptors/{id}","href":"/products/specter/api-reference/interceptors/update-interceptor"},{"title":"Delete interceptor","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml delete /api/admin/interceptors/{id}","href":"/products/specter/api-reference/interceptors/delete-interceptor"},{"title":"Activate interceptor","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/interceptors/{id}/activate","href":"/products/specter/api-reference/interceptors/activate-interceptor"},{"title":"Rollback interceptor","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/interceptors/{id}/rollback","href":"/products/specter/api-reference/interceptors/rollback-interceptor"}]},{"group":"Blacklist","pages":[{"title":"Create blacklist value","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/blacklist","href":"/products/specter/api-reference/blacklist/create-blacklist-value"},{"title":"Get blacklist values","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/blacklist","href":"/products/specter/api-reference/blacklist/get-blacklist-values"},{"title":"Get blacklist value","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/blacklist/{id}","href":"/products/specter/api-reference/blacklist/get-blacklist-value"},{"title":"Delete blacklist value","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml delete /api/admin/blacklist/{id}","href":"/products/specter/api-reference/blacklist/delete-blacklist-value"}]},{"group":"Webhooks","pages":[{"title":"Event notification","version":null,"deprecated":null,"openapi":"/products/specter/openapi.yaml webhook event_notification","href":"/products/specter/api-reference/event-notification"},{"title":"Register webhook","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml post /api/admin/webhooks","href":"/products/specter/api-reference/webhooks/register-webhook"},{"title":"Get webhooks","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/webhooks","href":"/products/specter/api-reference/webhooks/get-webhooks"},{"title":"Get webhook details","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml get /api/admin/webhooks/{id}","href":"/products/specter/api-reference/webhooks/get-webhook-details"},{"title":"Update webhook","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml patch /api/admin/webhooks/{id}","href":"/products/specter/api-reference/webhooks/update-webhook"},{"title":"Delete webhook","deprecated":null,"version":null,"openapi":"/products/specter/openapi.yaml delete /api/admin/webhooks/{id}","href":"/products/specter/api-reference/webhooks/delete-webhook"}]}]}]},{"tab":"Changelog","pages":[{"title":"Changelog","href":"/products/specter/changelog"}]}]},{"product":"Commerce","icon":"cart-shopping","pages":[{"title":"Commerce","href":"/products/commerce/v2/overview"}]},{"product":"Link","icon":"link","pages":[{"title":"Link","href":"/products/link/overview"}]}]},"basePath":"","title":"Update ruleset - Hellgate Cloud Platform","buildId":"dpl_5tKr6cUUeuVJdEZ9JAEY3h4soMJ8","deploymentId":"dpl_5tKr6cUUeuVJdEZ9JAEY3h4soMJ8","dynamics":["c2aa737661","b0f84f53ff","a9bb46feb3","2b790eda92"],"chunks":["OpenApiReferenceProvider-DbeN8Wo6.js","utils-CwSb5Doc.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-BmREeM3N.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-AYjWerCj.js","snippetPresets-QD4ULQlp.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","conditional-requirements-CFM0id0Q.js","Page-CqcDm2To.js","ApiExamples-Digj5cjF.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-CiHrzluR.js","shallow-D7h7pFGg.js","getApiPlaygroundDisplay-mlxkpAUf.js","CodeSnippet-D2rH5lZI.js","CodeSnippets-B53ulOaL.js","getPastedJson-DxGeNMih.js","icon-BHbyrebp.js","MethodPill-C7F_KEEJ.js","DuplicateIcon-j3c5-73z.js","createIcon-OvCnyFqD.js","DynamicMarkdown-DpAS3GCb.js","SectionHeading-DMPpdrvW.js","axios-vUo_bRtp.js","Markdown-D7pDRguu.js","clsx-2_c6mSt_.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-Cu7_SikL.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-ydqCcy4b.js"]}