Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt

Use this file to discover all available pages before exploring further.

This page is the detailed API reference design for the public OpenClaw App SDK. It is intentionally separate from the Plugin SDK.
@openclaw/sdk is the external app/client package for talking to the Gateway. openclaw/plugin-sdk/* is the in-process plugin authoring contract. Do not import Plugin SDK subpaths from apps that only need to run agents.
The public app SDK should be built in two layers:
  1. A low-level generated Gateway client.
  2. A high-level ergonomic wrapper with OpenClaw, Agent, Session, Run, Task, Artifact, Approval, and Environment objects.

Namespace design

The low-level namespaces should closely follow Gateway resources:
oc.agents.list();
oc.agents.get("main");
oc.agents.create(...);
oc.agents.update(...);

oc.sessions.list();
oc.sessions.create(...);
oc.sessions.resolve(...);
oc.sessions.send(...);
oc.sessions.messages(...);
oc.sessions.fork(...);
oc.sessions.compact(...);
oc.sessions.abort(...);

oc.runs.create(...);
oc.runs.get(runId);
oc.runs.events(runId, { after });
oc.runs.wait(runId);
oc.runs.cancel(runId);

oc.tasks.list(); // future API: current SDK throws unsupported
oc.tasks.get(taskId); // future API: current SDK throws unsupported
oc.tasks.cancel(taskId); // future API: current SDK throws unsupported
oc.tasks.events(taskId, { after }); // future API

oc.models.list();
oc.models.status(); // Gateway models.authStatus

oc.tools.list();
oc.tools.invoke(...); // future API: current SDK throws unsupported

oc.artifacts.list({ runId }); // future API: current SDK throws unsupported
oc.artifacts.get(artifactId); // future API: current SDK throws unsupported
oc.artifacts.download(artifactId); // future API: current SDK throws unsupported

oc.approvals.list();
oc.approvals.respond(approvalId, ...);

oc.environments.list(); // future API: current SDK throws unsupported
oc.environments.create(...); // future API: current SDK throws unsupported
oc.environments.status(environmentId); // future API: current SDK throws unsupported
oc.environments.delete(environmentId); // future API: current SDK throws unsupported
High-level wrappers should return objects that make common flows pleasant:
const run = await agent.run(inputOrParams);
await run.cancel();
await run.wait();

for await (const event of run.events()) {
  // normalized event stream
}

const artifacts = await run.artifacts.list();
const session = await run.session();

Event contract

The public SDK should expose versioned, replayable, normalized events.
type OpenClawEvent = {
  version: 1;
  id: string;
  ts: number;
  type: OpenClawEventType;
  runId?: string;
  sessionId?: string;
  sessionKey?: string;
  taskId?: string;
  agentId?: string;
  data: unknown;
  raw?: unknown;
};
id is a replay cursor. Consumers should be able to reconnect with events({ after: id }) and receive missed events when retention allows. Recommended normalized event families:
EventMeaning
run.createdRun accepted.
run.queuedRun is waiting for a session lane, runtime, or environment.
run.startedRuntime started execution.
run.completedRun finished successfully.
run.failedRun ended with an error.
run.cancelledRun was cancelled.
run.timed_outRun exceeded its timeout.
assistant.deltaAssistant text delta.
assistant.messageComplete assistant message or replacement.
thinking.deltaReasoning or plan delta, when policy allows exposure.
tool.call.startedTool call began.
tool.call.deltaTool call streamed progress or partial output.
tool.call.completedTool call returned successfully.
tool.call.failedTool call failed.
approval.requestedA run or tool needs approval.
approval.resolvedApproval was granted, denied, expired, or cancelled.
question.requestedRuntime asks the user or host app for input.
question.answeredHost app supplied an answer.
artifact.createdNew artifact available.
artifact.updatedExisting artifact changed.
session.createdSession created.
session.updatedSession metadata changed.
session.compactedSession compaction happened.
task.updatedBackground task state changed.
git.branchRuntime observed or changed branch state.
git.diffRuntime produced or changed a diff.
git.prRuntime opened, updated, or linked a pull request.
Runtime-native payloads should be available through raw, but apps should not have to parse raw for normal UI.

Result contract

Run.wait() should return a stable result envelope:
type RunResult = {
  runId: string;
  status: "accepted" | "completed" | "failed" | "cancelled" | "timed_out";
  sessionId?: string;
  sessionKey?: string;
  taskId?: string;
  startedAt?: string | number;
  endedAt?: string | number;
  output?: {
    text?: string;
    messages?: SDKMessage[];
  };
  usage?: {
    inputTokens?: number;
    outputTokens?: number;
    totalTokens?: number;
    costUsd?: number;
  };
  artifacts?: ArtifactSummary[];
  error?: SDKError;
};
The result should be boring and stable. Timestamp values preserve the Gateway shape, so current lifecycle-backed runs usually report epoch millisecond numbers while adapters may still surface ISO strings. Rich UI, tool traces, and runtime-native details belong in events and artifacts. accepted is a non-terminal wait result: it means the Gateway wait deadline expired before the run produced a lifecycle end/error. It must not be treated as timed_out; timed_out is reserved for a run that exceeded its own runtime timeout.

Approvals and questions

Approvals must be first-class because coding agents constantly cross safety boundaries.
run.onApproval(async (request) => {
  if (request.kind === "tool" && request.toolName === "exec") {
    return request.approveOnce({ reason: "CI command allowed by policy" });
  }

  return request.askUser();
});
Approval events should carry:
  • approval id
  • run id and session id
  • request kind
  • requested action summary
  • tool name or environment action
  • risk level
  • available decisions
  • expiration
  • whether the decision can be reused
Questions are separate from approvals. A question asks the user or host app for information. An approval asks for permission to perform an action.

ToolSpace model

Apps need to understand the tool surface without importing plugin internals.
const tools = await run.toolSpace();

for (const tool of tools.list()) {
  console.log(tool.name, tool.source, tool.requiresApproval);
}
The SDK should expose:
  • normalized tool metadata
  • source: OpenClaw, MCP, plugin, channel, runtime, or app
  • schema summary
  • approval policy
  • runtime compatibility
  • whether a tool is hidden, readonly, write capable, or host capable
Tool invocation through the SDK should be explicit and scoped. Most apps should run agents, not call arbitrary tools directly.

Artifact model

Artifacts should cover more than files.
type ArtifactSummary = {
  id: string;
  runId?: string;
  sessionId?: string;
  type:
    | "file"
    | "patch"
    | "diff"
    | "log"
    | "media"
    | "screenshot"
    | "trajectory"
    | "pull_request"
    | "workspace";
  title?: string;
  mimeType?: string;
  sizeBytes?: number;
  createdAt: string;
  expiresAt?: string;
};
Common examples:
  • file edits and generated files
  • patch bundles
  • VCS diffs
  • screenshots and media outputs
  • logs and trace bundles
  • pull request links
  • runtime trajectories
  • managed environment workspace snapshots
Artifact access should support redaction, retention, and download URLs without assuming every artifact is a normal local file.

Security model

The app SDK must be explicit about authority. Recommended token scopes:
ScopeAllows
agent.readList and inspect agents.
agent.runStart runs.
session.readRead session metadata and messages.
session.writeCreate, send to, fork, compact, and abort sessions.
task.readRead background task state.
task.writeCancel or modify task notification policy.
approval.respondApprove or deny requests.
tools.invokeInvoke exposed tools directly.
artifacts.readList and download artifacts.
environment.writeCreate or destroy managed environments.
adminAdministrative operations.
Defaults:
  • no secret forwarding by default
  • no unrestricted environment variable pass-through
  • secret references instead of secret values
  • explicit sandbox and network policy
  • explicit remote environment retention
  • approvals for host execution unless policy proves otherwise
  • raw runtime events redacted before they leave Gateway unless the caller has a stronger diagnostic scope

Managed environment provider

Managed agents should be implemented as environment providers.
type EnvironmentProvider = {
  id: string;
  capabilities: {
    checkout?: boolean;
    sandbox?: boolean;
    networkPolicy?: boolean;
    secrets?: boolean;
    artifacts?: boolean;
    logs?: boolean;
    pullRequests?: boolean;
    longRunning?: boolean;
  };
};
The first implementation does not need to be a hosted SaaS. It can target existing node hosts, ephemeral workspaces, CI-style runners, or Testbox-style environments. The important contract is:
  1. prepare workspace
  2. bind safe environment and secrets
  3. start run
  4. stream events
  5. collect artifacts
  6. clean up or retain by policy
Once this is stable, a hosted cloud service can implement the same provider contract.

Package structure

Recommended packages:
PackagePurpose
@openclaw/sdkPublic high-level SDK and generated low-level Gateway client.
@openclaw/sdk-reactOptional React hooks for dashboards and app builders.
@openclaw/sdk-testingTest helpers and fake Gateway server for app integrations.
The repo already has openclaw/plugin-sdk/* for plugins. Keep that namespace separate to avoid confusing plugin authors with app developers.

Generated client strategy

The low-level client should be generated from versioned Gateway protocol schemas, then wrapped by handwritten ergonomic classes. Layering:
  1. Gateway schema source of truth.
  2. Generated low-level TypeScript client.
  3. Runtime validators for external inputs and event payloads.
  4. High-level OpenClaw, Agent, Session, Run, Task, and Artifact wrappers.
  5. Cookbook examples and integration tests.
Benefits:
  • protocol drift is visible
  • tests can compare generated methods with Gateway exports
  • App SDK stays independent from Plugin SDK internals
  • low-level consumers still have full protocol access
  • high-level consumers get the small product API