---
summary: "Public OpenClaw App SDK for external apps, scripts, dashboards, CI jobs, and IDE extensions"
title: "OpenClaw App SDK"
sidebarTitle: "App SDK"
read_when:
  - You are building an external app, script, dashboard, CI job, or IDE extension that talks to OpenClaw
  - You are choosing between the App SDK and the Plugin SDK
  - You are integrating with Gateway agent runs, sessions, events, approvals, models, or tools
---

The **OpenClaw App SDK** is the public client API for apps outside the
OpenClaw process. Use `@openclaw/sdk` when a script, dashboard, CI job, IDE
extension, or other external app wants to connect to the Gateway, start agent
runs, stream events, wait for results, cancel work, or inspect Gateway
resources.

<Note>
  The App SDK is different from the [Plugin SDK](/plugins/sdk-overview).
  `@openclaw/sdk` talks to the Gateway from outside OpenClaw.
  `openclaw/plugin-sdk/*` is only for plugins that run inside OpenClaw and
  register providers, channels, tools, hooks, or trusted runtimes.
</Note>

## What Ships Today

`@openclaw/sdk` ships with:

| Surface                   | Status  | What it does                                                                 |
| ------------------------- | ------- | ---------------------------------------------------------------------------- |
| `OpenClaw`                | Ready   | Main client entry point. Owns transport, connection, requests, and events.   |
| `GatewayClientTransport`  | Ready   | WebSocket transport backed by the Gateway client.                            |
| `oc.agents`               | Ready   | Lists, creates, updates, deletes, and gets agent handles.                    |
| `Agent.run()`             | Ready   | Starts a Gateway `agent` run and returns a `Run`.                            |
| `oc.runs`                 | Ready   | Creates, gets, waits for, cancels, and streams runs.                         |
| `Run.events()`            | Ready   | Streams normalized per-run events with replay for fast runs.                 |
| `Run.wait()`              | Ready   | Calls `agent.wait` and returns a stable `RunResult`.                         |
| `Run.cancel()`            | Ready   | Calls `sessions.abort` by run id, with session key when available.           |
| `oc.sessions`             | Ready   | Creates, resolves, sends to, patches, compacts, and gets session handles.    |
| `Session.send()`          | Ready   | Calls `sessions.send` and returns a `Run`.                                   |
| `oc.models`               | Ready   | Calls `models.list` and the current `models.authStatus` status RPC.          |
| `oc.tools`                | Partial | Lists tool catalog and effective tools; direct tool invocation is not wired. |
| `oc.approvals`            | Ready   | Lists and resolves exec approvals through Gateway approval RPCs.             |
| `oc.rawEvents()`          | Ready   | Exposes raw Gateway events for advanced consumers.                           |
| `normalizeGatewayEvent()` | Ready   | Converts raw Gateway events into the stable SDK event shape.                 |

The SDK also exports the core types used by those surfaces:
`AgentRunParams`, `RunResult`, `RunStatus`, `OpenClawEvent`,
`OpenClawEventType`, `GatewayEvent`, `OpenClawTransport`,
`GatewayRequestOptions`, `SessionCreateParams`, `SessionSendParams`,
`RuntimeSelection`, `EnvironmentSelection`, `WorkspaceSelection`,
`ApprovalMode`, and related result types.

## Connect To A Gateway

Create a client with an explicit Gateway URL, or inject a custom transport for
tests and embedded app runtimes.

```typescript
import { OpenClaw } from "@openclaw/sdk";

const oc = new OpenClaw({
  url: "ws://127.0.0.1:14565",
  token: process.env.OPENCLAW_GATEWAY_TOKEN,
  requestTimeoutMs: 30_000,
});

await oc.connect();
```

`new OpenClaw({ gateway: "ws://..." })` is equivalent to `url`. The
`gateway: "auto"` option is accepted by the constructor, but automatic Gateway
discovery is not a separate SDK feature yet; pass `url` when the app does not
already know how to discover the Gateway.

For tests, pass an object that implements `OpenClawTransport`:

```typescript
const oc = new OpenClaw({
  transport: {
    async request(method, params) {
      return { method, params };
    },
    async *events() {},
  },
});
```

## Run An Agent

Use `oc.agents.get(id)` when the app wants an agent handle, then call
`agent.run()`.

```typescript
const agent = await oc.agents.get("main");

const run = await agent.run({
  input: "Review this pull request and suggest the smallest safe fix.",
  model: "openai/gpt-5.5",
  sessionKey: "main",
  timeoutMs: 30_000,
});

for await (const event of run.events()) {
  const data = event.data as { delta?: unknown };
  if (event.type === "assistant.delta" && typeof data.delta === "string") {
    process.stdout.write(data.delta);
  }
}

const result = await run.wait({ timeoutMs: 120_000 });
console.log(result.status);
```

Provider-qualified model refs such as `openai/gpt-5.5` are split into Gateway
`provider` and `model` overrides. `timeoutMs` stays milliseconds in the SDK and
is converted to Gateway timeout seconds for the `agent` RPC.

`run.wait()` uses the Gateway `agent.wait` RPC. A wait deadline that expires
while the run is still active returns `status: "accepted"` instead of pretending
the run itself timed out. Runtime timeouts, aborted runs, and cancelled runs are
normalized into `timed_out` or `cancelled`.

## Create And Reuse Sessions

Use sessions when the app wants durable transcript state.

```typescript
const session = await oc.sessions.create({
  agentId: "main",
  label: "release-review",
});

const run = await session.send("Prepare release notes from the current diff.");
await run.wait();
```

`Session.send()` calls `sessions.send` and returns a `Run`. Session handles also
support:

```typescript
await session.abort(run.id);
await session.patch({ label: "renamed-session" });
await session.compact({ maxLines: 200 });
```

## Stream Events

The SDK normalizes raw Gateway events into a stable `OpenClawEvent` envelope:

```typescript
type OpenClawEvent = {
  version: 1;
  id: string;
  ts: number;
  type: OpenClawEventType;
  runId?: string;
  sessionId?: string;
  sessionKey?: string;
  taskId?: string;
  agentId?: string;
  data: unknown;
  raw?: GatewayEvent;
};
```

Common event types include:

| Event type            | Source Gateway event                        |
| --------------------- | ------------------------------------------- |
| `run.started`         | `agent` lifecycle start                     |
| `run.completed`       | `agent` lifecycle end                       |
| `run.failed`          | `agent` lifecycle error                     |
| `run.cancelled`       | Aborted/cancelled lifecycle end             |
| `run.timed_out`       | Timeout lifecycle end                       |
| `assistant.delta`     | Assistant streaming delta                   |
| `assistant.message`   | Assistant message                           |
| `thinking.delta`      | Thinking or plan stream                     |
| `tool.call.started`   | Tool/item/command start                     |
| `tool.call.delta`     | Tool/item/command update                    |
| `tool.call.completed` | Tool/item/command completion                |
| `tool.call.failed`    | Tool/item/command failure or blocked status |
| `approval.requested`  | Exec or plugin approval request             |
| `approval.resolved`   | Exec or plugin approval resolution          |
| `session.created`     | `sessions.changed` create                   |
| `session.updated`     | `sessions.changed` update                   |
| `session.compacted`   | `sessions.changed` compaction               |
| `task.updated`        | Task update events                          |
| `artifact.updated`    | Patch stream events                         |
| `raw`                 | Any event without a stable SDK mapping yet  |

`Run.events()` filters events to one run id and replays already-seen events for
fast runs. That means the documented flow is safe:

```typescript
const run = await agent.run("Summarize the latest session.");

for await (const event of run.events()) {
  if (event.type === "run.completed") {
    break;
  }
}
```

For app-wide streams, use `oc.events()`. For raw Gateway frames, use
`oc.rawEvents()`.

## Models, Tools, And Approvals

Model helpers map to current Gateway methods:

```typescript
await oc.models.list();
await oc.models.status({ probe: false }); // calls models.authStatus
```

Tool helpers expose the Gateway catalog and effective tool view:

```typescript
await oc.tools.list();
await oc.tools.effective({ sessionKey: "main" });
```

Approval helpers use the exec approval RPCs:

```typescript
const approvals = await oc.approvals.list();
await oc.approvals.respond("approval-id", { decision: "approve" });
```

## Explicitly Unsupported Today

The SDK includes names for the product model we want, but it does not silently
pretend Gateway RPCs exist. These calls currently throw explicit unsupported
errors:

```typescript
await oc.tasks.list();
await oc.tasks.get("task-id");
await oc.tasks.cancel("task-id");

await oc.tools.invoke("tool-name", {});

await oc.artifacts.list();
await oc.artifacts.get("artifact-id");
await oc.artifacts.download("artifact-id");

await oc.environments.list();
await oc.environments.create({});
await oc.environments.status("environment-id");
await oc.environments.delete("environment-id");
```

Per-run `workspace`, `runtime`, `environment`, and `approvals` fields are typed
as future shape, but the current Gateway does not support those overrides on
the `agent` RPC. If callers pass them, the SDK throws before submitting the run
so work does not accidentally execute with default workspace, runtime,
environment, or approval behavior.

## App SDK Versus Plugin SDK

Use the App SDK when code lives outside OpenClaw:

- Node scripts that start or observe agent runs
- CI jobs that call a Gateway
- dashboards and admin panels
- IDE extensions
- external bridges that do not need to become channel plugins
- integration tests with fake or real Gateway transports

Use the Plugin SDK when code runs inside OpenClaw:

- provider plugins
- channel plugins
- tool or lifecycle hooks
- agent harness plugins
- trusted runtime helpers

App SDK code should import from `@openclaw/sdk`. Plugin code should import from
documented `openclaw/plugin-sdk/*` subpaths. Do not mix the two contracts.

## Related Docs

- [OpenClaw App SDK API design](/reference/openclaw-sdk-api-design)
- [Gateway RPC reference](/reference/rpc)
- [Agent loop](/concepts/agent-loop)
- [Agent runtimes](/concepts/agent-runtimes)
- [Sessions](/concepts/session)
- [Background tasks](/automation/tasks)
- [ACP agents](/tools/acp-agents)
- [Plugin SDK overview](/plugins/sdk-overview)
