Documentation
Action Gateway
Verify checks whether an action is allowed. The Action Gateway enforces that decision by executing only supported allowed actions through BehalfID.
When to use the gateway
Use verify() when your app already owns the executor and only needs a allow/deny decision. Use the Action Gateway when you want BehalfID to both decide and perform a narrow, safe action — today that means a public web GET — so denied or unsupported requests never reach your fetch logic.
Passports define permissions. Verify checks those permissions. The gateway adds an execution boundary: denied or unsupported actions return executed: false, and BehalfID does not fetch after denial.
MVP scope
The current gateway supports one executor: public web reads. It accepts browse_web on the web resource, fetches a public URL with GET, and returns status, content type, optional title, and a limited text excerpt.
The gateway does not submit forms, log in, purchase items, send email, write calendars, run browser automation, forward arbitrary headers, or use credentials or cookies.
- Supported action:
browse_web - Supported resource:
web(passed asvendorduring verify) - Required input field:
input.url - Auth: agent API key (
Authorization: Bearer bhf_sk_…)
Request shape
{
"agentId": "agent_xxx",
"action": "browse_web",
"resource": "web",
"input": {
"url": "https://example.com"
}
}curl -X POST "$BASE_URL/api/actions/execute" \
-H "Authorization: Bearer $BEHALFID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"agentId":"agent_xxx","action":"browse_web","resource":"web","input":{"url":"https://example.com"}}'import { BehalfID } from "@behalfid/sdk";
const behalf = new BehalfID({ apiKey: process.env.BEHALFID_API_KEY! });
const result = await behalf.executeAction({
agentId: process.env.BEHALFID_AGENT_ID!,
action: "browse_web",
resource: "web",
input: { url: "https://example.com" }
});
if (result.executed) {
console.log(result.result?.title, result.result?.excerpt);
}Response shapes
Allowed and executed responses include the fetch result. Denied decisions never fetch. Permission must allow browse_web on web; otherwise the gateway returns executed: false.
{
"requestId": "req_xxx",
"allowed": true,
"decision": "allowed",
"reason": "Action allowed by active permission.",
"executed": true,
"result": {
"url": "https://example.com/",
"status": 200,
"contentType": "text/html",
"title": "Example Domain",
"excerpt": "Example Domain…",
"truncated": false
}
}{
"requestId": "req_xxx",
"allowed": false,
"decision": "denied",
"reason": "No matching permission found.",
"executed": false
}{
"requestId": "req_xxx",
"allowed": true,
"decision": "allowed",
"reason": "Action allowed by active permission.",
"executed": false,
"error": "Gateway redirect limit exceeded."
}Fail-closed rules
Missing / invalid JSON fields400 — request rejected before verify.
Unsupported action or resourceVerified as denied with reason that MVP only supports browse_web on web.
decision.allowed === falseexecuted: false — no network fetch.
Verification throws / unavailable503 with executed: false.
SSRF / private URL / bad redirect400 with allowed: true, executed: false, and error message.
decision.allowed === true + successful GETexecuted: true with result excerpt.
Security controls
- Only
GETis used. - No cookies, credentials, or user-supplied request headers are sent.
- Only
http://andhttps://URLs are accepted. - Localhost, private IP ranges, link-local addresses, metadata IPs, and internal hostnames are blocked (SSRF protection).
- Redirects are followed manually (max 3); each target is re-validated.
- Requests time out at 5 seconds; response bodies are capped at 64 KB before excerpts are returned.
Webhooks and audit
Each gateway call emits verification.allowed or verification.denied like a normal verify. See Webhooks for delivery details and SDK Quickstart for the fail-closed executor pattern when you keep execution in your own process.
Runnable demo
The examples/enforcement-demo package creates permissions, runs an allowed web read through the Action Gateway, proves denied executors do not run, and checks that request IDs appear in audit logs.
cd examples/enforcement-demo npm install cp .env.example .env npm run setup npm run demo
Later connectors
Future connectors can add more actions, but each needs its own narrow executor, permission check, input validation, and fail-closed behavior. Unsupported actions today are denied rather than forwarded.