---
title: "Connect Your App with the SDK"
url: "https://docs.caracal.run/tutorials/connect-an-agent/"
markdown_url: "https://docs.caracal.run/markdown/tutorials/connect-an-agent.md"
description: "Wire app code through the SDK so protected calls carry Caracal authority and audit context."
page_type: "workflow"
concepts: []
requires: []
---

# Connect Your App with the SDK

Canonical URL: https://docs.caracal.run/tutorials/connect-an-agent/
Markdown URL: https://docs.caracal.run/markdown/tutorials/connect-an-agent.md
Description: Wire app code through the SDK so protected calls carry Caracal authority and audit context.
Page type: workflow
Concepts: none
Requires: none

---

This tutorial connects application code to Caracal. The examples use a generated runtime profile, open one agent session, and call a Gateway-protected resource.

## 1. Confirm the App and Profile

In the Console, confirm that the selected zone has:

- one confidential agent app;
- a client secret stored in the secret file named by the generated runtime profile;
- one protected resource with an active policy;
- a Gateway URL and resource binding when using Gateway-routed HTTP.

Set the profile path and the resource target for the examples:

```sh
export CARACAL_CONFIG=/path/to/caracal.toml
export CARACAL_RESOURCE_ID=resource://billing-api
export CARACAL_RESOURCE_PATH=/charge
```

## 2. Connect with the SDK

The client constructor is the standard entry point. It loads the generated profile when available, then falls back to environment configuration. See [Configuration Order](/reference/config-precedence/) for the exact resolution order.

### TypeScript

```sh
npm install @caracalai/sdk
```

```typescript
import { Caracal } from "@caracalai/sdk";

const caracal = new Caracal();
const resourceId = process.env.CARACAL_RESOURCE_ID!;
const resourcePath = process.env.CARACAL_RESOURCE_PATH!;

await caracal.spawn(async () => {
  const request = caracal.gatewayRequest(resourceId, resourcePath);
  const response = await caracal.transport()(request.url, {
    method: "POST",
    headers: { ...request.headers, "Content-Type": "application/json" },
    body: JSON.stringify({ amount: 1200 }),
  });

  console.log(await response.text());
});
```

### Python

```sh
pip install caracalai-sdk
```

```python
import asyncio
import os

from caracalai import Caracal

caracal = Caracal()
resource_id = os.environ["CARACAL_RESOURCE_ID"]
resource_path = os.environ["CARACAL_RESOURCE_PATH"]

async def main():
    async with caracal.spawn():
        request = caracal.gateway_request(resource_id, resource_path)
        async with caracal.transport() as client:
            response = await client.post(
                request.url,
                headers=request.headers,
                json={"amount": 1200},
            )
            print(response.text)

asyncio.run(main())
```

### Go

```sh
go get github.com/garudex-labs/caracal/packages/sdk/go
```

```go
c, err := caracal.New()
if err != nil {
    panic(err)
}

resourceID := os.Getenv("CARACAL_RESOURCE_ID")
resourcePath := os.Getenv("CARACAL_RESOURCE_PATH")

err = c.Spawn(context.Background(), func(ctx context.Context) error {
    target, err := c.GatewayRequest(resourceID, resourcePath)
    if err != nil {
        return err
    }

    req, err := http.NewRequestWithContext(ctx, http.MethodPost, target.URL, strings.NewReader(`{"amount":1200}`))
    if err != nil {
        return err
    }
    req.Header = target.Header.Clone()
    req.Header.Set("Content-Type", "application/json")

    resp, err := c.Transport(nil).Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    return nil
})
if err != nil {
    panic(err)
}
```

## 3. Understand the Runtime Behavior

| SDK call | What happens |
| --- | --- |
| `new Caracal()` / `Caracal()` / `New()` | Loads configuration and prepares app-secret token exchange. |
| `spawn()` / `Spawn()` | Creates an agent session, binds context, and tears it down after the block returns. |
| `gatewayRequest()` / `GatewayRequest()` | Builds the Gateway URL and `X-Caracal-Resource` header. |
| `transport()` / `Transport()` | Injects Authorization, trace, baggage, and Gateway routing behavior. |

`spawn()` is the session boundary. If the function exits or raises, the SDK terminates the agent session and active authority for that session is revoked.

One application credential backs every agent session you spawn. A workload registers one application, then opens an agent session per unit of work — you do not register a new application per agent. See [Should I create one application per agent?](/reference/faq/#faq-006).

If your app spawns child agents or delegates work, continue to [Implement Multi-Agent Delegation](/guides/delegation/) after this tutorial path.

## 4. Verify Audit

Open Console **audit** and find the request. A complete Gateway-routed call produces:

1. an authorization event from STS;
2. an action-result event from Gateway.

Open **explain** with the request ID to confirm the application, resource, scopes, policy, policy set version, and decision.

## Next Step

Continue with [Trace One Protected Request](../inspect-a-run/) to investigate the request you just made.
