# SDK Reference

Complete reference for both Maritime SDKs. New here? Start with the [Build on Maritime](/content/docs/build/index.html) guide. Methods are shown with their TypeScript names; Python uses the same names in snake_case (`setEnv` is `set_env`) with keyword arguments.

| Language         | Package          | Install                             |
|------------------|------------------|-------------------------------------|
| TypeScript / JS  | `maritime-sdk`   | `npm install maritime-sdk`          |
| Python           | `maritime`       | `pip install maritime`              |

Both are zero-dependency. Source: [github.com/maritime-sh/maritime-sdk](https://github.com/maritime-sh/maritime-sdk).

## Client

The API key defaults to `MARITIME_API_KEY`; the base URL to `MARITIME_API_URL` or `api.maritime.sh`.

### TypeScript

```typescript
import { Maritime } from 'maritime-sdk'

const maritime = new Maritime({
  apiKey: 'mk_...',                     // or MARITIME_API_KEY
  baseUrl: 'https://api.maritime.sh',   // or MARITIME_API_URL
  timeout: 60_000,                      // per-request ms
  maxRetries: 2,                        // network + 5xx/429
})
```

### Python

```python
from maritime import Maritime

client = Maritime(
    api_key="mk_...",                   # or MARITIME_API_KEY
    base_url="https://api.maritime.sh", # or MARITIME_API_URL
    timeout=60.0,                       # per-request seconds
    max_retries=2,                      # network + 5xx/429
)
```

A failed request is retried up to `maxRetries` times (default 2), waiting 0.5 s, then 1 s, doubling up to 8 s, or the server's `Retry-After` up to 20 s. `GET` and `DELETE`, the lifecycle calls (start, stop, sleep, restart, reloadEnv) and `chat`, which sends an `Idempotency-Key`, retry on `429`, `500`, `502`, `503`, `504`, timeouts and network errors. Other writes retry only on `429` and `503`, and when the connection was refused or DNS failed, so nothing was sent; a timeout or another `5xx` may already have applied the write, so it is thrown.

## Agents

Access via `maritime.agents` (TS) / `client.agents` (Python).

| Method                                     | Does                                                                 |
|--------------------------------------------|----------------------------------------------------------------------|
| `create(params)`                           | Create an agent and start its deploy. Prefer template: a bare framework yields a broken image. |
| `provision({externalId, …})`              | Idempotent get-or-create by externalId. The recommended per-customer entry point. |
| `get(id)`                                  | Fetch one agent.                                                  |
| `list({externalId?, name?})`              | List agents, optionally filtered.                                 |
| `chat(id, message, {conversationId?})`    | Send a message, wait for the reply. Auto-wakes a sleeping agent.  |
| `start / stop / sleep / restart(id)`       | Lifecycle transitions.                                            |
| `setEnv(id, key, value, {secret})`        | Upsert an env var (secrets encrypted at rest).                   |
| `listEnv(id) / deleteEnv(id, key)`        | Read / remove env vars.                                         |
| `reloadEnv(id)`                            | Push env changes into the running container.                       |
| `resize(id, {memMb?, diskGb?, alwaysOn?})`| Change RAM or disk (applies on the next restart) or turn always-on on or off (applies at once). null clears a value back to the base allocation. |
| `setVisibility(id, visibility)`            | 'private' (share-link holders only) or 'public' (anyone with the agent URL; a visit wakes it). public needs a verified account. |
| `webhookToken(id)`                         | The agent's invoke token and URL; POST to the URL wakes the agent with a payload. |
| `rotateWebhookToken(id)`                   | Replace the invoke token. The old one stops working at once.      |
| `network(id) / updateNetwork(id, update) / testNetwork(id, destination)` | Read or change internet policy, exit and private access; try one TCP connection to host:port from inside the agent. |
| `exec(id, command, {timeout?})`           | Run a one-shot shell command (a string, or argv tokens as an array) and wait for it. timeout is seconds, default 60. Wakes a sleeping agent. |
| `logs(id, {limit?, level?})`              | Recent log entries.                                             |
| `delete(id)`                               | Tear down the agent (container + volume + network).               |

Files, skills and share links of an agent hang off `maritime.agents`:

| Method                                     | Does                                                  |
|--------------------------------------------|------------------------------------------------------|
| `files.list(id, path?) / download(id, path)`              | Browse the agent's files; download returns bytes.     |
| `files.upload(id, {content, filename, destDir?}) / write(id, path, content)` | Upload bytes or write a text file.                      |
| `files.mkdir(id, path) / move(id, from, to) / delete(id, path)` | Create a folder, move or delete a path.               |
| `skills.list(id) / search(id, q, {limit?})` | The agent's skills (eligible, blocked, unsupported), or search ClawHub. |
| `skills.install(id, {slug?, git?, version?, force?, env?})`   | Install from ClawHub (slug) or a git repo (git).     |
| `skills.upload(id, {archive, name?, env?})`            | Install a custom skill from a zip or a bare SKILL.md. |
| `skills.setEnv(id, env) / setAllowlist(id, allowlist)`          | Give a skill the env values it waits on; replace the allowlist (null for no limit). |
| `skills.remove(id, name) / export(id, name)`       | Remove a skill, or download it as a .tar.gz.          |
| `shareLinks.create(id, {ttlSeconds?, endUserExternalId?})` | A link that opens a private agent URL. Default one hour; null lasts until revoked. |
| `shareLinks.list(id) / revoke(id, linkId) / revokeForEndUser(id, externalId)` | List links, revoke one, or revoke every link bound to an end user. |

**create / provision parameters**

| Param       | Type         | Notes                                                  |
|-------------|--------------|-------------------------------------------------------|
| `name`      | `string`     | Unique per account. Required.                          |
| `template`  | `string`     | openclaw · zeroclaw · hermes · claude_code · dsh · … (see [frameworks](/content/docs/frameworks/index.html)) |
| `externalId`| `string`     | Your own id for this agent. Filterable; idempotency key for `provision`. |
| `instructions`| `string`     | Plain-English persona / system prompt.                |
| `env`      | `{key,value,secret}[]` | Seed env vars. Secrets encrypted at rest.            |
| `idleTtlSeconds` | `number` | Idle seconds before auto-sleep.                       |

### TypeScript

```typescript
// Idempotent per-customer provisioning (recommended)
const agent = await maritime.agents.provision({
  externalId: `customer_${userId}`,
  name: `assistant-${userId}`,
  template: 'openclaw',
})

const { response } = await maritime.agents.chat(agent.id, 'Hello')
await maritime.agents.setEnv(agent.id, 'STRIPE_KEY', 'sk_live_...', { secret: true })
const { root } = await maritime.agents.files.list(agent.id)   // browse / upload / download
await maritime.agents.exec(agent.id, ['ls', '-la', root])     // one-shot shell command
await maritime.agents.sleep(agent.id)
await maritime.agents.delete(agent.id)
```

### Python

```python
# Idempotent per-customer provisioning (recommended)
agent = client.agents.provision(
    external_id=f"customer_{user_id}",
    name=f"assistant-{user_id}",
    template="openclaw",
)

reply = client.agents.chat(agent["id"], "Hello")["response"]
client.agents.set_env(agent["id"], "STRIPE_KEY", "sk_live_...", secret=True)
root = client.agents.files.list(agent["id"])["root"]        # browse / upload / download
client.agents.exec(agent["id"], ["ls", "-la", root])        # one-shot shell command
client.agents.sleep(agent["id"])
client.agents.delete(agent["id"])
```

Exec waits by default. See [command execution](/content/docs/sdk/agents#exec/index.html) for detached Python submission, REST retrieval and cancellation, and timeout defaults.

## Sandboxes

Linux machines you create, run commands in, pause, snapshot and end from code. They need a key with the `sandboxes` scope (or `manage`). Access via `maritime.sandboxes`, `maritime.ephemeral`, `maritime.snapshots`, `maritime.secrets`, `maritime.usage` and `maritime.sandboxWebhooks` (Python: `client.sandbox_webhooks` and so on). Sandbox calls throw `MaritimeSandboxError` with a machine-readable `code` and `hint`. See [Sandboxes](/content/docs/ephemeral-sandboxes/index.html) and [sandbox events](/content/docs/sdk/webhooks#sandbox-events/index.html).

| Method                                     | Does                                                   |
|--------------------------------------------|-------------------------------------------------------|
| `sandboxes.create({name?, from?, size?, screen?, network?, endUser?, env?, idleTimeoutSeconds?, onIdle?, timeLimitSeconds?, keepAwake?, …})` | Create a sandbox and return a Sandbox object. from is a stock template or a saved snapshot. |
| `sandboxes.get(idOrName) / connect(id) / list({...})`            | Fetch one sandbox, attach to an existing one, or page through yours. |
| `sandbox.run(cmd, {background?, ...})`   | Run a command and wait, or start it in the background and get a CommandHandle. |
| `sandbox.files.read / write / list / remove / watch`           | Work with the sandbox's files.                         |
| `sandbox.url(port) / pause() / kill() / save(name) / update(params)` | Open a port, pause, end, save a snapshot, or change settings. |
| `sandbox.terminal() / runCode(code)`        | An interactive terminal, or run a code snippet.       |
| `ephemeral.createEphemeral / listEphemeral / extendEphemeral / killEphemeral` | Sandboxes with a fixed lifetime that end, disk included, when it runs out. |
| `snapshots.list / get / delete`              | Saved snapshots and the stock templates.               |
| `secrets.set({name, value}) / list / delete(name)` | Account secrets injected into every new sandbox except for EndUser ones. |
| `usage.list({from, to, sandbox?, endUser?})` | Sandbox usage records, one per running interval.      |
| `sandboxWebhooks.create({url, events}) / list / delete / deliveries` | Endpoints that receive signed sandbox events. See sandbox events. |

## End users

Your own users, keyed by the id you already hold. Access via `maritime.endUsers` (TS) / `client.end_users` (Python). See the [end users guide](/content/docs/sdk/end-users/index.html).

| Method                                     | Does                                                   |
|--------------------------------------------|-------------------------------------------------------|
| `upsert(externalId, {displayName?, metadata?, tags?})`      | Create the end user, or update the fields given.        |
| `get(externalId)`                            | One end user with its agents.                          |
| `list({status?, tag?, externalIdPrefix?, q?, projectId?, agentId?, sort?, lastActiveBefore?, lastActiveAfter?, cursor?, limit?})` | One page of end users, newest first. Follow nextCursor for more. |
| `activity(externalId, {limit?})`               | Recent messages with replies, wakes, sleeps and agent creates, newest first. |
| `createAgent(externalId, params?) / agents(externalId)` | Create the end user's agent exactly once; list their agents. |
| `setPolicy / getPolicy / clearPolicy(externalId)`  | Caps and sizing for every agent the end user owns.      |
| `mintToken(externalId, {agentId?, scopes?, ttlSeconds?}) / revokeTokens(externalId)` | Short-lived eu_ token for their own client; revoke every live token. |
| `suspend / resume / delete(externalId)`    | Pause, restore, or remove the end user with their agents. |

## Projects

The HTTP front door: route each of your users' messages to their own agent. Access via `maritime.projects`.

| Method                                     | Does                                                   |
|--------------------------------------------|-------------------------------------------------------|
| `get(projectId)`                           | Fetch a project. Every agent has a projectId.         |
| `update(projectId, {name?, newChatPolicy?, maxInstances?, warmPoolSize?})` | Routing for new end users: 'single', 'assign', 'spawn' (one agent per end user) or 'claim'. |
| `message(projectId, {externalUserId, message, wait?, claimCode?, displayName?})` | Send an end user's message to their agent (bind, wake, deliver). Waits up to wait seconds (default 30) for the reply. |
| `getMessage(projectId, messageId)`        | Poll a queued message for its reply.                   |
| `users(projectId) / getUser(projectId, externalUserId)` | Every bound end user, or one with its agent status.    |
| `unbindUser(projectId, externalUserId)`  | Unbind an end user. Their agent rejoins the warm pool. |

## Swarms

Run one command on many sandboxes and collect the results. Access via `maritime.swarms` (TS) / `client.swarms` (Python). See [Swarms](/content/docs/swarms/index.html).

| Method                                     | Does                                                   |
|--------------------------------------------|-------------------------------------------------------|
| `start({command, count? | inputs?, name?, template? | image?, size?, timeoutSeconds?, concurrency?, resultPath?}, {idempotencyKey?})` | Start a swarm: one command on many sandboxes. Needs the provision scope. A repeated idempotencyKey returns the first run. |
| `list({limit?})`                          | Your runs, newest first.                               |
| `get(swarmId)`                            | One run with every sandbox: status, sandboxId (sbx-...), exit code, output tails and result. |
| `cancel(swarmId)`                         | Stop a run; its sandboxes are stopped and ended.      |
| `results(swarmId)`                        | Every sandbox's outcome, in input order.              |
| `wait(swarmId, {intervalMs?, timeoutMs?})` | Poll until the run completes or is cancelled. Python takes interval and timeout in seconds. |

## Keys

Mint scoped API keys programmatically. Minting requires the caller's key to carry the `manage` scope. Scopes are `provision`, `deploy`, `secrets`, `manage`, `computers` and `sandboxes`. The default is the first four; `manage` grants every scope, so a default key also reaches [Computers](/content/docs/computers/index.html) and [Sandboxes](/content/docs/ephemeral-sandboxes/index.html). A key that should reach only Computers asks for `['computers']`, and one for only Sandboxes asks for `['sandboxes']`. A key created with `projectId` works only on that project's front door and usage routes and is refused everywhere else.

| Method                                     | Does                                                   |
|--------------------------------------------|-------------------------------------------------------|
| `create({name, scopes?, expiresInDays?, projectId?})`        | Mint an account key (mk_). The raw key is returned once. |
| `create({sandbox, ttlSeconds?})`         | Mint a per-sandbox key (msk_) that reaches only that sandbox. Default lifetime one day. The secret is returned once. |
| `list() / list({sandbox})`                | List your account keys, or one sandbox's keys (raw values never returned again). |
| `revoke(id)`                              | Revoke a key.                                         |

### TypeScript

```typescript
const worker = await maritime.keys.create({ name: 'chat-worker', scopes: ['deploy'] })
// worker.rawKey is shown once. Store it now.
const tenant = await maritime.keys.create({ name: 'tenant-a', projectId: agent.projectId })
```

### Python

```python
worker = client.keys.create("chat-worker", scopes=["deploy"])
# worker["raw_key"] is shown once. Store it now.
tenant = client.keys.create("tenant-a", project_id=agent["projectId"])
```

## Webhooks

Receive signed events instead of polling. The [webhooks guide](/content/docs/sdk/webhooks#events/index.html) lists every event and covers retries and verification.

| Method                                     | Does                                                   |
|--------------------------------------------|-------------------------------------------------------|
| `create({url, events?})`                  | Subscribe a URL. Returns the signing secret once.      |
| `list()`                                   | List subscriptions, with isActive for each.            |
| `deliveries(id, {status?, limit?})`      | The delivery log, newest first. status is 'pending', 'delivered', 'failed' or 'dead'. |
| `replay(deliveryId)`                      | Send one delivery again now and return the result.     |
| `test(id)`                                | Deliver a synthetic ping.                             |
| `delete(id)`                              | Remove a subscription.                                 |

Verify a delivery with `verifyWebhookSignature({secret, body, signature})` (TS, returns a Promise of boolean) or `verify_webhook_signature(secret, body, signature)` (Python), both exported at the package top level. Pass the raw body and the `X-Maritime-Signature` header.

## Billing, account and network

| Method                                     | Does                                                   |
|--------------------------------------------|-------------------------------------------------------|
| `billing.usage({from?, to?, projectId?, externalUserId?})` | Per-agent hosting and AI cost over a range (at most 92 days), with externalUserId on each row. |
| `billing.usageDaily({from?, to?})`       | The same costs by day.                                |
| `account.limits()`                        | Your plan, machine counts against their caps, and per-agent size ceilings. Any key works. |
| `network.presets.list() / create({name, allow?, deny?}) / delete(presetId)` | Saved internet allow and deny lists to apply to agents. |

## Feedback

Tell Maritime when a doc is wrong, an example does not run, or a call fails in a way you cannot explain. Built for coding agents. See the [feedback guide](/content/docs/sdk/feedback/index.html).

| Method                                     | Does                                                   |
|--------------------------------------------|-------------------------------------------------------|
| `send({message, kind?, subject?, agentId?, requestId?, statusCode?, tool?, context?})` | Send one report. Returns the stored row (202).        |
| `list()`                                   | The account's last 50 reports, newest first.          |

### TypeScript

```typescript
await maritime.feedback.send({
  kind: 'sdk',
  subject: 'agents.chat',
  message: 'chat returned 500 on a fresh agent; docs say sleeping agents auto-wake.',
  requestId: err.requestId,
  statusCode: err.status,
  tool: 'claude-code/2.1.0',
})
```

### Python

```python
client.feedback.send(
    "chat returned 500 on a fresh agent; docs say sleeping agents auto-wake.",
    kind="sdk",
    subject="agents.chat",
    request_id=err.request_id,
    status_code=err.status,
    tool="codex",
)
```

## Errors

Every failure is a subclass of `MaritimeError`. API errors expose `.status` and `.detail`.

| Class                                   | When                                                        |
|-----------------------------------------|------------------------------------------------------------|
| `MaritimeAuthError`                    | 401 / 403: bad or under-scoped key                          |
| `MaritimePaymentRequiredError`         | 402: plan limit reached, or an add-on needs a paid plan     |
| `MaritimeNotFoundError`                | 404: no such agent (or not yours)                           |
| `MaritimeConflictError`                | 409: name already taken                                     |
| `MaritimeRateLimitError`               | 429: rate limited                                           |
| `MaritimeSandboxError`                 | a sandbox call failed; carries code, hint and retryAfterSeconds |
| `MaritimeAPIError`                     | any other non-2xx                                           |
| `MaritimeConnectionError`              | never reached Maritime (network / timeout)                  |

```python
from maritime import MaritimeAPIError

try:
    client.agents.get("nope")
except MaritimeAPIError as err:
    print(err.status, err.detail, err.request_id)
```

## More resources

- [Computers](/content/docs/computers/index.html): `maritime.computers`, desktops your own model drives
- [Networks](/content/docs/networks/index.html): internet policy, exits and private access
- [Limits](/content/docs/limits/index.html): plan caps, size ceilings and error codes
- [Web apps](/content/docs/web-apps/index.html): public agent URLs, visibility and share links
