For agents and developers

API

Everything the dashboard does, the API does too. Four endpoints, one bearer token, an OpenAPI spec at /api/v1/openapi.json. This page as Markdown: /docs/api.md.

Authentication

You create API keys in the dashboard under Account. The plaintext is shown exactly once — only a SHA-256 hash is stored.

curl https://ai-agent-ready.com/api/v1/cases \
  -H "Authorization: Bearer ark_live_..."

Conventions

Endpoints

GET/api/v1/casesTool: list_casesScope: monitor:read

Returns the cases belonging to the account behind the API key, including the current overall score and the time of the last run. A key carrying the monitor:admin scope reads every tenant instead; in that case each entry also carries owner_email and the response is marked scope: all_tenants.

  • q (query) — Filters over case name and domain.
Response
{
  "cases": [
    {
      "id": "4f1e9b2c-0d8a-4f7b-9d21-1b7c0c9a55e1",
      "user_id": "6b3d1a70-25c8-4f0e-9a3d-77a1f2b4c8d9",
      "name": "Example CRM",
      "domain": "example-crm.com",
      "category": "CRM software for trade businesses",
      "competitors": [
        "Craftnote",
        "Meisterwerk",
        "ToolTime"
      ],
      "status": "active",
      "latest_score": 44,
      "last_run_at": "2026-08-01T03:12:00.000Z"
    }
  ],
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 401 unauthorized — Bearer token missing or unknown.
  • 403 no_account — No Monitor account exists for this key.
GET/api/v1/cases/{id}Tool: get_caseScope: monitor:read

Everything the monitor knows about the brand itself: fact sheet, ICP, competitors, use cases, region and languages. The fact sheet is the ground truth the accuracy scoring runs against, which makes this the endpoint an agent reads before writing or answering about the customer. Resolves across all tenants for a key with the monitor:admin scope, otherwise only within the key holder's own cases.

  • id (path, required) — Case id (UUID).
Response
{
  "case": {
    "id": "4f1e9b2c-0d8a-4f7b-9d21-1b7c0c9a55e1",
    "name": "Example CRM",
    "domain": "example-crm.com",
    "category": "CRM software for trade businesses",
    "icp": "Owners of trade businesses with 5 to 50 employees",
    "competitors": [
      "Craftnote",
      "Meisterwerk",
      "ToolTime"
    ],
    "use_cases": [
      "Quoting",
      "Time tracking",
      "Invoicing"
    ],
    "region": "Germany",
    "languages": [
      "en"
    ],
    "fact_sheet": {
      "Price": "From 29 EUR per user and month",
      "Trial": "30 days, no credit card"
    },
    "status": "active",
    "created_at": "2026-07-14T08:02:00.000Z",
    "updated_at": "2026-08-02T11:20:00.000Z"
  },
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 401 unauthorized — Bearer token missing or unknown.
  • 403 no_account — No Monitor account exists for this key.
  • 404 not_found — No case with this id belongs to the key holder.
GET/api/v1/cases/{id}/summaryTool: get_case_summaryScope: monitor:read

The result of the most recent finished run: overall score, score per model, the three score dimensions, share of answer against every competitor, top gaps, the domains the models cited as sources, and the delta to the previous run.

  • id (path, required) — Case id (UUID).
Response
{
  "case": {
    "id": "4f1e9b2c-0d8a-4f7b-9d21-1b7c0c9a55e1",
    "name": "Example CRM"
  },
  "run": {
    "id": "9a0c...",
    "label": "Aug 26",
    "kind": "monthly",
    "finished_at": "2026-08-01T03:41:00.000Z"
  },
  "summary": {
    "total_score": 44,
    "per_model": {
      "openai": 48,
      "anthropic": 39,
      "perplexity": 51,
      "google": 38
    },
    "dimensions": {
      "visibility": 1.1,
      "accuracy": 1.4,
      "sentiment": 0.2
    },
    "share_of_answer": [
      {
        "name": "Craftnote",
        "share": 0.8,
        "mentions": 7,
        "judge_mentions": 1,
        "total": 10,
        "is_self": false
      },
      {
        "name": "Example CRM",
        "share": 0.4,
        "mentions": 4,
        "judge_mentions": 0,
        "total": 10,
        "is_self": true
      }
    ],
    "top_gaps": [
      "No prices are named",
      "Nothing is said about a trial"
    ],
    "delta": {
      "total_score": -6,
      "per_model": {
        "openai": -4
      },
      "previous_run_id": "7c21..."
    },
    "top_cited_domains": [
      {
        "domain": "craftnote.de",
        "citations": 9,
        "answers": 6,
        "share": 0.75,
        "is_self": false,
        "competitor": "Craftnote",
        "by_model": {
          "perplexity": 4,
          "openai": 2
        },
        "by_block": {
          "A": 2,
          "E": 4
        }
      },
      {
        "domain": "example-crm.com",
        "citations": 3,
        "answers": 2,
        "share": 0.25,
        "is_self": true,
        "competitor": null,
        "by_model": {
          "perplexity": 2
        },
        "by_block": {
          "B": 2
        }
      }
    ],
    "own_citation_rate": 0.25,
    "competitor_citation_rate": {
      "Craftnote": 0.75,
      "Meisterwerk": 0,
      "ToolTime": 0
    },
    "sources_meta": {
      "answers_total": 10,
      "answers_with_sources": 8,
      "training_recall": {
        "raw_mentions": 1,
        "raw_total": 6,
        "online_mentions": 4,
        "online_total": 6,
        "excluded_models": [
          "perplexity"
        ]
      }
    }
  },
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 404 not_found — Case does not exist or belongs to another account.
  • 409 no_run — No run has finished for this case yet.
GET/api/v1/cases/{id}/runsTool: list_runsScope: monitor:read

All measurement runs of a case, newest first, with status, overall score and the delta to the preceding run.

  • id (path, required) — Case id (UUID).
  • limit (query) — Maximum number of runs (1–100).
Response
{
  "runs": [
    {
      "id": "9a0c...",
      "label": "Aug 26",
      "kind": "monthly",
      "status": "done",
      "scheduled_for": "2026-08-01T03:00:00.000Z",
      "finished_at": "2026-08-01T03:41:00.000Z",
      "total_score": 44,
      "delta": -6
    }
  ],
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 404 not_found — Case does not exist or belongs to another account.
POST/api/v1/cases/{id}/runsREST onlyScope: monitor:write

Queues a run. The worker picks it up within seconds. Pro plan only; a run that is already queued or in progress for the same case is not duplicated.

Body
{
  "label": "After relaunch"
}
  • id (path, required) — Case id (UUID).
Response
{
  "run": {
    "id": "b71f...",
    "label": "After relaunch",
    "kind": "manual",
    "status": "queued"
  },
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 402 plan_required — The plan does not allow manual re-runs.
  • 409 run_pending — A run is already queued or in progress for this case.
POST/api/v1/scansTool: scan_domainScope: optional

Scans a domain live against nine technical criteria in four pillars (Retrievable, Explainable, Actionable, Distributed) and returns a score from 0 to 100. The score is a percentage of the APPLICABLE points, not a plain sum: areas that do not apply to a domain are removed from numerator and denominator, and signals that cannot be verified from outside count as half. A check may therefore report state "unknown" with max 0. Takes 5-30 seconds. Free; without a bearer key you get 3 scans per day per IP.

Body
{
  "domain": "example.com"
}
Response
{
  "scan": {
    "id": "0e2f8c11-6b4a-4a7e-9f10-2c3d4e5f6a7b",
    "domain": "example.com",
    "score": 62,
    "scoring_version": 2,
    "created_at": "2026-09-14T08:31:12.000Z",
    "url": "https://ai-agent-ready.com/check/scan/0e2f8c11-6b4a-4a7e-9f10-2c3d4e5f6a7b",
    "checks": [
      {
        "id": "crawler",
        "pillar": "R",
        "title": "AI crawler access",
        "state": "good",
        "score": 18,
        "max": 18,
        "findings": [
          {
            "state": "good",
            "text": "GPTBot, ClaudeBot and PerplexityBot all receive 200."
          }
        ]
      }
    ]
  },
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 400 invalid_domain — The domain is missing or cannot be parsed.
  • 401 invalid_key — A bearer token was sent but is unknown.
  • 503 auth_unavailable — A bearer token was sent but could not be verified right now.
  • 429 rate_limited — Anonymous daily scan limit reached.
  • 500 scan_failed — The scan could not be completed.
GET/api/v1/scans/{id}Tool: get_scanScope: optional

Returns a previously created scan with all nine checks and their findings. Results are public: the same payload backs the shareable result page.

  • id (path, required) — Scan id (UUID).
Response
{
  "scan": {
    "id": "0e2f8c11-6b4a-4a7e-9f10-2c3d4e5f6a7b",
    "domain": "example.com",
    "score": 62,
    "scoring_version": 2,
    "created_at": "2026-09-14T08:31:12.000Z",
    "url": "https://ai-agent-ready.com/check/scan/0e2f8c11-6b4a-4a7e-9f10-2c3d4e5f6a7b",
    "checks": [
      {
        "id": "crawler",
        "pillar": "R",
        "title": "AI crawler access",
        "state": "good",
        "score": 18,
        "max": 18,
        "findings": [
          {
            "state": "good",
            "text": "GPTBot, ClaudeBot and PerplexityBot all receive 200."
          }
        ]
      }
    ]
  },
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 401 invalid_key — A bearer token was sent but is unknown.
  • 503 auth_unavailable — A bearer token was sent but could not be verified right now.
  • 404 not_found — There is no result for this id.
GET/api/v1/statsTool: get_statsScope: optional

Anonymous aggregates over every scan ever run at the current scoring version: number of domains, score distribution and how often each check fails. No domain names, no personal data. This is the data behind the public report.

Response
{
  "stats": {
    "domains": 1284,
    "scans": 1731,
    "scoring_version": 2,
    "median_score": 54,
    "updated_at": "2026-09-14T08:00:00.000Z"
  },
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 401 invalid_key — A bearer token was sent but is unknown.
  • 503 auth_unavailable — A bearer token was sent but could not be verified right now.
POST/api/v1/scans/batchREST onlyScope: scans:write

Queues a technical scan for each domain and returns a job id straight away. A single scan takes about a minute, so the work runs through a durable queue rather than inside the request. Poll GET /api/v1/scans/batch/{id} for status and per-domain results. Quantity limits depend on the plan behind the key and are enforced atomically, so two simultaneous requests cannot exceed them together. The free single scan on POST /api/v1/scans is unaffected.

Body
{
  "domains": [
    "example-crm.com",
    "craftnote.de",
    "not a domain"
  ]
}
Response
{
  "job": {
    "id": "b3d0f6a1-77c2-4c2e-9a1f-9f2c3d4e5f60",
    "status": "queued",
    "total": 2,
    "created_at": "2026-09-16T10:04:00.000Z",
    "url": "https://ai-agent-ready.com/api/v1/scans/batch/b3d0f6a1-77c2-4c2e-9a1f-9f2c3d4e5f60"
  },
  "accepted": [
    "example-crm.com",
    "craftnote.de"
  ],
  "rejected": [
    {
      "domain": "not a domain",
      "reason": "That is not a valid domain."
    }
  ],
  "limits": {
    "plan": "Starter",
    "per_batch": 10,
    "per_day": 25
  },
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 400 invalid_request — The body carries no usable domains array.
  • 402 plan_required — The subscription has ended. Existing results stay readable; new measurements do not.
  • 403 insufficient_scope — The key does not carry scans:write.
  • 429 quota_exceeded — The batch or daily limit of the plan is reached.
GET/api/v1/scans/batch/{id}REST onlyScope: scans:read

Returns the job counters plus one row per domain: its score and scan id once done, or the reason it failed. A partial failure is a result, not an abort — the remaining domains keep running. Jobs belonging to another account read as not found.

  • id (path, required) — The job id.
Response
{
  "job": {
    "id": "b3d0f6a1-77c2-4c2e-9a1f-9f2c3d4e5f60",
    "status": "running",
    "created_at": "2026-09-16T10:04:00.000Z",
    "finished_at": null,
    "counts": {
      "total": 2,
      "queued": 1,
      "done": 1,
      "failed": 0
    }
  },
  "results": [
    {
      "domain": "example-crm.com",
      "status": "done",
      "score": 61,
      "scan_id": "4a1c34f1-fce3-4674-bbfb-d7da43265095",
      "error": null,
      "finished_at": "2026-09-16T10:05:07.000Z"
    },
    {
      "domain": "craftnote.de",
      "status": "queued",
      "score": null,
      "scan_id": null,
      "error": null,
      "finished_at": null
    }
  ],
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 403 insufficient_scope — The key does not carry scans:read.
  • 404 not_found — No such job for this account.
GET/api/v1/webhooksREST onlyScope: webhooks:manage

Lists the webhook endpoints of the account together with the last deliveries, including status code, attempt count and error text. The signing secret is never returned — it goes over the wire exactly once, when the endpoint is created.

Response
{
  "events": [
    "scan.completed",
    "run.completed",
    "score.changed"
  ],
  "endpoints": [
    {
      "id": "0d0a5b1f-2c3d-4e5f-8a9b-0c1d2e3f4a5b",
      "url": "https://example.com/hooks/agentready",
      "label": "Shopify app",
      "events": [
        "scan.completed",
        "run.completed"
      ],
      "active": true,
      "created_at": "2026-09-16T09:00:00.000Z",
      "last_delivery_at": "2026-09-16T10:05:09.000Z"
    }
  ],
  "recent_deliveries": [
    {
      "id": "9f8e7d6c-5b4a-4938-2716-0f1e2d3c4b5a",
      "endpoint_id": "0d0a5b1f-2c3d-4e5f-8a9b-0c1d2e3f4a5b",
      "event_id": "7c6b5a49-3827-4615-a0f1-e2d3c4b5a697",
      "event": "scan.completed",
      "status": "delivered",
      "attempts": 1,
      "last_status": 200,
      "last_error": null,
      "created_at": "2026-09-16T10:05:08.000Z",
      "delivered_at": "2026-09-16T10:05:09.000Z"
    }
  ],
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 403 insufficient_scope — The key does not carry webhooks:manage.
POST/api/v1/webhooksREST onlyScope: webhooks:manage

Registers an https endpoint and returns its signing secret once. Each delivery carries x-agentready-event-id, x-agentready-timestamp and x-agentready-signature, the latter being v1=HMAC-SHA256(secret, `${timestamp}.${rawBody}`). Verify both the signature and the timestamp age; the event id stays stable across retries so you can deduplicate. URLs must be https and must not point into a private network — this is checked when you register and again against the resolved address before every delivery.

Body
{
  "url": "https://example.com/hooks/agentready",
  "events": [
    "scan.completed",
    "run.completed"
  ],
  "label": "Shopify app"
}
Response
{
  "endpoint": {
    "id": "0d0a5b1f-2c3d-4e5f-8a9b-0c1d2e3f4a5b",
    "url": "https://example.com/hooks/agentready",
    "label": "Shopify app",
    "events": [
      "scan.completed",
      "run.completed"
    ],
    "active": true,
    "created_at": "2026-09-16T09:00:00.000Z"
  },
  "secret": "whsec_9f1c...",
  "signature": {
    "header": "x-agentready-signature",
    "scheme": "v1=HMAC-SHA256(secret, `${timestamp}.${rawBody}`)",
    "timestamp_header": "x-agentready-timestamp",
    "event_id_header": "x-agentready-event-id"
  },
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 400 invalid_url — Not https, carries credentials, or points at a private host.
  • 403 insufficient_scope — The key does not carry webhooks:manage.
  • 409 duplicate_endpoint — That URL is already registered for this account.
PATCH/api/v1/webhooks/{id}REST onlyScope: webhooks:manage

Changes the subscribed events, the label, or pauses delivery. The URL cannot be changed: together with the secret it is the contract with one receiver, so a different target gets its own secret.

Body
{
  "events": [
    "score.changed"
  ],
  "active": false
}
  • id (path, required) — The endpoint id.
Response
{
  "endpoint": {
    "id": "0d0a5b1f-2c3d-4e5f-8a9b-0c1d2e3f4a5b",
    "url": "https://example.com/hooks/agentready",
    "label": "Shopify app",
    "events": [
      "score.changed"
    ],
    "active": false
  },
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 400 invalid_event — An unknown event name was sent.
  • 404 not_found — No such endpoint for this account.
DELETE/api/v1/webhooks/{id}REST onlyScope: webhooks:manage

Removes the endpoint and every delivery still waiting for it.

  • id (path, required) — The endpoint id.
Response
{
  "deleted": "0d0a5b1f-2c3d-4e5f-8a9b-0c1d2e3f4a5b",
  "meta": {
    "pricing": {
      "model": "free"
    },
    "generated_at": "2026-08-25T09:12:44.000Z",
    "docs": "https://ai-agent-ready.com/docs/api"
  }
}
Errors
  • 404 not_found — No such endpoint for this account.

MCP v0 (in-app)

This is v0: the MCP server runs inside this Next.js application rather than as a separate service, and its tools call the same data layer the dashboard uses instead of going through the REST API above. It is planned to move to a standalone Cloudflare Worker in front of /api/v1; the endpoint URL and the tool names stay the same when it does.

It exposes the read-only endpoints above as tools — list_cases, get_case, list_runs and get_case_summary. Each tool matches exactly one endpoint. create_run is deliberately not exposed: a run costs model credits.

Use the same API key you use for REST:

claude mcp add --transport http agentready https://ai-agent-ready.com/api/mcp \
  --header "Authorization: Bearer ark_live_..."

Scope follows the key, not the transport: a key reads its own cases, and a key carrying the monitor:admin scope reads every tenant — over REST and MCP alike, so the same key never sees more on one path than the other. In that mode list_cases adds owner_email per case and takes a query filter over name and domain.

Clients that cannot send their own Authorization header — the Claude desktop app among them — use the key as a path segment instead: https://ai-agent-ready.com/api/mcp/ark_live_.... That variant is functionally identical, but a key in a URL shows up in server and proxy logs while a header does not. If that matters to you, issue a second key for it on /app/account and revoke it independently.