# A2A Delegation

Agent-to-Agent (A2A) delegation lets one agent grant another agent a subset of its OAuth 2.0
scopes for a defined period. This is the foundation for building secure multi-agent pipelines
where an orchestrator agent coordinates specialist sub-agents.

---

## Prerequisites

- A running AgentIdP instance
- Two registered agents: the delegator (has a Bearer token) and the delegatee (knows its
  `agentId`)
- The delegator's scopes must be a superset of the scopes it wants to delegate

---

## How delegation works

```
Delegator agent                            Delegatee agent
     |                                           |
     |-- POST /oauth2/token/delegate ----------->|  (creates chain server-side)
     |<-- { delegationToken, chainId, scopes } --|
     |                                           |
     |-- passes delegationToken out-of-band ---->|
     |                                           |
     |                   POST /oauth2/token/verify-delegation
     |                   <-- { valid: true, scopes, expiresAt }
     |                                           |
     |           (optional) DELETE /oauth2/token/delegate/{chainId}
```

---

## Step 1 — Create a delegation chain

The delegator agent creates the chain by specifying the delegatee's `agentId`, the scopes to
delegate (must be a strict subset of the delegator's own scopes), and the TTL in seconds.

```bash
curl -s -X POST http://localhost:3000/api/v1/oauth2/token/delegate \
  -H "Authorization: Bearer $DELEGATOR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "delegateeAgentId": "'$DELEGATEE_AGENT_ID'",
    "scopes": ["agents:read"],
    "ttlSeconds": 3600
  }' | jq .
```

Response (`201 Created`):

```json
{
  "delegationToken": "sa_del_a1b2c3d4e5f6...",
  "chainId": "d4e5f6a7-b8c9-0123-def0-123456789abc",
  "delegatorAgentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "delegateeAgentId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "scopes": ["agents:read"],
  "expiresAt": "2026-04-04T10:00:00.000Z"
}
```

Save the `delegationToken` and `chainId`:

```bash
export DELEGATION_TOKEN="sa_del_a1b2c3d4e5f6..."
export CHAIN_ID="d4e5f6a7-b8c9-0123-def0-123456789abc"
```

**TTL constraints**: minimum 60 seconds, maximum 86400 seconds (24 hours). Choose the minimum
TTL that covers the delegatee's task.

---

## Step 2 — Pass the delegation token to the delegatee

Pass `DELEGATION_TOKEN` to the delegatee agent out-of-band. This can be via a shared queue,
a direct API call to the sub-agent, or any other channel. The token is a signed opaque string —
do not parse it; treat it as an opaque credential.

---

## Step 3 — Verify the delegation token

The delegatee (or any agent checking the delegation) calls the verify endpoint. This confirms
the chain is valid and not expired or revoked.

```bash
curl -s -X POST http://localhost:3000/api/v1/oauth2/token/verify-delegation \
  -H "Authorization: Bearer $DELEGATEE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "delegationToken": "'$DELEGATION_TOKEN'" }' | jq .
```

Response (`200 OK` — valid delegation):

```json
{
  "valid": true,
  "chainId": "d4e5f6a7-b8c9-0123-def0-123456789abc",
  "delegatorAgentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "delegateeAgentId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "scopes": ["agents:read"],
  "issuedAt": "2026-04-04T09:00:00.000Z",
  "expiresAt": "2026-04-04T10:00:00.000Z",
  "revokedAt": null
}
```

Response (`200 OK` — expired delegation):

```json
{
  "valid": false,
  "chainId": "d4e5f6a7-b8c9-0123-def0-123456789abc",
  "delegatorAgentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "delegateeAgentId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "scopes": ["agents:read"],
  "issuedAt": "2026-04-03T09:00:00.000Z",
  "expiresAt": "2026-04-03T10:00:00.000Z",
  "revokedAt": null
}
```

> The verify endpoint always returns `200 OK`. Check the `valid` field — it is never an error
> response for an expired or revoked token.

---

## Step 4 — (Optional) Revoke the delegation early

If the delegatee has completed its task and you want to revoke the delegation before it expires,
the delegator calls:

```bash
curl -s -X DELETE "http://localhost:3000/api/v1/oauth2/token/delegate/$CHAIN_ID" \
  -H "Authorization: Bearer $DELEGATOR_TOKEN" \
  -o /dev/null -w "%{http_code}\n"
```

Expected response: `204` (no body).

After revocation, verify requests for this chain return `{ "valid": false, "revokedAt": "<timestamp>" }`.

---

## Scope rules

- Delegated scopes must be a strict subset of the delegator's own token scopes
- You cannot delegate scopes you do not have
- You cannot delegate to yourself (delegateeAgentId must differ from delegatorAgentId)
- Delegation is not transitive — a delegatee cannot re-delegate to a third agent

---

## Common errors

### `400 VALIDATION_ERROR` — scope not a subset

The delegator attempted to delegate a scope it does not hold. Check `GET /api/v1/token/introspect`
to confirm which scopes your token carries.

### `400 VALIDATION_ERROR` — ttlSeconds out of range

Min: 60, Max: 86400. Values outside this range return a validation error.
