# Authentication, registration, rate limits

The public agent endpoints are intentionally permissive — anonymous calls work. Authentication is for **identity, attribution, and elevated rate limits**, not access.

## Tiers

| Tier | Identification | Rate limit | Conversion attribution |
|---|---|---|---|
| Anonymous | None | 12 req/min per IP | None |
| Identified | `X-Agent-Id` header | 30 req/min per agent ID | None |
| Registered | `X-Caller-Key` (caller_*) | 120 req/min per caller | ✅ Tied to caller |
| Elevated | Admin-granted | 240 req/min | ✅ |
| Partner | Admin-granted (revshare) | 600 req/min | ✅ + revshare |

Rate limits are enforced via a Postgres-backed counter (atomic UPSERT per 60-second window) — distributed across all serverless instances, not in-memory per-instance.

## Register a caller agent

```bash
curl -X POST https://agilityos.co/api/public/recommendation/register \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Personal Assistant",
    "contactEmail": "you@example.com",
    "description": "AI shopping helper for end users"
  }'
```

Returns:

```json
{
  "ok": true,
  "callerId": "my-personal-assistant-3f2a1c",
  "callerKey": "caller_2c5852cbfebda0...",
  "tier": "standard",
  "rateLimit": "120 requests / minute",
  "usage": {
    "headers": {
      "X-Agent-Id": "my-personal-assistant-3f2a1c",
      "X-Caller-Key": "caller_2c5852cbfebda0..."
    },
    "note": "Send both headers on every /recommend call. Treat the key like a password — never log or commit it."
  }
}
```

**The `callerKey` is shown once.** Store it securely. Lose it = re-register.

## Headers

| Header | Required? | Purpose |
|---|---|---|
| `X-Agent-Id` | Recommended | Identifies your agent for rate-limit accounting + attribution |
| `X-Caller-Key` | Optional | Elevated tier; ties conversions to your registered caller |
| `X-Caller-Mode` | Optional | `b2c` or `b2b` — shapes `pitchContext` tone |
| `X-Session-Id` | Optional | Reuse for follow-up calls; stable across the conversation |
| `X-Conversion-Token` | Required for /track-conversion | Issued by /recommend; HMAC-signed, 24h TTL |

## Request anatomy

A registered caller making a recommendation call:

```bash
curl -X POST https://agilityos.co/api/public/agents/savoir/recommend \
  -H "Content-Type: application/json" \
  -H "X-Agent-Id: my-personal-assistant-3f2a1c" \
  -H "X-Caller-Key: caller_xxxxxxx" \
  -H "X-Caller-Mode: b2c" \
  -d '{
    "goal": "...",
    "budget": 80,
    "maxResults": 3
  }'
```

## Rate-limit response headers

Every response includes:

```http
RateLimit-Limit: 120
RateLimit-Remaining: 117
RateLimit-Reset: 47
```

When exceeded:

```http
HTTP/2 429
{ "error": "RATE_LIMITED", "message": "120 requests/minute exceeded for tier..." }
```

## Conversion attribution & idempotency

`/track-conversion` requires a `conversionToken` issued by `/recommend`. The token:

- Is HMAC-SHA256 of `<sessionId>|<agentId>|<expiresAt>` signed with the server secret
- Expires 24 hours after issue
- Cannot be reused across agents (HMAC binds to agentId)

The DB has a unique index on `(sessionId, productId, callerAgentId)`. Duplicate conversion POSTs return `{ idempotent: true, conversionId: <existing> }` — never double-count.

## CORS

All public agent endpoints set:

```http
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Agent-Id, X-Caller-Key, X-Caller-Mode, X-Session-Id, X-Conversion-Token
```

Browser-based agents can call directly. Preflight (`OPTIONS`) handled.

## Tier upgrades

To upgrade from `standard` to `elevated` or `partner`, contact `hello@agilityautomations.com` with:

- Your registered `callerId`
- Expected sustained req/min
- Use case (consumer agent, B2B procurement, MCP client, etc.)
- Conversion volume estimate

Partner tier comes with a revshare structure — terms negotiated per partner.
