Schemas
AuditCheck
object
Stable check identifier (e.g. 'api-error-model'). Route fixes and dedupe by this - names are for display, ids are stable.
Human-readable check title
One of: pass | fail | warning | error | pending | na. Act on 'fail'/'warning'; skip 'na'; 'pending' means the scan has not finished.
Points earned WITHIN this layer. Not 0-100 score points - layers are normalized to a weight before they count, so do NOT read maxScore-score as score uplift. Use estScoreGain for that.
Points available within this layer for this check (the within-layer denominator, not the 0-100 scale).
Estimated points this fix would add to the overall 0-100 score, already normalized to the layer weight. THIS is the uplift signal - rank fixes by it. Present on actionable (fail/warning) checks; an estimate, not exact.
Upside-only check: passing it raises the score, failing it never lowers the score. Build it only if the surface genuinely exists.
verified (counts toward the 0-100 score) or emerging (forward-looking, excluded from the denominator - a lower priority)
How strongly ora expects this check: required (the baseline every product is measured against), recommended (scored, outside the baseline), or emerging (excluded from the score). Display metadata - rank fixes by estScoreGain, not by tier.
Canonical spec / standard URL this check evaluates against, when one exists. Advisory - the link may change on any release.
When the check ran against a specific MCP server within a multi-MCP bundle: that server's kind (currently product | docs | other; advisory - new kinds may appear on any release). Absent for non-MCP checks and single-MCP scans.
The URL of the MCP server this check scored against. Present only alongside mcpKind.
What the scan observed for this check
Concrete fix that would make this check pass. The primary thing to act on.
Why this check does not apply to this product - it is skipped, not a deduction
AuditLayer
object
AuditScanResult
object
100
Present only when the caller passed ?include=essentials. Carries its own `score` - there is no separate top-level essentials score field.
complete = all checks resolved; partial/stuck = still running - re-scan before actingcomplete | partial | stuck
Ids of checks still resolving (empty/absent when analysisStatus is complete)
Actionable (fail/warning) checks ranked by ora: non-bonus first, then estimated uplift descending, capped at 6. Render verbatim - do not re-rank.
Canonical ora.ai deep link for the domain
ora.ai
The contract version this payload conforms to. SemVer: a major means a response-envelope break - a stable field removed, renamed, or changed in meaning, or the default response format flipping - and is safe to pin. Additive changes and check-catalog membership changes ship on a minor. See docs/api.md -> Contract and versioning.1.25.0
Present only when this body is a stored result served by the freshness gate instead of a fresh scan. Absent on a live scan.true
Age of the served stored result in seconds (also sent as the Age response header). Present with servedFromCache.
Legacy marker retained for compatibility with stored results. New authentication-required scans return an MCP_AUTH_REQUIRED error without score or grade; public reads of a historical marked result return the same error.true
How the scanned input was classified and stored. 'ephemeral' = disposable result (tunnel host, or ephemeral: true) that is excluded from rankings and deleted after a few days. Absent on older stored results.domain | mcp | mcp-app | ephemeral
Canonical market category ora classified the domain into (e.g. 'Infrastructure & DevOps'). Advisory; absent when the domain is unclassified.
One-sentence natural-language verdict on the domain's agent-readiness, generated after analysis completes. Advisory; absent on partial results and on older stored results.
The URL the scan actually fetched after following redirects. Distinct from `url`, which is the canonical ora.ai deep link for the domain.
Present only when the scan is stuck (partial for over 30 minutes; the worker likely failed) or on the score route's 404 miss - the HTTP request that recovers the score. A plain partial resolves on its own: poll the 202's Location URL instead of re-scanning.
Experimental (no stability guarantee): competitive context appended when the request carries ?competitors=1. Null when the domain is unranked or the leaderboard read failed. Shape may change on any release.
AuditScoreResult
object
100
Present only when the caller passed ?include=essentials. Carries its own `score` - there is no separate top-level essentials score field.
complete = all checks resolved; partial/stuck = still running - re-scan before actingcomplete | partial | stuck
Ids of checks still resolving (empty/absent when analysisStatus is complete)
Actionable (fail/warning) checks ranked by ora: non-bonus first, then estimated uplift descending, capped at 6. Render verbatim - do not re-rank.
Canonical ora.ai deep link for the domain
ora.ai
The contract version this payload conforms to. SemVer: a major means a response-envelope break - a stable field removed, renamed, or changed in meaning, or the default response format flipping - and is safe to pin. Additive changes and check-catalog membership changes ship on a minor. See docs/api.md -> Contract and versioning.1.25.0
Wall-clock duration of the scan that produced this stored result
Legacy marker retained for compatibility with stored results. New authentication-required scans return an MCP_AUTH_REQUIRED error without score or grade; public reads of a historical marked result return the same error.true
How the scanned input was classified and stored. 'ephemeral' = disposable result (tunnel host, or ephemeral: true) that is excluded from rankings and deleted after a few days. Absent on older stored results.domain | mcp | mcp-app | ephemeral
Canonical market category ora classified the domain into (e.g. 'Infrastructure & DevOps'). Advisory; absent when the domain is unclassified.
One-sentence natural-language verdict on the domain's agent-readiness, generated after analysis completes. Advisory; absent on partial results and on older stored results.
The URL the scan actually fetched after following redirects. Distinct from `url`, which is the canonical ora.ai deep link for the domain.
Present only when the scan is stuck (partial for over 30 minutes; the worker likely failed) or on the score route's 404 miss - the HTTP request that recovers the score. A plain partial resolves on its own: poll the 202's Location URL instead of re-scanning.
Experimental (no stability guarantee): competitive context appended when the request carries ?competitors=1. Null when the domain is unranked or the leaderboard read failed. Shape may change on any release.
CheckCatalog
object
The contract version this catalog conforms to - identical to the OpenAPI info.version and the MCP server version. SemVer: a major means a response-envelope break (a stable field, or a layer id, removed or renamed or changed in meaning, or the default response format flipping) and is safe to pin; check-catalog membership changes ship on a minor. The full versioning policy is published in the API description at /api/openapi.json.1.25.0
The four scored layers in scoring order, with display name and current weight.
All catalogued checks. Array order is not contractual: key by id.
CatalogCheck
object
Stable check identifier, safe to persist, to gate CI on, and to pass in POST /api/scan/checks. An id never changes meaning while it exists. Catalog membership is not frozen: an id can be retired or renamed on a MINOR version, always with a contract changelog entry and a deprecation window. Check ids identify catalog entries rather than response-envelope fields, so a client parsing responses keeps working when one disappears; a client gating on an explicit id list reads the changelog.
Human-readable check title. Advisory display prose.
What the check verifies and why it matters to agents. Advisory display prose.
The check's scored layer id, always one of the ids in layers[]. A check can move to a different layer on a minor version with a changelog entry.
The check's maximum contribution to its layer. Do not sum maxScore into a score denominator: emerging checks sit outside scoring, a bonus counts only the points it earned (it can raise a score, never lower it), and N/A results drop out. Rebalances land on a minor version with a changelog entry.
Always present. A bonus check can only add score: a site that lacks the surface is never penalised for failing it.
The check's declared applicability rule. One of: all | domain-only | mcp | mcp-app | api. A value change lands on a minor version with a changelog entry.
Present only when applicability is 'api'. One of: rest | graphql | either - the API surface the check evaluates.
The scan kinds this check can run for - a subset of: domain | mcp | mcp-app. Eligibility, not a guarantee: MCP checks run once per MCP surface detected on the target and report N/A when none is present, and 'api' checks report N/A when the target has no REST or GraphQL surface.
One of: required | recommended | emerging. Advisory: the required set may grow on a minor version, and every tier change carries a changelog entry. Gate CI on explicit check ids, not on tiers.
One of: verified | emerging. Emerging checks are shown on score pages but excluded from scoring, so do not treat them as score-affecting when selecting checks.
Always present. True when the check's underlying spec is a draft or emerging standard.
Always present. True marks a beta placeholder held at not-applicable - it runs but cannot affect any score; do not offer it as fixable.
Canonical spec or standard URL. Omitted when the check has none.
Generic, target-independent fix guidance. Omitted for the few checks that have none.
CatalogLayer
object
Stable layer id: discovery, accessibility, usability, or payments. Removing or renaming a layer id is a major version change. Note one intentional divergence: the id 'accessibility' carries the display name 'Access'.
Display name for the layer. Advisory: it may change on a minor version.
The layer's weight in the overall 0-100 score. Weights sum to 100 across the four layers. Advisory: a rebalance lands on a minor version with a changelog entry.
RunChecksRequest
object
The website, MCP server, or API URL to run checks against. A bare domain like example.com is accepted.
Array of check ids from GET /api/checks - one id minimum, up to the catalogued check count. Duplicate ids are deduplicated; ids the catalog does not list are rejected with error code UNKNOWN_CHECK_IDS.
Optional URL of the target's MCP server, matching the same field on POST /api/scan. It pins the MCP endpoint; a failed handshake never substitutes a discovered server. When omitted, ora auto-discovers MCP endpoints. An empty string is treated as absent.
RunChecksResponse
object
The contract version this response conforms to - identical to the OpenAPI info.version and the MCP server version. The full versioning policy is published in the API description at /api/openapi.json.1.25.0
The apex domain derived from the requested URL.
The normalized URL the run targeted.
The execution kind detected for the target - domain, mcp, or mcp-app. Detected server-side; requested ids that cannot apply to this kind resolve as 'na' entries instead of executing.domain | mcp | mcp-app
Whether these results were patched into the target's current stored scan. Patching is available to scan API key callers today - keyless runs are stateless and always report false. When true, GET /api/score/{domain} and the public score page reflect the re-verified checks, recomputed over the full stored check set. When false, no public surface changed; a full POST /api/scan is the way to establish a stored scan.
One entry per executed check slot, plus one 'na' entry for each requested id that cannot apply to the detected kind. Every requested id yields at least one entry, and MCP fan-out can yield several entries per id, keyed by (id, mcpUrl).-> RunChecksResultEntry
RunChecksResultEntry
object
The check id, as listed in GET /api/checks.
Human-readable check title.
One of: pass | fail | warning | na | error. 'error' means ora could not complete the probe - retry it. A failed fix reads 'fail', never 'error'. 'pending' cannot appear: selective runs resolve synchronously.pass | fail | warning | na | error
Points the check earned on this run.
The check's maximum points. Do not sum maxScore values into an aggregate - a selective response deliberately carries no overall score.
What was observed on the target.
How to fix the finding. Omitted when no guidance applies.
Why the check did not apply. Present on 'na' results, including requested ids that cannot apply to the detected kind.
The classification of the MCP surface this entry scored against - one of: product | docs | other | app. Present on MCP fan-out entries.product | docs | other | app
The URL of the MCP server this entry scored against. Present on MCP fan-out entries.
ScanResult
object
The scanned domain (pre-redirect). Compare with new URL(finalUrl).hostname to detect cross-domain redirects.
The normalized URL
The final URL after redirects. If the host differs from domain, the score reflects a redirected site.
Overall score (0-100)
Maximum possible score
Letter grade (A+ >= 95, A >= 86, B >= 70, C >= 48, D >= 28, F < 28)A+ | A | B | C | D | F
Completeness of the score. 'partial' = analysis still in progress (deep checks, relevance assessment, or summary generation); 'complete' = all post-processing done, score is final; 'stuck' = scan got stuck in partial for >30 minutes (worker likely failed) - the score will not advance on its own and the response will also include a `next_action` envelope pointing at POST /api/scan.complete | partial | stuck
IDs of checks not yet resolved. Empty when analysisStatus is 'complete'. Poll GET /api/score/{domain} until this is empty for a final score.
Call-to-action message based on score
CTA tiertop | high | mid | low
Breakdown by scoring layer-> LayerResult
When the scan was performed
Scan duration in milliseconds
Optional. A one-sentence natural-language verdict generated after analysis completes (e.g. "Stripe offers excellent developer resource discoverability and SDK availability, but lacks a published OpenAPI specification for agent integration."). Absent on older cached results or when the summary generation step did not run.
Optional. How the scan is stored. 'domain' for a regular website, 'mcp' for an MCP server endpoint (handshake succeeded but no Apps support detected; also returned for catalog pages where we resolved a validated embedded MCP server URL), 'mcp-app' for an MCP server that negotiates the MCP Apps extension `io.modelcontextprotocol/ui` (or exposes `ui://` resources or tool `_meta.ui.resourceUri`), 'ephemeral' for a disposable scan (requested with `ephemeral: true`, or a public tunnel hostname) which is excluded from the leaderboard, coverage counts, research statistics, and score history and is deleted after a few days. The first three are detected; 'ephemeral' describes storage, and an ephemeral scan still runs the full check set for the kind it was detected as. Absent on older cached results.domain | mcp | mcp-app | ephemeral
Legacy marker retained for compatibility. New authentication-required scans and reads of historical marked results return HTTP 422 MCP_AUTH_REQUIRED without score or grade.
Present (and always true) only when POST /api/scan answered from the freshness window with a stored result instead of running a scan. Absent on a live scan and on GET /api/score/{domain}, which is always a cached read.true
Age of the served stored result in seconds. Sent with servedFromCache, and mirrored in the Age response header.
LayerResult
object
CheckResult
object
Check identifier
Check display name
What this check tests
Check result status. 'pending' = deep scan not yet resolved; 'na' = not applicable for this product.pass | fail | warning | error | pending | na
Points earned
Maximum points for this check
Human-readable explanation of the result
Optional. Concrete fix that would make this check pass. Absent on passing checks and on some N/A rows.
Optional. Upside-only check: earning it raises the score, missing it never lowers it. An unearned bonus is excluded from the denominator entirely, so it reports score 0 without costing points.
Optional. 'verified' = evidence that agents rely on this signal, counts toward the 0-100 score. 'emerging' = early or low-adoption signal, shown but excluded from the denominator. Absent on older cached results.verified | emerging
Optional. How strongly ora expects the check: 'required' = the baseline every product is measured against, 'recommended' = scored but outside the baseline, 'emerging' = excluded from the score. Display metadata derived from maturity plus the baseline - it never changes the score. Rank fixes by estScoreGain, not by tier.required | recommended | emerging
Optional. Why the check does not apply to this product. Present on 'na' rows; the check is skipped, not deducted.
Optional. Estimated points fully fixing this check would add to the overall 0-100 score, already normalized to the layer weight. Present on actionable (fail/warning) checks only; an estimate, not exact. This is the uplift signal - do not read maxScore minus score as score uplift.
CompetitorSet
object
The market category the competitors are drawn from. Always a real market category: for unclassified domains (Community, or no leaderboard row) the endpoint returns `competitors: null` instead of a set.
Top 5 of the category by score, descending.-> Competitor
Up to 5 rows centred on the queried domain: 2 above, self, 2 below, within the same category pool. Always contains the `isSelf` row.-> Competitor
Competitor
object
1-based position on the category board the slices are drawn from.
Competitor domain
Competitor display name
Agent-readiness score (0-100)
Letter gradeA+ | A | B | C | D | F
True for the row representing the queried domain. Subdomains share their company's leaderboard row, so querying a subdomain marks the company's row as self.
NextAction
object
NotScannedResponse
object
Human-readable error message
Machine-readable error codeDOMAIN_NOT_SCANNED
Normalized domain that was looked up
Present instead of `next_action` when the request passed `?format=audit`. Same recovery step, camelCase and versioned, matching the `nextAction` on AuditScanResult / AuditScoreResult.
ArdErrorResponse
object
Machine-readable ARD error code (spec Appendix B).INVALID_ARGUMENT | UNAUTHENTICATED | NOT_FOUND | RATE_LIMIT_EXCEEDED | INTERNAL_ERROR
Human-readable error explanation.
Optional structured validation detail (Zod flatten) on INVALID_ARGUMENT.
Optional recovery hint (e.g. /api/scan on NOT_FOUND).
McpAuthRequiredResponse
object
ErrorResponse
object
Error type or human-readable message (e.g. 'Not found', 'Rate limited')
Optional longer explanation with recovery steps
Optional machine-readable error code (e.g. EPHEMERAL_CLOBBER, ENDPOINT_NOT_FOUND, RATE_LIMITED, INVALID_DOMAIN)
Present on a 429 from the durable daily scan budget: milliseconds until a slot frees, the same interval the Retry-After header carries in seconds. Every rate-limited ora endpoint sends the same deny body, so one client handler covers them all.
Optional structured validation detail (Zod flatten) on a schema rejection.
DiscoverResult
object
AgentFeedback
object
Unique agent identifier
Original user request that led to this interaction
What the agent was trying to do
success | partial_failure | failure
Detailed feedback
recommend | neutral | not_recommend
Per-stage scores (1-5). Current funnel stages: discovery, identity, access, payments, experience. Legacy keys (integration, in-agent-experience) are still accepted for backward compatibility.