Docs

Actions

What a protected action is, which fields and statuses it has, and where K-Block's exactly-once guarantee starts and ends.

A protected action (an action for short) is the record of one tool call an agent made through the Gateway. When an agent calls an exposed tool, the Gateway decides the call (allow, require approval or deny), records the decision as an action, and follows that action until it reaches a final state: denied, rejected, expired, cancelled, succeeded or failed.

Actions are what you review on the Actions page, what approvers decide on, and what receipts are issued for. Every action belongs to one agent and one tool, and carries the exact arguments of the call.

What an action records

FieldWhat it is
AgentThe agent whose gateway API key made the call, and the key's environment (live or test).
ToolThe exposed tool that was called, with its MCP server. See Tools and MCP servers.
ActionThe K-Block action the tool maps to, for example refund.create.
ArgumentsThe exact arguments of the call, and their digest. Members who can read actions see them.
AmountThe amount and currency read from the arguments through the tool's amount mapping, if it has one.
Decisionallow, require_approval or deny.
RuleThe policy, policy version (with its digest) and the rule that decided, or the policy's default. Also how the decision was enforced (enforce or observe).
StatusWhere the action is in its lifecycle (below).
Error code and detailWhy it was denied, expired or failed, when that applies.
Idempotency keyThe key the agent sent, if any.
ExpiryUntil when an approval can be given and an approved call can still run.

The signed records behind an action (the task, proposal, approval or rejection, cancellation, execution and result) are listed on the action page under K-Block chain. Which of them a receipt contains is explained in Receipts and evidence bundles.

How the Gateway decides a call

For a new call the Gateway checks the gateway API key and its agent, the tool and its binding, the arguments and the amount, the agent's policy, whether a delegation grants the action and, for require_approval, whether there are eligible approvers. The outcome is recorded all at once or not at all: the action and its decision, together with its signed records and approval request when the call has them (a denied call has neither).

  • Recorded denials. A policy denial (POLICY_DENIED) and the checks around it (NO_POLICY, NO_DELEGATION, NO_APPROVERS, INVALID_AMOUNT and others) are recorded as actions with status denied, and they count toward the organization's monthly limit.
  • Refusals that record nothing. An unknown tool name (UNKNOWN_TOOL), a tool whose binding needs re-authorization (TOOL_REAUTHORIZATION_REQUIRED), invalid arguments (INVALID_ARGUMENTS), a reused idempotency key (IDEMPOTENCY_KEY_REUSED), a reached plan limit (PLAN_LIMIT_EXCEEDED) and authentication failures are answered without creating an action.

The Gateway does not check the arguments against the tool's input schema. INVALID_ARGUMENTS means arguments K-Block cannot record or sign: not a JSON object, too large, nested more than 28 levels deep, containing a non-integer number or U+0000, or holding two member names that differ only by letter case.

The codes the agent can receive are listed in Error codes.

Statuses

An action has exactly one of ten statuses. Seven of them are final: nothing leaves them.

StatusMeaningFinal
deniedThe call was denied. Nothing ran.Yes
pending_approvalWaiting for an approver.No
approvedAllowed by the policy, or approved by a person, and not executed yet. An allowed call normally runs at once, in the same request. A call a person approved runs when the agent repeats it, or at once if the agent is still waiting. For a decision-only tool, it is authorized for the caller instead (authorized).No
rejectedAn approver rejected it. Nothing ran.Yes
expiredIt can no longer run: the approval request expired (APPROVAL_EXPIRED), the approved call was not executed in time (TASK_EXPIRED), or it was superseded (SUPERSEDED). Nothing ran.Yes
cancelledThe organization cancelled it before it ran.Yes
executingThe approval has been consumed and the upstream call is in progress.No
succeededThe upstream ran the call and answered with a result.Yes
failedThe execution failed after the approval was consumed (UPSTREAM_FAILED), or it was interrupted (INTERRUPTED). The upstream may still have acted; see Outcome unknown.Yes
authorizedDecision only: K-Block authorized the exact call and handed it to the caller, which is responsible for executing it. K-Block executed nothing and cannot confirm whether the caller did.Yes

approved covers two cases: an allow decision that has not executed yet, and a person's approval. The action's decision (allow or require_approval) tells them apart.

The full transition table, with the error codes and the webhook events of each transition, is in Action statuses.

Lifecycle

  1. Decided

    denied, approved or pending_approval

  2. Approved

    by policy or by a person

  3. Executing

    approval consumed

  4. Finished

    succeeded or failed, with a receipt

The lifecycle of a protected action (concept)
  1. Decision. A deny ends the action as denied. An allow makes it approved. A require_approval makes it pending_approval and creates an approval request.
  2. Approval. An eligible approver approves (approved) or rejects (rejected) it in the dashboard. If nobody decides in time it becomes expired with APPROVAL_EXPIRED. See Approvals.
  3. Execution. The Gateway executes an allowed action right away, in the call that was decided. It executes an action a person approved when the agent repeats the same call, or while the agent is still waiting for the decision. Before it consumes the approval it re-checks the gateway API key, the agent, the tool and its binding, the delegation and, for a call the policy allowed, the policy now in force. An approved action that is not executed before its expiry becomes expired with TASK_EXPIRED.
  4. Result. The Gateway records the upstream's answer as succeeded or failed and issues a receipt. See Execution and results.

At any point before execution, owners and admins can cancel a pending_approval or approved action with Cancel action… on the action page. The cancellation is signed in the browser by the organization principal, so the principal's key must be in that browser. An executing action cannot be cancelled.

Superseded actions

An open action (pending_approval or approved) is closed as expired with SUPERSEDED when it can no longer run as it was decided. Most of these checks happen when the agent repeats the call; replacing the organization principal, a rollback or an emergency stop can close actions at once.

  • Any open action: the tool was re-authorized after a change of its binding, or the organization principal was replaced.
  • A pending action, or one the policy allowed: also when the signed proposal no longer matches the tool, or the delegation it relied on was revoked (or its revocation is pending).
  • An action the policy allowed: also when the agent's active policy version, the tool's exposed name or its amount mapping changed, or the observe window that relaxed it ended.

An action a person approved is not closed when its delegation is revoked: it stays approved and every retry is refused (VERIFICATION_FAILED) until it expires or is cancelled. After a superseded action, the identical call gets a new decision.

Receipts

Receipts are issued for succeeded, failed, rejected and cancelled actions (in rare cases an interrupted execution that K-Block closes as failed has no receipt; the action page shows the failure either way). Denied and expired actions have no receipt: a receipt ends with a signed result, rejection or cancellation, and these actions end without one. See What a receipt proves.

Repeating a call: idempotency and resume

The Gateway identifies a call by its agent, its tool and its exact arguments. What happens when an agent sends the same call again depends on whether it sends an idempotency key in _meta (kblock/idempotency-key, 1–200 characters):

SituationResult
Same idempotency key, same callThe action of that key. A finished action returns its stored outcome (a succeeded call returns the same stored result; nothing runs again); an open one continues as below.
Same idempotency key, another tool or other argumentsRefused with IDEMPOTENCY_KEY_REUSED.
A new idempotency keyA new action with its own decision, even while an identical call is open.
No key, the same call is still open (pending_approval or approved)The call resumes that action: it reports the pending approval, or executes the approved action.
No key, the earlier identical call already finishedA new action with a new decision.

Changing any argument always makes a new action. An approval covers only the exact call it was given for. Which of the agent's gateway keys sends the repeated call, and that key's environment, play no part in matching it: any key of the same agent resumes the open action.

A repeated call is resolved by the tool's exposed name first: if the tool was disabled or renamed in the meantime, even a call with a known idempotency key is refused with UNKNOWN_TOOL instead of returning the stored outcome.

An agent can also wait for a decision in the same call by sending kblock/wait-seconds in _meta. K-Block limits how long one call can wait: 60 seconds by default; a longer request is lowered to the limit (see Limits and supported versions). If the wait ends before anyone decides, the agent receives approval_pending and can repeat the call later. See the TypeScript client and Python for how the SDKs send these.

The exactly-once boundary

K-Block guarantees that each protected action, and each approval, is consumed at most once. K-Block records at most one execution per action and per approval request. If the same approved call arrives several times at once, it executes once; the other attempts get replay (EXECUTION_IN_PROGRESS or REPLAY).

What this does not cover:

  • The upstream call itself is at most once, not exactly once. The Gateway never retries an upstream call. If the call was sent and no valid answer came back, the action is recorded as failed and the approval stays consumed.
  • Identical calls without an idempotency key are merged only while the first one is open. Once it has finished, the same call is a new action.
  • The guarantee covers the calls made through K-Block. It says nothing about what the upstream does with duplicate requests from other sources.

Outcome unknown

Sometimes K-Block cannot know whether the upstream acted. There is no separate "outcome unknown" status: these cases end as failed, with a detail that says so.

What happenedWhat K-Block recordsWhat you should do
The request reached the upstream but no valid answer came back (timeout, cut connection, malformed answer)failed (UPSTREAM_FAILED); the detail says the upstream may have acted. Receipt issued.Check the effect in the upstream system before repeating the call.
The execution was interrupted after the approval was consumed and before the result was recordedThe action shows executing until K-Block closes it, then becomes failed (INTERRUPTED, detail interrupted; upstream outcome unknown), usually with a receipt.Check the upstream before repeating the call.
K-Block could not confirm that the approval was consumedThe upstream is not called and the agent receives DATABASE_UNAVAILABLE (not retryable). If the consumption did take effect, K-Block later records the action as failed (INTERRUPTED) with the detail that the upstream was not called; otherwise the action is still approved.Check the action before you repeat the call.
The upstream answered but its result could not be stored as it isfailed (UPSTREAM_FAILED) with RESULT_NOT_STORABLE in the detail, without the upstream's result. Receipt issued.Check the upstream: it answered, possibly with success.
The upstream answered but K-Block could not record the outcome (a K-Block service was temporarily unavailable)The agent receives RESULT_NOT_RECORDED; the action shows executing until K-Block closes it as failed (INTERRUPTED).Check the upstream: it answered, possibly with success.

K-Block closes interrupted executions automatically, normally 11 to 15 minutes after they started. Until then the action shows executing; do not repeat the call before you have checked its effect upstream. If an action stays executing much longer, contact K-Block support and give them the action's ID and the time the call was made.

Next steps