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

# Authenticate your AgentRail SDK client with API keys

> Configure AGENTRAIL_BASE_URL and AGENTRAIL_API_KEY, understand bearer tokens vs key IDs, assign scopes, and resolve 401 and 403 errors.

Every SDK client requires two values: the base URL of your AgentRail API and a bearer token that proves the client's identity. Both values are generated when you run `agentrail init` or `agentrail agent create` and are written to `~/.agentrail/agent.env`.

## Load credentials from the env file

The generated file looks like this:

```bash theme={null}
AGENTRAIL_BASE_URL=http://127.0.0.1:3000
AGENTRAIL_API_KEY=ar_live_...
```

Source it in your shell before running your harness:

```bash theme={null}
source ~/.agentrail/agent.env
```

When you have a hosted AgentRail API, replace the base URL with that address. The `AGENTRAIL_API_KEY` value stays the same.

## The bearer token vs the key ID

<Warning>
  Use the `ar_live_...` value as your API key. Values that start with `akey_` are key IDs used for rotation and usage reporting — they are not bearer tokens and will not authenticate requests.
</Warning>

## Construct the client

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { AgentRailClient } from "@agentrail-core/sdk";

    const baseUrl = process.env.AGENTRAIL_BASE_URL ?? "http://127.0.0.1:3000";
    const apiKey = process.env.AGENTRAIL_API_KEY;

    if (!apiKey) {
      throw new Error("Set AGENTRAIL_API_KEY before using the SDK.");
    }

    const client = new AgentRailClient({ baseUrl, apiKey });
    ```

    `baseUrl` is required by the constructor. Passing it explicitly avoids a hidden dependency on local defaults.
  </Tab>

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

    from agentrail import AgentRailClient

    async def main():
        async with AgentRailClient(
            base_url=os.getenv("AGENTRAIL_BASE_URL", "http://127.0.0.1:3000"),
            api_key=os.environ["AGENTRAIL_API_KEY"],
        ) as client:
            ...

    asyncio.run(main())
    ```

    The Python client is used as an async context manager. It closes the underlying `httpx` connection pool when the block exits.
  </Tab>
</Tabs>

## Auth errors

| Status | Meaning                         | Fix                                                                                              |
| ------ | ------------------------------- | ------------------------------------------------------------------------------------------------ |
| `401`  | Missing or wrong bearer token   | Load `~/.agentrail/agent.env` and confirm you're using `AGENTRAIL_API_KEY`, not an `akey_` value |
| `403`  | Key is missing a required scope | Create or rotate an agent key with the scope your operation requires                             |

## Auth scopes

Agent keys are scoped. Assign only the scopes your harness needs.

| Operation                  | Required scope                    |
| -------------------------- | --------------------------------- |
| List or read tasks         | `tasks:read`                      |
| Submit task work           | `tasks:write`                     |
| Read CI summaries          | `ci:read`                         |
| Read review feedback       | `reviews:read`                    |
| Ship or merge work         | `ship:write`                      |
| Stream task events         | `events:read`                     |
| Manage event subscriptions | `webhooks:read`, `webhooks:write` |

<Note>
  `auth:admin` is for bootstrap and administration only. Do not grant it to normal worker harnesses.
</Note>

## Idempotency keys

Mutating calls — `submitTask`, `shipTask`, `createApiKey`, and subscription creation — require an idempotency key. Reusing the same key with the same request body is safe and will return the original response. Reusing a key with a different body returns a `409 Conflict`.

Include the operation, task ID, and attempt identifier to keep keys unique:

```text theme={null}
submit-tsk_123-attempt-1
ship-tsk_123-b5bc7f8
event-subscription-primary-v1
```

Use a new key for each genuinely new attempt.
