Skip to main content

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.

  1. Sign in to the dashboard and choose the workspace the key should belong to.
  2. Open Settings, then the API keys tab.
  3. 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.

MethodPathScope
GET/api/v1/reportsreports:read
GET/api/v1/reports/{id}reports:read
GET/api/v1/reports/{id}/versionsreports:read
POST/api/v1/reports/{id}/runsruns: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.

ToolScopeSummary
list-reportsreports:readList reports in the calling key's workspace.
get-reportreports:readFetch one report with its latest version body.
list-versionsreports:readList versions of a report, freshest first.
get-versionreports:readFetch a single version body by id.
start-runruns:startTrigger 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: 1735689600

Once 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.