SDK

DocumentationSDK

Simple mode on — some technical details are condensed. Switch to Dev in the nav for full API reference.

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);

rotateKey immediately invalidates the previous agent API key. Store the new key from the response — it is returned once. Permissions and agent ID are unchanged.

Edge cases to handle

  • Approval required. allowed is false and reason indicates approval is needed. Surface requestId to a human; do not auto-retry in a tight loop. See Deploy approvals.
  • Blocked actions override allows. If any active permission lists the action under blockedActions, verify denies even when another permission would allow it.
  • Allowed-actions narrowing. A non-empty allowedActions list requires the exact action string you pass to verify. A broad parent action does not satisfy a narrowed list.
  • Missing constrained inputs. When a permission has vendor, resource, or amount constraints, omit those fields and the decision fails closed rather than bypassing the constraint.
  • Network / 5xx from BehalfID. Treat unavailable checks as deny in production executors. Do not fail open.

Webhook signature helper

Receivers must verify HMAC signatures before trusting payload contents. Full delivery semantics are documented under Webhooks.

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"]
});

Related docs