> This page is also available as Markdown: append .md to its URL. The full index of Sikaru docs is at https://docs.sikaru.ai/llms.txt. Code examples use only operations from the public API reference at https://docs.sikaru.ai/api-reference.

# Run your first agent

Sequence of public API operations used on this page (Python SDK `sikaru_api`):

1. `client.managed_agents.create_managed_agent(project, agent_slug=..., display_name=..., source=CreateManagedAgentSourceRequest(content_digest=..., definition=...), status="active")`, run once per slug.
2. `client.runs.start(project, agent_slug, tenant_id=..., user_id=..., idempotency_key=..., input={"text": ...}, product_context={}, policy={})` returns admission, not completion.
3. Poll `client.runs.events(project, run_id, after=cursor)` and `client.runs.get(project, run_id)` until status is `completed`, `failed`, or `cancelled`. Stop and call `client.runs.pending_actions(project, run_id)` on `waiting_for_tool`.
4. `client.runs.get_trajectory(project, run_id)` reads retained execution evidence.

Required API key permissions: `managed_agents:write`, `runs:create`, `runs:read`, `events:read`. Install: `uv add sikaru_api` (Python) or `npm install @sikaru/sdk` (TypeScript). Environment variables: `SIKARU_API_KEY`, `SIKARU_BASE_URL`, `SIKARU_PROJECT_ID`.

This example uses managed execution and the project default model.

## Before you start

* Python 3.10 or later and [uv](https://docs.astral.sh/uv/).
* A Sikaru project ID.
* A project API key with `managed_agents:write`, `runs:create`, `runs:read`, and `events:read`.
* Agent provisioning and managed execution enabled for your project.

Set up credentials with [Authentication](/get-started/authentication).

> **Warning**
>
> The commands below make hosted requests and can consume your project's usage allowance.

## Steps

#### Install the Python SDK

Create a project and add the SDK. See [SDKs](/sdks-and-cli/sdks) for TypeScript.

```sh
uv init sikaru-quickstart
cd sikaru-quickstart
uv add sikaru_api
```

#### Create the agent

Save this as `first_agent.py`. The definition holds the agent's instructions; its digest identifies that exact content. Creation returns the managed agent and its release.

```python
import hashlib
import json
import os
import time
import uuid

from sikaru_api import CreateManagedAgentSourceRequest, SikaruApi

project = os.environ["SIKARU_PROJECT_ID"]
client = SikaruApi(
    api_key=os.environ["SIKARU_API_KEY"],
    base_url=os.environ["SIKARU_BASE_URL"],
    timeout=20,
    max_retries=0,
)
agent_slug = "support-quickstart"
definition = {
    "schema": "sikaru.agent.contract.v1",
    "instructions": (
        "Summarize support cases using only the supplied facts. "
        "State what is known, what is missing, and the recommended next step. "
        "Do not take external actions."
    ),
    "sources": [],
}
encoded = json.dumps(
    definition, sort_keys=True, separators=(",", ":"),
    ensure_ascii=False, allow_nan=False,
).encode("utf-8")
created = client.managed_agents.create_managed_agent(
    project,
    agent_slug=agent_slug,
    display_name="Support quickstart",
    source=CreateManagedAgentSourceRequest(
        content_digest="sha256:" + hashlib.sha256(encoded).hexdigest(),
        definition=definition,
    ),
    status="active",
)
print("Created agent:", created["managedAgent"])
```

> **Note**
>
> Run creation once. If `support-quickstart` already exists, find it with `client.managed_agents.list_managed_agents(project)` and reuse it, or pick another slug. Do not repeat creation after an uncertain response.

#### Submit a task

Append this to the same file:

```python
request_id = "quickstart-" + str(uuid.uuid4())
print("Request ID:", request_id, flush=True)
run = client.runs.start(
    project,
    agent_slug,
    tenant_id="demo-customer",
    user_id="demo-user",
    idempotency_key=request_id,
    input={
        "text": (
            "A customer reports two charges for the same order. "
            "We have the order number but no transaction IDs. "
            "Summarize the case and recommend the next step."
        )
    },
    product_context={},
    policy={},
)
print("Run:", run.run_id, "Status:", run.status, flush=True)
```

The response confirms **admission**, not completion. Keep the run ID.

If the request times out before a response, retry with the same `request_id` and identical input. A new ID means new work.

`tenant_id` and `user_id` identify the caller in your application. The demo values are fine here; in an application, derive them from your authenticated user.

#### Watch the result

Append this bounded polling loop, then run `uv run first_agent.py`:

```python
cursor = "0"
deadline = time.monotonic() + 120
terminal = {"completed", "failed", "cancelled"}

while time.monotonic() < deadline:
    page = client.runs.events(project, run.run_id, after=cursor)
    for event in page.events:
        print(event.event_type, event.payload)
    cursor = str(page.next_after)
    current = client.runs.get(project, run.run_id)
    if current.status in terminal:
        # Drain events committed between the page read and the status read.
        while True:
            page = client.runs.events(project, run.run_id, after=cursor)
            for event in page.events:
                print(event.event_type, event.payload)
            cursor = str(page.next_after)
            if not page.events:
                break
        print("Final status:", current.status)
        break
    if current.status == "waiting_for_tool":
        print("Action needed:", client.runs.pending_actions(project, run.run_id))
        break
    time.sleep(1)
else:
    print("Stopped waiting. Hosted work may still be running:", run.run_id)
```

> **Check**
>
> A successful run emits a `run.completed` event with the output, and the final status is `completed`. The answer should name the duplicate-charge report, the missing transaction IDs, and a next step based on those facts. Wording varies by model.

The two-minute deadline only bounds polling; each HTTP request also has its own timeout. It does not cancel hosted work. A waiting action needs handling; see [Runs and tool requests](/run/runs-and-tool-requests).

> **Warning**
>
> Do not log event payloads indiscriminately in production. They can contain customer data.

#### Inspect what happened

Use the same run ID to read retained execution evidence:

```python
evidence = client.runs.get_trajectory(project, run.run_id)
print("Trajectory available for run:", evidence["runId"])
```

A trajectory is a permitted view of the retained execution structure. It is separate from the final answer and may be partial or unavailable. See [Trace ingestion](/improve/trace-ingestion) for missing measurements and dashboard navigation.

## Using TypeScript

Install the [TypeScript SDK](/sdks-and-cli/sdks) with `npm install @sikaru/sdk`, then start work with an existing active agent:

```typescript
import { SikaruApi } from "@sikaru/sdk";

const client = new SikaruApi({
  apiKey: process.env.SIKARU_API_KEY!,
  baseUrl: process.env.SIKARU_BASE_URL!,
});
const run = await client.runs.start(
  process.env.SIKARU_PROJECT_ID!, "support-quickstart", {
    tenant_id: "demo-customer",
    user_id: "demo-user",
    input: { text: "Summarize this case: an order has two charges and no transaction IDs." },
    product_context: {},
    policy: {},
  },
);
console.log(run.runId);
```

## Next steps

#### [Sessions and steering](/run/sessions-and-steering)

Add follow-up messages that reuse retained context.

#### [Tools and connections](/build/tools-and-connections)

Connect your agent to useful actions.

#### [Models and output](/build/models-and-output)

Pick a model and constrain output shape.

#### [Troubleshooting](/help/troubleshooting)

Fix access, provisioning, waiting, and timeout failures.