# SDKs

Install the Python and TypeScript SDKs, sign in with a service account and make your first calls in a minute.

Liyagent has two official SDKs for the [HTTP API](/docs/reference/api). Both cover agents, runs, approvals, evals, knowledge and solution templates, and both send the same requests with the same retries and errors.

| Language | Package | Install |
| --- | --- | --- |
| Python 3.10+ | [`liyagent` on PyPI](https://pypi.org/project/liyagent/) | `pip install liyagent` |
| TypeScript / JavaScript (Node 18+, Deno, Bun, browsers, edge) | [`@liyagent/sdk` on npm](https://www.npmjs.com/package/@liyagent/sdk) | `npm install @liyagent/sdk` |

## Get a token

The SDKs sign in with a **service account**, the credential a script or pipeline uses instead of a person's password. It keeps working where SSO or an authenticator code is required.

1. Under **Access** → **Users**, add an account with no password and the role the script needs. Installing a template writes agents and guardrails, so it needs a role such as **Administrator**. Reading templates and asking agents needs less.
2. Open **Access** → **Service accounts** and choose **+ New service account**. Pick that account and how many days the secret lasts.
3. Copy the secret. It is shown once. The token is `svc/<name>:<secret>`.

See [Service accounts](/docs/govern/identity-roles#service-accounts) for rotating and disabling them.

The address is the URL you open the console at: `https://liyagent.com` for the hosted service, or your own host if you run Liyagent yourself.

```bash
export LIYA_URL="https://liyagent.com"
export LIYA_TOKEN="svc/ci-deployer:..."
```

## Python quickstart

```python
from liyagent import Client

with Client() as liya:                      # reads LIYA_URL and LIYA_TOKEN
    for t in liya.templates.list(solution="itsm"):
        print(t["key"], t["version"], t.get("title", ""))

    plan = liya.templates.preview("it-ticket-triage", agent_id="desk-triage")
    if plan["valid"]:
        liya.templates.install("it-ticket-triage", agent_id="desk-triage")
    else:
        print("would be refused at", plan["step"], plan["refusal"]["detail"])

    reply = liya.agents.ask("desk-triage", "VPN drops every 10 minutes since this morning")
    print(reply["reply"] or reply.get("skipped"))
```

`AsyncClient` has the same calls for asyncio: `await liya.templates.list()`, `async for agent in liya.agents.list()`.

## TypeScript quickstart

```ts
import { Client } from "@liyagent/sdk";

const liya = new Client(); // reads LIYA_URL and LIYA_TOKEN; or new Client({ baseUrl, token })

const { templates } = await liya.templates.list("itsm");
for (const t of templates) console.log(t.key, t.version, t.title);

const plan = await liya.templates.preview("it-ticket-triage", { agent_id: "desk-triage" });
if (plan.valid) await liya.templates.install("it-ticket-triage", { agent_id: "desk-triage" });
else console.log("would be refused at", plan.step, plan.refusal.detail);

const reply = await liya.agents.ask("desk-triage", "VPN drops every 10 minutes since this morning");
console.log(reply.reply || reply.skipped);
```

CommonJS works too: `const { Client } = require("@liyagent/sdk")`. On the edge or in a browser, pass `baseUrl` and `token` explicitly. Never ship a service account secret to a browser.

## What the SDKs cover

| Namespace | What it does |
| --- | --- |
| `agents` | List (every page), get, create or change, delete, export and apply bundles (agents as code), ask, start, stop, run a workflow |
| `runs` | Recent runs, a suspended run's checkpoint, resume it |
| `approvals` | The queue of held calls: list, get, approve or reject by digest |
| `evals` | Datasets, start a run, wait for it, compare with a pinned baseline |
| `knowledge` | Answers from the knowledge base (API key), collections, draft articles to approve |
| `templates` | List, preview, install with parameters, uninstall; author and publish your own |

Anything else: `liya.request(...)` in Python, `liya.send(...)` in TypeScript, with the same credentials, retries and errors. `check_server()` / `checkServer()` lists the operations the SDK uses that an older server does not serve.

## Errors and retries

A refusal raises (or throws) a subclass of `LiyagentError`. Each one carries the HTTP `status`, the server's `detail` and the request id the server logged it under (`request_id` in Python, `requestId` in TypeScript).

| Status | Class | `kind` |
| --- | --- | --- |
| 401 | `AuthenticationError` | `auth` |
| 403 | `PermissionDeniedError` (the message names the missing permission) | `forbidden` |
| 404 | `NotFoundError` | `not_found` |
| 400, 422 | `BadRequestError` | `invalid` |
| 409, 412 | `ConflictError` | `conflict` |
| 429 | `RateLimitError` | `rate_limited` |
| 5xx | `InternalServerError` | `server` |
| none | `APIConnectionError` (not reached, or timed out) | `unreachable` |

A 429 or 503, or a request that never reached the server, is sent again up to twice. A 500, 502 or 504 is also retried for GET, PUT and DELETE. The client waits as long as `Retry-After` says, or otherwise backs off exponentially. Set `max_retries` / `maxRetries` to change the number of retries, and `timeout` (seconds) / `timeoutMs` to change the timeout.

## Support

- Questions and problems: [docs@liyagent.com](mailto:docs@liyagent.com). Include the SDK version, the call you made and the error's request id.
- Security: see [Security](https://liyagent.com/security).
- What changed in each release: [SDK changelog](/docs/reference/sdk-changelog).

## Next steps

- [API overview](/docs/reference/api)
- [API endpoints](/docs/reference/api-endpoints)
- [SDK changelog](/docs/reference/sdk-changelog)
