---
title: Implement Multi-Agent Delegation
description: Open child agent sessions, attach typed constraints, traverse the delegation graph, and trigger cascade revocation using the SDK and CLI.
pageType: workflow
---

Delegation transfers authority from one agent session to another. The Coordinator records a directed edge in the delegation graph. The STS validates the edge on every token exchange. Revoking an edge cascades automatically to all downstream agents that received authority through it.

## Prerequisites

- The Caracal SDK installed in the orchestrating service (TypeScript, Python, or Go).
- The Coordinator URL reachable at `CARACAL_COORDINATOR_URL`.
- An active policy that allows delegation exchanges.

## Spawn a root agent session

Every execution begins with `spawn()`. The SDK opens a session on the Coordinator and binds it to the current async context:

**TypeScript:**
```typescript
import { Caracal, AgentKind } from '@caracalai/sdk';

const caracal = Caracal.fromEnv();

await caracal.spawn(async () => {
  const ctx = caracal.current()!;
  console.log('root session:', ctx.agentSessionId);
  // depth = 0
}, { kind: AgentKind.Instance, ttlSeconds: 3600 });
```

**Python:**
```python
from caracalai_sdk import Caracal
from caracalai_sdk.advanced import AgentKind

caracal = Caracal.from_env()

async with caracal.spawn(kind=AgentKind.INSTANCE, ttl_seconds=3600) as ctx:
    print('root session:', ctx.agent_session_id)
```

**Go:**
```go
err = client.Spawn(ctx, func(ctx context.Context) error {
    cc, _ := sdk.Current(ctx)
    fmt.Printf("root session: %s\n", cc.AgentSessionID)
    return nil
}, sdk.SpawnOptions{Kind: sdk.KindInstance, TTLSeconds: 3600})
```

## Spawn a child agent (nested spawn)

Call `spawn()` again inside the first callback. The Coordinator records the parent-child relationship:

**TypeScript:**
```typescript
await caracal.spawn(async () => {
  const parentCtx = caracal.current()!;

  // Second spawn inside the first — becomes a child
  await caracal.spawn(async () => {
    const childCtx = caracal.current()!;
    console.log('child session:', childCtx.agentSessionId);
    // depth = 1, parent = parentCtx.agentSessionId
  }, { kind: AgentKind.Ephemeral });
});
```

Session limits enforced by the Coordinator:

| Limit | Value |
| ----- | ----- |
| Max depth | 10 |
| Max children per session | 10 |
| Max active sessions per zone per application | 50 |
| Max active sessions per application across zones | 200 |

## Delegate authority to a target session

`delegate()` creates a directed authority edge from the current session to the target session. The target can then exchange that edge for a mandate scoped to the delegated permissions.

**TypeScript:**
```typescript
await caracal.spawn(async () => {
  const sourceCtx = caracal.current()!;

  // targetAgentSessionId comes from the spawned child or another agent
  await caracal.delegate(
    {
      to: targetAgentSessionId,
      toApplicationId: 'payments-worker',
      scopes: ['payment:submit', 'payment:read'],
      ttlSeconds: 300,
    },
    async () => {
      const ctx = caracal.current()!;
      console.log('delegation edge:', ctx.delegationEdgeId);
      // The delegated context is now active
    }
  );
});
```

**Python:**
```python
from caracalai_sdk import DelegationConstraints

async with caracal.spawn() as source:
    async with caracal.delegate(
        to=target_agent_session_id,
        to_application_id='payments-worker',
        scopes=['payment:submit', 'payment:read'],
        constraints=DelegationConstraints(
            resources=['resource://payments'],
            max_depth=2,
        ),
        ttl_seconds=300,
    ) as delegated:
        print('edge:', delegated.delegation_edge_id)
```

**Go:**
```go
err = client.Spawn(ctx, func(ctx context.Context) error {
    return client.Delegate(ctx, sdk.DelegateOptions{
        To:              targetAgentSessionID,
        ToApplicationID: "payments-worker",
        Scopes:          []string{"payment:submit", "payment:read"},
        Constraints: &sdk.DelegationConstraints{
            Resources: []string{"resource://payments"},
            MaxDepth:  2,
        },
        TTLSeconds: 300,
    }, func(ctx context.Context) error {
        cc, _ := sdk.Current(ctx)
        fmt.Printf("edge: %s\n", cc.DelegationEdgeID)
        return nil
    })
})
```

## Attach constraints to a delegation edge

`DelegationConstraints` / `constraints_json` narrows what the target can do:

| Constraint | Effect |
| ---------- | ------ |
| `resources` | Restricts the edge to specific resource identifiers |
| `actions` | Restricts the allowed action strings |
| `max_depth` | Caps how many more hops authority can flow through from the target |
| `expires_at` | Hard expiry for the edge independent of the session TTL |

The STS validates all constraints on each exchange. A constraint violation fails the exchange before OPA is evaluated.

## Read the delegation graph from the CLI

Inspect active sessions and their delegation edges:

```bash
# List all active agent sessions in the zone
caracal agent list --json

# Show the child sessions of a specific session
caracal agent tree sess-abc123

# Show inbound edges (authority flowing IN to the session)
caracal delegation inbound sess-abc123

# Show outbound edges (authority flowing OUT from the session)
caracal delegation outbound sess-abc123

# Walk the full delegation chain from a specific edge
caracal delegation traverse edge-def456
```

## Revoke a delegation edge

Revoking an edge cascades forward through the entire subgraph. All sessions that received authority through the revoked edge are terminated:

```bash
caracal delegation revoke edge-def456
```

The Coordinator runs the cascade revocation CTE and publishes a `caracal.sessions.revoke` event for every affected session. The Gateway blocks requests from affected sessions within seconds.

Terminate a specific agent session and all its descendants:

```bash
caracal agent terminate sess-abc123
```

## Suspend and resume

Suspension affects the entire subtree. Active mandates for suspended sessions are still valid until their 15-minute TTL expires, but new exchanges are blocked:

```bash
caracal agent suspend sess-abc123   # Block new exchanges for entire subtree
caracal agent resume sess-abc123    # Re-enable the subtree
```

## Verify a mandate includes the expected chain

On the receiving side, check that the delegation chain contains the expected orchestrator application:

**TypeScript:**
```typescript
import { verify } from '@caracalai/identity';

const claims = await verify(token, {
  issuer: process.env.CARACAL_STS_URL!,
  audience: 'resource://payments',
  requireChainContains: ['orchestrator-app'],
});
```

**Go:**
```go
claims, err := identity.Verify(token, identity.Config{
    Issuer:               "http://sts:8080",
    Audience:             "resource://payments",
    RequireChainContains: []string{"orchestrator-app"},
})
```

## What to read next

- [Author a Rego Policy](./author-policy/) — `input.delegation_edge` and `input.context.delegation_edge_id` are available in policy
- [Tail and Query the Audit Stream](./audit-stream/) — `determining_policies` and `diagnostics` per delegation exchange
- [Step-Up Re-Authentication](./step-up/) — combine delegation with step-up for high-value actions
