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

# Direct API access

> Call Archal clone endpoints directly from curl, Python, Lambda, or edge workers using the two-header route auth and service-shaped Authorization pattern.

Use this page only when you are calling clone URLs yourself from `curl`,
Python, a Lambda, an edge worker, or another tool outside the Archal CLI.

Most Node.js code should use `@archal/runtime`; it handles this auth shape for
you.

Direct clone calls need route auth plus a normal service-shaped auth header:

## The two-header pattern

```
x-route-authorization: Bearer $ARCHAL_TOKEN
Authorization: Bearer <non-empty-service-token>
```

* **`x-route-authorization`** authenticates you to Archal. For raw HTTP,
  export `ARCHAL_TOKEN` yourself. Use a workspace API key (`archal_ws_...`)
  created with `archal workspace api-key create <label>` in CI or headless
  shells. `archal workspace api-keys` lists keys and
  `archal workspace api-key revoke <key-id>` revokes one. Browser or CLI login
  is enough for CLI commands, but it does not automatically add a bearer token
  to your own `curl`, SDK, Lambda, or worker.
* **`Authorization`** is the service credential the clone sees. The accepted
  value depends on the clone. GitHub accepts any non-empty token (public reads
  need no token at all). Slack is stricter: it rejects any non-bootstrap token
  with `account_inactive`, so you must send that clone's exact bootstrap token.

Pick the token from the [Authentication](/guides/authentication) bootstrap
table. For the GitHub examples below, use
`ghp_AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTt`.

Always send both headers when you want the clone to see a service-shaped token
instead of your Archal token.

During `archal run --docker` or sandboxed runs, harnesses can read the route
pieces directly from the environment:

* `AGENT_CLONE_URLS` is a JSON map. In controlled routing it can point SDKs at
  normal service-shaped hosts such as `https://api.github.com`; in direct mode
  it can point at hosted clone URLs such as `https://.../github/api`.
* `AGENT_ROUTE_HEADERS` is a JSON object containing the route auth headers.

Use those values instead of hardcoding a session URL or copying
`ARCHAL_TOKEN` into the harness. When your agent SDK is already using normal
service hosts inside Docker or sandbox mode, prefer the injected URL/header
contract and do not add Archal-specific clone URLs to the prompt.

## Example (curl)

Given a running clone session:

```bash theme={null}
archal clone start github
# → Session: ses-01hxyz
# → github clone ready: https://ses-01hxyz.clones.archal.ai/github/api
```

You can hit the clone's REST surface directly:

```bash theme={null}
# A workspace API key works for CI and headless local shells.
export ARCHAL_TOKEN=archal_ws_<your-key>

# Create a repo on the clone with the supported GitHub REST shape.
curl -X POST https://ses-01hxyz.clones.archal.ai/github/api/user/repos \
  -H "x-route-authorization: Bearer $ARCHAL_TOKEN" \
  -H "Authorization: Bearer ghp_AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTt" \
  -H "Content-Type: application/json" \
  -d '{"name": "test-repo", "private": false}'
```

If route auth is missing or invalid, Archal rejects the request before it
reaches the clone. If service `Authorization` is missing, a strict clone can
return a real service-shaped auth error, such as:

```json theme={null}
{
  "message": "Requires authentication",
  "documentation_url": "https://docs.github.com/rest/overview/resources-in-the-rest-api#authentication",
  "status": "401"
}
```

## Example (Python urllib)

```python theme={null}
import json
import os
import urllib.request

session_id = "ses-01hxyz"
base_url = f"https://{session_id}.clones.archal.ai/github/api"

req = urllib.request.Request(
    f"{base_url}/user/repos",
    method="POST",
    data=json.dumps({"name": "test-repo", "private": False}).encode(),
    headers={
        "x-route-authorization": f"Bearer {os.environ['ARCHAL_TOKEN']}",
        "Authorization": "Bearer ghp_AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTt",
        "Content-Type": "application/json",
    },
)
response = urllib.request.urlopen(req)
print(json.loads(response.read()))
```

## Example (AWS Lambda / Cloudflare Worker)

```ts theme={null}
export async function handler(event: { body: string }) {
  const sessionId = process.env.ARCHAL_SESSION_ID!;
  const baseUrl = `https://${sessionId}.clones.archal.ai/github/api`;

  const response = await fetch(`${baseUrl}/user/repos`, {
    method: 'POST',
    headers: {
      'x-route-authorization': `Bearer ${process.env.ARCHAL_TOKEN}`,
      'Authorization': 'Bearer ghp_AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTt',
      'Content-Type': 'application/json',
    },
    body: event.body,
    // Always cap clone requests with a timeout. Without it, a hung clone
    // worker keeps the Lambda invocation alive until the platform kills
    // it - burning budget and producing no useful failure signal.
    signal: AbortSignal.timeout(15000),
  });
  return { statusCode: response.status, body: await response.text() };
}
```

## Header semantics

Request handling order:

1. Validate `x-route-authorization: Bearer <archal-token>` against your session. Bad/expired/wrong-user tokens get a 401 or 403 here - the clone never sees them.
2. Strip route-control request headers.
3. Forward the request to the clone with your original service `Authorization`.

Your real Archal token only authenticates the outer hop. The clone sees
whatever you placed in the standard `Authorization` header.

## See also

* [Clone sessions (`archal clone`)](/guides/clone-sessions) - how to
  create, list, and stop clone sessions with the CLI.
* [Authentication](/guides/authentication) - bootstrap tokens for each clone.
