Schemas

AuditCheck

object

id
string
required
Stable check identifier (e.g. 'api-error-model'). Route fixes and dedupe by this - names are for display, ids are stable.
name
string
required
Human-readable check title
status
string
required
One of: pass | fail | warning | error | pending | na. Act on 'fail'/'warning'; skip 'na'; 'pending' means the scan has not finished.
score
number
required
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.
maxScore
number
required
Points available within this layer for this check (the within-layer denominator, not the 0-100 scale).
estScoreGain
number
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.
bonus
boolean
Upside-only check: passing it raises the score, failing it never lowers the score. Build it only if the surface genuinely exists.
maturity
string
verified (counts toward the 0-100 score) or emerging (forward-looking, excluded from the denominator - a lower priority)
tier
string
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.
specUrl
string
Canonical spec / standard URL this check evaluates against, when one exists. Advisory - the link may change on any release.
mcpKind
string
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.
mcpUrl
string
The URL of the MCP server this check scored against. Present only alongside mcpKind.
details
string
What the scan observed for this check
recommendation
string
Concrete fix that would make this check pass. The primary thing to act on.
naReason
string
Why this check does not apply to this product - it is skipped, not a deduction

AuditLayer

object

id
string
required
Layer id: discovery | accessibility | usability | payments (historical scans may carry retired ids)
name
string
required
score
number
required
maxScore
number
required
checks
array
required

AuditScanResult

object

domain
string
required
name
string
required
score
integer
required
scoreMax
number
required
100
essentials
object
Present only when the caller passed ?include=essentials. Carries its own `score` - there is no separate top-level essentials score field.
grade
string
required
gradeColor
string
required
ctaMessage
string | null
required
scannedAt
string
required
durationMs
integer | null
required
analysisStatus
string
complete = all checks resolved; partial/stuck = still running - re-scan before actingcomplete | partial | stuck
pendingChecks
array
Ids of checks still resolving (empty/absent when analysisStatus is complete)
layers
array
required
topFixes
array
required
Actionable (fail/warning) checks ranked by ora: non-bonus first, then estimated uplift descending, capped at 6. Render verbatim - do not re-rank.
url
string
required
Canonical ora.ai deep link for the domain
generatedAt
string
required
source
string
required
ora.ai
contractVersion
string
required
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
servedFromCache
boolean
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
resultAgeSeconds
integer
Age of the served stored result in seconds (also sent as the Age response header). Present with servedFromCache.
mcpAuthRequired
boolean
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
urlKind
string
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
category
string
Canonical market category ora classified the domain into (e.g. 'Infrastructure & DevOps'). Advisory; absent when the domain is unclassified.
agenticSummary
string
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.
finalUrl
string
The URL the scan actually fetched after following redirects. Distinct from `url`, which is the canonical ora.ai deep link for the domain.
nextAction
object
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.
competitors
object | null
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

domain
string
required
name
string
required
score
integer
required
scoreMax
number
required
100
essentials
object
Present only when the caller passed ?include=essentials. Carries its own `score` - there is no separate top-level essentials score field.
grade
string
required
gradeColor
string
required
scannedAt
string | null
required
analysisStatus
string
complete = all checks resolved; partial/stuck = still running - re-scan before actingcomplete | partial | stuck
pendingChecks
array
Ids of checks still resolving (empty/absent when analysisStatus is complete)
layers
array
required
topFixes
array
required
Actionable (fail/warning) checks ranked by ora: non-bonus first, then estimated uplift descending, capped at 6. Render verbatim - do not re-rank.
url
string
required
Canonical ora.ai deep link for the domain
generatedAt
string
required
source
string
required
ora.ai
contractVersion
string
required
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
durationMs
integer | null
required
Wall-clock duration of the scan that produced this stored result
mcpAuthRequired
boolean
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
urlKind
string
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
category
string
Canonical market category ora classified the domain into (e.g. 'Infrastructure & DevOps'). Advisory; absent when the domain is unclassified.
agenticSummary
string
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.
finalUrl
string
The URL the scan actually fetched after following redirects. Distinct from `url`, which is the canonical ora.ai deep link for the domain.
nextAction
object
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.
competitors
object | null
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

contractVersion
string
required
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
layers
array
required
The four scored layers in scoring order, with display name and current weight.
checks
array
required
All catalogued checks. Array order is not contractual: key by id.

CatalogCheck

object

id
string
required
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.
name
string
required
Human-readable check title. Advisory display prose.
description
string
required
What the check verifies and why it matters to agents. Advisory display prose.
layer
string
required
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.
maxScore
number
required
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.
bonus
boolean
required
Always present. A bonus check can only add score: a site that lacks the surface is never penalised for failing it.
applicability
string
required
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.
protocol
string
Present only when applicability is 'api'. One of: rest | graphql | either - the API surface the check evaluates.
appliesTo
array
required
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.
tier
string
required
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.
maturity
string
required
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.
draft
boolean
required
Always present. True when the check's underlying spec is a draft or emerging standard.
beta
boolean
required
Always present. True marks a beta placeholder held at not-applicable - it runs but cannot affect any score; do not offer it as fixable.
specUrl
string
Canonical spec or standard URL. Omitted when the check has none.
recommendation
string
Generic, target-independent fix guidance. Omitted for the few checks that have none.

CatalogLayer

object

id
string
required
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'.
name
string
required
Display name for the layer. Advisory: it may change on a minor version.
weight
number
required
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

url
string
required
The website, MCP server, or API URL to run checks against. A bare domain like example.com is accepted.
checkIds
array
required
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.
mcpUrl
string
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

contractVersion
string
required
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
domain
string
required
The apex domain derived from the requested URL.
url
string
required
The normalized URL the run targeted.
urlKind
string
required
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
storedScanUpdated
boolean
required
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.
results
array
required
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

id
string
required
The check id, as listed in GET /api/checks.
name
string
required
Human-readable check title.
status
string
required
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
score
number
required
Points the check earned on this run.
maxScore
number
required
The check's maximum points. Do not sum maxScore values into an aggregate - a selective response deliberately carries no overall score.
details
string
required
What was observed on the target.
recommendation
string
How to fix the finding. Omitted when no guidance applies.
naReason
string
Why the check did not apply. Present on 'na' results, including requested ids that cannot apply to the detected kind.
mcpKind
string
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
mcpUrl
string
The URL of the MCP server this entry scored against. Present on MCP fan-out entries.

ScanResult

object

domain
string
The scanned domain (pre-redirect). Compare with new URL(finalUrl).hostname to detect cross-domain redirects.
url
string
The normalized URL
finalUrl
string
The final URL after redirects. If the host differs from domain, the score reflects a redirected site.
score
integer
Overall score (0-100)
maxScore
integer
Maximum possible score
grade
string
Letter grade (A+ >= 95, A >= 86, B >= 70, C >= 48, D >= 28, F < 28)A+ | A | B | C | D | F
analysisStatus
string
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
pendingChecks
array
IDs of checks not yet resolved. Empty when analysisStatus is 'complete'. Poll GET /api/score/{domain} until this is empty for a final score.
ctaMessage
string
Call-to-action message based on score
ctaTier
string
CTA tiertop | high | mid | low
layers
array
Breakdown by scoring layer-> LayerResult
scannedAt
string (date-time)
When the scan was performed
durationMs
integer
Scan duration in milliseconds
agenticSummary
string
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.
urlKind
string
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
mcpAuthRequired
boolean
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.
servedFromCache
boolean
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
resultAgeSeconds
integer
Age of the served stored result in seconds. Sent with servedFromCache, and mirrored in the Age response header.

LayerResult

object

id
string
Layer identifier
name
string
Layer display name
description
string
Layer description
checks
array
score
integer
Layer score
maxScore
integer
Layer maximum possible score

CheckResult

object

id
string
Check identifier
name
string
Check display name
description
string
What this check tests
status
string
Check result status. 'pending' = deep scan not yet resolved; 'na' = not applicable for this product.pass | fail | warning | error | pending | na
score
integer
Points earned
maxScore
integer
Maximum points for this check
details
string
Human-readable explanation of the result
recommendation
string
Optional. Concrete fix that would make this check pass. Absent on passing checks and on some N/A rows.
bonus
boolean
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.
maturity
string
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
tier
string
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
naReason
string
Optional. Why the check does not apply to this product. Present on 'na' rows; the check is skipped, not deducted.
estScoreGain
number
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

category
string
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.
leaders
array
Top 5 of the category by score, descending.-> Competitor
neighbors
array
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

rank
integer
1-based position on the category board the slices are drawn from.
domain
string
Competitor domain
name
string
Competitor display name
score
integer
Agent-readiness score (0-100)
grade
string
Letter gradeA+ | A | B | C | D | F
isSelf
boolean
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

method
string
required
HTTP methodPOST
endpoint
string
required
API path to call (e.g. /api/scan)
body
object
required
Body to POST. For /api/scan this is { url }.
description
string
required
Human-readable explanation of the recovery step

NotScannedResponse

object

error
string
required
Human-readable error message
code
string
required
Machine-readable error codeDOMAIN_NOT_SCANNED
domain
string
required
Normalized domain that was looked up
next_action
object
nextAction
object
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

errorCode
string
required
Machine-readable ARD error code (spec Appendix B).INVALID_ARGUMENT | UNAUTHENTICATED | NOT_FOUND | RATE_LIMIT_EXCEEDED | INTERNAL_ERROR
message
string
required
Human-readable error explanation.
details
object
Optional structured validation detail (Zod flatten) on INVALID_ARGUMENT.
Optional recovery hint (e.g. /api/scan on NOT_FOUND).

McpAuthRequiredResponse

object

error
string
required
Why the MCP server could not be inspected.
code
string
required
mcpAuthRequired
boolean
required
mcpUrl
string
required
The MCP endpoint that required authentication.
urlKind
string
required
mcp | mcp-app

ErrorResponse

object

error
string
required
Error type or human-readable message (e.g. 'Not found', 'Rate limited')
message
string
Optional longer explanation with recovery steps
code
string
Optional machine-readable error code (e.g. EPHEMERAL_CLOBBER, ENDPOINT_NOT_FOUND, RATE_LIMITED, INVALID_DOMAIN)
retry_after_ms
integer
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.
details
object
Optional structured validation detail (Zod flatten) on a schema rejection.

DiscoverResult

object

domain
string
Product domain
name
string
Product name
category
string
Product category
score
integer
Agent-readiness score (0-100)
grade
string
A | B | C | D | F
tags
array
Product tags
matchScore
number
Relevance to your query

AgentFeedback

object

id
integer
domain
string
agent_id
string
Unique agent identifier
user_intent
string
Original user request that led to this interaction
task_description
string
What the agent was trying to do
outcome
string
success | partial_failure | failure
content
string
Detailed feedback
friction_points
array
recommendation
string
recommend | neutral | not_recommend
layer_scores
object
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.
created_at
string (date-time)

FeedbackStats

object

total
integer
Total feedback count
success_rate
number
Proportion of successful outcomes (0-1)
recommend_rate
number
Proportion recommending (0-1)
outcomes
object
recommendations
object