AI Governance: Introspection & Approvals
Flash puts every AI-initiated action behind a per-action authorization gear — auto, review or forbidden— configured per workspace in the AI Activity & Approvals console. For an AI client over MCP this means two things: you can discoverthe business objects and the actions you're allowed to take before acting, and your write calls return structured results — executed, pending human approval, or refused with a machine-readable reason.
This guide covers the introspection tools, the write semantics under a review gear, and the approval lifecycle. It builds on the same governed MCP server documented in the MCP Integration guide. For the business overview, see the AI Governance & Approvals product page. Gear routing is enabled progressively — the behavior below applies as governed actions roll out to your workspace.
Business-object introspection (scope introspection:read)
Three read tools let an AI client learn the shape of the business before touching it: which objects exist (including the workspace's own custom fields and custom objects), what each attribute means, how objects relate, and which actions are executable — with their preconditions and current gear. A well-behaved agent calls these first and only proposes actions the gears actually allow.
list_objectsreadDiscover the business objects available to the key's workspace: member, order, coupon, campaign, store, product, segment, tag — plus any custom objects the workspace has defined. Returns each object's key, label and a one-line meaning. Call first to learn what exists before reading or acting.
(none)describe_objectreadOne object's full shape: its attributes (including the workspace's custom fields) with their type and business meaning, and its relationships to other objects (e.g. an order belongs to a member; a coupon can be issued to a member). PII fields are returned as metadata only — name, type, semantics and a pii flag — never their values.
objectlist_actionsreadThe actions an AI client may take, with each action's preconditions and its current authorization gear (auto / review / forbidden) as configured by the workspace. Filter by object to see only the actions that touch it. What this returns is exactly what the governance layer will enforce — use it to avoid proposing actions that will be refused.
object?PII is metadata-only, by design. Introspection describes fields that hold personal information (their name, type, meaning and a pii flag) but never returns their values. Reading actual member data goes through the member read tools and their own scopes.
Example: describe an object, then check the gears
Descriptive examples of the request/response shape (abbreviated — real responses list every field and action):
// describe_object { "object": "member" }
{
"object": "member",
"label": "Member",
"meaning": "A loyalty program member owned by this workspace.",
"fields": [
{ "name": "email", "type": "string", "pii": true,
"meaning": "Contact email. PII: metadata only — values are never returned here." },
{ "name": "tier", "type": "string", "pii": false,
"meaning": "Current loyalty tier." },
{ "name": "membership_no", "type": "string", "pii": false, "custom": true,
"meaning": "Workspace-defined custom field." }
],
"relationships": [
{ "to": "order", "kind": "has_many", "meaning": "Orders placed by this member." },
{ "to": "coupon", "kind": "has_many", "meaning": "Coupons issued to this member." },
{ "to": "tag", "kind": "has_many", "meaning": "Tags currently applied to this member." }
]
}// list_actions { "object": "member" }
{
"actions": [
{ "action": "issue_coupon", "gear": "review",
"preconditions": ["member has consent", "coupon pool has stock"] },
{ "action": "earn_points", "gear": "auto",
"preconditions": ["amount within the workspace cap"] },
{ "action": "assign_tag", "gear": "auto",
"preconditions": ["tag exists and is active"] },
{ "action": "add_member_note", "gear": "auto",
"preconditions": ["note passes server-side sanitation"] },
{ "action": "send_campaign", "gear": "review", "locked": true,
"preconditions": ["sending can never be auto — a human always approves"] }
]
}The gear you see is the gear that will be enforced — the workspace configures it per action in the console, and a per-action circuit breaker can demote an action to review at any time (it only moves down automatically; only a human restores it). Re-check with list_actions rather than caching gears for long sessions.
Write tools under a review gear: pending / refused
When an MCP write tool targets an action whose gear is review, the call does not execute the side effect. It returns a structured result: either the proposal was queued for human approval (pending), or it was refused outright (refused) with a machine-readable reason_code and a suggested_action.
// proposed — waiting for a human
{
"status": "pending",
"proposal_id": "prop_9f2c…",
"action": "issue_coupon",
"expires_at": "2026-07-10T00:00:00Z", // 7 days
"message": "Queued for human approval in
AI Activity & Approvals."
}// refused — with a reason and a next step
{
"status": "refused",
"action": "issue_coupon",
"reason_code": "recently_rejected",
"suggested_action": "A human rejected this
proposal recently. Do not re-propose;
consider a different action or ask the
operator."
}| reason_code | Meaning | What your agent should do |
|---|---|---|
| gear_forbidden | The action's gear is set to forbidden for this workspace (or the action can never be automatic — e.g. sending — and was invoked in a way that requires auto). | Don't retry. Surface the refusal; follow suggested_action (typically an alternative action or 'ask a human to change the gear'). |
| recently_rejected | A human recently rejected the same proposal for the same target — the platform won't re-queue it. | Respect the human decision. Don't re-propose or re-word it; follow suggested_action. |
Every refusal carries a reason_code in this shape plus a suggested_action — treat the codes above as the ones to branch on and the field as extensible. A refusal is a hard boundary read for you, not an error to retry around: never re-word a proposal to slip past a refusal.
The approval lifecycle
A pending proposal resolves exactly one way: approved, rejected, or expired. Humans act on it in the AI Activity & Approvals console (single or batch by action type); a daily digest email surfaces what's waiting.
| State | How it happens | Then |
|---|---|---|
| pending | A write tool was called while the action's gear is review — the proposal is queued for human approval. | approved · rejected · expired |
| approved | A human approved it. Before executing, the platform re-validates the AI's stated reasoning; if it no longer holds (e.g. the customer already repurchased), the action is not executed. | terminal (result recorded in the decision log; individually revocable) |
| rejected | A human rejected it, optionally with a note. The same proposal is then refused with recently_rejected if re-submitted. | terminal |
| expired | No decision within 7 days — the proposal expires on its own and nothing executes. | terminal |
Idempotency guarantees
- One approval, one execution. Approving a proposal executes its action exactly once; a repeated approval replays the stored result with no second side effect.
- Re-submitting doesn't duplicate. Calling the write tool again for the same proposal while it is pending returns the same
pendingresult andproposal_id— it does not queue a second card. - Approval re-validates, execution is conditional.At approval time the platform re-checks whether the AI's stated reasoning still holds; if it no longer does (the customer already repurchased, for example), nothing executes — the outcome is recorded instead of the action.
- Everything is audited and individually revocable. Proposal, decision, authorization path (
autoor a named human), execution and result all land in the decision log, where a single entry can be undone.
The server is the boundary.Gears, re-validation, expiry and the per-action circuit breaker are enforced on Flash's side — never trusted to the agent's reasoning or its client-side confirmation. Sending-type actions can never run automatically, whatever the client asks for.