authorize_use
authorize_use answers the central governance question: is this proposed use of a governed asset allowed? You name an asset, describe the use, state why (purpose_key), and optionally add a transfer destination or a granted exception to verify. The tool derives an advisory decision from the current deployment publication's deployed instruction decisions, cites every instruction that applied, and returns two chainable identifiers: a decision_id for the winning published rule and an authorization_id for this evaluation. Protocol alias: authorize-use.
It never enforces, approves, masks, or blocks anything, and it never changes your policies, instructions, publications, or served decision rows. It is not side-effect-free, though: every answer is recorded as a durable decision-ledger record before it is returned. See Recorded evidence below.
When to useDirect link to When to use
Call authorize_use before an agent acts on data for a stated purpose — training a model, exporting rows to a CRM, building a marketing segment, quoting a value in a support reply. It is the purpose-level counterpart of validate_query_context: authorize a use when the question is "may I do this with that asset," validate a query when you hold actual SQL. Use discover_context first to find governed assets and their valid scenario_key values, and inspect_governance_rules when you want every rule rather than one effective decision.
InputDirect link to Input
| Field | Type | Required | Description |
|---|---|---|---|
asset | object | Yes | The governed asset to authorize against. |
asset.database | string | Yes | Normalized database name (1–255 chars). |
asset.schema | string | Yes | Normalized schema name. |
asset.table | string | Yes | Normalized table name. |
asset.column | string | No | Column name; omit for a table-level question. |
scenario_key | string | No | Canonical scenario key (the 65-key library). Preferred; non-canonical values are rejected with invalid_parameters. |
use | string | Yes | Free-text description of the proposed use (1–2,000 chars). |
purpose_key | string | No | A registry purpose such as research.general or commercial.general. Decision-bearing — see Purpose. Exact match only. |
data_access_context | object | No | Evaluation anchor for purpose-bound access windows. The current shape is {as_of: <ISO-8601 timestamp>}; required by an as_of policy and omitted for a server-anchored rolling window. |
on_behalf_of | string | No | Who the agent is acting for. Audit metadata only, decision-neutral, never echoed back. Trimmed, single-line, 1–320 code points. |
satisfied_conditions | array | No | Exceptions to verify: [{kind: "approval", exception_id}], 1–8 entries, duplicate ids rejected. See Consuming an exception. |
operation | string | No | Transfer operation token, for example export (1–64 chars, letters, digits, spaces, _, ., -). |
destination | object | No | Transfer destination. When present, at least one of system or jurisdiction is required. |
destination.system | string | No | Destination system token, for example SALESFORCE. |
destination.jurisdiction | string | No | Destination jurisdiction token, for example US. |
consumer_jurisdiction | string | No | Jurisdiction of the data consumer, for example EU. |
Inputs are strict — unknown keys are rejected. In particular there is no input for the caller's role: an invented bound_role, actor_role, or role key is invalid_parameters. The evaluated role comes only from the verified token's bound_role. There is likewise no caller-asserted anonymization_applied and no caller-supplied purpose category or registry version; those are permanently rejected, not deferred.
The scenario resolves in a fixed order: an explicit canonical scenario_key always wins; otherwise the use text is mapped deterministically onto exactly one canonical key; otherwise, when transfer context is present, the question defaults to residency.cross_border_transfer; anything still ambiguous returns the typed scenario_unresolved answer. The mapper never guesses, and free text never becomes a runtime key.
An operation alone is not transfer context — transfer rules activate only when a destination or consumer_jurisdiction is present. A context-less call answers with each transfer rule set's collapsed published verdict.
PurposeDirect link to Purpose
purpose_key states why the data is being used. It is optional in the schema and evaluated when supplied. The complete semantics are on the Purpose keys page; the short version:
- Purpose is decision-bearing only where a
usage_guidancerule participates in the answer. With no such rule participating, purpose adds no restriction and the answer is unchanged. - Matching is normalized exact key or category equality. A category token (
ai) matches every key in that family and only those; a key (ai.training) matches only itself. There is no fuzzy, prefix, or similarity matching anywhere. - A matching prohibition denies. A matching permitted entry can keep
allow. An unmatched purpose, an unmapped legacy authored string, or an absentpurpose_keyyieldsrequire_reviewinstead of a permitted-uses allow. - Purpose never overrides a restriction from another instruction family. Any surviving deny, mask, role restriction, retention, transfer, or AI restriction still appears in the answer and keeps
can_proceed_now: false. - Purpose also scopes exception verification, on a separate axis — see Consuming an exception.
An unknown or non-exact purpose_key (AI.TRAINING, ai.training with a trailing space, ai_training) is invalid_parameters: the call never runs and nothing is recorded.
OutputDirect link to Output
Every response is one of the three typed answer states, discriminated on state, and always carries advisory: true. All three states carry the evaluation-provenance fields and the ledger id.
Common to every state:
| Field | Description |
|---|---|
advisory | Always true. |
authorization_id | The durable ledger record for this evaluation — pass to explain_why as {"kind": "authorization", "authorization_id": ...}. Distinct from decision_id. |
evaluated_at | RFC 3339 UTC instant, minted once per call and identical to the ledger record's. Validity windows are filtered at this instant. |
evaluated_purpose | {purpose_key, purpose_category, registry_version}, or null when the call stated no purpose. |
evaluated_actor_role | The token's bound role, or null. |
satisfied_conditions | Result echo for each supplied entry, in input order: verified or not_verified. |
An answered payload adds:
| Field | Description |
|---|---|
decision | The effective decision: allow, deny, mask_full, mask_partial, require_review, conditional, retain, or log_only. |
decision_id | The winning instruction's serving-row UUID — pass it to explain_why as {"kind": "decision", "decision_id": ...}. |
reason | The winning instruction's decision reason. |
scenario_key, scenario_lane | The resolved canonical scenario and its lane (platform, agent, or human). |
asset | The asset scope; column is explicitly null for table-level answers. |
instructions | Every applicable instruction record, ranked winner first, each with its own decision_id, key and family, decision and reason, policy provenance, resolution paths, validity window (effective_from / effective_until), and conflict fields. |
context_facts | Allow-listed published data-meaning facts (sensitivity, category, PII flag, masking), or null. |
obligations | Handling steps derived from mask_full/mask_partial rows and retain rows, each with a typed projection of the source rule's parameters. |
conditions | For a conditional decision: the gates to satisfy — approval_required, anonymize_first, role_restricted, or ai_restriction — each with satisfied, satisfied_by_exception_id, and a typed projection. Empty otherwise. |
prohibitions | Reasons derived from deny rows, tagged deny_decision, prohibited_use, or ai_governance by instruction family. |
next_actions | Fixed guidance strings matched to the decision. |
can_proceed_now | true only when nothing blocks the use. A conditional answer stays false until every satisfiable condition is covered by a server-verified exception — and any role_restricted or ai_restriction condition keeps it false regardless. |
satisfied_exceptions | Verified-only citations [{exception_id, expires_at}], matching the recorded evidence exactly. |
publication | The publication_id and published_at of the current deployment publication. |
When multiple instructions apply, the effective decision is deterministic: specificity first (column-level beats table-level; within a level, selector beats collection beats taxonomy), then priority, then restrictiveness (deny > mask_full > mask_partial > require_review > conditional > retain > log_only > allow), then a stable key tiebreak.
With transfer context present, each transfer-governance row's verdict is re-derived at read time from its published rule parameters: rules are checked in authored order, and the first rule whose present constraints (operations, destination systems, destination jurisdictions, consumer jurisdictions) all match wins; matching is case-insensitive, and a constraint the context cannot answer never matches. No matching rule falls to the authored default effect. A matched conditional rule emits its specific conditions — approval and anonymize-first can both come from one rule.
Obligation and condition projectionsDirect link to Obligation and condition projections
Obligation and condition entries carry a read-only typed projection of the source rule's served parameters. This adds no evaluation and no new decisions — it is the same authored configuration, shaped:
| Projection kind | Source family | Shape |
|---|---|---|
mask | masking | {method, config, exempt_roles} |
retain | retention | {period, trigger, action} |
row_filter | row_access | {rules} |
ai | ai_governance | {requires_anonymization, restrictions} |
Rules of other families project null. The projection keys are snake_case; the nested config and rules payloads are your authored policy DSL carried through verbatim. aggregate_only is a reserved projection kind — documented, never emitted today.
Answer statesDirect link to Answer states
| State | Reason codes | When |
|---|---|---|
answered | — | Published rows are sufficient; any of the eight decisions, with citations. |
review_required | decision_requires_review, conflicted_published_state | The winner is an explicit require_review (including one produced by purpose evaluation), or applicable rows conflict. The compatibility decision field is pinned to require_review, and a conflicted answer carries the conflict group with every opposing source — conflicts are never ranked away. |
not_enough_published_state | no_current_publication, no_published_instruction_state, unresolved_published_target, scenario_unresolved, not_currently_effective | No current publication; zero applicable rows; unresolved published targets relevant to this asset (listed in unresolved_targets); an unresolvable scenario (scenario_key is then null); or rows exist for the asset and scenario but none is inside its validity window at evaluated_at. |
Unknown assets are a typed asset_not_found error, not an answer state, and a cross-tenant reference is indistinguishable from not found.
Purpose-bound access windowsDirect link to Purpose-bound access windows
An access_window instruction matches the token's exact bound role plus the
declared purpose_key. A rolling rule anchors at the server's evaluated_at;
an as_of rule requires data_access_context.as_of. The answer returns an
access_window condition with time_column, window_type, lookback_days,
anchor_at, and lower_bound_at.
Missing purpose, missing required as-of context, a role mismatch, or an
unmatched rule fails closed as
review_required / access_window_context_required. The authorization is not
proof that later SQL respects the bound—call validate_query_context with the
same purpose and data-access context.
Consuming an exceptionDirect link to Consuming an exception
When a steward grants an exception through the request lane, the agent retries with the exception_id it got from check_request:
{
"asset": { "database": "master", "schema": "public", "table": "customers" },
"use": "sync approved customer fields to the CRM",
"scenario_key": "residency.cross_border_transfer",
"operation": "export",
"destination": { "system": "SALESFORCE", "jurisdiction": "US" },
"consumer_jurisdiction": "EU",
"satisfied_conditions": [
{ "kind": "approval", "exception_id": "0b7a9e2c-6c1f-4f5f-9f2e-2a2d0c9f4a11" }
]
}
The server verifies each cited exception entirely in SQL, twice — once on the read and again at the write instant — checking that the subject is the calling token, that it is live (unexpired and unrevoked), and that its asset, scenario, and purpose scope match. Nothing you send is trusted.
What holds:
- One uniform
not_verifiedresult. Forged, expired, revoked, foreign-subject, foreign-workspace, and out-of-scope exception ids are byte-identical on the wire. No probe can learn whether a foreign exception exists. - A verified exception satisfies conditions, nothing more. It satisfies
approval_requiredandanonymize_firston aconditionalanswer, and exactly one review outcome:review_requiredwith reasondecision_requires_review, which is transformed into a satisfied approval condition. It never satisfiesrole_restrictedorai_restriction, never overrides a deny, and never overrides a conflict or any insufficient-state answer. - Revalidation never satisfies
anonymize_first. Submitting rewritten SQL tovalidate_query_contextproves only what the analyzer can observe; it does not certify anonymization. That gate needs an approval-reference exception. - Purpose scope is exact and disjoint. A purpose-scoped exception verifies only for a call stating a purpose in that category. A
NULLpurpose scope means purposeless and matches only a purposeless call — it is not "any purpose", and no wildcard exists.
validate_query_context takes no satisfied_conditions, by design.
Recorded evidenceDirect link to Recorded evidence
Every answer — in all three states — is written to the decision ledger before it returns. The write is awaited: if it fails, the call fails with internal_error rather than returning an answer nobody can cite later.
The record holds normalized, redacted evidence: the canonical inputs, the winning decision, the cited serving decision ids, the verified exception ids, the recorded purpose context, and an immutable actor snapshot. It never holds your raw use text. Raw content is stored only in a protected admin/owner-readable table, and only when your workspace enables the corresponding privacy switch (default off).
Retrieve it any time with explain_why {"kind": "authorization", "authorization_id": "..."} — including in a later session, after the publication has changed.
ExamplesDirect link to Examples
Purposeful: stating why unlocks the permitted useDirect link to Purposeful: stating why unlocks the permitted use
On the Customer 360 sample workspace, the customers table is governed by a policy whose permitted-uses list names the analytics category:
{
"asset": { "database": "master", "schema": "public", "table": "customers" },
"use": "build a churn analytics dashboard",
"scenario_key": "purpose.allowed_use",
"purpose_key": "analytics.reporting"
}
The stated purpose's category (analytics) matches the authored category token, so the permitted-uses rule answers allow:
{
"state": "answered",
"advisory": true,
"authorization_id": "6f0a1c58-2e4f-4a3a-9c5b-1f70b2a5d9c4",
"decision": "allow",
"decision_id": "accef000-0000-4000-8000-000000000020",
"reason": "Purpose `analytics.reporting` is named by this policy's permitted-uses list (policy Customer 360 customer use guardrails).",
"scenario_key": "purpose.allowed_use",
"scenario_lane": "human",
"can_proceed_now": true,
"evaluated_at": "2026-07-29T10:15:00.000Z",
"evaluated_purpose": {
"purpose_key": "analytics.reporting",
"purpose_category": "analytics",
"registry_version": 2
},
"evaluated_actor_role": null,
"satisfied_conditions": [],
"satisfied_exceptions": [],
"instructions": [
{
"instruction_key": "usage_guidance:spec.usage.permittedUses:permitted",
"instruction_family": "usage_guidance",
"scenario_key": "purpose.allowed_use",
"decision": "allow",
"parameters": { "kind": "permitted", "uses": ["analytics", "reporting", "support"] },
"effective_from": null,
"effective_until": null
}
]
}
(Payloads on this page are trimmed to the fields each example turns on. A real answer carries every field listed under Output, and each instruction record is complete.)
Purpose-missing: the same call fails closedDirect link to Purpose-missing: the same call fails closed
Drop purpose_key and nothing else changes about the request:
{
"asset": { "database": "master", "schema": "public", "table": "customers" },
"use": "build a churn analytics dashboard",
"scenario_key": "purpose.allowed_use"
}
The identical published rule now answers review, because a purpose-blind call can never ride a permitted-uses allow:
{
"state": "review_required",
"advisory": true,
"reason_code": "decision_requires_review",
"decision": "require_review",
"reason": "Permitted uses require a declared purpose; this call stated none (policy Customer 360 customer use guardrails).",
"can_proceed_now": false,
"evaluated_purpose": null,
"evaluated_actor_role": null
}
This is the single most common surprise when adopting purpose. If a call that used to answer allow now answers review_required on unchanged policy, add the purpose_key.
The reverse case is just as important: on an asset where no usage_guidance rule participates — a masking question, say — adding or removing purpose_key changes nothing at all.
Temporal: published, but not in force right nowDirect link to Temporal: published, but not in force right now
A policy version may declare a validity window (spec.validity). Every instruction of that version carries it, and rows are served only while effective_from <= evaluated_at < effective_until — the lower bound is inclusive, the upper bound is not, and a null bound is unbounded.
If governance for the asset and scenario exists but no rule is currently in force, you do not get silence and you do not get a guess:
{
"state": "not_enough_published_state",
"advisory": true,
"reason_code": "not_currently_effective",
"message": "Not enough published governance state — review required.",
"unresolved_targets": [],
"next_actions": ["..."],
"asset": { "database": "master", "schema": "public", "table": "customers", "column": null },
"scenario_key": "purpose.allowed_use",
"evaluated_at": "2026-07-29T10:15:00.000Z",
"evaluated_purpose": null,
"evaluated_actor_role": null
}
Distinguish it from its neighbour: no_published_instruction_state means there are no rows at all for that asset and scenario; not_currently_effective means rows exist but the clock is outside every window. The fix for the first is authoring coverage; the fix for the second is a window that includes now — or simply waiting.
Time-of-day windows, calendars, and per-instruction windows are out of scope today; validity is declared per policy version.
Destination-aware transferDirect link to Destination-aware transfer
Authorizing an Customer 360 customer export to a specific destination:
{
"asset": { "database": "master", "schema": "public", "table": "customers" },
"use": "sync approved customer fields to the CRM",
"scenario_key": "residency.cross_border_transfer",
"operation": "export",
"destination": { "system": "SALESFORCE", "jurisdiction": "US" },
"consumer_jurisdiction": "EU"
}
The transfer rules resolve this destination to a conditional decision with its exact gates:
{
"state": "answered",
"advisory": true,
"decision": "conditional",
"decision_id": "accef000-0000-4000-8000-000000000010",
"reason": "Transfer to SALESFORCE (US) for consumer jurisdiction EU requires approval and anonymization and role PRIVACY_ADMIN (policy Customer 360 transfer guardrails).",
"scenario_key": "residency.cross_border_transfer",
"scenario_lane": "platform",
"can_proceed_now": false,
"conditions": [
{
"kind": "anonymize_first",
"required_role": null,
"requirement": "This data must be anonymized before the transfer.",
"satisfied_by": "Anonymize or de-identify the columns in scope, then re-run authorize_use.",
"required_by_policy": "Customer 360 transfer guardrails",
"satisfied": false,
"satisfied_by_exception_id": null,
"projection": null
},
{
"kind": "approval_required",
"required_role": "PRIVACY_ADMIN",
"requirement": "Transfer to SALESFORCE (US) for consumer jurisdiction EU requires approval and anonymization and role PRIVACY_ADMIN (policy Customer 360 transfer guardrails).",
"satisfied_by": "Obtain approval from PRIVACY_ADMIN.",
"required_by_policy": "Customer 360 transfer guardrails",
"satisfied": false,
"satisfied_by_exception_id": null,
"projection": null
}
],
"obligations": [],
"prohibitions": [],
"satisfied_conditions": [],
"satisfied_exceptions": [],
"context_facts": {
"sensitivity": "restricted",
"category": "pii",
"subcategory": null,
"pii": true,
"masking": { "type": "partial", "exempt_roles": ["DATA_STEWARD", "PRIVACY_ADMIN"] }
},
"publication": {
"publication_id": "accef000-0000-4000-8000-000000000001",
"published_at": "2026-07-16T00:00:00.000Z"
}
}
The same call with "system": "ADS_PLATFORM" answers deny, and an unmatched destination falls to the authored default effect — all from the same published instruction. This answer is the natural starting point for the request lane: the two conditions are exactly what a steward would be asked to waive.
Limits and notesDirect link to Limits and notes
- Answers read only the current deployment publication. Draft policies, approved-but-unpublished versions, and superseded publications never affect the decision.
- The tool never mutates governance state and never pushes anything to your data platforms; enforcement stays in your systems.
- A non-canonical
scenario_keyorpurpose_key, or any malformed input, isinvalid_parametersand is not metered; every accepted invocation consumes one metered MCP call before any read. explain_whyon a transferdecision_idexplains the published row's collapsed verdict; destination-specific reasoning lives in this tool'sreasonandconditions, andexplain_why {"kind": "authorization"}replays the whole evaluation.- Obligations, conditions, prohibitions, and next actions derive only from published instruction rows and allow-listed context facts — never from your
usetext. on_behalf_ofis recorded as a presence flag always, and as raw text only under your workspace's raw-text privacy switch. It is never returned in any answer.- Every response is schema-validated before it leaves the server; a violation returns
output_validation_failed, never a partial answer.
RelatedDirect link to Related
- Purpose keys — the complete
purpose_keysemantics request_accessandcheck_request— what to do with a non-allowanswerexplain_why— evidence for adecision_idor anauthorization_idvalidate_query_context— the SQL-shaped counterpart- Typed answer states and Scenarios
- MCP tool schemas — full input/output schemas