> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orchagent.io/llms.txt
> Use this file to discover all available pages before exploring further.

# OrchAgent SDK

> Use the official orchagent-sdk package for calling other agents

The orchagent SDK provides a simple interface for building orchestrator agents that call other agents. It handles authentication, call chain propagation, and error handling automatically. SDKs are available for both **Python** and **JavaScript/TypeScript**.

## Installation

<Tabs>
  <Tab title="Python">
    ```bash theme={null}
    pip install orchagent-sdk
    ```

    Requires Python 3.9+.
  </Tab>

  <Tab title="JavaScript / TypeScript">
    ```bash theme={null}
    npm install orchagent-sdk
    ```

    Requires Node.js 18+ (uses built-in `fetch`). Zero dependencies.
  </Tab>
</Tabs>

## Basic Usage

The SDK provides `AgentClient` for calling other agents from within your agent. Most agents use stdin/stdout — create a client with no arguments and the SDK reads all context from environment variables:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import json, sys
    from orchagent import AgentClient

    input_data = json.loads(sys.stdin.read())
    client = AgentClient()  # Reads ORCHAGENT_SERVICE_KEY + context from env

    result = await client.call("joe/leak-finder@v1", {"url": input_data["repo_url"]})
    print(json.dumps({"findings": result}))
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const { AgentClient } = require('orchagent-sdk');
    const fs = require('fs');

    async function main() {
      const input = JSON.parse(fs.readFileSync('/dev/stdin', 'utf-8'));

      const client = new AgentClient();
      const result = await client.call('joe/leak-finder@v1', { url: input.repo_url });

      console.log(JSON.stringify({ findings: result }));
    }

    main();
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import { AgentClient } from 'orchagent-sdk';

    async function main() {
      const input = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf-8'));

      const client = new AgentClient();
      const result = await client.call('joe/leak-finder@v1', { url: input.repo_url });

      console.log(JSON.stringify({ findings: result }));
    }

    main();
    ```
  </Tab>
</Tabs>

The SDK automatically extracts context from environment variables (injected by the gateway):

* Service key for authentication
* Call chain for cycle detection
* Deadline for timeout propagation
* Remaining hops count

## Local vs Server Mode

Both SDKs automatically detect whether to route calls locally or through the gateway based on the `ORCHAGENT_LOCAL_EXECUTION` environment variable.

| Mode       | `ORCHAGENT_LOCAL_EXECUTION` | Behavior                               |
| ---------- | --------------------------- | -------------------------------------- |
| **Server** | Not set or `false`          | Calls route through `api.orchagent.io` |
| **Local**  | `true`                      | Calls route to locally running agents  |

<Note>
  When you run an orchestrator with `orch run --with-deps`, the CLI automatically sets `ORCHAGENT_LOCAL_EXECUTION=true` so your agent calls dependencies locally.
</Note>

### Server Mode (Default)

In server mode, calls go through the gateway which handles routing, authentication, and sandbox management.

### Local Mode

In local mode, calls go directly to locally running agent processes. The SDK spawns subprocesses using `python3` for `.py` entrypoints and `node` for `.js` entrypoints.

## API Reference

### AgentClient

The main class for calling other agents.

#### Creating a Client

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from orchagent import AgentClient

    # From environment variables (recommended — reads ORCHAGENT_SERVICE_KEY etc.)
    client = AgentClient()

    # From an incoming HTTP request (advanced — for custom FastAPI servers)
    # Propagates call chain, deadline, and billing context from headers
    client = AgentClient.from_request(request)

    # With explicit configuration
    client = AgentClient(
        service_key="sk_agent_...",
        gateway_url="https://api.orchagent.io"
    )
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const { AgentClient } = require('orchagent-sdk');

    // From environment variables (recommended — reads ORCHAGENT_SERVICE_KEY etc.)
    const client = new AgentClient();

    // From an incoming HTTP request (advanced — for custom Express/Koa/Fastify servers)
    const client = AgentClient.fromRequest(req);

    // With explicit configuration
    const client = new AgentClient({
      serviceKey: 'sk_agent_...',
      gatewayUrl: 'https://api.orchagent.io'
    });
    ```
  </Tab>
</Tabs>

#### call()

Call another agent asynchronously.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    result = await client.call(
        agent_id,      # Required: "org/name@version"
        input_data,    # Required: dict of input parameters
        endpoint=None, # Optional: specific endpoint (default: agent's default)
        timeout=None   # Optional: timeout in seconds (default: inherited from request)
    )
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const result = await client.call(
      'org/name@version',  // Required: agent reference
      { input: 'data' },   // Required: input parameters
      { endpoint: 'run', timeout: 30 }  // Optional
    );
    ```
  </Tab>
</Tabs>

**Parameters:**

| Parameter    | Type               | Required | Description                                      |
| ------------ | ------------------ | -------- | ------------------------------------------------ |
| `agent_id`   | `str`              | Yes      | Agent identifier in format `org/name@version`    |
| `input_data` | `dict` / `object`  | Yes      | Input parameters for the agent                   |
| `endpoint`   | `str`              | No       | Specific endpoint to call (e.g., `"deep-scan"`)  |
| `timeout`    | `float` / `number` | No       | Timeout in seconds, overrides inherited deadline |

**Returns:** `dict` / `object` - The agent's response data

**Raises:** `DependencyCallError`, `TimeoutExceededError`, `CallChainCycleError`

#### Examples

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    # Basic call
    result = await client.call("joe/summarizer@v1", {"text": document})

    # Call specific endpoint
    result = await client.call("joe/scanner@v1", {"url": repo}, endpoint="deep-scan")

    # Override timeout
    result = await client.call("joe/analyzer@v1", {"data": data}, timeout=60.0)
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    // Basic call
    const result = await client.call('joe/summarizer@v1', { text: document });

    // Call specific endpoint
    const result = await client.call('joe/scanner@v1', { url: repo }, { endpoint: 'deep-scan' });

    // Override timeout
    const result = await client.call('joe/analyzer@v1', { data }, { timeout: 60 });
    ```
  </Tab>
</Tabs>

## Environment Variables

The SDK uses these environment variables:

| Variable                    | Required | Description                                                                                                   |
| --------------------------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `ORCHAGENT_SERVICE_KEY`     | Yes      | Service key for agent authentication (`sk_agent_...`)                                                         |
| `ORCHAGENT_GATEWAY_URL`     | No       | Gateway URL (default: `https://api.orchagent.io`)                                                             |
| `ORCHAGENT_LOCAL_EXECUTION` | No       | Set to `true` to route calls locally                                                                          |
| `ORCHAGENT_CALL_CHAIN`      | No       | Propagated automatically; tracks call ancestry                                                                |
| `ORCHAGENT_DEADLINE`        | No       | Propagated automatically; deadline timestamp                                                                  |
| `ORCHAGENT_SKILLS_DIR`      | No       | Path to mounted skills directory (only set when skills are passed). Contains `.md` files and `manifest.json`. |

<Tip>
  When running on orchagent's servers, `ORCHAGENT_SERVICE_KEY` is automatically injected into your agent's environment.
</Tip>

## Error Handling

Both SDKs provide specific exception/error types for different failure modes:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from orchagent import (
        AgentClient,
        DependencyCallError,
        TimeoutExceededError,
        CallChainCycleError
    )

    try:
        result = await client.call("joe/analyzer@v1", input_data)
    except DependencyCallError as e:
        # The called agent returned an error
        print(f"Agent error: {e.status_code}")
        print(f"Response: {e.response_body}")
    except TimeoutExceededError:
        # Request exceeded deadline
        print("Call timed out")
    except CallChainCycleError:
        # Cycle detected (A -> B -> A)
        print("Circular dependency detected")
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const {
      AgentClient,
      DependencyCallError,
      TimeoutExceededError,
      CallChainCycleError
    } = require('orchagent-sdk');

    try {
      const result = await client.call('joe/analyzer@v1', inputData);
    } catch (err) {
      if (err instanceof DependencyCallError) {
        console.error(`Agent error: ${err.statusCode}`);
        console.error(`Response: ${JSON.stringify(err.responseBody)}`);
      } else if (err instanceof TimeoutExceededError) {
        console.error('Call timed out');
      } else if (err instanceof CallChainCycleError) {
        console.error('Circular dependency detected');
      }
    }
    ```
  </Tab>
</Tabs>

### Exception Reference

| Exception              | When Raised                   | Common Causes                                 |
| ---------------------- | ----------------------------- | --------------------------------------------- |
| `DependencyCallError`  | Agent returned non-2xx status | Agent bug, invalid input, agent unavailable   |
| `TimeoutExceededError` | Deadline exceeded             | Slow agent, network issues, timeout too short |
| `CallChainCycleError`  | Cycle detected in call chain  | A calls B calls A (circular dependency)       |

### Handling Partial Failures

When calling multiple agents, handle failures gracefully:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import asyncio

    async def analyze_repo(client, repo_url):
        results = {}

        # Use asyncio.gather with return_exceptions=True
        outcomes = await asyncio.gather(
            client.call("joe/leak-finder@v1", {"url": repo_url}),
            client.call("joe/vuln-scanner@v1", {"url": repo_url}),
            return_exceptions=True
        )

        for name, outcome in zip(["secrets", "vulnerabilities"], outcomes):
            if isinstance(outcome, Exception):
                results[name] = {"error": str(outcome)}
            else:
                results[name] = outcome

        return results
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    async function analyzeRepo(client, repoUrl) {
      const results = {};

      const outcomes = await Promise.allSettled([
        client.call('joe/leak-finder@v1', { url: repoUrl }),
        client.call('joe/vuln-scanner@v1', { url: repoUrl }),
      ]);

      const names = ['secrets', 'vulnerabilities'];
      for (let i = 0; i < outcomes.length; i++) {
        const outcome = outcomes[i];
        results[names[i]] = outcome.status === 'fulfilled'
          ? outcome.value
          : { error: outcome.reason.message };
      }

      return results;
    }
    ```
  </Tab>
</Tabs>

## Best Practices

### When to Use the SDK

* **Building orchestrators** - Agents that compose multiple leaf agents
* **Multi-step workflows** - Chaining agent calls in sequence
* **Fan-out patterns** - Calling multiple agents in parallel

### Recommendations

1. **Use `AgentClient()` / `new AgentClient()` for stdin/stdout agents** (the common case) — the SDK reads all context from environment variables injected by the gateway. **Use `from_request()` / `fromRequest()` only for custom HTTP servers** (FastAPI, Express, etc.) where you need to propagate call chain and deadline from incoming request headers.

2. **Handle all exception types** - Don't let `DependencyCallError` crash your agent

3. **Use parallel calls when possible** - Fan-out to independent agents with `asyncio.gather()` (Python) or `Promise.allSettled()` (JavaScript)

4. **Pin dependency versions** - Use `org/agent@v1`, not `org/agent@latest`

5. **Set reasonable timeouts** - Account for all dependency calls in your `timeout_ms`

6. **Return partial results on failure** - Don't fail completely if one dependency fails

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    # Parallel calls with error handling
    async def review(client, repo):
        secrets, vulns = await asyncio.gather(
            client.call("joe/leak-finder@v1", {"url": repo}),
            client.call("joe/vuln-scanner@v1", {"url": repo}),
            return_exceptions=True
        )

        return {
            "secrets": secrets if not isinstance(secrets, Exception) else None,
            "vulnerabilities": vulns if not isinstance(vulns, Exception) else None,
        }
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    // Parallel calls with error handling
    async function review(client, repo) {
      const [secrets, vulns] = await Promise.allSettled([
        client.call('joe/leak-finder@v1', { url: repo }),
        client.call('joe/vuln-scanner@v1', { url: repo }),
      ]);

      return {
        secrets: secrets.status === 'fulfilled' ? secrets.value : null,
        vulnerabilities: vulns.status === 'fulfilled' ? vulns.value : null,
      };
    }
    ```
  </Tab>
</Tabs>

### Anti-patterns to Avoid

* **Don't hardcode service keys** — use `AgentClient()` (reads from env) or `from_request(request)` (reads from HTTP headers) to inherit context automatically
* **Don't ignore the call chain** — it prevents cycles and tracks lineage
* **Don't hardcode gateway URLs** — use the environment variable

## Agent Storage

The SDK also includes a storage module for persistent shared state between agents. See the dedicated [Agent Storage](/using-agents/storage) page for the full reference.

```python theme={null}
from orchagent import storage

storage.set("signals", "2026-03-05", {"pending": [], "done": []})
data = storage.get("signals", "2026-03-05")
storage.update("signals", "2026-03-05", lambda doc: {**doc, "status": "done"})
```

## Agent Messages

Send messages from your agents to [orch-hq](/orch-hq). Use messages for briefings, alerts, reports — anything your agent wants to tell you. See the dedicated [Agent Messages](/using-agents/messages) page for the full reference.

```python theme={null}
from orchagent import message

message.send("Daily Brief", "Here's what's happening today...")
message.send("Build Failed", "Error in main.py line 42", level="error")
```

## Agent Tasks

Create and manage tasks from your agents. Tasks appear in orch-hq's Tasks panel, grouped by urgency. See the dedicated [Agent Tasks](/using-agents/tasks) page for the full reference.

```python theme={null}
from orchagent import task

task.create("Fix login bug", due_date="2026-03-20", project="StockSure", priority="high")
overdue = task.list(status="open", overdue=True)
task.complete(task_id)
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Orchestration" icon="diagram-project" href="/building-agents/orchestration">
    Learn how to build orchestrator agents that compose multiple agents together
  </Card>

  <Card title="Agent Storage" icon="database" href="/using-agents/storage">
    Persistent shared state for multi-agent coordination and checkpoints
  </Card>

  <Card title="Messages" icon="envelope" href="/using-agents/messages">
    Send messages and notifications from your agents
  </Card>

  <Card title="Tasks" icon="list-check" href="/using-agents/tasks">
    Create and manage tasks from your agents
  </Card>
</CardGroup>
