API v1

Stable, versioned API for submitting scans, fetching reports, and managing webhooks. Source of truth: the OpenAPI 3.1 document at /docs/api/openapi.json.

Authentication

Every /api/v1/* call requires an API key. Create one at Account → API keys and include it in every request:

Authorization: Bearer ar_live_<32 hex>

Keys are SHA-256 hashed at rest. We display the raw key exactly once at creation. Lost keys can be revoked and replaced.

Response envelope

Every successful response is wrapped in a stable envelope:

{
  "data": { ... endpoint-specific shape ... },
  "meta": {
    "requestId": "req_…",
    "generatedAt": "2026-04-27T12:34:56.000Z"
  }
}

Send your own correlation id by setting X-AgentReserve-Request-Id on the request — we echo it back in meta.requestId and the response header.

Errors share a parallel shape:

{
  "error": { "code": "RATE_LIMITED", "message": "…" },
  "meta": { "requestId": "req_…", "generatedAt": "…" }
}

Rate limits

  • free: 10 scans/hr · 100 scans/month · max 100 results per directory page.
  • pro: 60 scans/hr · 5000 scans/month · max 500 results per directory page.
  • enterprise: unlimited scans/hr · unlimited scans/month · max 500 results per directory page.

Exceeded limits return HTTP 429 with Retry-After in seconds.

Submit a scan

curl -X POST https://agentreserve.dev/api/v1/scans \
  -H "Authorization: Bearer ar_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://mcp.example.com" }'

Response shape:

{
  "data": {
    "scanId": "scn_…",
    "status": "SUCCEEDED",
    "cached": false,
    "report": {
      "publicSlug": "abcdef1234",
      "score": 88,
      "grade": "B",
      "verdict": "allow",
      "riskLevel": "low",
      "topReasons": [...],
      "generatedAt": "2026-04-27T…"
    },
    "_links": {
      "self": "https://agentreserve.dev/api/v1/scans/scn_…",
      "report": "https://agentreserve.dev/reports/abcdef1234",
      "export_json": "https://agentreserve.dev/api/v1/scans/scn_…/export?format=json",
      "export_csv": "https://agentreserve.dev/api/v1/scans/scn_…/export?format=csv",
      "export_pdf": "https://agentreserve.dev/api/v1/scans/scn_…/export?format=pdf"
    }
  },
  "meta": { "requestId": "req_…", "generatedAt": "…" }
}

Get a scan

curl https://agentreserve.dev/api/v1/scans/scn_… \
  -H "Authorization: Bearer ar_live_…"

Returns the scan + its scored Report (when finished). Stored probe payloads are deliberately omitted from the v1 surface — they are internal storage and not part of the stable contract.

List servers

curl 'https://agentreserve.dev/api/v1/servers?verdict=allow&page=1&pageSize=50' \
  -H "Authorization: Bearer ar_live_…"

Filters: verdict, risk, grade, dcr=yes|no, recency=30, q=hostname. Sort: score (default), recency, popularity.

Free tier: pageSize capped at 100. Pro+ may request up to 500 per page.

Get a server

curl https://agentreserve.dev/api/v1/servers/mcp.example.com \
  -H "Authorization: Bearer ar_live_…"

Exports

# JSON (free)
curl https://agentreserve.dev/api/v1/scans/scn_…/export?format=json \
  -H "Authorization: Bearer ar_live_…"

# CSV (free) — one row per RuleResult
curl https://agentreserve.dev/api/v1/scans/scn_…/export?format=csv \
  -H "Authorization: Bearer ar_live_…" \
  -o report.csv

# PDF (Pro+) — single-page summary card
curl https://agentreserve.dev/api/v1/scans/scn_…/export?format=pdf \
  -H "Authorization: Bearer ar_live_…" \
  -o report.pdf

Webhooks

Manage subscriptions at Account → Webhooks. Each delivery is a POST with this body:

{
  "id": "whd_…",            // delivery id, useful for receiver-side dedupe
  "type": "scan.succeeded",
  "timestamp": "2026-04-27T…",
  "data": {
    "scanId": "scn_…",
    "hostname": "mcp.example.com",
    "serverUrl": "https://mcp.example.com",
    "publicSlug": "abcdef1234",
    "score": 88,
    "grade": "B",
    "verdict": "allow",
    "riskLevel": "low",
    "reportUrl": "https://agentreserve.dev/reports/abcdef1234"
  }
}

Available events:

  • scan.succeeded
  • scan.failed
  • scan.score_changed
  • scan.verdict_changed

Verifying signatures

Every delivery includes X-AgentReserve-Signature: t=<ts>,v1=<hex>. Re-compute on the receiver:

hmac_sha256(secret, "{ts}.{rawRequestBody}") === sig

Reject signatures whose ts drifts more than 5 minutes from your wall clock.

Retries

Non-2xx responses retry on backoff: 1m, 5m, 30m, 2h. After 5 consecutive delivery failures we mark the webhook auto-disabled and email the owner. Re-enable from the UI; the failure counter resets.

Idempotency

Receivers should be idempotent on data.scanId + type, or on the top-level id (delivery id). Our retries may deliver the same body twice if the receiver returned 5xx after processing.

Versioning

The /api/v1/* response shapes are a public contract. We will not break them without shipping /api/v2/* in parallel and offering a public deprecation window. Adding new fields to existing responses is not a breaking change — clients should ignore unknown fields.