Documentation
Quickstart
Create an agent, add one permission, verify before execution, and prove both allowed and denied actions in about five minutes.
The five-minute model
BehalfID sits between the AI agent and the tool it wants to run. Your code asks BehalfID first. If the decision is not allowed, the executor does not run.
- Create an agent. Use
/dashboard/onboardingorbehalf agents create. Store the one-timebhf_sk_...API key asBEHALFID_API_KEY. - Create a permission. Start with one clear rule, such as
browse_webonweb,read_calendarongoogle-calendar, orpurchaseonamazon.comwithmaxAmount: 25. - Install the SDK. Add the published Node SDK to the app that owns the tool execution.
- Call verify before the action. The SDK requires
agentId,action, and the API key. Passvendororresourcewhen a permission is scoped to a service. - Show an allowed request. Call
verifywith an action and resource covered by an active permission. - Show a denied request. Try a blocked action, a missing permission, a missing constrained vendor/resource, or an amount over the limit.
- Fail closed. Throw or return before the executor. Never run the tool when
decision.allowedis false.
npm install @behalfid/sdk
Copy-paste executor pattern
import { BehalfID } from "@behalfid/sdk";
const behalf = new BehalfID({
apiKey: process.env.BEHALFID_API_KEY!,
});
const agentId = process.env.BEHALFID_AGENT_ID!;
async function purchase(vendor: string, amount: number) {
const decision = await behalf.verify({
agentId,
action: "purchase",
vendor,
amount,
});
if (!decision.allowed) {
throw new Error(`Blocked by BehalfID: ${decision.reason}`);
}
return runPurchase({ vendor, amount });
}Allowed request
This succeeds when the agent has an active browse_web permission for web and no matching blocked action.
const decision = await behalf.verify({
agentId: process.env.BEHALFID_AGENT_ID!,
action: "browse_web",
resource: "web",
});
if (decision.allowed) {
await runBrowserRead("https://example.com");
}{
"requestId": "req_xxx",
"allowed": true,
"reason": "Action allowed by active permission.",
"risk": "low"
}Denied request
This fails closed when the purchase permission is missing, the vendor does not match, blockedActions includes purchase, approval is required, or the amount exceeds the permission limit.
const decision = await behalf.verify({
agentId: process.env.BEHALFID_AGENT_ID!,
action: "purchase",
vendor: "shop.example",
amount: 742,
});
if (!decision.allowed) {
throw new Error(`Blocked by BehalfID: ${decision.reason}`);
}
await runCheckout(); // not reached when denied{
"requestId": "req_xxx",
"allowed": false,
"reason": "Amount exceeds maxAmount constraint.",
"risk": "high"
}Create permission with the SDK
await behalf.createPermission({
agentId: process.env.BEHALFID_AGENT_ID!,
action: "purchase",
resource: "shop.example",
allowedActions: ["purchase"],
blockedActions: ["checkout without approval"],
requiresApproval: false,
constraints: {
maxAmount: 25,
allowedVendors: ["shop.example"],
},
});Manual mode vs enforcement
Passport links and manual preview forms help existing assistants understand the rules, but they do not control a provider directly. Automatic enforcement happens when your app, MCP server, or Action Gateway calls BehalfID before the action and refuses to run denied tools.
For a runnable end-to-end version of this loop, use examples/enforcement-demo. It creates demo permissions, runs allowed and denied actions, and checks the resulting audit log entries.