SDK

Documentation

JavaScript SDK

The SDK is published as @behalfid/sdk and uses fetch, so it works in Node 18+ without extra dependencies.

Install

terminal
npm install @behalfid/sdk

The package is published on npm as @behalfid/sdk.

Initialize

client.ts
import { BehalfID } from "@behalfid/sdk";

const behalf = new BehalfID({
  apiKey: process.env.BEHALFID_API_KEY!,
  baseUrl: "https://behalfid.com"
});

Add a connected agent

connected-agent.ts
const agent = await behalf.createAgent({
  name: "Ollie",
  agentType: "connected",
  provider: "ollie",
  externalAgentLabel: "Jasper's Ollie assistant",
  description: "Personal assistant used for planning"
});

Create a permission with structured scopes

permission.ts
await behalf.createPermission({
  agentId,
  action: "access_data",
  resource: "gmail.com",
  allowedActions: ["read labels", "summarize messages", "provide pricing metrics"],
  blockedActions: ["send email", "delete messages", "schedule events", "make purchases"],
  requiresApproval: true,
  template: "access_data"
});

Agent descriptions are informational. Permissions are the source of truth. Use allowedActions and blockedActions to make the permission explicit so external agents can read them from the passport page. Active blocked actions override allows, and non-empty allowed actions narrow the permission to those exact action strings.

Fail-closed enforcement

Gate every external action with verify. On denial, throw or return before calling the executor. This is fail closed: verify first, execute second, and stop when permission is missing, approval is required, constraints are missing, or a check fails.

enforce.ts
async function enforceAction(input) {
  const result = await behalf.verify({ agentId, ...input });
  if (!result.allowed) {
    throw new Error(`Action blocked by BehalfID: ${result.reason}`);
  }
  return result;
}

// Allowed — proceeds.
await enforceAction({ action: "browse_web", vendor: "web" });

// Denied — throws. The next line never runs.
await enforceAction({ action: "purchase", vendor: "coachella.com", amount: 742 });
console.log("Booking ticket..."); // ← never reached

Verify an action

verify.ts
const result = await behalf.verify({
  agentId,
  action: "access_data",
  vendor: "gmail.com",
  metadata: {
    scope: "read labels"
  }
});

In the current API, vendor can represent the resource or service being accessed. Pass amount only for transaction-like actions. Pass the exact action string you want to execute. If the permission has allowedActions, that action must appear in the list; a broad parent action does not bypass the narrowed scope.

Execute through the Action Gateway

executeAction routes execution through BehalfID-controlled infrastructure. The MVP supports only safe public web reads: browse_web on web. Denied actions return executed: false and no fetch happens.

gateway.ts
const result = await behalf.executeAction({
  agentId,
  action: "browse_web",
  resource: "web",
  input: {
    url: "https://example.com"
  }
});

if (result.executed) {
  console.log(result.result?.title, result.result?.excerpt);
}

Logs and key rotation

keys-and-logs.ts
const logs = await behalf.getLogs(agentId);
const rotated = await behalf.rotateKey(agentId);

Webhook signature helper

webhook.ts
import { verifyWebhookSignature } from "@behalfid/sdk";

const valid = await verifyWebhookSignature({
  secret: process.env.BEHALFID_WEBHOOK_SECRET!,
  payload: rawBody,
  timestamp: req.headers["behalfid-timestamp"],
  signature: req.headers["behalfid-signature"]
});