Skip to main content

What is the intent hash?

The intent hash is a keccak256 hash of canonical transaction parameters that ensures the Mandate server and the agent agree on exactly what transaction was validated. When the agent calls raw validation, both sides compute the same hash from the same inputs. If the hashes do not match, the transaction is blocked with intent_hash_mismatch. This mechanism is used only in raw validation flows. Action-based validation does not require an intent hash. The intent hash exists to prevent a specific attack: the envelope swap. Without it, an attacker could intercept the validation response and use the approval to broadcast a different transaction. The hash creates a cryptographic binding between what was validated and what gets signed.

How is the canonical string constructed?

The intent hash is computed from a pipe-delimited string of 10 transaction parameters in a fixed order. Every parameter must match exactly between the client (SDK) and the server (PolicyEngineService). The canonical string format is:
Each field has strict formatting rules: Example (5 USDC transfer on Base Sepolia):

Why does the intent hash exist?

The intent hash prevents the envelope swap attack. Without it, an attacker who intercepts the validation response could substitute different transaction parameters. The agent would sign a modified transaction (different recipient, different amount) and no check would catch the swap. With the intent hash, any modification produces a different hash. The server recomputes from the submitted parameters, compares, and returns intent_hash_mismatch on any divergence. The EnvelopeVerifierService provides a second defense layer: after broadcast, it fetches the on-chain transaction via RPC and compares from, to, nonce, calldata, and value against the stored intent. Mismatches trip the agent’s circuit breaker.

How does the SDK handle intent hash computation?

The @mandate.md/sdk package exports computeIntentHash() from intentHash.ts. The MandateWallet class calls it automatically during prepareTransaction(). You do not need to compute the hash manually unless you are building a custom integration without the SDK.
The server-side implementation in PolicyEngineService::computeIntentHash() uses the PHP kornrunner/keccak library. Both implementations produce identical output for the same input because they follow the same canonical string format and use keccak256 over UTF-8 encoded bytes.

What causes intent hash mismatches?

Six common issues cause the client hash to diverge from the server hash. All of them are parameter formatting problems, not bugs in the hash function.
The intent hash must be computed from the exact same values you submit to the API. If you estimate gas, then round it, use the rounded value in both the hash and the API payload.
Intent hash is only required for raw validation (POST /api/validate/raw). If you use action-based validation (POST /api/validate), you do not need to compute or submit an intent hash.

Next Steps

SDK Intent Hash

SDK reference for computeIntentHash() and MandateWallet integration.

Troubleshooting Mismatches

Step-by-step debugging guide for intent_hash_mismatch errors.

Intent Lifecycle

How intents move through states after successful validation.

Non-Custodial Model

Why the hash matters in a non-custodial architecture.