APPROVAL GATE

Human approvals

One human decision, bound to one exact payload, spendable once.

Human approvals park one exact action on a named person before it happens. A require_approval policy refuses the request with a 403, mints an approval bound to the SHA-256 of the canonicalised action plus the execution context it was proposed in, and holds the trace open until somebody decides. That approval authorises that payload and nothing else: change one argument and the hash no longer matches, so the retry is refused rather than allowed as near enough. It is single-use and it expires, at 60 minutes by default. Approving pushes nothing to the agent: the agent redeems it by repeating the identical request with its id.
Binding
SHA-256 of the canonical action plus its context
Lifetime
60 minutes by default; 1 minute to 7 days per policy
Refusal
403 with the approval id, a status URL and a resume contract
Redemption
Repeat the identical request with the approval id, once
Nothing tells the agentAn approval takes effect only when the agent retries
the problem

An approval not bound to a payload is a rubber stamp.

You added a human gate because somebody should look before an agent moves money. The failure mode is not that nobody approved — it is that the thing approved and the thing that ran were different things. An approval authorising a refund, rather than this refund of £240 on this order, is a standing licence for every refund that agent proposes afterwards.

The second failure is what makes a team switch the gate off. An agent waiting on a human has no callback, so it re-submits, and a gateway minting a fresh approval per re-submission buries the reviewer under duplicates of the one thing they are being asked to decide. The queue fills with rows that are individually valid and collectively unusable.

the mechanism

How it works

Step 7 of the request path. The order is encoded in the evaluator, and where each step sits is the reason the guarantee holds.

  1. 01

    A policy asks for a human, and the action is hashed

    The first require_approval match is held; a block anywhere in the same pass wins outright, so nobody is asked to approve what another rule already refuses. The action and its execution context are hashed into one canonical object, the trace is parked, and the request is refused with a 403 carrying the approval id, a status URL and a resume object.
  2. 02

    A human decides, with a reason

    Deciding needs the operator role or above and an evidence scope covering the trace’s team, and the console requires a written reason. The write is a compare-and-set on the pending state, so when two people decide at once only one is recorded as the decider.
  3. 03

    The agent retries the identical request

    The retry is hashed again and compared. Consumption is a guarded update, so a second retry is refused as already consumed and a pending re-poll takes no second reservation. Anything undecided expires, and expiring the row and releasing what it held are one transaction.
One request, four outcomes

For traffic routed through the gateway, configured policies can allow, redact, hold or block a request. Detection is heuristic and can miss sensitive data or misclassify a payload.

Allow

The request passes the configured checks and proceeds to its provider. Its trace records the policy decision.

Redact

A configured rule matches a value and masks or tokenises it before forwarding the payload to the provider. Unmatched values can still pass through.

Hold

A policy requires human approval. The approval binds to this payload, expires and can be used once; the caller receives a typed refusal with the approval identifier.

Block

The gateway denies the routed request and records the blocking policy and outcome in its trace.

Shadow mode records policy decisions without changing the request. Review false positives and missed detections before enforcing the policy on live traffic.

An approval is bound to one payload and one context.

Canonicalisation makes the comparison meaningful across two separate HTTP attempts: grants sorted, identities lower-cased, tags sorted. A retry differing only in map ordering hashes identically; one differing in substance does not. The context is in the hash because an approval granted to an agent holding one set of roles must not survive that agent being granted more.

The action
The sanitised model request, or the tool name and its arguments. Under an Effect Contract the contract id, version and digests are bound too, so an approval cannot survive the contract changing.
The subject
Id, kind, team, tags and effective role grants, normalised so ordering cannot change the hash. A permission added to a role after the human decided produces a mismatch.
The chain and the human
The ordered delegation identities with each hop’s grants, plus the on-behalf-of identity and the caller’s session id: everything altering routing, authority or attribution.
Excluded on purpose
Request ids, trace ids, credentials and generated session ids. A reconnect must not invalidate a reviewed action, and a fresh trace per attempt must not make an approval unredeemable.
The canonical object hashed for every approval
{
  version: 2,
  subject:    { id, kind: agent | seat, team, tags[], roles[] },
  delegation: [ { subjectId, roles[] } ],   // origin -> caller
  onBehalfOf, sessionId, requestTags[], taskTierHint,
  semanticHeaders: { … },                   // allowlisted
  action: { kind: tool_call, tool, args } | { kind: model_request }
}

canonicalJson(object)  ->  SHA-256  ->  payloadHash
the queue a policy fills, with nothing in it
The human approvals screen with an empty queue. The heading explains that each request is bound to one exact payload, is single-use and expires, and that decisions are written to the audit chain with a name and a reason. The list reads: nothing is waiting on a human.

Sample data in the real Token Observe interface, recorded from a local evaluation with a temporary database, a mock provider and one request. No live provider key was used.

Approved is a permission, not an execution.

Four states (pending, approved, denied, expired) plus one orthogonal fact: the timestamp recording that it has been spent. Denial is terminal. Expiry is terminal after approval as well as before it, because an approval granted at 09:00 with a 60-minute lifetime must not still authorise the action at 17:00. An expired approval is not a denial: the verdict returns to require-approval and a fresh request is raised.

A parked request keeps its conservatively priced estimate reserved against the agent’s hour, day and month windows, so the queue cannot be used to get past a ceiling. Denial and expiry release the hold in the transaction that ends the wait; an approved but unredeemed approval keeps it, because the money is still about to be spent.

the order is the guarantee
  1. beat 01
    Name the caller
    • 1Authenticate
    • 2Resolve the agent

    Nothing is decided for an anonymous caller. The key resolves to one registered agent with a named owner, and its roles, its ceilings and any stop switch in force are read live from the store on every request, not from a cached copy, so a suspension takes effect on the next call instead of after a redeploy.

  2. beat 02
    Start the record, then read what is being sent
    • 3Open the trace
    • 4Sanitise
    • 5Scan

    The trace is opened before anything can refuse the request, which is why a blocked call is recorded as thoroughly as one that went through. Only then is the text normalised and scanned, in that order, so the detectors see what the model will read rather than what a human reviewer can see.

  3. beat 03
    Decide once
    • 6Govern
    • 6bIntersect with the named human
    • 7Enact

    One function returns one verdict: allow, block, or hold it for a named person to approve. Every check lives in that one place rather than scattered along the path, which is what makes it possible to read your rules and know what they do.

  4. beat 04
    Send it, and only then spend the money
    • 8Route
    • 9Call upstream

    Routing fixes which provider and model will serve the call, so where an agent has a USD ceiling it is tested against the price you will pay, not the one the caller typed. Routing is also the last step that can still refuse locally: after it, the request has left your network.

  5. beat 05
    Govern the answer, then count the cost
    • 10Govern the response
    • 11Meter and record

    A tool call the model proposes is judged before it reaches your agent. Metering runs last and on every ending, including the refusals and the streams a client abandoned halfway, because the tokens were spent either way and a partial trace is still evidence.

Step names and their grouping are the product’s own, and the order is encoded in the evaluator rather than drawn for the page.

Each line closes off an assumption a reader would otherwise carry into a proof of concept.

What this does not do

What is an approval bound to?

The SHA-256 of one canonical object holding the action and its execution context: the subject with its team, tags and effective role grants, the ordered delegation chain, the on-behalf-of identity and the caller’s session id. Request ids, trace ids and credentials are excluded, so a reconnect does not invalidate a reviewed action while a widened permission does.

Does approving in the console make the agent carry on?

No. Token Observe has no channel that reaches an agent; event delivery goes to webhooks, Slack, Teams and email, all human channels. An approval is a permission the agent redeems by repeating the identical request with the approval id, so clicking approve moves nothing until it tries again. The console says so, and shows an approved-but-unspent approval in its own state.

Can the same approval be used twice?

No. Consumption is a guarded update stamping the row only if it is still approved, unspent and unexpired; the loser changes nothing, is told the approval was already consumed, and executes nothing.

Ask about this capability
Ask how this one works, where it sits in the request path, or what it will not do. Answers stay inside what this page claims.

Prefer to ask a person? Write to James

Your next step

Bring us the agent you are least comfortable with.

Create a cloud account and workspace. No card or server installation. Model calls and Google BigQuery scans can incur separate provider charges.