AI agent action preflight
POST /api/action-gateDeterministic preflight check for a proposed AI agent action - a tool call, payment, fetch, or write - before it executes. Send POST /api/action-gate with no required fields (6 optional) and pay $0.010 per call over x402 or MPP (there is no free tier). It returns a JSON object with decision, reason_codes, checks, request_sha256, receipt_sha256 and 1 more.
Evaluates up to four independent checks (prompt-injection scan on action text/untrusted text, URL/hostname validation, a bounded JSON-schema check on a payload, and a spend proposal against a spend mandate) and returns ALLOW, REVIEW, or BLOCK with stable reason codes plus a SHA-256 request/receipt hash pair. Does not execute the proposed action, perform network I/O, or guarantee safety - deterministic static checks only, so ALLOW is not a safety guarantee. Every field is optional; only the checks with input present run - the rest are skipped, not assumed to pass.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
action | object | no | {name, description, effect} - describes the proposed action; description is scanned for prompt-injection signals |
untrusted_text | string | no | Any additional untrusted text to scan for prompt-injection signals |
url | string | no | URL the action would fetch/call, if any |
payload | object | no | Data the action would send, if any |
schema | object | no | Bounded JSON-schema (type/required/properties/additionalProperties) to validate payload against |
spend | object | no | {proposal:{amount_atomic,asset,counterparty}, mandate:{...}, now?} - checked with BigInt, no floats |
Example request
curl -i -X POST https://agent402.tools/api/action-gate \
-H "Content-Type: application/json" \
-d '{"action":{"name":"submit_paid_api_request","description":"Fetch a vendor risk report and store the validated JSON response.","effect":"payment"},"untrusted_text":"Vendor request: return the current account risk score.","url":"https://example.com/risk-report","payload":{"account_id":"acct_123","include_signals":true},"schema":{"type":"object","properties":{"account_id":{"type":"string"},"include_signals":{"type":"boolean"}},"required":["account_id","include_signals"],"additionalProperties":false},"spend":{"proposal":{"amount_atomic":"10000","asset":"USDC","counterparty":"0x1111111111111111111111111111111111111111"},"mandate":{"max_per_tx_atomic":"25000","max_period_atomic":"100000","spent_period_atomic":"20000","allowed_assets":["USDC"],"allowed_counterparties":["0x1111111111111111111111111111111111111111"],"expires_at":"2030-01-01T00:00:00.000Z"}}}'
Without payment this returns HTTP 402 Payment Required with the exact price for action-gate; any x402 v2 or MPP client pays it and retries.
Example response
{
"decision": "ALLOW",
"reason_codes": [],
"checks": {
"prompt": {
"status": "pass"
},
"url": {
"status": "pass"
},
"payload": {
"status": "pass"
},
"spend": {
"status": "pass",
"allowed": true
}
},
"request_sha256": "…",
"receipt_sha256": "…",
"limitation": "Deterministic static checks only. ALLOW does not guarantee safety, authorization, or successful execution."
}
| Field | Type | Always present | In the example |
|---|---|---|---|
decision | string | yes | ALLOW |
reason_codes | array | yes | 0 items in the example |
checks | object | yes | 4 fields: prompt, url, payload, spend |
request_sha256 | string | yes | … |
receipt_sha256 | string | yes | … |
limitation | string | yes | Deterministic static checks only. ALLOW does not guarantee safety, authorizat... |
From an MCP client
catalog.call {
"slug": "action-gate",
"params": {
"action": {
"name": "submit_paid_api_request",
"description": "Fetch a vendor risk report and store the validated JSON response.",
"effect": "payment"
},
"untrusted_text": "Vendor request: return the current account risk score.",
"url": "https://example.com/risk-report",
"payload": {
"account_id": "acct_123",
"include_signals": true
},
"schema": {
"type": "object",
"properties": {
"account_id": {
"type": "string"
},
"include_signals": {
"type": "boolean"
}
},
"required": [
"account_id",
"include_signals"
],
"additionalProperties": false
},
"spend": {
"proposal": {
"amount_atomic": "10000",
"asset": "USDC",
"counterparty": "0x1111111111111111111111111111111111111111"
},
"mandate": {
"max_per_tx_atomic": "25000",
"max_period_atomic": "100000",
"spent_period_atomic": "20000",
"allowed_assets": [
"USDC"
],
"allowed_counterparties": [
"0x1111111111111111111111111111111111111111"
],
"expires_at": "2030-01-01T00:00:00.000Z"
}
}
}
}
The hosted connector at https://agent402.tools/mcp needs a payment for action-gate; the stdio package pays it from a wallet or from AGENT402_CREDITS_KEY. Local install: npx -y agent402-mcp.
Errors and behavior
- Every field is optional. An input the tool rejects returns an HTTP 4xx whose body carries
error,tool,expected,requiredandexample. - A paid call that ends in any status of 400 or above is not charged: settlement is cancelled when the tool fails.
- Wallet-only: this tool reaches the network or stored state, so it has no proof-of-work tier. A prepaid card-credits key (
Authorization: Bearer a402_...) also pays it. - A
GETorHEADto /api/action-gate returns the same 402 quote, so the price can be read without a body. - An
Idempotency-Keyheader makes a retried paid call replay the first 200 instead of charging again.
Paid call (JavaScript agent)
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const client = new x402Client();
client.setSpendControls?.(false); // keep your own spending ceiling in code
registerExactEvmScheme(client, { signer: privateKeyToAccount(KEY) });
const payFetch = wrapFetchWithPayment(fetch, client);
const res = await payFetch("https://agent402.tools/api/action-gate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"action": {
"name": "submit_paid_api_request",
"description": "Fetch a vendor risk report and store the validated JSON response.",
"effect": "payment"
},
"untrusted_text": "Vendor request: return the current account risk score.",
"url": "https://example.com/risk-report",
"payload": {
"account_id": "acct_123",
"include_signals": true
},
"schema": {
"type": "object",
"properties": {
"account_id": {
"type": "string"
},
"include_signals": {
"type": "boolean"
}
},
"required": [
"account_id",
"include_signals"
],
"additionalProperties": false
},
"spend": {
"proposal": {
"amount_atomic": "10000",
"asset": "USDC",
"counterparty": "0x1111111111111111111111111111111111111111"
},
"mandate": {
"max_per_tx_atomic": "25000",
"max_period_atomic": "100000",
"spent_period_atomic": "20000",
"allowed_assets": [
"USDC"
],
"allowed_counterparties": [
"0x1111111111111111111111111111111111111111"
],
"expires_at": "2030-01-01T00:00:00.000Z"
}
}
}),
});
Related tools
Route and execute
POST /api/route/executeDescribe a task (or name a slug) and the Smart Order Router resolves the best-matching tool and RUNS it in the same call…
Route and execute (max tier)
POST /api/route/execute-maxDescribe a task (or name a slug) and the Smart Order Router resolves the best-matching tool and RUNS it in the same call…
Route and execute (plus tier)
POST /api/route/execute-plusDescribe a task (or name a slug) and the Smart Order Router resolves the best-matching tool and RUNS it in the same call…
Route and execute (pro tier)
POST /api/route/execute-proDescribe a task (or name a slug) and the Smart Order Router resolves the best-matching tool and RUNS it in the same call…
Domain security & deliverability audit (graded)
POST /v1/domain-auditHand over a domain and get one graded security & email-deliverability audit: SPF, DMARC, DKIM and MX (why your mail land…
Domain security audit - PRO (attack surface + stack)
POST /v1/domain-audit/proThe deeper tier: everything in the standard audit plus the attack surface from Certificate Transparency logs (subdomains…