Control every AI action before it executes

Runplane is an enforcement layer, not an agent runtime.

Runplane intercepts every action before execution and returns a decision: ALLOW, BLOCK, or REQUIRE_APPROVAL.

Agent
guard()
Guard API
ALLOW/BLOCK/REQUIRE_APPROVAL
Execution

Gateway-First Architecture

All decisions are made server-side via Runplane's Guard API (https://runplane.ai/api/v1/guard). The SDK routes execution through the Guard API and enforces the result.

How Runplane Works

Every action flows through the Guard API before execution. The API evaluates policies and returns a decision. The callback only executes if the decision is ALLOW.

1

Action Sent

Agent calls guard() with action, target, context

2

Decision Made

Guard API evaluates policies and returns decision

3

Execution Controlled

Callback runs only if ALLOW, never if BLOCK

Decision Model

ALLOW

Callback executes immediately.

The action proceeds without delay. The callback function runs and returns its result.

BLOCK

Callback never runs.

The SDK throws ShieldError. The action is permanently blocked.

REQUIRE_APPROVAL

Execution pauses until human decision.

SDK polls for approval. If approved, callback executes. If denied, throws ShieldError.

System Flow

1. Import Tools

Bring your existing agent tools. These define what actions your agent can take.

LangChainVercel AI SDKOpenAPIManual

2. Action Mapping

Tools are normalized into canonical action types for consistent policy enforcement.

deleteUser()delete_record
sendEmail()send_email

3. Policy Configuration

Define rules based on action type, target system, and runtime context.

Action:delete_record
Target:users-database
Result:REQUIRE_APPROVAL

4. Guard API

Every action is sent to the Guard API before execution. This is the enforcement boundary.

await shield.guard(action, target, context, callback)

5. Decision

Guard API evaluates policies and returns one of three decisions.

ALLOW
BLOCK
REQUIRE_APPROVAL

6. Execution

ALLOWCallback executes immediately
BLOCKCallback never runs, throws ShieldError
REQUIRE_APPROVALPauses until human approves or denies

7. Audit

Every action and decision is recorded in the audit log.

Guard API

The Guard API (/api/v1/guard) is the core primitive. It receives action + context, evaluates policies, and returns a decision. The SDK's guard() method wraps this API.

TypeScript
await shield.guard(
  "delete_record",          // action type
  "users-database",       // target
  { recordId: "user_123" }, // context
  async () => {                // callback
    await deleteUser("user_123")
  }
)

Approval Flow

When the Guard API returns REQUIRE_APPROVAL, the SDK pauses execution and polls for a human decision. Approvals are managed in the Runplane dashboard.

How it works:

  1. 1SDK calls Guard API, receives REQUIRE_APPROVAL with approvalId
  2. 2SDK begins polling the approval endpoint
  3. 3Request appears in Runplane Dashboard → Approvals
  4. 4Human reviews action, context, and risk level
  5. 5aApprove → callback executes, guard() returns result
  6. 5bDeny → callback never runs, guard() throws ShieldError

The SDK does not provide approval UI.

Approvals must be managed through the Runplane dashboard. The SDK only polls for the decision.

What Happens on First Run

When you first integrate Runplane, you may see unexpected behavior. This section explains what to expect.

No output? Execution is waiting.

If your code appears to hang with no output, the action likely received REQUIRE_APPROVAL. The SDK is polling and waiting for a human decision.

To resolve: Go to Runplane Dashboard → Approvals and approve or deny the pending request.

ShieldError thrown? Action was blocked.

If ShieldError is thrown, the action received BLOCK. The callback never ran.

To resolve: Check your policy configuration. The action matched a BLOCK rule.

Callback executed immediately? Action was allowed.

If the callback ran without delay, the action received ALLOW. This is the expected behavior for low-risk actions.

Summary: If nothing happens on first run, check the Approvals tab in the dashboard. The SDK is waiting for human input.

SDK vs Direct API

SDK (Recommended)

  • Automatic decision enforcement
  • Approval polling handled internally
  • Throws ShieldError on BLOCK
  • Fail-closed by default

Direct API

  • You call /api/v1/guard directly
  • You must enforce the decision yourself
  • You must implement approval polling
  • You must handle failure modes

Failure Modes

The SDK provides two failure modes that determine behavior when Runplane is unavailable.

failMode: "closed"default

If Runplane is unavailable, all actions are blocked. This is the safe-by-default guarantee.

  • Invalid API key → BLOCK
  • Network failure → BLOCK
  • Timeout → BLOCK
  • Approval timeout → BLOCK
  • Denied approval → ShieldError (code: DENIED)
failMode: "open"

If Runplane is unavailable, actions proceed without control. Use only when explicitly desired.

Warning: This disables the safety guarantee. Actions will execute without policy evaluation.

const shield = new Shield({
  failMode: "closed" // blocks if unreachable (default)
})

Installation & Setup

Install
npm install @runplane/runplane-sdk
Setup
import { Shield } from "@runplane/runplane-sdk"

const shield = new Shield({
  baseUrl: "https://runplane.ai",
  apiKey: process.env.RUNPLANE_API_KEY,
  failMode: "closed"
})
Usage
await shield.guard(
  "delete_record",
  "users-database",
  { recordId: "user_123" },
  async () => {
    await deleteUser("user_123")
  }
)

SDK Integration Guides

Framework-specific guides for integrating Runplane with your existing agent infrastructure.

Start controlling AI execution

Full SDK reference available after account creation.