Docs: Create and drive agents with the SDK | Maritime

Agents

Everything you do to a customer's agent (create it, talk to it, configure it, and clean it up) is a method on maritime.agents.

Provision (get-or-create)

provision is idempotent on externalId: first call creates the agent, later calls return the same one. That makes it safe to run on every sign-in: you never double-create, and you never have to store Maritime's agent id. Use create instead if you want a hard failure when the name already exists.

create and the rawPOST /api/agents are not idempotent and take no idempotency key. If a create fails after the server stored the agent (a timeout or a dropped connection), a retry with the same name returns 409 and the first agent stays. To recover, list agents by externalId or name and use or delete the one you find. Use provision when you need retries to be safe.

TypeScript

const agent = await maritime.agents.provision({
  externalId: `customer_${user.id}`,
  name: `assistant-${user.id}`,
  template: 'openclaw',              // see /docs/frameworks
  instructions: 'You are a helpful assistant.',
  idleTtlSeconds: 3600,              // auto-sleep after an idle hour
})

Python

# Python SDK example code

Chat

Send a message and wait for the reply. Sleeping agents wake automatically. Pass a conversationId to keep a thread. Check error on every result. Uncertain delivery returns { response: null, error } without resending the turn. A deploying or stopped agent returns HTTP 503 with Retry-After. The SDK can throw after its HTTP retries are exhausted. Some wake refusals remain text in response. SDK chat calls retain one Idempotency-Key across retries. The backend keeps completed keyed results for 30 minutes in memory. A backend restart forgets those keys.

TypeScript

const { response, error } = await maritime.agents.chat(agent.id, 'What can you do?')
if (error) throw new Error(error)

// continue a thread:
await maritime.agents.chat(agent.id, 'And after that?', { conversationId: 'thread-1' })

Python

# Python SDK example code

Per-customer secrets & config

Give each customer's agent its own credentials and config as env vars. Secrets are encrypted at rest; changes reach a running container after a reload.

TypeScript

await maritime.agents.setEnv(agent.id, 'ACME_API_KEY', user.acmeKey, { secret: true })
await maritime.agents.setEnv(agent.id, 'ACME_REGION', 'us-east', { secret: false })
await maritime.agents.reloadEnv(agent.id)   // push into the running container

// read them back (secret values come back masked):
for (const v of await maritime.agents.listEnv(agent.id)) {
  console.log(v.key, v.isSecret ? '(secret)' : v.value)
}

Python

# Python SDK example code

Lifecycle, logs & teardown

Agents sleep and wake on their own, but you can drive the lifecycle directly. Find an agent by your own id, read its logs, or tear it all down when a customer leaves.

TypeScript

const [agent] = await maritime.agents.list({ externalId: `customer_${user.id}` })

await maritime.agents.logs(agent.id, { limit: 100, level: 'error' })

await maritime.agents.sleep(agent.id)     // cheapest resting state
await maritime.agents.start(agent.id)     // wake it
await maritime.agents.restart(agent.id)

await maritime.agents.delete(agent.id)    // container + volume + network

Python

# Python SDK example code

Network

What the agent may reach on the internet, where its traffic exits, and which of your private systems it may use. Set it at creation with network, change it later, and test a destination from inside the agent. The full object, presets, and the metadata the agent reads from inside are on the Networks page.

TypeScript

await maritime.agents.updateNetwork(agent.id, {
  internet: { mode: 'list', presetId: 'coding', allow: ['api.example.com'] },
})
const net = await maritime.agents.network(agent.id)       // net.exitIp, net.enforcement
const probe = await maritime.agents.testNetwork(agent.id, 'api.example.com:443')

Python

# Python SDK example code

Files & commands

Requires SDK 0.8.0 or later (maritime-sdk on npm, maritime on PyPI). On 0.7.x, call the underlying REST endpoints directly.

maritime.agents.files moves bytes both ways and manages the agent's disk; maritime.agents.exec runs a one-shot shell command. Browse and edit operations are scoped to the agent's persistent volume; take its mount from the root that list reports rather than hardcoding /data. Every call here wakes a sleeping agent; transfers cap at 100 MB per file. Uploading without destDir delivers the file as a chat attachment the agent is told about in its current conversation.

TypeScript

const { root, entries } = await maritime.agents.files.list(agent.id)

await maritime.agents.files.upload(agent.id, {
  content: csvBytes,
  filename: 'report.csv',
  destDir: `${root}/inbox`,   // omit destDir to deliver as a chat attachment
})

await maritime.agents.files.write(agent.id, `${root}/notes.md`, '# notes')
const bytes = await maritime.agents.files.download(agent.id, `${root}/notes.md`)

await maritime.agents.files.mkdir(agent.id, `${root}/docs`)
await maritime.agents.files.move(agent.id, `${root}/notes.md`, `${root}/docs/notes.md`)
await maritime.agents.files.delete(agent.id, `${root}/docs/notes.md`)

const { exitCode, stdout } = await maritime.agents.exec(agent.id, ['ls', '-la', root])

Python

# Python SDK example code

Run commands with the SDK

Attached execution is the default. Omitting the execution timeout selects 60 seconds. Stdout and stderr are separate, and each returned stream is limited to 262,144 characters plus a truncation marker. On agents with detached support, every accepted command has an execution ID. Results are temporary and can be lost on restart or agent replacement.

TypeScript

const execution = await maritime.agents.exec(
    agent.id, [