Source: https://docs.cumuluslabs.io/sdks
Content version: e498d0cc

# SDKs

The TypeScript and Python SDKs are generated from the same operation registry as the API: one method per operation, typed requests, validated responses, and the idempotency and retry rules built in.

> **Getting the packages**
> 
> The SDKs are not yet published to the public npm and PyPI registries. Your Cumulus contact provides the packages; until you have them, every operation works over plain HTTPS (see the curl samples).

## One method per operation {#methods}

An operation `domain.method` is `client.domain.method(…)` in both SDKs: `runs.create` is `client.runs.create`. Each call takes the request's parts by name: `path`, `query`, `body`and, for commands that take one, the idempotency key. The SDK sends `Authorization`, generates an `Idempotency-Key` when you pass none, and raises one error type carrying the API's `code`, `retryable`, `outcome` and the key it sent.

|  | TypeScript | Python |
| --- | --- | --- |
| Package | `@cumulus/sdk` | `cumulus-sdk` |
| Runtime | Node.js 20.3 or newer, ESM only | Python 3.10 or newer (httpx, pydantic 2); synchronous |
| Client | `new Cumulus({ apiKey, baseUrl, timeoutMs, fetch })` | `Cumulus(api_key, *, base_url, timeout, transport)` |
| Defaults | `CUMULUS_API_KEY`, `CUMULUS_BASE_URL`, https://api.cumuluslabs.io, 30 s | `CUMULUS_API_KEY`, `CUMULUS_BASE_URL`, https://api.cumuluslabs.io, 30 s |
| A call | `client.runs.get({ path: { run_id } })` | `client.runs.get(path={"run_id": run_id})` |
| Idempotency | `{ …, idempotencyKey }` | `idempotency_key=…` |
| Run events | `client.events({ path: { run_id }, lastEventId })` | `client.events(run_id, last_event_id=…)` |
| Errors | `PlatformError` | `PlatformError` |

In Python, a method named by a keyword takes a trailing underscore (`client.agents.import_`), and responses are Pydantic models. Both SDKs can also call an operation by id: `client.call("runs.get", …)`.

## TypeScript {#typescript}

**run.mjs**

```
import { Cumulus } from "@cumulus/sdk";

const client = new Cumulus({ apiKey: process.env.CUMULUS_API_KEY });
const run = await client.runs.create({
  body: { channel: "cloud", agent_id: "…", mode: "task", input: { ticket: "I was charged twice this month." } },
});
for await (const event of client.events({ path: { run_id: run.run_id } })) {
  console.log(event.sequence, event.type);
  if (event.type === "run.ended") break;
}
```

## Python {#python}

**run.py**

```
from cumulus import Cumulus

with Cumulus() as client:  # reads CUMULUS_API_KEY
    run = client.runs.create(
        body={"channel": "cloud", "agent_id": "…", "mode": "task", "input": {"ticket": "I was charged twice this month."}}
    )
    for event in client.events(str(run.run_id)):
        payload = event.model_dump(mode="json")
        print(payload["sequence"], payload["type"])
        if payload["type"] == "run.ended":
            break
```

## Errors {#errors}

### When to retry

*   `error.outcome === "unknown"`: the command may have happened (a timeout, a dropped connection). Retry with the same key: `error.idempotencyKey` / `error.idempotency_key`.
*   `error.retryable` with outcome `none`: nothing happened; retry after a pause.
*   Anything else: fix the request. `error.code` and `error.details` say what is wrong.

Every step of the main flows, in both SDKs, is in [Using the API](/docs/api).
