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.
Transparency primitives & the glass-box model inventory
The same glass-box discipline — every automated decision recorded and explainable, personal data kept out of model context — extends beyond governed actions: to experiment traffic optimization, to outbound LLM prompts, and to the classic-ML model inventory that powers ranking, prediction and detection across the product.
A/B traffic auto-optimization: the decision log
Recurring campaigns (automations and journeys) can opt in to Auto-optimize traffic over time, which reallocates delivery weights across experiment variants based on observed click performance. Every reallocation is auditable:
- Append-only decision log. Each hourly recompute writes one record with the weights applied and a human-readable rationale. Records are never rewritten — the log is the full history of why traffic looks the way it does.
- The configured split is never mutated.Your experiment's configured split stays untouched as the baseline; optimized weights are computed and logged alongside it.
- Exploration floor: 10%.No variant's weight ever drops below 10%, so every variant keeps receiving traffic and a reversal remains detectable.
- Even split until ≥100 delivered per variant.Weights stay at the configured split until every variant has at least ~100 deliveries — no reallocation on thin data, and no statistical “winner” is ever declared by this mechanism.
LLM PII masking at the provider seam
Before any prompt leaves Flash for an external LLM provider, member personal identifiers are masked — applied at the provider seam, so every AI feature passes through it:
- Deterministic pseudonyms.Email addresses, phone numbers and card-like digit runs are replaced with stable stand-in tokens. The same identifier always maps to the same pseudonym, so the model's reasoning across a prompt stays coherent.
- Default on, no configuration. Masking applies to every outbound prompt from every AI feature — there is nothing to enable, and it cannot be switched off from the product.
- Mask-only, never unmasked. The mapping is one-way at the output boundary: model output is never rewritten to reinstate real identifiers, so raw PII cannot round-trip through an LLM response.
Why this matters today.Flash's AI prompts are built from aggregates and campaign data rather than personal records — the masking seam is the enforced guarantee that holds as AI features grow, not a patch over a leak.
The glass-box model inventory
Every predictive or statistical capability in the product is a classic, deterministic model — no black-box scoring, no sampling, so the same data always produces the same model. Each one carries an explicit honesty gate: below the gate it emits nothing rather than a guess, and every score ships with the numbers needed to recompute it. The current inventory:
| Model | Powers | Honesty gates |
|---|---|---|
| Two-model uplift (two deterministic logistic fits) | SoClaw win-back worklist ranking — value-at-risk × uplift, trained on the program's own always-on holdout RCT | Activates only past ≥200 matured treated rows, ≥50 holdout rows and ≥20 conversions, with both fits converged — below that, ranking falls back to pure value-at-risk. Negative-uplift (do-not-disturb) members sink; each task stores its score snapshot. |
| Per-member inter-purchase Gamma (method-of-moments) | Expected next purchase date on the profile + the “Days Until Expected Next Purchase” segment condition (negative = overdue) | Needs ≥3 observed gaps (i.e. ≥4 purchases) and a coefficient-of-variation gate (≤1.5) on rhythm regularity — an irregular buyer gets NULL, never a made-up date. A past expected date is emitted as honest overdue signal. |
| Per-team L2-regularized logistic regression | Click propensity (0–100), refit nightly on the team's own last 90 days of sends | Clicks-only label (opens never — Apple MPP inflates them); leakage-guarded prior-click feature; deterministic full-batch gradient descent on a fixed schedule. Fit gates ≥500 sends / ≥30 clicks / ≥50 members; a member needs ≥3 recent sends to be scored. |
| Cosine-normalized item-kNN | “Also bought” product pairing across the recommendation surfaces | co_count / √(buyers(A) × buyers(B)) corrects popularity bias; pairs need ≥2 shared buyers to materialize; the raw co-purchase count is stored alongside the score as inspectable evidence. |
| Association rules (support / confidence / lift) | Cross-sell suggestions, mined nightly from the merchant's own order lines | A rule needs ≥3 co-occurring orders and ≥5% confidence; every rule stores the raw order counts it derives from, so any figure can be recomputed by hand. The member-profile surface excludes already-owned products and shows only for active buyers. |
| Robust-statistics outlier (median / MAD) | The behavior_outlier points-fraud detector | Robust z ≥ 6 plus absolute floors (≥10 earn transactions; teams under 50 active members skipped); month-bucketed dedup per member; freeze-only — it opens a review and never voids on its own. |
| Kaplan-Meier + Cox proportional hazards (Nelder-Mead partial likelihood) | Retention by monthly signup cohort + the “what retention correlates with” panel | Printed definition on the view: churned = 90 days without a purchase, never-purchasers excluded, cohorts under 30 members hidden and disclosed. Cox fit needs ≥30 observed churn events and is capped at 3 covariates; correlates are labelled correlation, not causation. |
| Holt-Winters (weekly seasonality, fixed constants) | The next-14-days revenue outlook on the dashboard | Fixed smoothing constants — no per-team tuning surface; reads the same certified revenue metric as every other screen; dashed line + error band with its dollar basis printed; hidden entirely under 4 weeks of history; labelled a projection, not a commitment. |
None of these models ever acts on its own: they rank, score and flag, and everything that moves money or contacts a member still passes the governed action gears above.
For the full engineering detail on every model — method, inputs, output columns, honesty gates and consumption surfaces — see the Predictive Models Reference.