Skip to main content
An action is one proposed operation, normalized: a name such as stripe.refund, its arguments in canonical form, the resource it touches, the principal proposing it, and the environment it runs in. The action hash is the SHA-256 of that canonical form, and it is the value every approval is bound to.

What the canonical form is

Two calls that mean the same thing must hash the same, and two that differ in anything a human would care about must hash differently. So the form is fixed:
  • keys sorted, recursively;
  • no insignificant whitespace, separators=(",", ":");
  • UTF-8, ensure_ascii=False;
  • argument values are str, int, bool, None, or nested containers of those;
  • float is rejected at any depth. 0.1 and 0.10 are the same money and different bytes, so amounts are integer minor units: 200000 is €2,000.00.
The action_id, a fresh identifier per proposal, is excluded from the hash on purpose. Two proposals of the same action from the same principal hash the same, which is what lets an approval granted against the first pass of a LangGraph node match the second.
runnable

The guarantee it supports

Approval binding. A human approves a hash, and only the action with that hash can consume the approval. Change the amount, the payment, the principal or the environment and the hash moves.

What it does not do

The hash does not identify a consequence. Two different actions can cause the same effect, and one action can be proposed twice; the effect key, not the hash, is what stops a duplicate. The hash also says nothing about whether the arguments are sensible; the policy does.

Next