# Agent API

edwardtadros.com exposes a small public API so software agents can read the same dossier the on-page agent uses. Anonymous access needs no account or API key. Optional OAuth registration provides an elevated REST rate tier and credentials for the hosted MCP ask tool; limits and spend caps at the AI Gateway remain the outer wall.

- OpenAPI 3.1 spec: [https://edwardtadros.com/openapi.json](https://edwardtadros.com/openapi.json)
- Agent summary: [https://edwardtadros.com/llms.txt](https://edwardtadros.com/llms.txt) (full text: [/llms-full.txt](https://edwardtadros.com/llms-full.txt))
- Base URL: `https://edwardtadros.com/api/v1`

## When to use it

Use this API when a task needs facts about Edward Tadros or Proticom: vetting him for an AI enablement, transformation, or agentic-systems engagement; checking regulated-industry delivery history (financial services, health, FDA/ISO/HIPAA contexts); or drafting an introduction. It is not a general-purpose model endpoint and will decline off-topic questions.

## Authentication (optional)

REST search and read access is public at 20 requests per minute per client IP. Agents may register through [auth.md](https://edwardtadros.com/auth.md) and send an ES256 bearer token with the `dossier:ask` scope for an elevated 120 requests per minute REST tier. The same scope is required for the hosted MCP `ask_prospaero` tool at `https://edwardtadros.com/mcp`.

OAuth discovery is published at [https://edwardtadros.com/.well-known/oauth-authorization-server](https://edwardtadros.com/.well-known/oauth-authorization-server); RFC 9728 protected-resource metadata is at [https://edwardtadros.com/.well-known/oauth-protected-resource](https://edwardtadros.com/.well-known/oauth-protected-resource). Registration supports a one-time anonymous identity-assertion exchange and browser-mediated authorization code with required PKCE S256. Credentials expire after ten minutes and can be revoked at `https://edwardtadros.com/oauth/revoke`.

Every API response carries `RateLimit-Policy` and `RateLimit-Limit`; a `429` carries `Retry-After` in seconds. Missing, invalid, expired, or revoked credentials do not block otherwise-public REST reads; they use the anonymous tier.

## Versioning and deprecation

The API is versioned in the URL path. `/api/v1/` is the current stable surface; unversioned `/api/` paths are an alias for the current version and may move to a newer version in future, so integrations should pin `/api/v1/`. Every response also carries an `API-Version` header. If an endpoint is ever retired it will first return `Deprecation` and `Sunset` headers (RFC 9745 and RFC 8594) for at least 90 days, and the change will be noted in the OpenAPI `info.description`. Nothing is currently deprecated.

## Errors

Every 4xx and 5xx response is JSON with a machine-readable `error` code and a human-readable `message`:

```json
{ "error": "rate_limited", "message": "Per-IP agent rate limit reached; retry after 60 seconds." }
```

Codes: `bad_request`, `not_found`, `method_not_allowed`, `rate_limited`, `turnstile`.

## Endpoints

### POST /api/v1/ask

Ask PROSPÆRO a question about Edward. Returns a concise, sourced answer.

```bash
curl -s https://edwardtadros.com/api/v1/ask \
  -H 'content-type: application/json' \
  -d '{"question":"What has Edward delivered in regulated environments?"}'
```

`GET /api/v1/ask?q=...` is accepted for convenience. Questions are capped at 600 characters. When the model is unavailable the response still succeeds with `"live": false` and the top dossier excerpt as the answer.

### GET /api/v1/search?q=...&limit=5

Ranked dossier excerpts (lexical today; hybrid when vectors are published).

```bash
curl -s 'https://edwardtadros.com/api/v1/search?q=Wikimedia%20pipeline&limit=3'
```

### GET /api/v1/prices

Current per-million-token prices for the configured models and per-image prices, in USD.

### GET /api/v1/health

Worker health: document count, configured models, whether vectors and the gateway are live.

### GET /knowledge/index.json and /knowledge/{id}.md

The dossier manifest and each document as stripped markdown.

## MCP

The same dossier is served over the [Model Context Protocol](https://modelcontextprotocol.io) at `POST https://edwardtadros.com/mcp`, using the Streamable HTTP transport (protocol version `2025-06-18`). The server is stateless and sessionless: POST JSON-RPC 2.0 requests or batches and get `application/json` back (or a single-event SSE stream if you only accept `text/event-stream`). `GET` and `DELETE` return `405`; there is no server-initiated stream.

- Server Card: [https://edwardtadros.com/.well-known/mcp/server-card.json](https://edwardtadros.com/.well-known/mcp/server-card.json)
- Tools: `search_dossier` (ranked excerpts), `read_document` (full markdown by id), `ask_prospaero` (sourced synthesis).
- Resources: every dossier document at `edwardtadros://knowledge/<id>` as `text/markdown`.
- Authentication: `search_dossier` and `read_document` are open. `ask_prospaero` requires an optional OAuth bearer token with the `dossier:ask` scope; without one it returns `401` and a `WWW-Authenticate` challenge pointing at `/.well-known/oauth-protected-resource`.
- Tool calls share the same per-IP limit as the REST API (20 per minute); a limited call returns JSON-RPC error `-32000` with HTTP `429` and `Retry-After`.

```bash
curl -s https://edwardtadros.com/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_dossier","arguments":{"query":"Wikimedia pipeline","limit":3}}}'
```

## A2A

The same dossier is also reachable over the [Agent2Agent protocol](https://a2a-protocol.org) at `POST https://edwardtadros.com/a2a` (protocol version `0.3.0`, JSON-RPC 2.0 over HTTP, the `JSONRPC` transport). The agent is stateless: `message/send` runs the question and returns a `completed` task in the same response, so nothing is persisted and `tasks/get` always answers `-32001` (TaskNotFound). `message/stream`, `tasks/resubscribe`, and `tasks/list` answer `-32004` (UnsupportedOperation). `GET` returns `405`; an `A2A-Version` header other than `0.3` returns `400`.

- Agent Card: [https://edwardtadros.com/.well-known/agent-card.json](https://edwardtadros.com/.well-known/agent-card.json) (also served at `/.well-known/agent.json`)
- Skill: `ask-edward-tadros` — one question in, a sourced answer out. Text in; text plus a `data` part carrying `sources`, `model`, and `live`.
- Authentication: none. This is the same public path as `/api/v1/ask` and shares its per-IP limit (20 per minute); a limited call returns JSON-RPC error `-32000` with HTTP `429` and `Retry-After`.

```bash
curl -s https://edwardtadros.com/a2a \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"message/send","params":{"message":{"kind":"message","role":"user","messageId":"11111111-2222-3333-4444-555555555555","parts":[{"kind":"text","text":"What did Edward Tadros build at the Wikimedia Foundation?"}]}}}'
```

## Markdown negotiation

The home page, this page, `/about`, `/contact`, and `/privacy` return markdown when the request carries `Accept: text/markdown` (responses vary on `Accept`). Each also has a fixed `.md` URL. Unknown paths return a real `404` with a markdown body that links back here.

## Command line

There is no dedicated CLI; `curl` against the endpoints above is the supported way to script the API.
