This walkthrough shows the intended developer workflow: write a policy, validate it, evaluate a transaction in advisory mode, handle each decision, and then move to enforced mode.
1. Write a policy
A policy is a JSON file, conventionally named intercept.policy.json, that declares the boundaries for one agent. Start in advisory mode so you can observe decisions without depending on Intercept to execute.
{
"version": "0.1",
"agent": "agent_7f3c",
"mode": "advisory",
"risk": {
"maxTransactionUsd": 25000,
"dailySpendLimitUsd": 100000,
"maxPositionPct": 0.15,
"maxLeverage": 3,
"maxDrawdownPct": 0.12,
"drawdownWindow": "7d",
"maxSlippageBps": 50,
"minPoolLiquidityUsd": 250000
},
"security": {
"allowedProtocols": ["jupiter", "drift", "kamino"],
"blockedAddresses": [],
"simulation": "required",
"requireApprovalAboveUsd": 10000,
"tokenApprovals": "deny_unlimited"
},
"circuitBreakers": [
{ "trigger": "drawdown_pct", "threshold": 0.08, "action": "reduce_only" },
{ "trigger": "consecutive_blocks", "threshold": 5, "action": "pause" }
],
"emergency": {
"paused": false,
"pauseOn": ["drawdown_breach", "critical_threat"],
"guardians": ["<guardian-pubkey>"]
}
}The protocols in allowedProtocols are illustrative allowlist entries, not integrations. Every field is explained in Risk Policies.
2. Initialize the client and validate the policy
Validation catches structural errors and inconsistent limits before the policy is used, for example a requireApprovalAboveUsd that can never be reached because it is above maxTransactionUsd, or a circuit breaker threshold above the hard drawdown limit.
import { Intercept } from "@intercept/sdk"; // illustrative — not yet published
import policy from "./intercept.policy.json";
const intercept = new Intercept({
apiKey: process.env.INTERCEPT_API_KEY,
policy: "./intercept.policy.json",
cluster: "mainnet-beta",
});
const validation = await intercept.policies.validate(policy);
if (!validation.valid) {
console.error(validation.errors);
process.exit(1);
}
await intercept.policies.publish(policy);Publishing creates a new policy version. Each decision records the policyVersion it was evaluated against.
3. Evaluate a transaction
Build the transaction as your agent already does, for example a swap built from a route quote, but do not sign and send it. Pass it to evaluate() with the agent identifier and a plain statement of intent.
const result = await intercept.evaluate(transaction, {
agentId: "agent_7f3c",
intent: "Swap 1,200 USDC to SOL on Jupiter",
});The intent string matters. The security engine compares it to the simulated effects of the transaction, so a transaction described as a swap that actually transfers funds to an unknown address is flagged.
4. Handle each decision
Every result has one of three decisions. Treat anything other than approved as a reason not to execute.
switch (result.decision) {
case "approved": {
const signature = await intercept.execute(result);
log.info({ evaluationId: result.evaluationId, signature });
break;
}
case "requires_approval": {
await intercept.requestApproval(result.evaluationId);
// Wait for an owner or operator; do not resubmit a new transaction.
break;
}
case "blocked": {
for (const reason of result.reasons) {
log.warn({ code: reason.code, layer: reason.layer, message: reason.message });
}
// Feed reasons back to the agent so it can adjust, for example reduce size.
break;
}
}A few practices are worth adopting early:
- Feed reasons back to the agent.
RISK_MAX_TX_SIZEorRISK_SLIPPAGEare actionable: the agent can split the order or tighten slippage.SEC_*andNET_*codes generally are not and should be escalated. - Do not retry blocked transactions unchanged. Repeated blocks count toward the
consecutive_blockscircuit breaker. - Treat
SYS_TIMEOUTas blocked. It means evaluation did not complete, not that the transaction is safe. - Store the
evaluationId. It links your logs to Intercept's audit record.
5. What enforced mode adds
In advisory mode, everything above depends on the agent choosing to call evaluate() and respecting the result. That is enough to test policies and catch honest mistakes. It is not enough against a compromised key, a prompt-injected agent, or a bug that skips the check, because the agent still holds a key that can move funds without Intercept.
Enforced mode, which is planned and not built, changes the custody model:
- Funds move into a policy-controlled account, such as a program-owned vault or a multisig-style smart account.
- Intercept's policy signer becomes a required co-signer for that account.
intercept.execute(result)adds the policy signature only for approved evaluations.- You set
"mode": "enforced"in the policy and publish a new version.
The agent's code barely changes. What changes is that the agent key alone can no longer move funds, so blocking becomes a real control. Blocking only works if the integration is enforced. See Execution Layer for the co-signing model and its custody trade-offs.
Next steps
- Risk Policies for every policy field.
- Transaction Lifecycle for timeouts, idempotency, and the audit trail.
- Agent Security for the threat model behind the security rules.