# Sift API

This service accepts AgentOnboard. Verified agent requests are authorized against the
account the verified email maps to.

## Audience

sift.agentonboard.xyz

    get_token(domain: "sift.agentonboard.xyz")

Send the token on every request as `Authorization: Bearer <token>`.

## Endpoints

### `POST /api/v1/agents/signup`

Create the calling account. This is the one endpoint that may mint an account: an agent that
got `403 ACCOUNT_REQUIRED` elsewhere calls this with the same token, then retries its original
request. Calling it for an email that already has an account signs in instead — no duplicate.

- Request: no body
- Success: `201` with `{ "user": string, "created": boolean }`
- Required header: `Authorization: Bearer <token>`

### `GET /api/v1/agents/sites`

List all websites accessible to the calling account. Never returns another account's sites.

- Request: no body
- Success: `200` with `{ "sites": Array<{ "id": string, "domain": string, "timezone": string, "isPublic": boolean, "role": string }> }`
- Required header: `Authorization: Bearer <token>`

### `POST /api/v1/agents/sites`

Register a new website for analytics tracking.

- Request: `{ "domain": string, "timezone"?: string }`
- Success: `201` with `{ "site": { "id": string, "domain": string, "timezone": string, "isPublic": boolean }, "snippet": string }`
- Required header: `Authorization: Bearer <token>`

### `GET /api/v1/agents/sites/{domain}`

Get website details, user role, tracking verification status, and snippet.

- Request: no body
- Success: `200` with `{ "site": { "id": string, "domain": string, "timezone": string, "isPublic": boolean }, "role": string, "verified": boolean, "snippet": string }`
- Required header: `Authorization: Bearer <token>`

### `PATCH /api/v1/agents/sites/{domain}`

Update settings for a website (domain, timezone, or public visibility).

- Request: `{ "domain"?: string, "timezone"?: string, "isPublic"?: boolean }`
- Success: `200` with `{ "ok": true, "site": { "id": string, "domain": string, "timezone": string, "isPublic": boolean } }`
- Required header: `Authorization: Bearer <token>`

### `DELETE /api/v1/agents/sites/{domain}`

Delete a website and purge all associated analytics data. Requires owner role.

- Request: no body
- Success: `200` with `{ "ok": true }`
- Required header: `Authorization: Bearer <token>`

### `GET /api/v1/agents/stats/{domain}`

Get analytics stats, timeseries, and breakdowns (top pages, sources, channels, countries, devices, browsers, operating systems) for a website.

- Request: optional query `period` (`today`, `yesterday`, `7d`, `30d`, `month`, `last-month`), or custom `from` and `to` ISO timestamps
- Success: `200` with `{ "ok": true, "domain": string, "period": string, "stats": { "visitors": number, "pageviews": number, "bounceRate": number, "visitDuration": number }, "series": Array<{ "timestamp": string, "visitors": number, "pageviews": number }>, "breakdowns": object }`
- Required header: `Authorization: Bearer <token>`

### `GET /api/v1/agents/stats/{domain}/realtime`

Get current realtime visitor count, active pages, and countries in the last 5 minutes.

- Request: no body
- Success: `200` with `{ "ok": true, "domain": string, "data": { "visitors": number, "pageviews": number, "pages": Array<{ "path": string, "visitors": number }>, "countries": Array<{ "country": string, "visitors": number }> } }`
- Required header: `Authorization: Bearer <token>`

## When a request is rejected

| Status | `code` | What to do |
|---|---|---|
| 401 | any | The token was rejected. Call `get_token` tool for `sift.agentonboard.xyz` again and retry **once**. If it fails again, stop and tell the human — do not retry in a loop. |
| 403 | `ACCOUNT_REQUIRED` | The identity is genuine but has no Sift account. Create one at `POST /api/v1/agents/signup` with the same token, then retry the original request **once**. **Retrying the original without that will never work.** |
| 500 | `RESOLVER_ERROR` or `CONFIG_ERROR` | Our account lookup failed, or our configuration is wrong. Retry after a short delay. Do not re-authenticate; the token is fine. |
