An approval is a person's signed decision that one specific, recorded tool call may run once. When a
policy rule answers require_approval, the Gateway records
the call as a protected action, creates an approval request and executes nothing
until an eligible approver signs it in the dashboard.
This page explains the model: what is bound, who may decide, how long a request lives, and what an approval does and does not authorize. For the step-by-step dashboard workflow, see Review and decide.
Agent calls a tool
the policy answers require_approval
Approval request
pending, with a deadline
Approver signs
in the browser, with a personal key
Agent retries
the identical call
Gateway executes once
and issues a receipt
Why a call needs approval
A call needs approval when the policy evaluated for the calling agent decides
require_approval for it — for example a rule that holds refunds above a certain amount. Only an
applied require_approval creates an approval request. A call that is allowed runs without one, and a
denied call is refused without one.
Before the request is created, the Gateway resolves the approvers. If nobody can approve, the call is not held but denied:
| Situation | Agent receives |
|---|---|
| No eligible approver exists | denied / NO_APPROVERS |
| More than 32 members would be approvers | denied / TOO_MANY_APPROVERS |
The approval request
When a call is held, the Gateway records three things together, all or nothing: the protected action
(status pending_approval), the signed description of the call, and an approval request (status
pending) that lists the approvers and a deadline.
The agent gets a refusal with status approval_pending and code APPROVAL_PENDING. Its
structuredContent.kblock carries protected_action_id, approval_request_id and expires_at.
Nothing has run. The agent continues by repeating the identical call later, or by waiting on the same
call with _meta["kblock/wait-seconds"] (see The Gateway).
The request appears under Approvals in the dashboard. If you subscribed a webhook, an
approval.requested event is sent (see Webhook events).
Exact-call binding
An approval is not a permission for a tool in general. It covers one exact call. When the call is decided, the Gateway signs a TASK and a PROPOSAL object with its service identity (see Identities and KIDs). The approver's signed APPROVAL names that PROPOSAL as its parent, so it covers everything the PROPOSAL and its TASK contain:
| Bound to the approval | Signed into the evidence |
|---|---|
| The call's arguments, verbatim, and their digest | yes |
| The upstream tool name, the action, and the executing server's KID | yes |
The amount and currency (0 XXX when the tool has no amount mapping) | yes |
| The upstream endpoint: MCP server ID, name and URL | yes |
| The delegation used and the organization principal it acts for | yes |
| The approver KIDs (1–32) | yes |
| Organization, protected action ID, agent, API key, environment, exposed tool name | yes |
| Policy ID, version, digest, deciding rule and effect | yes |
The deadline (expires_at) | yes |
| The tool's binding: server, URL, server KID, credential version, upstream name, action, amount and currency mapping, input-schema digest | no — stored by K-Block and enforced by the Gateway |
The consequence is concrete: if the agent calls the same tool with different arguments, that is a new protected action with its own decision. The old approval still covers only its own exact call.
Who can approve
The approvers are fixed when the request is created. The Gateway lists the active personal identities of members who meet all of these conditions:
- their role is owner, admin or approver;
- the deciding rule admits them: a rule lists approver roles, named users, or both, and a member qualifies through either (a named user still needs one of the three roles above);
- they are enabled members and their identity is bound to their account, not revoked and not marked as equivocated.
At most the 5 newest identities of each member are listed, and at most 32 KIDs in total.
When someone signs, K-Block checks the conditions again: the signer must still be an active member whose role the rule admits, the signing KID must still be bound to that user, and it must be one of the KIDs listed on the request. A member who joins later, or binds a new identity later, cannot decide a request that was created before.
Any one listed approver decides the request. There is no quorum: the first valid decision wins and later attempts are refused because the request is already decided. Deciding requires a signed-in dashboard session; management API keys can never approve or reject.
Signing in the browser
Approvals and rejections are signed in your browser with your personal K-Block identity: a key pair generated in the browser and bound to your account under Account. The private key stays in the browser; K-Block Cloud receives only the signed decision. See Identities and KIDs for how to create one and back it up.
On the approval page, the dashboard first verifies, in your browser, the signed call (TASK and PROPOSAL) against the Gateway identity the dashboard trusts. Deciding stays disabled until that check succeeds; if it fails, the page says The signed evidence could not be verified — do not decide this request.
If the dashboard has no trusted Gateway identity to check against, the page shows the warning No gateway identity is pinned. Your browser then checks only that a valid service identity signed the call; it cannot tell whether that identity is K-Block's Gateway. Before you sign, compare the KID under Gateway (signer) in Identities and digests with the Gateway KID that K-Block gave you. If you do not have that KID, contact K-Block support for it before you sign.
Once the check succeeds, the Decision panel shows:
- which key will sign — "Signing with your key …", or a Sign with list if more than one of your listed keys is in this browser;
- an optional Note or reason (optional) field, signed into the approval (note) or the rejection (reason), at most 1,000 characters in the dashboard;
- the buttons Approve and sign and Reject and sign.
Approve and sign is disabled when approving could not lead to an execution, with the reason shown under the buttons. This happens when the tool's binding changed after the call was decided (the Gateway would refuse to execute it), and, until you tick the confirmation, when the call's text contains hidden characters or right-to-left text. Reject and sign stays available.
If none of your keys in this browser is listed on the request, the panel says you cannot sign from this browser. Import a key backup that holds a listed identity, or ask another approver.
Approve or reject
| Decision | Request becomes | Action becomes | Receipt |
|---|---|---|---|
| Approve and sign | approved | approved | Not yet — issued after the execution |
| Reject and sign | rejected | rejected | Yes, issued at once |
After an approval, nothing runs until the agent repeats the identical call, or until a call that is already waiting wakes up. The Gateway then executes the approved terms once. The dashboard confirms this with: "Approved. When the agent retries the same call, the gateway executes exactly these terms once." For a decision-only tool, K-Block instead authorizes the approved terms once when the caller asks again, and the caller executes them; the dashboard then says so.
After a rejection, a call that is waiting for the decision, or a repeat with the same idempotency key,
is answered rejected / REJECTED, with the receipt IDs in _meta.kblock. A repeat without a key
is a new call: it gets a new decision, and a new approval request if the policy still requires one.
Expiry
Every approval request has a deadline, expires_at, set when the call is decided:
- the deciding rule's
approval_ttl_seconds, or else the organization's Default approval lifetime (seconds) under Settings (default 86,400 seconds, that is 24 hours; allowed 60 seconds to 30 days); - never later than the moment the delegation the call acts under expires.
The same deadline also limits the execution. An approved call must be executed before expires_at;
the approval page shows it as Run before.
| What happens | Result |
|---|---|
| Nobody decides before the deadline | Request expired; action expired with code APPROVAL_EXPIRED |
| Approved, but the agent does not retry before the deadline | Action expired with code TASK_EXPIRED; the approval is never consumed |
K-Block marks a request or an approved action expired within about 15 seconds after its deadline,
and at once when the agent repeats the call. A request whose deadline has passed can no longer be
decided, even during those few seconds. No receipt is issued for an expiry. A request that expires
undecided sends an approval.expired webhook event; an approved call that expires unexecuted sends
no webhook event. A keyless retry of the same call after expiry gets a new decision.
Cancellation
An owner or admin can cancel a protected action that is still pending_approval or approved. A
cancellation is a CANCELLATION object signed by the organization principal key, so it can only be
made in a browser that holds that key (see
Identities and KIDs).
In the dashboard, open the action and use the Cancel this action card: enter an optional Reason (optional), select Cancel action… and confirm with Sign cancellation.
A cancelled action can never be approved or executed. The action becomes cancelled, a pending
request becomes cancelled (the request of an action that was already approved keeps the status
approved), and a receipt is issued. The receipt's receipt.created event is the only webhook
event a cancellation sends. A waiting call, or a repeat with the same idempotency key, is refused
cancelled / CANCELLED with the receipt IDs. A repeat without a key is a new call with a new
decision; cancelling does not block the agent from asking again.
You cannot cancel an action that is already executing, or one that has finished.
What approving authorizes
Approving authorizes exactly one execution of exactly the signed call:
- the same arguments, tool, action, amount and currency, executing server and upstream URL;
- under the same delegation, for the same organization principal;
- once, before
expires_at.
It does not authorize:
- a call with different arguments, even one character different — that is a new protected action;
- a second execution of the same call — after the call ran, a new identical call needs a new decision;
- an execution after the deadline;
- an execution after the tool's binding changed, after the organization principal was replaced, or under a revoked or expired delegation;
- anything about the real-world effect of the call. The approval says a person agreed to the call, not that the upstream behaved correctly.
One approval, one execution
When the agent repeats the call (or a waiting call wakes), the Gateway consumes the approval in a
single all-or-nothing step: it re-checks the caller's key and agent, the tool and its server, the
delegation and the organization principal, then moves the action from approved to executing and
records the execution. K-Block allows only one execution per protected action and per approval
request. If the same approved call arrives several times at once, over one connection or several,
exactly one execution happens; the others are answered replay /
EXECUTION_IN_PROGRESS or replay / REPLAY. Without an idempotency key, a repeat that arrives
after the execution started is a new call with a new decision.
If a check fails before the consumption, the Gateway refuses the call with a detail that says nothing
was executed and the approval was not consumed, and the action normally stays approved. Once
consumed, the approval stays consumed whatever the upstream does. If the upstream call then fails,
the action ends failed with a receipt, and a new attempt needs a new decision. See
Execution and results.
When the tool or policy changes
Time can pass between the decision and the execution. K-Block re-checks the parts that matter:
| Change after the decision | Effect on a pending request | Effect on an approved action |
|---|---|---|
| The tool is disabled or its server deactivated | The agent's retry gets invalid_request / UNKNOWN_TOOL; the request can still be decided | The retry gets UNKNOWN_TOOL, a call already waiting gets denied / UPSTREAM_CHANGED; nothing is consumed, and the call can still run if the tool is enabled again before the deadline |
| A bound field of the tool or server changes (URL, server KID, credential, upstream name, action, amount or currency mapping, input schema) | Approve and sign is disabled in the dashboard. The next identical call supersedes the request at once if the server, URL, server KID, upstream name or action differs, otherwise once the tool is re-authorized; until then, new decisions for the tool are refused TOOL_REAUTHORIZATION_REQUIRED | Refused on every retry: UPSTREAM_CHANGED if the server, URL, server KID, upstream name or action differs, otherwise TOOL_REAUTHORIZATION_REQUIRED. Once an owner or admin re-authorizes the tool, the next identical call supersedes it (a call already waiting gets UPSTREAM_CHANGED). It can never run |
| The delegation is revoked or expires | The next identical call supersedes the request | Refused at execution with denied / VERIFICATION_FAILED, nothing consumed; the action stays approved until it expires or is cancelled |
| The organization principal is replaced | Superseded at once | Superseded at once |
| A new policy version is activated | The request stands; the new policy applies to new calls | The approval stands; it is not re-evaluated |
| An emergency stop or rollback that closes open actions | Closed; approving it is refused | Closed only when the emergency stop chose Close pending approvals and approved calls that have not run (the default for deny all); an execution attempt is refused POLICY_CHANGED |
A superseded action ends expired with code SUPERSEDED. A keyless retry of the identical call then
gets a new decision under the current configuration and policy. See
Emergency stop and Activate, roll out and roll back.
What can go wrong
| Symptom | Cause | What to do |
|---|---|---|
| "You cannot sign this decision from this browser" | None of your keys in this browser is listed, or the rule does not admit your role | Import a backup with a listed key, or ask a listed approver |
| "Your role cannot decide approvals" | Your role is developer or viewer | Ask an owner, admin or approver |
| The request is already decided | Another approver decided first | Open the request to see the decision |
| "This request expired … and can no longer be decided" | The deadline passed | The agent repeats the call to get a new decision |
The agent gets TASK_EXPIRED after you approved | The agent did not retry before the deadline | The agent repeats the call; a new approval is needed |
The agent gets TOOL_REAUTHORIZATION_REQUIRED or UPSTREAM_CHANGED | The tool's binding changed after the decision | Re-authorize the tool, then let the agent make the call again without the old idempotency key (a repeat with the old key is answered expired / SUPERSEDED); it gets a new decision |
More cases: Policies and approvals troubleshooting.