Developers
API and MCP
Deeperer ships a public REST API and an MCP endpoint on every tier, free included. The two surfaces share state, a key, and the same run path. The OpenAPI document at /api/v1/openapi is the canonical machine-readable contract.
Get an API key
API keys belong to a workspace. Anyone with admin access to a workspace can create or revoke them from the dashboard.
- Sign in to the dashboard and choose the workspace the key should belong to.
- Open Settings, then the API keys tab.
- Create a key. The full token is shown once at creation; copy it then. Lose the token, revoke and reissue.
Keys ship a structured prefix that public secret scanners recognise, so leaked tokens posted to a public Git repository are detected automatically. Keys store as a hash on the server; a revoked key stops authorising the next request.
Authentication
Every request carries a workspace-scoped API key in the Authorization header. Keys are created from the dashboard Settings tab and revoked the same way. The foundation ships static API-key authentication only; an OAuth flow is the subject of a future authorization spec and is not part of foundation.
Scope vocabulary
reports:read- read reports, versions, and version bodies.reports:write- reserved for future writer endpoints. No public route consumes it in foundation.runs:start- trigger a new generation run on an existing report.
Missing or revoked key
A request without a valid key returns 401 with a WWW-Authenticate header per RFC 9728. The header carries the realm and the scope the requested route requires, so a client can request the right scope on retry.
HTTP/1.1 401 Unauthorized
Content-Type: application/json
WWW-Authenticate: Bearer realm="deeperer-api-v1", scope="reports:read"
{ "error": "Authentication required." }A key whose scopes do not cover the requested route returns 403 with the same header shape and error="insufficient_scope".
HTTP/1.1 403 Forbidden
Content-Type: application/json
WWW-Authenticate: Bearer realm="deeperer-api-v1", scope="runs:start", error="insufficient_scope"
{ "error": "Insufficient scope." }MCP discovery
The MCP endpoint at /api/mcp uses the realm string deeperer-mcp and otherwise follows the same 401 / 403 shape. Foundation does not serve a /.well-known/oauth-protected-resource document; RFC 9728 requires that document to advertise an authorization server, and the foundation has none.
REST endpoints
Four public routes ship under /api/v1. The full machine-readable surface lives at /api/v1/openapi as an OpenAPI 3.1 document, including the full status code union per route. Administrative paths are never enumerated there.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/reports | reports:read |
| GET | /api/v1/reports/{id} | reports:read |
| GET | /api/v1/reports/{id}/versions | reports:read |
| POST | /api/v1/reports/{id}/runs | runs:start |
List reports
List reports in the calling key's workspace. Cursor pagination via limit (1 to 100) and cursor.
curl -H "Authorization: Bearer deep_sk_live_..." \
"https://deeperer.com/api/v1/reports?limit=20"Fetch one report
Fetch a report with its latest version body.
curl -H "Authorization: Bearer deep_sk_live_..." \
"https://deeperer.com/api/v1/reports/<report-uuid>"List versions
List versions of a report, freshest first.
curl -H "Authorization: Bearer deep_sk_live_..." \
"https://deeperer.com/api/v1/reports/<report-uuid>/versions"Trigger a run
Trigger a new generation run on a report in a terminal state. Requires an Idempotency-Key header; a retry with the same key returns the original response. The response is 202 with { report_id, slug, state: "generating" }; poll the report read endpoint for completion.
curl -X POST \
-H "Authorization: Bearer deep_sk_live_..." \
-H "Idempotency-Key: a-stable-string-the-client-controls" \
"https://deeperer.com/api/v1/reports/<report-uuid>/runs"MCP endpoint
The Model Context Protocol endpoint at /api/mcp exposes the same primitives over Streamable HTTP, the canonical MCP TypeScript SDK transport. Tool names are stable across foundation; schema evolves additively and breaking changes ship as side-by-side parallel tools rather than version suffixes.
| Tool | Scope | Summary |
|---|---|---|
| list-reports | reports:read | List reports in the calling key's workspace. |
| get-report | reports:read | Fetch one report with its latest version body. |
| list-versions | reports:read | List versions of a report, freshest first. |
| get-version | reports:read | Fetch a single version body by id. |
| start-run | runs:start | Trigger a new generation run; pass a stable idempotency_key to dedupe retries. |
Discover the tool set
curl -X POST \
-H "Authorization: Bearer deep_sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }' \
"https://deeperer.com/api/mcp"Trigger a run from MCP
The start-run tool routes through the same kernel the REST run endpoint uses. Both paths converge on one set of state transitions, so a client can mix transports without splitting state.
curl -X POST \
-H "Authorization: Bearer deep_sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "start-run",
"arguments": {
"id": "<report-uuid>",
"idempotency_key": "a-stable-string-the-client-controls"
}
}
}' \
"https://deeperer.com/api/mcp"Rate limits
Every authenticated request runs against two caps in series. The first is per IP and bounds the rate from a single network origin. The second is per API key. A request fails the slower of the two; rotating keys does not bypass the per-IP cap.
Successful responses carry the three standard headers. The counter lives in a durable cache, not a database hot row.
X-RateLimit-Limit- the ceiling for the active window.X-RateLimit-Remaining- requests left before the cap engages.X-RateLimit-Reset- Unix epoch seconds when the window resets.
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1735689600Once the cap engages, the response is 429 with the same three headers; the retry hint is X-RateLimit-Reset (Unix epoch seconds when the window resets). The body carries the scope that engaged the cap so a client can attribute the rejection to its key or its network origin.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1735689600
{ "error": "Too many requests", "scope": "per-IP" }Questions reach the team at hello@deeperer.com. Back to landing.