Nuance confirmation
A nuance is one statement about what your data means. “Debooking__c marks a finance reversal, not a loss.” “Waste management accounts are not serviceable, so leave them out.” The knowledge lives with the people who run the business rather than in the CRM schema, and ax1om captures it as an entry with a version, a source, and a reason.
Anyone can propose one. Only a person in your workspace answers one, and until they do, the entry gates nothing anywhere.
Propose, then decide
Section titled “Propose, then decide”An agent proposes. A person decides. That split is the whole shape of this surface.
propose_nuance is the agent’s only write here. It creates a proposed entry and nothing else: nothing a proposal says changes a number until somebody confirms it. list_nuances reads what your workspace knows, merged across the universal tier ax1om brings and your own entries, with every entry labelled by tier and status.
There is no confirm tool over MCP. An agent that asks to clean up the queue can triage, summarise and pre-fill; the submit belongs to a person.
Confirming and declining
Section titled “Confirming and declining”Two endpoints. Both take a JSON body, both return the new entry.
Confirm
Section titled “Confirm”POST /semantics/nuances/{entry_id}/confirm{ "why": "Finance confirmed it: these are reversals, not losses.", "scopes": ["assembly", "training-features"]}why is required, 1 to 4000 characters. The reason is the point of the whole surface: it is what a future reader sees when they ask why the numbers behave this way.
scopes is what the confirmation binds, and it is the confirmer’s decision rather than the proposer’s. The proposal carries its own scopes, and those are a suggestion to show the reviewer, nothing more.
| Scope | What it binds |
|---|---|
assembly |
How the training dataset is assembled |
training-features |
Which fields reach the model as features |
forecasting-cohorts |
Forecast cohort construction |
readout-annotation |
Annotation only, shown in readouts, changing no number |
The vocabulary is closed and it holds one further value, which binds a
reporting surface this site does not document. Sending a scope outside the set
returns a 422 whose message lists every accepted value, so the current set is
always readable from the API itself.
The key has no server-side default. Leaving it out is a 422 rather than a confirmation that binds nothing, because a client bug that drops it should fail loudly instead of quietly confirming a rule with no scopes.
"scopes": [] is a real answer, not an empty-state accident. It says the statement is true and it gates nothing, and it is how an org rules that one of ax1om’s own base-tier exclusions does not apply to them.
Decline
Section titled “Decline”POST /semantics/nuances/{entry_id}/decline{ "why": "That field is free text, so the detector misread it."}No scopes: a decline binds nothing.
Declining is not deleting. It mints a retired version carrying the decliner’s reason, so “we looked at this and said no, because X” is a durable answer. The word for the action is decline.
A decline changes nothing else. The row it returns always has empty scopes and no base reference, even when the proposal pinned one. That matters: a retired row carrying a base reference would suppress that base-tier rule for your whole org, so declining ax1om’s own warning about a field would have deleted the platform’s exclusion of it. A refusal never becomes an override. Standing a base rule down is a separate, explicit confirmation with the scope left off.
Who may call them
Section titled “Who may call them”An org owner or admin, and a person rather than an agent.
Both endpoints refuse an agent with 403 before the role is even considered, so an agent acting under an admin’s credential gets the honest answer rather than a role complaint. An agent is recognised either by the marker our MCP server stamps on its calls or by the client claim on an agent-minted token, which is what a browser session never carries.
The response
Section titled “The response”200, with the new row. Its version is the proposal’s plus one and its status is the answer. The proposal itself becomes superseded and stays readable at its own pinned reference.
{ "tier": "org", "ref": "field:debooking__c@v2", "id": "sem_4c1f9ab2e770", "subject_kind": "field", "subject_key": "debooking__c", "version": 2, "superseded_by": null, "rule_type": "exclude-when", "rule": { "predicate": { "field": "Debooking__c", "operator": "equals", "value": "true" } }, "why": "Finance confirmed it: these are reversals, not losses.", "provenance": { "source": "detector", "detector": "find_presence_leakage" }, "status": "confirmed", "scopes": ["assembly", "training-features"], "created_at": "2026-09-12T18:04:11.220913+00:00"}The subject, the rule, the base reference and the provenance are copied from the proposal verbatim. No row’s content is ever edited. What the person supplies is the reason, the scopes and their own identity.
A confirmed entry changes numbers, and it says so where it does: a readout that applied one carries a disclosure line naming the rule and its tier.
Errors
Section titled “Errors”| Status | Code or condition | What the API says |
|---|---|---|
403 |
The caller is an agent | “Agents cannot confirm or decline a nuance. Propose it instead - a person in this workspace confirms it in the console.” |
403 |
The caller is not an owner or admin | “Only org owners and admins can confirm or decline a nuance.” |
404 |
No such entry in your workspace | “No such entry in this workspace.” |
409 |
The entry is not a live proposed row | “This entry is superseded, not proposed - only a proposed entry can be confirmed or declined.” |
409 |
A newer proposal exists on the same subject | “A newer version of this subject exists - review field:debooking__c@v3 instead.” |
422 |
The payload is invalid | Two shapes, below. |
The 404 text is identical for “wrong workspace” and “no such id” on purpose, so the surface cannot be used to probe another tenant’s ids.
Both 409s mean refresh rather than retry. The first is what a double submit actually hits, because answering a proposal supersedes it, and the state word in the sentence is the real state, so it can also read confirmed or retired. Branch on the status code rather than on the word.
The 422 comes back in two shapes, deliberately not normalised, because they come from two different layers:
{ "detail": [ { "type": "missing", "loc": ["body", "scopes"], "msg": "Field required" } ] }{ "detail": "unknown application scope 'everything' - one of ['assembly', ...]" }A list is body validation: a missing or empty why, a missing scopes, a wrong type. A string is the service’s own vocabulary refusal: an unknown scope, a why that is blank after trimming, or one over the length cap. A string is safe to show as written.
Both 403s are recorded in your org’s audit log under the refusal category, and they use different codes because the remediation differs: agent_control_forbidden means no credential of any role may do this from an agent, and permission_denied means this person needs the role. Every confirm and decline is recorded too, under a semantics category, with the action, the reference, the predecessor and the scopes.
A proposal can be stranded
Section titled “A proposal can be stranded”Proposing never supersedes, so two proposals can stack on one subject. The older one then has no exit: it cannot be confirmed or declined, because a newer version of the subject exists, and it does not appear in the list of nuances, because that returns only the latest version per subject. It is neither answerable nor visible.
In practice the newer proposal is the one a reviewer sees, and answering it supersedes the whole chain, so nothing is lost. The older row is not cleaned up.
What this door does not do
Section titled “What this door does not do”Stated as absence rather than as a roadmap:
- Editing a confirmed entry. There is no edit. A correction is a new proposal and a new confirmation.
- Un-confirming. There is no rewind. A confirmed rule is answered by proposing a replacement.
- Bulk confirm. There is no bulk action. The reason field is the product, and a bulk action would produce a queue of identical empty reasons.
- Lifting a deploy refusal. The deploy policy refusal names an override rule type. This door does not accept that rule type.
There is no confirmation queue in the app; an org owner or admin answers a proposal over the API.
propose_nuance· the agent’s writelist_nuances· what your workspace knows, by tier and status- Validation state and deploy policy · the other governed decision on a model