/** * This file was auto-generated by openapi-typescript. * Do not make direct changes to the file. */ /** OneOf type helpers */ type Without = { [P in Exclude]?: never; }; type XOR = (T | U) extends object ? (Without & U) | (Without & T) : T | U; type OneOf = T extends [infer Only] ? Only : T extends [infer A, infer B, ...infer Rest] ? OneOf<[XOR, ...Rest]> : never; export interface paths { "/api/scan": { /** * Scan a domain, MCP server URL, or MCP App URL for agent-readiness * @description Runs a full agent-readiness scan on the given URL. Accepts a domain, MCP server URL, or MCP App URL (server that supports the MCP Apps extension `io.modelcontextprotocol/ui`) - the server auto-detects which kind of input was provided and selects the appropriate check set. Catalog-style listing pages are folded into the `mcp` kind by classifying the first validated embedded MCP URL. Returns score, grade, and detailed layer breakdown. The response includes an optional `urlKind` field indicating the detected kind ('domain', 'mcp', or 'mcp-app'). Scoring completes within the request, but deeper analysis can continue asynchronously afterwards: when the returned analysisStatus is 'partial', the response is a 202 Accepted with a Location header pointing at the polling endpoint for the remaining work; a 200 means analysis is already complete. For real-time progress updates, use GET /api/scan/stream which serves a text/event-stream. A complete stored result younger than the freshness window is returned as-is (servedFromCache, resultAgeSeconds, Age header) without running or persisting a scan - pass force: true, or widen/narrow maxAgeSeconds, to control that. Rate limited two ways: 10 requests per minute per IP (burst) and a durable daily scan budget shared with the other scan entry points - both return 429 with a Retry-After header. */ post: operations["scanDomain"]; }; "/api/v2/scan": { /** * Instant-trigger entry point for the v2 async scan lifecycle (rollout, gated) * @description Phase 1b of the scan lifecycle redesign (see docs/plans/scan-lifecycle-prd.md). Validates the input (rate-limit 10/min/IP, Zod, isValidUrl, reachability, URL-kind classification), de-dupes against any in-progress v2 row for the domain, INSERTs the scans row at status="pending" with is_current=true and flow_version='v2', and returns the scanId plus a pollUrl. The downstream pipeline (Stage 1 context + static checks, Stage 2 deep checks via the Fly worker, Stage 3 finalize) is NOT YET WIRED in this PR - rows created here stay at status="pending" until follow-up PRs ship Stage 1 and the recovery cron. The endpoint is gated behind the SCAN_V2_ENABLED env flag and returns 503 in environments where it is unset, so external callers must not rely on it before cutover. The v1 read paths (/api/score/[domain], the leaderboard, the sitemap) already filter out flow_version='v2' rows. */ post: operations["scanDomainV2"]; }; "/api/scan/stream": { /** * Stream an agent-readiness scan as Server-Sent Events * @description Runs a full agent-readiness scan on the given URL and streams progress as text/event-stream. Accepts a domain, MCP server URL, or MCP App URL (server that supports the MCP Apps extension `io.modelcontextprotocol/ui`) - the server auto-detects which kind of input was provided and selects the appropriate check set. Catalog-style listing pages are folded into the `mcp` kind by classifying the first validated embedded MCP URL. The stream emits a `kind_detecting` event immediately after the cheap reachability probe, followed by exactly one `kind_detected` event with payload `{ kind: 'domain' | 'mcp' | 'mcp-app', mcpUrl?: string, embeddedMcpUrls?: string[], hint?: string }` once URL-kind detection resolves. Subsequent events include `scan_init`, `layer_start`, `check_start`, `check_complete`, `layer_complete`, and finally `scan_complete` whose payload mirrors the ScanResult schema (including the optional `urlKind` field indicating the detected kind). The same freshness gate as POST /api/scan applies: a hit is a `kind_detected` frame followed by the terminal `scan_complete` event, rather than a full run. Rate limited two ways: 10 requests per minute per IP (burst) and a durable daily scan budget shared with the other scan entry points - both return 429 with a Retry-After header. */ get: operations["scanDomainStream"]; }; "/api/scan/checks": { /** * Run a selected subset of checks against a URL * @description Runs only the checks you select against the given URL and returns per-check results - the re-verify step after shipping a fix, with check ids from GET /api/checks. The run always executes; results are never served from a cache, so a re-check reflects the fix you just deployed (allow for DNS and CDN caches clearing). For most or all of the catalog, use POST /api/scan instead: same budget unit, and it returns a score. The response carries no aggregate score; GET /api/score/{domain} is the score surface. Callers holding a scan API key also get stored-scan patching - see storedScanUpdated on the response. Rate limited two ways: 10 requests per minute per IP, and one run spends one unit of the daily scan budget shared with POST /api/scan, spent once the target has been probed and classified; requests rejected earlier (invalid input, unknown ids, unreachable) spend nothing. Scan API key callers are exempt from both - an exemption, not a larger allowance; keys are issued manually on request - contact ora. */ post: operations["runChecks"]; }; "/api/score/{domain}": { /** * Get cached score for a domain * @description Returns the most recent cached scan result for the given domain. Read-only: never triggers a scan. On miss (404) or when the previous scan got stuck mid-flight (200 with `analysisStatus: "stuck"`), the response carries a structured `next_action` envelope pointing at `POST /api/scan` so agent callers have a machine-parseable next step. Successful responses are cached for 1 hour; stuck, 404, and ephemeral (disposable, `urlKind: "ephemeral"`) responses are uncached (`Cache-Control: no-store`) so a successful re-scan is observable immediately and a deleted disposable row is never served from cache. Rate limited to 10 requests per minute per IP - returns 429 if exceeded. */ get: operations["getScore"]; }; "/api/badge/{domain}": { /** * Get SVG badge for a domain * @description Returns an SVG badge showing the domain's ora score and grade. Embed in READMEs or websites. Cached for 1 hour. */ get: operations["getBadge"]; }; "/api/checks": { /** * Get the complete catalog of scanner checks * @description Returns every check the ora scanner can run - stable id, scored layer, max score, applicability, eligible scan kinds, tier, maturity, and fix guidance per check, plus the four scored layers with their weights. Check ids are stable: gate CI on an explicit id list, not on tiers (the required set can grow on a minor version). Ids are also what POST /api/scan/checks takes, and every check carries a `beta` boolean for building check pickers - a beta check runs but cannot affect any score. The document is static and byte-stable between check-set changes, so diffing it detects catalog updates. One optional parameter: `?include=essentials` adds `essentialsTier`, `essentialsBonusOnly`, and `essentialsExcluded` (the essentials-model classification; excluded checks are ignored by that model outright) to every check; without it the body is byte-identical to previous releases. Sends Access-Control-Allow-Origin: * and is CDN-cached for 1 hour. Rate limited to 60 requests per minute per IP - returns 429 with a Retry-After header if exceeded. */ get: operations["listChecks"]; }; "/api/discover": { /** * Discover agent-ready products by intent * @description Find the most agent-ready products for a given need. Describe what you're looking for and get products ranked by agent-readiness score. Cached for 5 minutes. */ get: operations["discoverProducts"]; }; "/api/feedback/check": { /** * Report an issue with a specific check result * @description Submit feedback about an inaccurate check result. Accepts both human and agent submissions. Agent submissions require HATCHA verification. Check state (score, status, details) is snapshotted server-side from the latest scan. */ post: operations["reportCheckIssue"]; }; "/api/contact": { /** * Send a message to the ora team * @description Submit a contact-form message. Open endpoint - no authentication required. Sends an inquiry email to the ora team and an auto-responder to the submitter. Rate limited to 3 submissions per IP per 10 minutes. Agents are welcome to use this endpoint, though email is the simpler path for most cases. */ post: operations["submitContactInquiry"]; }; "/api/ard/search": { /** * Search agentic resources (Agentic Resource Discovery) * @description Runs an Agentic Resource Discovery (ARD) search over ora's catalog of agent-ready resources. Returns resources ranked by relevance to a free-text query, with optional field filters and federation control. The per-result `score` is a readiness-weighted relevance score (0-100): match quality for the query (dominant), multiplied by a 0.6-1.0 factor from the domain's agent-readiness. It is distinct from the raw agent-readiness score returned by POST /api/scan and GET /api/score/{domain}. Rate limited to 30 requests per minute per IP - returns 429 if exceeded. */ post: operations["ardSearch"]; }; "/api/ard/explore": { /** * Faceted exploration of agentic resources (Agentic Resource Discovery) * @description Returns facet aggregations (counts per field value) for an Agentic Resource Discovery (ARD) result set. Use this to build filter UIs over the ARD catalog. An optional query narrows the set before faceting. Rate limited to 30 requests per minute per IP - returns 429 if exceeded. */ post: operations["ardExplore"]; }; "/api/ard/agents": { /** * List discoverable agentic resources (Agentic Resource Discovery) * @description Returns a paginated listing of the agentic resources ora publishes for Agentic Resource Discovery (ARD). Deterministic browsing via the spec EBNF `filter` expression and `orderBy`. Rate limited to 60 requests per minute per IP - returns 429 if exceeded. */ get: operations["ardListAgents"]; }; "/api/ard/attestation/{domain}": { /** * Get a signed scorecard attestation for a domain (Agentic Resource Discovery) * @description Returns an Agentic Resource Discovery (ARD) scorecard attestation for the given domain. When ora has an attestation signing key configured, the payload is returned as an EdDSA detached JWS that verifies against the public JWK set at GET /api/ard/jwks (also served at /.well-known/jwks.json); without a configured key the attestation is returned unsigned. Rate limited to 60 requests per minute per IP - returns 429 if exceeded. */ get: operations["ardGetAttestation"]; }; "/api/ard": { /** * Get the ARD registry service descriptor (Agentic Resource Discovery) * @description Returns the application/ai-registry+json service descriptor at ora's registry base URL - the document ora advertises in its AI Catalog's application/ai-registry+json entry. Names the live search/explore/agents/catalog endpoints, the pageToken pagination model (max page size 100), the filter/orderBy/facet fields, the served media types, and the federation modes. A crawler that ingests /.well-known/ai-catalog.json and follows the registry entry lands here. */ get: operations["ardGetRegistryDescriptor"]; }; "/api/ard/catalog": { /** * Get ora's AI Catalog manifest (Agentic Resource Discovery) * @description Returns ora's AI Catalog manifest for Agentic Resource Discovery (ARD). Also served at /.well-known/ai-catalog.json via a rewrite, and advertised by a Link header with rel="ai-catalog" on the homepage. */ get: operations["ardGetCatalog"]; }; "/api/ard/catalog.json": { /** * Get the full ARD catalog dump (Agentic Resource Discovery) * @description Returns every indexed Agentic Resource Discovery (ARD) entry - ora's own products, their detected MCP server / skill resources, and crawled external catalogs - in one uncapped document { version, generatedAt, publisher, data }, for bulk ingest without paging through /api/ard/agents. Shares the same index pipeline as /agents and /explore. Sends Access-Control-Allow-Origin: * and is CDN-cached. Rate limited to 60 requests per minute per IP - returns 429 if exceeded. Also reachable at directory.ora.ai/api.json. */ get: operations["ardGetCatalogDump"]; }; "/api/ard/jwks": { /** * Get the public JWK set for verifying signed attestations (Agentic Resource Discovery) * @description Returns the public JSON Web Key Set used to verify ora's signed Agentic Resource Discovery (ARD) scorecard attestations. Also served at /.well-known/jwks.json via a rewrite. */ get: operations["ardGetJwks"]; }; "/api/web-bot-auth/directory": { /** * Get the Web Bot Auth signature agent card (public keys for verifying ora's crawler) * @description Returns ora's Web Bot Auth signature agent card: the client name, contact, stated purpose, and the public Ed25519 keys that verify HTTP Message Signatures (RFC 9421) on requests from ora's scanner. Bot-management verifiers resolve a signed request's `keyid` against the `kid` of a key here. Also served at /.well-known/http-message-signatures-directory via a rewrite, which is the path the specification fixes and the one verifiers fetch. `keys` is an empty array when no signing key is configured, so the endpoint is always valid JSON and can be probed unconditionally. */ get: operations["getWebBotAuthDirectory"]; }; "/api/journey/runs": { /** * Run an agent journey against a domain * @description Triggers a real agent run: an AI agent (harness + model) attempts a task (an intent) against the given domain, and ora records the trajectory and derives insights. Two-step flow: this endpoint returns fast with a run record whose `stream_url` serves the live trajectory as Server-Sent Events; poll GET /api/journey/runs/{id} instead if you do not want the stream. Two caller tiers. Anonymous: curated intents only (see GET /api/journey/intents) - the server derives the actual agent prompt from the intent id, so no free-text prompt can reach the engine. Keyed: a caller presenting an ora-issued partner API key ('Authorization: Bearer ', issued manually - contact ora) may instead send bounded free text (`intent.custom`, 4 to 300 characters, with `intent.domain` required), which the server anchors to the requested domain before dispatch and echoes back on the 201. Free text without a recognized key is a 401 with code CUSTOM_INTENT_REQUIRES_KEY; a curated body with a missing or unrecognized key is never an error and simply runs on the anonymous tier. The keyed free-text tier is also reachable from the ora CLI (ax deep-journey --task, v0.5+). Only publicly runnable agents are accepted (see GET /api/journey/agents). Unknown body fields are rejected (strict schema). Anonymous rate limits, three ways: a 20-per-minute burst cap per IP; a per-target cap of 100 runs per rolling 24h per (domain, intent, harness, model) - when a target is over the cap the response is HTTP 200 with the most recent stored run for that target (`rate_limited: true`) plus Retry-After, the freshness analog: a denied trigger still returns the newest result and consumes nothing; and a durable per-caller cap of 200 runs per rolling 24h per IP - exceeded, that one is a real 429 with `retry_after_ms` (there is no cached result to serve for a caller). Keyed rate limit, one way: 1000 runs per rolling 24h per key, exhaustion being the same 429 with `retry_after_ms`. A keyed caller skips both the burst cap and the per-target cap - free text fragments the target key, so a per-target window over it could never fill. Successful and capped responses carry X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset headers describing exactly one window: the per-target window on an anonymous response, the per-key caller window on a keyed one. Note: journey insights use the 5-layer journey taxonomy (discovery, identity, access, payments, experience), deliberately distinct from the 4 scoring layers of the audit report (POST /api/scan) - never map between the two. */ post: operations["createJourneyRun"]; }; "/api/journey/runs/{id}": { /** * Get a journey run record (non-streaming) * @description Returns the run record by id. While the run executes, `status` is 'running' - open the stream or poll. Once finished and persisted, `status` is 'succeeded' and the body carries `verdict`, `step_count` (billable steps - the same counter run pricing uses), and the full `result` (trajectory + insight + signals). `status` 'failed' is terminal with no result. `result` is present iff status is 'succeeded'. */ get: operations["getJourneyRun"]; }; "/api/journey/domains/{host}": { /** * Get or create a domain's agent journey * @description Returns the agent journey for a domain, running one only if there is not one already. Send only the domain and no body: ora selects the intent and the agent. Requires an ora-issued partner API key ('Authorization: Bearer ', issued manually - contact ora); without one, 401 PARTNER_KEY_REQUIRED. 200 means nothing was dispatched and an existing run answered, either finished (carrying its `result`) or still running (open `stream_url` to watch it). 201 means a new run was dispatched. Branch on `dispatched`. Two response views (see the `view` parameter): `graph` (JourneyDomainRunGraph, the default - `result` narrowed to what drawing the journey needs) and `full` (JourneyDomainRun, the complete projection, on `?view=full`). A finished run is served indefinitely; `run_age_seconds` gives its age. There is no per-caller rate limit on this endpoint. Each domain is capped at 100 runs per rolling 24h, and one domain cannot start two runs at once - both are per-domain, neither limits how many domains you may ask for or how fast. */ post: operations["getOrCreateDomainJourney"]; }; "/api/journey/runs/{id}/stream": { /** * Stream a journey run's trajectory as Server-Sent Events * @description The live trajectory SSE for a run created by POST /api/journey/runs. Event names (stable): `run_id` ({ run_id }), then progressive `trajectory` frames (cumulative snapshots, each a { steps, ... } object of JourneyTrajectoryStep items), optionally `processing` ({ message }) while insights generate, and finally exactly one of `result` (a JourneyRunResult) or `error` ({ message }). Reopening the stream of a finished run replays the stored result instead of re-executing the agent (pass ?replay=1 for paced frames, plus &quick=1 to skip the startup delay); a plain reopen renders the finished journey in one frame. Partner keys on the graph stream tier receive graph-narrowed frames instead (the JourneyDomainRunGraph subset: trajectory frames carry only `steps`, and the result frame only verdict, finished_at, intent_id, the step tree, and the insight summary); event names are identical. Long-lived: a live run can hold the connection for many minutes (function ceiling 800s). Rate limited 20 per minute per IP. */ get: operations["streamJourneyRun"]; }; "/api/journey/intents": { /** * List the curated journey intents * @description The curated tasks a public journey run can execute. Each entry carries the stable `id` (what POST /api/journey/runs takes), a short `label`, a one-line `hint`, and the user-facing `template` phrasing. Cached statically. */ get: operations["listJourneyIntents"]; }; "/api/journey/agents": { /** * List the publicly runnable journey agents * @description The agents an anonymous POST /api/journey/runs accepts - every listed (harness, model) pair is publicly runnable. Entries carry display metadata (label, variant, brand, blurb) plus the wire fields `harness` and `model`. Cached statically. */ get: operations["listJourneyAgents"]; }; "/api/feedback/{domain}": { /** * Get agent feedback for a product * @description Returns feedback submitted by AI agents about their experience using a product. Includes aggregate stats and individual reviews. */ get: operations["getAgentFeedback"]; }; } export type webhooks = Record; export interface components { schemas: { AuditCheck: { /** @description Stable check identifier (e.g. 'api-error-model'). Route fixes and dedupe by this - names are for display, ids are stable. */ id: string; /** @description Human-readable check title */ name: string; /** @description One of: pass | fail | warning | error | pending | na. Act on 'fail'/'warning'; skip 'na'; 'pending' means the scan has not finished. */ status: string; /** @description 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. */ score: number; /** @description Points available within this layer for this check (the within-layer denominator, not the 0-100 scale). */ maxScore: number; /** @description 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. */ estScoreGain?: number; /** @description Upside-only check: passing it raises the score, failing it never lowers the score. Build it only if the surface genuinely exists. */ bonus?: boolean; /** @description verified (counts toward the 0-100 score) or emerging (forward-looking, excluded from the denominator - a lower priority) */ maturity?: string; /** @description 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. */ tier?: string; /** @description Canonical spec / standard URL this check evaluates against, when one exists. Advisory - the link may change on any release. */ specUrl?: string; /** @description 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. */ mcpKind?: string; /** @description The URL of the MCP server this check scored against. Present only alongside mcpKind. */ mcpUrl?: string; /** @description What the scan observed for this check */ details?: string; /** @description Concrete fix that would make this check pass. The primary thing to act on. */ recommendation?: string; /** @description Why this check does not apply to this product - it is skipped, not a deduction */ naReason?: string; }; AuditLayer: { /** @description Layer id: discovery | accessibility | usability | payments (historical scans may carry retired ids) */ id: string; name: string; score: number; maxScore: number; checks: ({ /** @description Stable check identifier (e.g. 'api-error-model'). Route fixes and dedupe by this - names are for display, ids are stable. */ id: string; /** @description Human-readable check title */ name: string; /** @description One of: pass | fail | warning | error | pending | na. Act on 'fail'/'warning'; skip 'na'; 'pending' means the scan has not finished. */ status: string; /** @description 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. */ score: number; /** @description Points available within this layer for this check (the within-layer denominator, not the 0-100 scale). */ maxScore: number; /** @description 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. */ estScoreGain?: number; /** @description Upside-only check: passing it raises the score, failing it never lowers the score. Build it only if the surface genuinely exists. */ bonus?: boolean; /** @description verified (counts toward the 0-100 score) or emerging (forward-looking, excluded from the denominator - a lower priority) */ maturity?: string; /** @description 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. */ tier?: string; /** @description Canonical spec / standard URL this check evaluates against, when one exists. Advisory - the link may change on any release. */ specUrl?: string; /** @description 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. */ mcpKind?: string; /** @description The URL of the MCP server this check scored against. Present only alongside mcpKind. */ mcpUrl?: string; /** @description What the scan observed for this check */ details?: string; /** @description Concrete fix that would make this check pass. The primary thing to act on. */ recommendation?: string; /** @description Why this check does not apply to this product - it is skipped, not a deduction */ naReason?: string; })[]; }; AuditScanResult: { domain: string; name: string; score: number; /** @enum {number} */ scoreMax: 100; /** @description Present only when the caller passed ?include=essentials. Carries its own `score` - there is no separate top-level essentials score field. */ essentials?: { /** @description Alternate 0-100 'essentials' reading of the same scan: required checks carry 80 points, recommended 20, forward-looking signals are upside-only. Independent of `score` - the canonical ora score and grade do not use it. Null when too few checks apply to score. */ score: number | null; /** @description Server-owned copy for the score band; render verbatim */ label: string; /** @description Fixed 80-point budget, equal-weighted */ required: { earned: number; available: number; passing: number; total: number; }; /** @description Fixed 20-point budget, equal-weighted */ recommended: { earned: number; available: number; passing: number; total: number; }; bonusPoints: number; /** @description Bonus-side checks with any earned signal */ bonusSignals: number; /** @description Checks in the score denominator (required + recommended) */ eligibleChecks: number; activeSurfaces: ({ /** @enum {string} */ id: "web" | "api" | "auth" | "mcp" | "graphql" | "commerce"; label: string; score: number; passing: number; total: number; })[]; accessSignals: ({ id: string; label: string; description: string; /** @enum {string} */ state: "clear" | "mixed" | "blocked"; passing: number; total: number; })[]; /** @description One entry per eligible check id (post-averaging); every id in `issues` and `scoreEvidence` resolves here. Also includes zero-fraction bonus checks no array references - available but unearned signals */ checks: { [key: string]: { /** * @description Essentials-model tier. Deliberately diverges from this check's `tier` in `layers[]` - the two hold different values by design. * @enum {string} */ tier: "required" | "recommended" | "emerging"; /** @description Upside-only in the essentials model: can add, never subtract. Independent of `bonus` in `layers[]`. */ bonus: boolean; /** @description Earned share of this check, 0-1; duplicate per-MCP-kind runs are averaged into one entry */ fraction: number; /** @description How many per-MCP-kind runs were averaged into this entry */ occurrences: number; /** @description Uplift of a full fix in ESSENTIALS points. An estimate: it does not model surface activation. NOT comparable with `estScoreGain` in `layers[]`, which is denominated in canonical score points. */ essentialsGain: number | null; /** @description Present ONLY where the essentials model overrides ora's copy; absent otherwise, in which case render the `recommendation` from `layers[].checks[]`. */ recommendation?: string; }; }; /** @description Ids into `checks`: scored checks below full credit, pre-sorted (critical-access first, then required, then worst) - render in order, do not re-rank */ issues: string[]; scoreEvidence: { /** @description Ids into `checks` */ essential: string[]; /** @description Ids into `checks` */ recommended: string[]; /** @description Ids into `checks`; earned bonus signals only */ bonus: string[]; }; }; grade: string; gradeColor: string; ctaMessage: string | null; scannedAt: string; durationMs: number | null; /** * @description complete = all checks resolved; partial/stuck = still running - re-scan before acting * @enum {string} */ analysisStatus?: "complete" | "partial" | "stuck"; /** @description Ids of checks still resolving (empty/absent when analysisStatus is complete) */ pendingChecks?: string[]; layers: ({ /** @description Layer id: discovery | accessibility | usability | payments (historical scans may carry retired ids) */ id: string; name: string; score: number; maxScore: number; checks: ({ /** @description Stable check identifier (e.g. 'api-error-model'). Route fixes and dedupe by this - names are for display, ids are stable. */ id: string; /** @description Human-readable check title */ name: string; /** @description One of: pass | fail | warning | error | pending | na. Act on 'fail'/'warning'; skip 'na'; 'pending' means the scan has not finished. */ status: string; /** @description 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. */ score: number; /** @description Points available within this layer for this check (the within-layer denominator, not the 0-100 scale). */ maxScore: number; /** @description 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. */ estScoreGain?: number; /** @description Upside-only check: passing it raises the score, failing it never lowers the score. Build it only if the surface genuinely exists. */ bonus?: boolean; /** @description verified (counts toward the 0-100 score) or emerging (forward-looking, excluded from the denominator - a lower priority) */ maturity?: string; /** @description 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. */ tier?: string; /** @description Canonical spec / standard URL this check evaluates against, when one exists. Advisory - the link may change on any release. */ specUrl?: string; /** @description 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. */ mcpKind?: string; /** @description The URL of the MCP server this check scored against. Present only alongside mcpKind. */ mcpUrl?: string; /** @description What the scan observed for this check */ details?: string; /** @description Concrete fix that would make this check pass. The primary thing to act on. */ recommendation?: string; /** @description Why this check does not apply to this product - it is skipped, not a deduction */ naReason?: string; })[]; })[]; /** @description Actionable (fail/warning) checks ranked by ora: non-bonus first, then estimated uplift descending, capped at 6. Render verbatim - do not re-rank. */ topFixes: ({ /** @description Check id of the fix (stable) - matches a check in layers */ id: string; /** @description Id of the layer the check belongs to */ layerId: string; /** @description Human-readable check title */ name: string; /** @description Estimated points this fix would add to the overall 0-100 score. Absent when the uplift cannot be estimated. */ estScoreGain?: number; /** @description Upside-only check: build it only if the surface genuinely exists. Bonus fixes rank after non-bonus ones. */ bonus: boolean; /** @description Concrete fix that would make this check pass */ recommendation?: string; /** @description 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. */ mcpKind?: string; /** @description The URL of the MCP server this check scored against. Present only alongside mcpKind. */ mcpUrl?: string; })[]; /** @description Canonical ora.ai deep link for the domain */ url: string; generatedAt: string; /** @enum {string} */ source: "ora.ai"; /** * @description 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. * @enum {string} */ contractVersion: "1.25.0"; /** * @description Present only when this body is a stored result served by the freshness gate instead of a fresh scan. Absent on a live scan. * @enum {boolean} */ servedFromCache?: true; /** @description Age of the served stored result in seconds (also sent as the Age response header). Present with servedFromCache. */ resultAgeSeconds?: number; /** * @description 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. * @enum {boolean} */ mcpAuthRequired?: true; /** * @description 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. * @enum {string} */ urlKind?: "domain" | "mcp" | "mcp-app" | "ephemeral"; /** @description Canonical market category ora classified the domain into (e.g. 'Infrastructure & DevOps'). Advisory; absent when the domain is unclassified. */ category?: string; /** @description 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. */ agenticSummary?: string; /** @description The URL the scan actually fetched after following redirects. Distinct from `url`, which is the canonical ora.ai deep link for the domain. */ finalUrl?: string; /** @description 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. */ nextAction?: { /** @enum {string} */ kind: "scan"; /** @enum {string} */ method: "POST"; /** @enum {string} */ endpoint: "/api/scan"; body: { url: string; }; /** @description Why this request is the recommended next step */ reason: string; }; /** @description 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. */ competitors?: { [key: string]: unknown; } | null; }; AuditScoreResult: { domain: string; name: string; score: number; /** @enum {number} */ scoreMax: 100; /** @description Present only when the caller passed ?include=essentials. Carries its own `score` - there is no separate top-level essentials score field. */ essentials?: { /** @description Alternate 0-100 'essentials' reading of the same scan: required checks carry 80 points, recommended 20, forward-looking signals are upside-only. Independent of `score` - the canonical ora score and grade do not use it. Null when too few checks apply to score. */ score: number | null; /** @description Server-owned copy for the score band; render verbatim */ label: string; /** @description Fixed 80-point budget, equal-weighted */ required: { earned: number; available: number; passing: number; total: number; }; /** @description Fixed 20-point budget, equal-weighted */ recommended: { earned: number; available: number; passing: number; total: number; }; bonusPoints: number; /** @description Bonus-side checks with any earned signal */ bonusSignals: number; /** @description Checks in the score denominator (required + recommended) */ eligibleChecks: number; activeSurfaces: ({ /** @enum {string} */ id: "web" | "api" | "auth" | "mcp" | "graphql" | "commerce"; label: string; score: number; passing: number; total: number; })[]; accessSignals: ({ id: string; label: string; description: string; /** @enum {string} */ state: "clear" | "mixed" | "blocked"; passing: number; total: number; })[]; /** @description One entry per eligible check id (post-averaging); every id in `issues` and `scoreEvidence` resolves here. Also includes zero-fraction bonus checks no array references - available but unearned signals */ checks: { [key: string]: { /** * @description Essentials-model tier. Deliberately diverges from this check's `tier` in `layers[]` - the two hold different values by design. * @enum {string} */ tier: "required" | "recommended" | "emerging"; /** @description Upside-only in the essentials model: can add, never subtract. Independent of `bonus` in `layers[]`. */ bonus: boolean; /** @description Earned share of this check, 0-1; duplicate per-MCP-kind runs are averaged into one entry */ fraction: number; /** @description How many per-MCP-kind runs were averaged into this entry */ occurrences: number; /** @description Uplift of a full fix in ESSENTIALS points. An estimate: it does not model surface activation. NOT comparable with `estScoreGain` in `layers[]`, which is denominated in canonical score points. */ essentialsGain: number | null; /** @description Present ONLY where the essentials model overrides ora's copy; absent otherwise, in which case render the `recommendation` from `layers[].checks[]`. */ recommendation?: string; }; }; /** @description Ids into `checks`: scored checks below full credit, pre-sorted (critical-access first, then required, then worst) - render in order, do not re-rank */ issues: string[]; scoreEvidence: { /** @description Ids into `checks` */ essential: string[]; /** @description Ids into `checks` */ recommended: string[]; /** @description Ids into `checks`; earned bonus signals only */ bonus: string[]; }; }; grade: string; gradeColor: string; scannedAt: string | null; /** * @description complete = all checks resolved; partial/stuck = still running - re-scan before acting * @enum {string} */ analysisStatus?: "complete" | "partial" | "stuck"; /** @description Ids of checks still resolving (empty/absent when analysisStatus is complete) */ pendingChecks?: string[]; layers: ({ /** @description Layer id: discovery | accessibility | usability | payments (historical scans may carry retired ids) */ id: string; name: string; score: number; maxScore: number; checks: ({ /** @description Stable check identifier (e.g. 'api-error-model'). Route fixes and dedupe by this - names are for display, ids are stable. */ id: string; /** @description Human-readable check title */ name: string; /** @description One of: pass | fail | warning | error | pending | na. Act on 'fail'/'warning'; skip 'na'; 'pending' means the scan has not finished. */ status: string; /** @description 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. */ score: number; /** @description Points available within this layer for this check (the within-layer denominator, not the 0-100 scale). */ maxScore: number; /** @description 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. */ estScoreGain?: number; /** @description Upside-only check: passing it raises the score, failing it never lowers the score. Build it only if the surface genuinely exists. */ bonus?: boolean; /** @description verified (counts toward the 0-100 score) or emerging (forward-looking, excluded from the denominator - a lower priority) */ maturity?: string; /** @description 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. */ tier?: string; /** @description Canonical spec / standard URL this check evaluates against, when one exists. Advisory - the link may change on any release. */ specUrl?: string; /** @description 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. */ mcpKind?: string; /** @description The URL of the MCP server this check scored against. Present only alongside mcpKind. */ mcpUrl?: string; /** @description What the scan observed for this check */ details?: string; /** @description Concrete fix that would make this check pass. The primary thing to act on. */ recommendation?: string; /** @description Why this check does not apply to this product - it is skipped, not a deduction */ naReason?: string; })[]; })[]; /** @description Actionable (fail/warning) checks ranked by ora: non-bonus first, then estimated uplift descending, capped at 6. Render verbatim - do not re-rank. */ topFixes: ({ /** @description Check id of the fix (stable) - matches a check in layers */ id: string; /** @description Id of the layer the check belongs to */ layerId: string; /** @description Human-readable check title */ name: string; /** @description Estimated points this fix would add to the overall 0-100 score. Absent when the uplift cannot be estimated. */ estScoreGain?: number; /** @description Upside-only check: build it only if the surface genuinely exists. Bonus fixes rank after non-bonus ones. */ bonus: boolean; /** @description Concrete fix that would make this check pass */ recommendation?: string; /** @description 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. */ mcpKind?: string; /** @description The URL of the MCP server this check scored against. Present only alongside mcpKind. */ mcpUrl?: string; })[]; /** @description Canonical ora.ai deep link for the domain */ url: string; generatedAt: string; /** @enum {string} */ source: "ora.ai"; /** * @description 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. * @enum {string} */ contractVersion: "1.25.0"; /** @description Wall-clock duration of the scan that produced this stored result */ durationMs: number | null; /** * @description 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. * @enum {boolean} */ mcpAuthRequired?: true; /** * @description 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. * @enum {string} */ urlKind?: "domain" | "mcp" | "mcp-app" | "ephemeral"; /** @description Canonical market category ora classified the domain into (e.g. 'Infrastructure & DevOps'). Advisory; absent when the domain is unclassified. */ category?: string; /** @description 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. */ agenticSummary?: string; /** @description The URL the scan actually fetched after following redirects. Distinct from `url`, which is the canonical ora.ai deep link for the domain. */ finalUrl?: string; /** @description 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. */ nextAction?: { /** @enum {string} */ kind: "scan"; /** @enum {string} */ method: "POST"; /** @enum {string} */ endpoint: "/api/scan"; body: { url: string; }; /** @description Why this request is the recommended next step */ reason: string; }; /** @description 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. */ competitors?: { [key: string]: unknown; } | null; }; /** @description The complete catalog of scanner checks. Stability classes: every field's shape and presence rule is stable within a major version; layer ids are envelope identity, so removing or renaming one is a major version change; check ids never change meaning while they exist, but catalog membership may change on a minor, with a contract changelog entry and a deprecation window; every other value (scores, weights, layer assignments, applicability, tiers, maturity, prose) is advisory and may change on a minor version, with score-relevant changes recorded in the contract changelog. Gate CI on an explicit list of check ids. */ CheckCatalog: { /** * @description 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. * @enum {string} */ contractVersion: "1.25.0"; /** @description The four scored layers in scoring order, with display name and current weight. */ layers: { /** @description 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'. */ id: string; /** @description Display name for the layer. Advisory: it may change on a minor version. */ name: string; /** @description 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. */ weight: number; }[]; /** @description All catalogued checks. Array order is not contractual: key by id. */ checks: ({ /** @description 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. */ id: string; /** @description Human-readable check title. Advisory display prose. */ name: string; /** @description What the check verifies and why it matters to agents. Advisory display prose. */ description: string; /** @description 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. */ layer: string; /** @description 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. */ maxScore: number; /** @description Always present. A bonus check can only add score: a site that lacks the surface is never penalised for failing it. */ bonus: boolean; /** @description 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. */ applicability: string; /** @description Present only when applicability is 'api'. One of: rest | graphql | either - the API surface the check evaluates. */ protocol?: string; /** @description 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. */ appliesTo: string[]; /** @description 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. */ tier: string; /** @description 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. */ maturity: string; /** @description Always present. True when the check's underlying spec is a draft or emerging standard. */ draft: boolean; /** @description Always present. True marks a beta placeholder held at not-applicable - it runs but cannot affect any score; do not offer it as fixable. */ beta: boolean; /** @description Canonical spec or standard URL. Omitted when the check has none. */ specUrl?: string; /** @description Generic, target-independent fix guidance. Omitted for the few checks that have none. */ recommendation?: string; })[]; }; CatalogCheck: { /** @description 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. */ id: string; /** @description Human-readable check title. Advisory display prose. */ name: string; /** @description What the check verifies and why it matters to agents. Advisory display prose. */ description: string; /** @description 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. */ layer: string; /** @description 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. */ maxScore: number; /** @description Always present. A bonus check can only add score: a site that lacks the surface is never penalised for failing it. */ bonus: boolean; /** @description 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. */ applicability: string; /** @description Present only when applicability is 'api'. One of: rest | graphql | either - the API surface the check evaluates. */ protocol?: string; /** @description 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. */ appliesTo: string[]; /** @description 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. */ tier: string; /** @description 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. */ maturity: string; /** @description Always present. True when the check's underlying spec is a draft or emerging standard. */ draft: boolean; /** @description Always present. True marks a beta placeholder held at not-applicable - it runs but cannot affect any score; do not offer it as fixable. */ beta: boolean; /** @description Canonical spec or standard URL. Omitted when the check has none. */ specUrl?: string; /** @description Generic, target-independent fix guidance. Omitted for the few checks that have none. */ recommendation?: string; }; CatalogLayer: { /** @description 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'. */ id: string; /** @description Display name for the layer. Advisory: it may change on a minor version. */ name: string; /** @description 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. */ weight: number; }; /** @description Request body for POST /api/scan/checks - run a selected subset of catalogued checks against a URL and get per-check results back. */ RunChecksRequest: { /** @description The website, MCP server, or API URL to run checks against. A bare domain like example.com is accepted. */ url: string; /** @description 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. */ checkIds: string[]; /** @description 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. */ mcpUrl?: unknown; }; /** @description The outcome of a selective check run. The run always executes - results are never served from a cache. The response carries no aggregate score of any kind: per-check score and maxScore only, with GET /api/score/{domain} as the score surface. Results carry no tier or layer fields - join with GET /api/checks by id to group or rank them. */ RunChecksResponse: { /** * @description 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. * @enum {string} */ contractVersion: "1.25.0"; /** @description The apex domain derived from the requested URL. */ domain: string; /** @description The normalized URL the run targeted. */ url: string; /** * @description 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. * @enum {string} */ urlKind: "domain" | "mcp" | "mcp-app"; /** @description 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. */ storedScanUpdated: boolean; /** @description 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). */ results: components["schemas"]["RunChecksResultEntry"][]; }; RunChecksResultEntry: { /** @description The check id, as listed in GET /api/checks. */ id: string; /** @description Human-readable check title. */ name: string; /** * @description 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. * @enum {string} */ status: "pass" | "fail" | "warning" | "na" | "error"; /** @description Points the check earned on this run. */ score: number; /** @description The check's maximum points. Do not sum maxScore values into an aggregate - a selective response deliberately carries no overall score. */ maxScore: number; /** @description What was observed on the target. */ details: string; /** @description How to fix the finding. Omitted when no guidance applies. */ recommendation?: string; /** @description Why the check did not apply. Present on 'na' results, including requested ids that cannot apply to the detected kind. */ naReason?: string; /** * @description The classification of the MCP surface this entry scored against - one of: product | docs | other | app. Present on MCP fan-out entries. * @enum {string} */ mcpKind?: "product" | "docs" | "other" | "app"; /** @description The URL of the MCP server this entry scored against. Present on MCP fan-out entries. */ mcpUrl?: string; }; JourneyRun: { /** @description Run id - the handle for GET /api/journey/runs/{id} and the stream */ id: string; /** * @description Lifecycle status. failed is terminal; a failed run has no result. * @enum {string} */ status: "running" | "succeeded" | "failed"; /** @description Curated intent id (see GET /api/journey/intents) */ intent_id?: string; /** @description Target domain */ domain?: string; /** @description The agent configuration that ran (or is running) */ agent: { /** @description Agent harness wire name */ harness: string; /** @description Model the harness drives */ model: string; }; started_at: string; finished_at?: string; /** @description SSE stream for this run: run_id -> trajectory -> processing -> result | error */ stream_url: string; /** * @description Canonical success verdict, present once the run finished * @enum {string} */ verdict?: "satisfied" | "partial" | "unsatisfied" | "not_gradable"; /** @description Billable steps the run took (tool calls, excluding narration and filesystem ops), present once the run finished. This is the same counter run pricing uses. */ step_count?: number; /** @description Journey/audit contract version (shared SemVer; see docs) */ contractVersion: string; }; JourneyCreatedRun: { /** @description Run id - the handle for GET /api/journey/runs/{id} and the stream */ id: string; /** * @description Lifecycle status. failed is terminal; a failed run has no result. * @enum {string} */ status: "running" | "succeeded" | "failed"; /** @description Curated intent id (see GET /api/journey/intents) */ intent_id?: string; /** @description Target domain */ domain?: string; /** @description The agent configuration that ran (or is running) */ agent: { /** @description Agent harness wire name */ harness: string; /** @description Model the harness drives */ model: string; }; started_at: string; finished_at?: string; /** @description SSE stream for this run: run_id -> trajectory -> processing -> result | error */ stream_url: string; /** * @description Canonical success verdict, present once the run finished * @enum {string} */ verdict?: "satisfied" | "partial" | "unsatisfied" | "not_gradable"; /** @description Billable steps the run took (tool calls, excluding narration and filesystem ops), present once the run finished. This is the same counter run pricing uses. */ step_count?: number; /** @description Journey/audit contract version (shared SemVer; see docs) */ contractVersion: string; /** @description Echo of the custom intent text the caller sent on this request. Create-response-only: it is never returned by GET /api/journey/runs/{id} or the capped 200, and it is absent on curated runs. Experimental: may change or disappear without a major version. */ intent?: string; }; JourneyCappedRun: { /** @description Run id - the handle for GET /api/journey/runs/{id} and the stream */ id: string; /** * @description Lifecycle status. failed is terminal; a failed run has no result. * @enum {string} */ status: "running" | "succeeded" | "failed"; /** @description Curated intent id (see GET /api/journey/intents) */ intent_id?: string; /** @description Target domain */ domain?: string; /** @description The agent configuration that ran (or is running) */ agent: { /** @description Agent harness wire name */ harness: string; /** @description Model the harness drives */ model: string; }; started_at: string; finished_at?: string; /** @description SSE stream for this run: run_id -> trajectory -> processing -> result | error */ stream_url: string; /** * @description Canonical success verdict, present once the run finished * @enum {string} */ verdict?: "satisfied" | "partial" | "unsatisfied" | "not_gradable"; /** @description Billable steps the run took (tool calls, excluding narration and filesystem ops), present once the run finished. This is the same counter run pricing uses. */ step_count?: number; /** @description Journey/audit contract version (shared SemVer; see docs) */ contractVersion: string; /** * @description Marks a per-target-capped response: this is the cached latest run, not a fresh one * @enum {boolean} */ rate_limited: true; /** @description The per-target cap that was hit */ limit: { /** @description Runs allowed per target per window */ max: number; /** @description Window length in ms */ window_ms: number; }; /** @description Milliseconds until a per-target slot frees */ retry_after_ms: number; }; JourneyRunDetail: { /** @description Run id - the handle for GET /api/journey/runs/{id} and the stream */ id: string; /** * @description Lifecycle status. failed is terminal; a failed run has no result. * @enum {string} */ status: "running" | "succeeded" | "failed"; /** @description Curated intent id (see GET /api/journey/intents) */ intent_id?: string; /** @description Target domain */ domain?: string; /** @description The agent configuration that ran (or is running) */ agent: { /** @description Agent harness wire name */ harness: string; /** @description Model the harness drives */ model: string; }; started_at: string; finished_at?: string; /** @description SSE stream for this run: run_id -> trajectory -> processing -> result | error */ stream_url: string; /** * @description Canonical success verdict, present once the run finished * @enum {string} */ verdict?: "satisfied" | "partial" | "unsatisfied" | "not_gradable"; /** @description Billable steps the run took (tool calls, excluding narration and filesystem ops), present once the run finished. This is the same counter run pricing uses. */ step_count?: number; /** @description Journey/audit contract version (shared SemVer; see docs) */ contractVersion: string; /** @description The full run result. Present iff status is 'succeeded'. */ result?: { /** @description Run id. Absent on legacy persisted runs. */ run_id?: string; /** @description Curated intent id the run executed */ intent_id?: string; /** @description Target domain */ domain?: string; /** @description Agent harness: claude-agent-sdk | openai-agents | ash */ harness?: string; /** @description Model the harness drove */ model?: string; started_at?: string; finished_at?: string; /** @description Agent lifecycle outcome. Key 'did it work?' off verdict, not this. */ outcome: string; /** * @description Canonical success verdict, judge-authoritative and always resolved (legacy runs included). THE field to key success on. * @enum {string} */ verdict: "satisfied" | "partial" | "unsatisfied" | "not_gradable"; /** @description The agent's final free-text answer. Experimental: raw model output that can reflect content fetched from the target site - may change or disappear without a major version. */ agent_response?: string; /** @description Agent turns the run took */ num_turns?: number; /** @description Wall-clock run duration */ duration_ms?: number; /** @description Model spend for the run in USD. Experimental: tracks provider accounting and may change or disappear without a major version. */ cost_usd?: number; /** @description Total input tokens the run consumed. Experimental. */ input_tokens?: number; /** @description Total output tokens the run produced. Experimental. */ output_tokens?: number; /** @description Prompt-cache read tokens. Experimental. */ cache_read_tokens?: number; /** @description Prompt-cache write tokens. Experimental. */ cache_write_tokens?: number; /** @description The full step tree the agent took. Absent on legacy persisted runs. */ trajectory?: { /** @description Cumulative step tree, in emission order */ steps: ({ /** @description Index in steps[] - stable node identifier */ id: number; /** @description Agent turn this step belongs to */ turn?: number; /** * @description tool_call = an action against the target; text = narrative reasoning between actions * @enum {string} */ type: "tool_call" | "text"; /** @description Index of the parent step in this same array (tree edge) */ parent_id?: number; /** @description Action family: search | fetch | api_call | text | bash_fs | skill */ action?: string; /** @description Concrete tool the harness invoked */ tool?: string; /** @description Full URL the step targeted, when it targeted one */ url?: string; /** @description Host of the targeted URL */ url_host?: string; /** @description Path of the targeted URL */ url_path?: string; /** @description Query string, on search steps */ search_query?: string; /** @description direct = the agent navigated on its own; follow = it followed a link */ source?: string; /** @description Relation of the target to the run's domain: exact | subdomain | external */ anchor_relation?: string; /** @description Why the agent went here - the navigation-source attribution */ attribution?: { /** @description How the agent found this step: prior_knowledge | web_search | previous_artifact | other */ kind: string; /** @description Artifact kind this step was attributed to */ artifact_kind?: string; /** @description Signal id backing the attribution, when one matched */ signal_id?: string; /** @description Attribution method: heuristic | llm */ method?: string; /** @description Attribution confidence: strong | weak */ confidence?: string; /** @description The referring step, when attributed to a previous artifact */ referrer?: { /** @description Turn index of the referring step */ turn: number; /** @description Parent step id - the tree edge the trajectory graph renders */ step_id?: number; /** @description Artifact kind of the referring step (e.g. llms_txt, sitemap) */ artifact_kind?: string; /** @description URL path of the referring step */ url_path?: string; }; }; /** @description Key of the artifact this step fetched, when recognised */ artifact_key?: string; /** @description HTTP method of a fetch step */ fetch_method?: string; /** @description HTTP status the step observed, once resolved */ status?: number; /** @description Wall-clock duration of the step */ duration_ms?: number; /** @description Engine-provided display label for the node, when present */ label?: string; /** @description tool_call steps only: whether the call has resolved. False while the step is still in flight on a live stream. */ completed?: boolean; /** @description Name of the invoked skill, on skill steps */ skill_name?: string; /** @description text steps only: the narrative the agent emitted between actions. Advisory - raw model prose. */ text?: string; })[]; /** @description Compact fingerprint of the action sequence */ action_sequence: string; /** @description Ids of agent-readiness signals the run observed */ signals_observed: string[]; /** @description How the run resolved friction, when classified (e.g. succeeded_natively, needs_human_bridge) */ friction_outcome?: string; /** @description HTTP status distribution over the run's requests */ status_profile?: { count_2xx: number; count_3xx: number; count_4xx: number; count_5xx: number; /** @description Step id of the first 4xx/5xx, when any */ first_error_step?: number; /** @description Artifact kind of the first errored step */ first_error_kind?: string; }; /** @description Which discovery artifacts the agent probed for, and what it found */ well_known_probes: ({ /** @description Probed artifact (e.g. llms.txt, openapi.json) */ artifact: string; signal_id?: string; /** @description root_file | well_known | api_spec */ catalog: string; probed: boolean; fetched: boolean; attempt_count: number; first_status?: number; first_step_ord?: number; first_turn_index?: number; /** @description The agent looked for this artifact and it was absent */ near_miss: boolean; })[]; /** @description How the agent's visited paths were discovered */ path_origin_distribution?: { prior_knowledge: number; web_search: number; previous_artifact: number; other: number; }; /** @description Share of navigations that followed an on-page link rather than a guess */ link_following_rate: number; }; /** @description ora's generated read of the run. Absent on legacy persisted runs. */ insight?: { /** @description ora's generated one-paragraph read of the run */ summary: string; /** @description Bullet observations backing the summary */ key_observations: string[]; generated_at: string; /** @description The journey layers this intent touched. NOTE: this is the journey taxonomy (5 layers), deliberately distinct from the audit report's 4 scoring layers - never map between the two. */ journey_layers?: ("discovery" | "identity" | "access" | "payments" | "experience")[]; }; /** @description Flat per-run signal summary. Experimental; absent on legacy runs. */ run_signals?: { /** @description run_signals contract version (4 = current; answer_grounding/answer_efficiency present from v4) */ version: number; /** @description Engine-classified intent category */ intent_category: string; /** @description high | low */ category_confidence: string; /** @description The independent judge's grade: satisfied | partial | unsatisfied */ task_satisfied?: string; /** @description Engine-reconciled success verdict; prefer the top-level verdict field, which is always resolved */ verdict?: string; /** @description Agent lifecycle outcome. Key 'did it work?' off the top-level verdict, not this. */ outcome: string; friction_outcome?: string; bridge_classification?: string; first_action?: string; /** @description Where the agent went first */ first_target?: { page_role: string; anchor_relation?: string; source?: string; }; /** @description Engine's raw step count. Display step counts use the record's step_count (billable steps) instead. */ steps_count: number; search_count: number; /** @description Whether the agent reached the run's domain at all */ reached_anchor: boolean; link_following_rate: number; prior_knowledge_ratio: number; signals_observed: string[]; /** @description Per page-role reach summary */ page_reach: { [key: string]: { reached: boolean; first_turn: number; steps_to: number; }; }; /** @description Per artifact probe summary */ artifacts: { [key: string]: { probed: boolean; fetched: boolean; first_turn?: number; }; }; /** @description Step ids on the success path, when the run succeeded */ success_step_ids: number[]; success_route: { artifacts: { [key: string]: string; }; page_roles: { [key: string]: string; }; closer?: { artifact_key?: string; page_role?: string; }; }; /** @description Answer grounding: how much of the answer is based on the target site itself. Derived from the judge's answer-source steps. Added in run_signals v4. */ answer_grounding?: { /** @description Share of the answer's sources that are pages on the target site (0..1); null when no graded sources */ on_site_ratio: number | null; sources_total: number; on_site: number; third_party: number; /** @description External hosts the answer was built from */ third_party_hosts: string[]; }; /** @description Share of the run's fetches that fed the answer (0..1). Added in run_signals v4. */ answer_efficiency?: number | null; /** @description Judge-tagged answer-section source counts. Added in run_signals v4. */ answer_basis?: { sections_total: number; /** @description Sections carried by fetched pages on the target site */ from_site: number; /** @description Sections carried by fetched third-party pages */ from_external: number; /** @description Sections carried only by search-result snippets */ from_search: number; from_memory: number; /** @description Share of answer sections whose substance came from the target site's own pages (0..1) — the headline 'answer from your site' metric */ site_share: number; /** @description Share of answer sections whose substance came from the model's own knowledge rather than retrieved material (0..1) */ memory_share: number; }; /** @description Journey-taxonomy layers (5), distinct from the audit report's 4 scoring layers. Falls back to insight.journey_layers on v1/v2 signals; absent when the run was never classified. */ journey_layers?: ("discovery" | "identity" | "access" | "payments" | "experience")[]; }; }; }; JourneyDomainRun: { /** @description Run id - the handle for GET /api/journey/runs/{id} and the stream */ id: string; /** * @description Lifecycle status. failed is terminal; a failed run has no result. * @enum {string} */ status: "running" | "succeeded" | "failed"; /** @description Curated intent id (see GET /api/journey/intents) */ intent_id?: string; /** @description Target domain */ domain?: string; /** @description The agent configuration that ran (or is running) */ agent: { /** @description Agent harness wire name */ harness: string; /** @description Model the harness drives */ model: string; }; started_at: string; finished_at?: string; /** @description SSE stream for this run: run_id -> trajectory -> processing -> result | error */ stream_url: string; /** * @description Canonical success verdict, present once the run finished * @enum {string} */ verdict?: "satisfied" | "partial" | "unsatisfied" | "not_gradable"; /** @description Billable steps the run took (tool calls, excluding narration and filesystem ops), present once the run finished. This is the same counter run pricing uses. */ step_count?: number; /** @description Journey/audit contract version (shared SemVer; see docs) */ contractVersion: string; /** @description The full run result. Present iff status is 'succeeded'. */ result?: { /** @description Run id. Absent on legacy persisted runs. */ run_id?: string; /** @description Curated intent id the run executed */ intent_id?: string; /** @description Target domain */ domain?: string; /** @description Agent harness: claude-agent-sdk | openai-agents | ash */ harness?: string; /** @description Model the harness drove */ model?: string; started_at?: string; finished_at?: string; /** @description Agent lifecycle outcome. Key 'did it work?' off verdict, not this. */ outcome: string; /** * @description Canonical success verdict, judge-authoritative and always resolved (legacy runs included). THE field to key success on. * @enum {string} */ verdict: "satisfied" | "partial" | "unsatisfied" | "not_gradable"; /** @description The agent's final free-text answer. Experimental: raw model output that can reflect content fetched from the target site - may change or disappear without a major version. */ agent_response?: string; /** @description Agent turns the run took */ num_turns?: number; /** @description Wall-clock run duration */ duration_ms?: number; /** @description Model spend for the run in USD. Experimental: tracks provider accounting and may change or disappear without a major version. */ cost_usd?: number; /** @description Total input tokens the run consumed. Experimental. */ input_tokens?: number; /** @description Total output tokens the run produced. Experimental. */ output_tokens?: number; /** @description Prompt-cache read tokens. Experimental. */ cache_read_tokens?: number; /** @description Prompt-cache write tokens. Experimental. */ cache_write_tokens?: number; /** @description The full step tree the agent took. Absent on legacy persisted runs. */ trajectory?: { /** @description Cumulative step tree, in emission order */ steps: ({ /** @description Index in steps[] - stable node identifier */ id: number; /** @description Agent turn this step belongs to */ turn?: number; /** * @description tool_call = an action against the target; text = narrative reasoning between actions * @enum {string} */ type: "tool_call" | "text"; /** @description Index of the parent step in this same array (tree edge) */ parent_id?: number; /** @description Action family: search | fetch | api_call | text | bash_fs | skill */ action?: string; /** @description Concrete tool the harness invoked */ tool?: string; /** @description Full URL the step targeted, when it targeted one */ url?: string; /** @description Host of the targeted URL */ url_host?: string; /** @description Path of the targeted URL */ url_path?: string; /** @description Query string, on search steps */ search_query?: string; /** @description direct = the agent navigated on its own; follow = it followed a link */ source?: string; /** @description Relation of the target to the run's domain: exact | subdomain | external */ anchor_relation?: string; /** @description Why the agent went here - the navigation-source attribution */ attribution?: { /** @description How the agent found this step: prior_knowledge | web_search | previous_artifact | other */ kind: string; /** @description Artifact kind this step was attributed to */ artifact_kind?: string; /** @description Signal id backing the attribution, when one matched */ signal_id?: string; /** @description Attribution method: heuristic | llm */ method?: string; /** @description Attribution confidence: strong | weak */ confidence?: string; /** @description The referring step, when attributed to a previous artifact */ referrer?: { /** @description Turn index of the referring step */ turn: number; /** @description Parent step id - the tree edge the trajectory graph renders */ step_id?: number; /** @description Artifact kind of the referring step (e.g. llms_txt, sitemap) */ artifact_kind?: string; /** @description URL path of the referring step */ url_path?: string; }; }; /** @description Key of the artifact this step fetched, when recognised */ artifact_key?: string; /** @description HTTP method of a fetch step */ fetch_method?: string; /** @description HTTP status the step observed, once resolved */ status?: number; /** @description Wall-clock duration of the step */ duration_ms?: number; /** @description Engine-provided display label for the node, when present */ label?: string; /** @description tool_call steps only: whether the call has resolved. False while the step is still in flight on a live stream. */ completed?: boolean; /** @description Name of the invoked skill, on skill steps */ skill_name?: string; /** @description text steps only: the narrative the agent emitted between actions. Advisory - raw model prose. */ text?: string; })[]; /** @description Compact fingerprint of the action sequence */ action_sequence: string; /** @description Ids of agent-readiness signals the run observed */ signals_observed: string[]; /** @description How the run resolved friction, when classified (e.g. succeeded_natively, needs_human_bridge) */ friction_outcome?: string; /** @description HTTP status distribution over the run's requests */ status_profile?: { count_2xx: number; count_3xx: number; count_4xx: number; count_5xx: number; /** @description Step id of the first 4xx/5xx, when any */ first_error_step?: number; /** @description Artifact kind of the first errored step */ first_error_kind?: string; }; /** @description Which discovery artifacts the agent probed for, and what it found */ well_known_probes: ({ /** @description Probed artifact (e.g. llms.txt, openapi.json) */ artifact: string; signal_id?: string; /** @description root_file | well_known | api_spec */ catalog: string; probed: boolean; fetched: boolean; attempt_count: number; first_status?: number; first_step_ord?: number; first_turn_index?: number; /** @description The agent looked for this artifact and it was absent */ near_miss: boolean; })[]; /** @description How the agent's visited paths were discovered */ path_origin_distribution?: { prior_knowledge: number; web_search: number; previous_artifact: number; other: number; }; /** @description Share of navigations that followed an on-page link rather than a guess */ link_following_rate: number; }; /** @description ora's generated read of the run. Absent on legacy persisted runs. */ insight?: { /** @description ora's generated one-paragraph read of the run */ summary: string; /** @description Bullet observations backing the summary */ key_observations: string[]; generated_at: string; /** @description The journey layers this intent touched. NOTE: this is the journey taxonomy (5 layers), deliberately distinct from the audit report's 4 scoring layers - never map between the two. */ journey_layers?: ("discovery" | "identity" | "access" | "payments" | "experience")[]; }; /** @description Flat per-run signal summary. Experimental; absent on legacy runs. */ run_signals?: { /** @description run_signals contract version (4 = current; answer_grounding/answer_efficiency present from v4) */ version: number; /** @description Engine-classified intent category */ intent_category: string; /** @description high | low */ category_confidence: string; /** @description The independent judge's grade: satisfied | partial | unsatisfied */ task_satisfied?: string; /** @description Engine-reconciled success verdict; prefer the top-level verdict field, which is always resolved */ verdict?: string; /** @description Agent lifecycle outcome. Key 'did it work?' off the top-level verdict, not this. */ outcome: string; friction_outcome?: string; bridge_classification?: string; first_action?: string; /** @description Where the agent went first */ first_target?: { page_role: string; anchor_relation?: string; source?: string; }; /** @description Engine's raw step count. Display step counts use the record's step_count (billable steps) instead. */ steps_count: number; search_count: number; /** @description Whether the agent reached the run's domain at all */ reached_anchor: boolean; link_following_rate: number; prior_knowledge_ratio: number; signals_observed: string[]; /** @description Per page-role reach summary */ page_reach: { [key: string]: { reached: boolean; first_turn: number; steps_to: number; }; }; /** @description Per artifact probe summary */ artifacts: { [key: string]: { probed: boolean; fetched: boolean; first_turn?: number; }; }; /** @description Step ids on the success path, when the run succeeded */ success_step_ids: number[]; success_route: { artifacts: { [key: string]: string; }; page_roles: { [key: string]: string; }; closer?: { artifact_key?: string; page_role?: string; }; }; /** @description Answer grounding: how much of the answer is based on the target site itself. Derived from the judge's answer-source steps. Added in run_signals v4. */ answer_grounding?: { /** @description Share of the answer's sources that are pages on the target site (0..1); null when no graded sources */ on_site_ratio: number | null; sources_total: number; on_site: number; third_party: number; /** @description External hosts the answer was built from */ third_party_hosts: string[]; }; /** @description Share of the run's fetches that fed the answer (0..1). Added in run_signals v4. */ answer_efficiency?: number | null; /** @description Judge-tagged answer-section source counts. Added in run_signals v4. */ answer_basis?: { sections_total: number; /** @description Sections carried by fetched pages on the target site */ from_site: number; /** @description Sections carried by fetched third-party pages */ from_external: number; /** @description Sections carried only by search-result snippets */ from_search: number; from_memory: number; /** @description Share of answer sections whose substance came from the target site's own pages (0..1) — the headline 'answer from your site' metric */ site_share: number; /** @description Share of answer sections whose substance came from the model's own knowledge rather than retrieved material (0..1) */ memory_share: number; }; /** @description Journey-taxonomy layers (5), distinct from the audit report's 4 scoring layers. Falls back to insight.journey_layers on v1/v2 signals; absent when the run was never classified. */ journey_layers?: ("discovery" | "identity" | "access" | "payments" | "experience")[]; }; }; /** @description Whether this request started a new agent run. false means an existing run answered (finished, or already in flight) and nothing was spent; true means a fresh run was dispatched and is now running. */ dispatched: boolean; /** @description Age of the run being served, in seconds. Absent on a freshly dispatched run, and on a stored run whose timestamp does not parse. Use it to label how current the journey is - runs are served indefinitely, so this can be large. */ run_age_seconds?: number; /** @description Present only when the domain's per-target run cap is saturated, so the run being served is the newest stored one rather than a fresh dispatch. Milliseconds until a slot frees. */ retry_after_ms?: number; }; JourneyDomainRunGraph: { /** @description Run id - the handle for GET /api/journey/runs/{id} and the stream */ id: string; /** * @description Lifecycle status. failed is terminal; a failed run has no result. * @enum {string} */ status: "running" | "succeeded" | "failed"; /** @description Curated intent id (see GET /api/journey/intents) */ intent_id?: string; /** @description Target domain */ domain?: string; /** @description The agent configuration that ran (or is running) */ agent: { /** @description Agent harness wire name */ harness: string; /** @description Model the harness drives */ model: string; }; started_at: string; finished_at?: string; /** @description SSE stream for this run: run_id -> trajectory -> processing -> result | error */ stream_url: string; /** * @description Canonical success verdict, present once the run finished * @enum {string} */ verdict?: "satisfied" | "partial" | "unsatisfied" | "not_gradable"; /** @description Billable steps the run took (tool calls, excluding narration and filesystem ops), present once the run finished. This is the same counter run pricing uses. */ step_count?: number; /** @description Journey/audit contract version (shared SemVer; see docs) */ contractVersion: string; /** @description The graph-view run result. Present iff status is 'succeeded'. */ result?: { /** * @description Canonical success verdict, judge-authoritative and always resolved. THE field to key success on. * @enum {string} */ verdict: "satisfied" | "partial" | "unsatisfied" | "not_gradable"; finished_at?: string; /** @description Curated intent id the run executed */ intent_id?: string; /** @description The step tree the agent took, steps only. Absent on legacy persisted runs. */ trajectory?: { /** @description Cumulative step tree, in emission order - the graph view's entire trajectory */ steps: ({ /** @description Index in steps[] - stable node identifier */ id: number; /** @description Agent turn this step belongs to */ turn?: number; /** * @description tool_call = an action against the target; text = narrative reasoning between actions * @enum {string} */ type: "tool_call" | "text"; /** @description Index of the parent step in this same array (tree edge) */ parent_id?: number; /** @description Action family: search | fetch | api_call | text | bash_fs | skill */ action?: string; /** @description Concrete tool the harness invoked */ tool?: string; /** @description Host of the targeted URL */ url_host?: string; /** @description Path of the targeted URL */ url_path?: string; /** @description Query string, on search steps */ search_query?: string; /** @description Why the agent went here - the navigation-source attribution */ attribution?: { /** @description How the agent found this step: prior_knowledge | web_search | previous_artifact | other */ kind: string; /** @description Artifact kind this step was attributed to */ artifact_kind?: string; /** @description The referring step, when attributed to a previous artifact */ referrer?: { /** @description Turn index of the referring step */ turn: number; /** @description Parent step id - the tree edge the trajectory graph renders */ step_id?: number; }; }; /** @description HTTP method of a fetch step */ fetch_method?: string; /** @description HTTP status the step observed, once resolved */ status?: number; /** @description Wall-clock duration of the step */ duration_ms?: number; /** @description Engine-provided display label for the node, when present */ label?: string; /** @description tool_call steps only: whether the call has resolved. False while the step is still in flight on a live stream. */ completed?: boolean; /** @description Name of the invoked skill, on skill steps */ skill_name?: string; /** @description text steps only: the narrative the agent emitted between actions. Advisory - raw model prose. */ text?: string; })[]; }; /** @description ora's generated summary of the run. Absent on legacy persisted runs. */ insight?: { /** @description ora's generated one-paragraph read of the run */ summary: string; }; }; /** @description Whether this request started a new agent run. false means an existing run answered (finished, or already in flight) and nothing was spent; true means a fresh run was dispatched and is now running. */ dispatched: boolean; /** @description Age of the run being served, in seconds. Absent on a freshly dispatched run, and on a stored run whose timestamp does not parse. Use it to label how current the journey is - runs are served indefinitely, so this can be large. */ run_age_seconds?: number; /** @description Present only when the domain's per-target run cap is saturated, so the run being served is the newest stored one rather than a fresh dispatch. Milliseconds until a slot frees. */ retry_after_ms?: number; }; JourneyRunResult: { /** @description Run id. Absent on legacy persisted runs. */ run_id?: string; /** @description Curated intent id the run executed */ intent_id?: string; /** @description Target domain */ domain?: string; /** @description Agent harness: claude-agent-sdk | openai-agents | ash */ harness?: string; /** @description Model the harness drove */ model?: string; started_at?: string; finished_at?: string; /** @description Agent lifecycle outcome. Key 'did it work?' off verdict, not this. */ outcome: string; /** * @description Canonical success verdict, judge-authoritative and always resolved (legacy runs included). THE field to key success on. * @enum {string} */ verdict: "satisfied" | "partial" | "unsatisfied" | "not_gradable"; /** @description The agent's final free-text answer. Experimental: raw model output that can reflect content fetched from the target site - may change or disappear without a major version. */ agent_response?: string; /** @description Agent turns the run took */ num_turns?: number; /** @description Wall-clock run duration */ duration_ms?: number; /** @description Model spend for the run in USD. Experimental: tracks provider accounting and may change or disappear without a major version. */ cost_usd?: number; /** @description Total input tokens the run consumed. Experimental. */ input_tokens?: number; /** @description Total output tokens the run produced. Experimental. */ output_tokens?: number; /** @description Prompt-cache read tokens. Experimental. */ cache_read_tokens?: number; /** @description Prompt-cache write tokens. Experimental. */ cache_write_tokens?: number; /** @description The full step tree the agent took. Absent on legacy persisted runs. */ trajectory?: { /** @description Cumulative step tree, in emission order */ steps: ({ /** @description Index in steps[] - stable node identifier */ id: number; /** @description Agent turn this step belongs to */ turn?: number; /** * @description tool_call = an action against the target; text = narrative reasoning between actions * @enum {string} */ type: "tool_call" | "text"; /** @description Index of the parent step in this same array (tree edge) */ parent_id?: number; /** @description Action family: search | fetch | api_call | text | bash_fs | skill */ action?: string; /** @description Concrete tool the harness invoked */ tool?: string; /** @description Full URL the step targeted, when it targeted one */ url?: string; /** @description Host of the targeted URL */ url_host?: string; /** @description Path of the targeted URL */ url_path?: string; /** @description Query string, on search steps */ search_query?: string; /** @description direct = the agent navigated on its own; follow = it followed a link */ source?: string; /** @description Relation of the target to the run's domain: exact | subdomain | external */ anchor_relation?: string; /** @description Why the agent went here - the navigation-source attribution */ attribution?: { /** @description How the agent found this step: prior_knowledge | web_search | previous_artifact | other */ kind: string; /** @description Artifact kind this step was attributed to */ artifact_kind?: string; /** @description Signal id backing the attribution, when one matched */ signal_id?: string; /** @description Attribution method: heuristic | llm */ method?: string; /** @description Attribution confidence: strong | weak */ confidence?: string; /** @description The referring step, when attributed to a previous artifact */ referrer?: { /** @description Turn index of the referring step */ turn: number; /** @description Parent step id - the tree edge the trajectory graph renders */ step_id?: number; /** @description Artifact kind of the referring step (e.g. llms_txt, sitemap) */ artifact_kind?: string; /** @description URL path of the referring step */ url_path?: string; }; }; /** @description Key of the artifact this step fetched, when recognised */ artifact_key?: string; /** @description HTTP method of a fetch step */ fetch_method?: string; /** @description HTTP status the step observed, once resolved */ status?: number; /** @description Wall-clock duration of the step */ duration_ms?: number; /** @description Engine-provided display label for the node, when present */ label?: string; /** @description tool_call steps only: whether the call has resolved. False while the step is still in flight on a live stream. */ completed?: boolean; /** @description Name of the invoked skill, on skill steps */ skill_name?: string; /** @description text steps only: the narrative the agent emitted between actions. Advisory - raw model prose. */ text?: string; })[]; /** @description Compact fingerprint of the action sequence */ action_sequence: string; /** @description Ids of agent-readiness signals the run observed */ signals_observed: string[]; /** @description How the run resolved friction, when classified (e.g. succeeded_natively, needs_human_bridge) */ friction_outcome?: string; /** @description HTTP status distribution over the run's requests */ status_profile?: { count_2xx: number; count_3xx: number; count_4xx: number; count_5xx: number; /** @description Step id of the first 4xx/5xx, when any */ first_error_step?: number; /** @description Artifact kind of the first errored step */ first_error_kind?: string; }; /** @description Which discovery artifacts the agent probed for, and what it found */ well_known_probes: ({ /** @description Probed artifact (e.g. llms.txt, openapi.json) */ artifact: string; signal_id?: string; /** @description root_file | well_known | api_spec */ catalog: string; probed: boolean; fetched: boolean; attempt_count: number; first_status?: number; first_step_ord?: number; first_turn_index?: number; /** @description The agent looked for this artifact and it was absent */ near_miss: boolean; })[]; /** @description How the agent's visited paths were discovered */ path_origin_distribution?: { prior_knowledge: number; web_search: number; previous_artifact: number; other: number; }; /** @description Share of navigations that followed an on-page link rather than a guess */ link_following_rate: number; }; /** @description ora's generated read of the run. Absent on legacy persisted runs. */ insight?: { /** @description ora's generated one-paragraph read of the run */ summary: string; /** @description Bullet observations backing the summary */ key_observations: string[]; generated_at: string; /** @description The journey layers this intent touched. NOTE: this is the journey taxonomy (5 layers), deliberately distinct from the audit report's 4 scoring layers - never map between the two. */ journey_layers?: ("discovery" | "identity" | "access" | "payments" | "experience")[]; }; /** @description Flat per-run signal summary. Experimental; absent on legacy runs. */ run_signals?: { /** @description run_signals contract version (4 = current; answer_grounding/answer_efficiency present from v4) */ version: number; /** @description Engine-classified intent category */ intent_category: string; /** @description high | low */ category_confidence: string; /** @description The independent judge's grade: satisfied | partial | unsatisfied */ task_satisfied?: string; /** @description Engine-reconciled success verdict; prefer the top-level verdict field, which is always resolved */ verdict?: string; /** @description Agent lifecycle outcome. Key 'did it work?' off the top-level verdict, not this. */ outcome: string; friction_outcome?: string; bridge_classification?: string; first_action?: string; /** @description Where the agent went first */ first_target?: { page_role: string; anchor_relation?: string; source?: string; }; /** @description Engine's raw step count. Display step counts use the record's step_count (billable steps) instead. */ steps_count: number; search_count: number; /** @description Whether the agent reached the run's domain at all */ reached_anchor: boolean; link_following_rate: number; prior_knowledge_ratio: number; signals_observed: string[]; /** @description Per page-role reach summary */ page_reach: { [key: string]: { reached: boolean; first_turn: number; steps_to: number; }; }; /** @description Per artifact probe summary */ artifacts: { [key: string]: { probed: boolean; fetched: boolean; first_turn?: number; }; }; /** @description Step ids on the success path, when the run succeeded */ success_step_ids: number[]; success_route: { artifacts: { [key: string]: string; }; page_roles: { [key: string]: string; }; closer?: { artifact_key?: string; page_role?: string; }; }; /** @description Answer grounding: how much of the answer is based on the target site itself. Derived from the judge's answer-source steps. Added in run_signals v4. */ answer_grounding?: { /** @description Share of the answer's sources that are pages on the target site (0..1); null when no graded sources */ on_site_ratio: number | null; sources_total: number; on_site: number; third_party: number; /** @description External hosts the answer was built from */ third_party_hosts: string[]; }; /** @description Share of the run's fetches that fed the answer (0..1). Added in run_signals v4. */ answer_efficiency?: number | null; /** @description Judge-tagged answer-section source counts. Added in run_signals v4. */ answer_basis?: { sections_total: number; /** @description Sections carried by fetched pages on the target site */ from_site: number; /** @description Sections carried by fetched third-party pages */ from_external: number; /** @description Sections carried only by search-result snippets */ from_search: number; from_memory: number; /** @description Share of answer sections whose substance came from the target site's own pages (0..1) — the headline 'answer from your site' metric */ site_share: number; /** @description Share of answer sections whose substance came from the model's own knowledge rather than retrieved material (0..1) */ memory_share: number; }; /** @description Journey-taxonomy layers (5), distinct from the audit report's 4 scoring layers. Falls back to insight.journey_layers on v1/v2 signals; absent when the run was never classified. */ journey_layers?: ("discovery" | "identity" | "access" | "payments" | "experience")[]; }; }; JourneyTrajectoryStep: { /** @description Index in steps[] - stable node identifier */ id: number; /** @description Agent turn this step belongs to */ turn?: number; /** * @description tool_call = an action against the target; text = narrative reasoning between actions * @enum {string} */ type: "tool_call" | "text"; /** @description Index of the parent step in this same array (tree edge) */ parent_id?: number; /** @description Action family: search | fetch | api_call | text | bash_fs | skill */ action?: string; /** @description Concrete tool the harness invoked */ tool?: string; /** @description Full URL the step targeted, when it targeted one */ url?: string; /** @description Host of the targeted URL */ url_host?: string; /** @description Path of the targeted URL */ url_path?: string; /** @description Query string, on search steps */ search_query?: string; /** @description direct = the agent navigated on its own; follow = it followed a link */ source?: string; /** @description Relation of the target to the run's domain: exact | subdomain | external */ anchor_relation?: string; /** @description Why the agent went here - the navigation-source attribution */ attribution?: { /** @description How the agent found this step: prior_knowledge | web_search | previous_artifact | other */ kind: string; /** @description Artifact kind this step was attributed to */ artifact_kind?: string; /** @description Signal id backing the attribution, when one matched */ signal_id?: string; /** @description Attribution method: heuristic | llm */ method?: string; /** @description Attribution confidence: strong | weak */ confidence?: string; /** @description The referring step, when attributed to a previous artifact */ referrer?: { /** @description Turn index of the referring step */ turn: number; /** @description Parent step id - the tree edge the trajectory graph renders */ step_id?: number; /** @description Artifact kind of the referring step (e.g. llms_txt, sitemap) */ artifact_kind?: string; /** @description URL path of the referring step */ url_path?: string; }; }; /** @description Key of the artifact this step fetched, when recognised */ artifact_key?: string; /** @description HTTP method of a fetch step */ fetch_method?: string; /** @description HTTP status the step observed, once resolved */ status?: number; /** @description Wall-clock duration of the step */ duration_ms?: number; /** @description Engine-provided display label for the node, when present */ label?: string; /** @description tool_call steps only: whether the call has resolved. False while the step is still in flight on a live stream. */ completed?: boolean; /** @description Name of the invoked skill, on skill steps */ skill_name?: string; /** @description text steps only: the narrative the agent emitted between actions. Advisory - raw model prose. */ text?: string; }; /** @description The default response body of POST /api/scan and GET /api/score/{domain}. Fields not listed below may be present: they are ora internals, are not part of the contract, and may change or disappear in any release without a major version bump. Pass `?format=audit` for the versioned, fully documented shape (AuditScanResult / AuditScoreResult). */ ScanResult: { /** @description The scanned domain (pre-redirect). Compare with new URL(finalUrl).hostname to detect cross-domain redirects. */ domain?: string; /** @description The normalized URL */ url?: string; /** @description The final URL after redirects. If the host differs from domain, the score reflects a redirected site. */ finalUrl?: string; /** @description Overall score (0-100) */ score?: number; /** @description Maximum possible score */ maxScore?: number; /** * @description Letter grade (A+ >= 95, A >= 86, B >= 70, C >= 48, D >= 28, F < 28) * @enum {string} */ grade?: "A+" | "A" | "B" | "C" | "D" | "F"; /** * @description 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. * @enum {string} */ analysisStatus?: "complete" | "partial" | "stuck"; /** @description IDs of checks not yet resolved. Empty when analysisStatus is 'complete'. Poll GET /api/score/{domain} until this is empty for a final score. */ pendingChecks?: string[]; /** @description Call-to-action message based on score */ ctaMessage?: string; /** * @description CTA tier * @enum {string} */ ctaTier?: "top" | "high" | "mid" | "low"; /** @description Breakdown by scoring layer */ layers?: components["schemas"]["LayerResult"][]; /** * Format: date-time * @description When the scan was performed */ scannedAt?: string; /** @description Scan duration in milliseconds */ durationMs?: number; /** @description 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. */ agenticSummary?: string; /** * @description 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. * @enum {string} */ urlKind?: "domain" | "mcp" | "mcp-app" | "ephemeral"; /** @description 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. */ mcpAuthRequired?: boolean; /** * @description 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. * @enum {boolean} */ servedFromCache?: true; /** @description Age of the served stored result in seconds. Sent with servedFromCache, and mirrored in the Age response header. */ resultAgeSeconds?: number; [key: string]: unknown; }; LayerResult: { /** @description Layer identifier */ id?: string; /** @description Layer display name */ name?: string; /** @description Layer description */ description?: string; checks?: components["schemas"]["CheckResult"][]; /** @description Layer score */ score?: number; /** @description Layer maximum possible score */ maxScore?: number; }; CheckResult: { /** @description Check identifier */ id?: string; /** @description Check display name */ name?: string; /** @description What this check tests */ description?: string; /** * @description Check result status. 'pending' = deep scan not yet resolved; 'na' = not applicable for this product. * @enum {string} */ status?: "pass" | "fail" | "warning" | "error" | "pending" | "na"; /** @description Points earned */ score?: number; /** @description Maximum points for this check */ maxScore?: number; /** @description Human-readable explanation of the result */ details?: string; /** @description Optional. Concrete fix that would make this check pass. Absent on passing checks and on some N/A rows. */ recommendation?: string; /** @description 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. */ bonus?: boolean; /** * @description 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. * @enum {string} */ maturity?: "verified" | "emerging"; /** * @description 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. * @enum {string} */ tier?: "required" | "recommended" | "emerging"; /** @description Optional. Why the check does not apply to this product. Present on 'na' rows; the check is skipped, not deducted. */ naReason?: string; /** @description 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. */ estScoreGain?: number; }; /** @description The competitive slice around a domain, drawn from the leaderboard. Both arrays hold lean rows (never full scan reports). The two slices are views over one ranked board and can overlap - for a top-5 domain the same row appears in both, so do not concatenate them naively. Mirrors the Competitive Analytics panel on the score page. */ CompetitorSet: { /** @description 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. */ category?: string; /** @description Top 5 of the category by score, descending. */ leaders?: components["schemas"]["Competitor"][]; /** @description Up to 5 rows centred on the queried domain: 2 above, self, 2 below, within the same category pool. Always contains the `isSelf` row. */ neighbors?: components["schemas"]["Competitor"][]; }; Competitor: { /** @description 1-based position on the category board the slices are drawn from. */ rank?: number; /** @description Competitor domain */ domain?: string; /** @description Competitor display name */ name?: string; /** @description Agent-readiness score (0-100) */ score?: number; /** * @description Letter grade * @enum {string} */ grade?: "A+" | "A" | "B" | "C" | "D" | "F"; /** @description 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. */ isSelf?: boolean; }; /** * @description Machine-parseable next step for an agent caller. Tells clients exactly which endpoint to hit and with what body to recover a missing or stuck score. * @example { * "method": "POST", * "endpoint": "/api/scan", * "body": { * "url": "stripe.com" * }, * "description": "Trigger a fresh scan for this domain" * } */ NextAction: { /** * @description HTTP method * @enum {string} */ method: "POST"; /** @description API path to call (e.g. /api/scan) */ endpoint: string; /** @description Body to POST. For /api/scan this is { url }. */ body: { /** @description Domain or URL to scan */ url?: string; }; /** @description Human-readable explanation of the recovery step */ description: string; }; /** * @description Returned by GET /api/score/{domain} (and similar read endpoints) when no cached score exists for the domain. The recovery envelope tells agent callers exactly how to recover: `next_action` on the default body, `nextAction` under `?format=audit`. Exactly one of the two is always present. * @example { * "error": "No cached score for this domain", * "code": "DOMAIN_NOT_SCANNED", * "domain": "stripe.com", * "next_action": { * "method": "POST", * "endpoint": "/api/scan", * "body": { * "url": "stripe.com" * }, * "description": "Trigger a fresh scan for this domain" * } * } */ NotScannedResponse: { /** @description Human-readable error message */ error: string; /** * @description Machine-readable error code * @enum {string} */ code: "DOMAIN_NOT_SCANNED"; /** @description Normalized domain that was looked up */ domain: string; next_action?: components["schemas"]["NextAction"]; /** @description Present instead of `next_action` when the request passed `?format=audit`. Same recovery step, camelCase and versioned, matching the `nextAction` on AuditScanResult / AuditScoreResult. */ nextAction?: { /** @enum {string} */ kind: "scan"; /** @enum {string} */ method: "POST"; /** @enum {string} */ endpoint: "/api/scan"; body: { url: string; }; /** @description Why this request is the recommended next step */ reason: string; }; }; /** * @description Error envelope for the Agentic Resource Discovery (ARD) routes, matching the ARD registry spec (Appendix B). Distinct from ora's house ErrorResponse: ARD uses `errorCode` + `message` with the spec's standard codes. * @example { * "errorCode": "RATE_LIMIT_EXCEEDED", * "message": "Too many requests - please try again later" * } */ ArdErrorResponse: { /** * @description Machine-readable ARD error code (spec Appendix B). * @enum {string} */ errorCode: "INVALID_ARGUMENT" | "UNAUTHENTICATED" | "NOT_FOUND" | "RATE_LIMIT_EXCEEDED" | "INTERNAL_ERROR"; /** @description Human-readable error explanation. */ message: string; /** @description Optional structured validation detail (Zod flatten) on INVALID_ARGUMENT. */ details?: { [key: string]: unknown; }; /** @description Optional recovery hint (e.g. /api/scan on NOT_FOUND). */ next?: string; }; McpAuthRequiredResponse: { /** @description Why the MCP server could not be inspected. */ error: string; /** @constant */ code: "MCP_AUTH_REQUIRED"; /** @constant */ mcpAuthRequired: true; /** @description The MCP endpoint that required authentication. */ mcpUrl: string; /** @enum {string} */ urlKind: "mcp" | "mcp-app"; }; /** * @description ora's house error envelope. `error` is always present; the other fields depend on which guard rejected the request, so a client should branch on `code` / `retry_after_ms` being present rather than assume them. * @example { * "error": "Daily scan limit reached (30 per day). Try again in about 4 hours.", * "retry_after_ms": 14400000 * } */ ErrorResponse: { /** @description Error type or human-readable message (e.g. 'Not found', 'Rate limited') */ error: string; /** @description Optional longer explanation with recovery steps */ message?: string; /** @description Optional machine-readable error code (e.g. EPHEMERAL_CLOBBER, ENDPOINT_NOT_FOUND, RATE_LIMITED, INVALID_DOMAIN) */ code?: string; /** @description 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. */ retry_after_ms?: number; /** @description Optional structured validation detail (Zod flatten) on a schema rejection. */ details?: { [key: string]: unknown; }; }; DiscoverResult: { /** @description Product domain */ domain?: string; /** @description Product name */ name?: string; /** @description Product category */ category?: string; /** @description Agent-readiness score (0-100) */ score?: number; /** @enum {string} */ grade?: "A" | "B" | "C" | "D" | "F"; /** @description Product tags */ tags?: string[]; /** @description Relevance to your query */ matchScore?: number; }; AgentFeedback: { id?: number; domain?: string; /** @description Unique agent identifier */ agent_id?: string; /** @description Original user request that led to this interaction */ user_intent?: string | null; /** @description What the agent was trying to do */ task_description?: string; /** @enum {string} */ outcome?: "success" | "partial_failure" | "failure"; /** @description Detailed feedback */ content?: string; friction_points?: string[]; /** @enum {string} */ recommendation?: "recommend" | "neutral" | "not_recommend"; /** @description 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. */ layer_scores?: { discovery?: number; identity?: number; access?: number; payments?: number; experience?: number; integration?: number; "in-agent-experience"?: number; } | null; /** Format: date-time */ created_at?: string; }; FeedbackStats: { /** @description Total feedback count */ total?: number; /** @description Proportion of successful outcomes (0-1) */ success_rate?: number; /** @description Proportion recommending (0-1) */ recommend_rate?: number; outcomes?: { success?: number; partial_failure?: number; failure?: number; }; recommendations?: { recommend?: number; neutral?: number; not_recommend?: number; }; }; }; responses: never; parameters: never; requestBodies: never; headers: { /** @description Present only once this endpoint (or the request's API version) has been deprecated: the date the deprecation took effect, per the IETF Deprecation header. Absent on every endpoint today - its appearance is the machine-readable start of the deprecation window described in this API's versioning policy. */ Deprecation: string; /** @description Present only once a removal date has been committed for this endpoint (RFC 8594): the HTTP-date after which the endpoint stops answering. Per the versioning policy, this is always at least 90 days after the Deprecation header first appears, and the migration path is documented in the API reference at /docs. */ Sunset: string; }; pathItems: never; } export type $defs = Record; export type external = Record; export interface operations { /** * Scan a domain, MCP server URL, or MCP App URL for agent-readiness * @description Runs a full agent-readiness scan on the given URL. Accepts a domain, MCP server URL, or MCP App URL (server that supports the MCP Apps extension `io.modelcontextprotocol/ui`) - the server auto-detects which kind of input was provided and selects the appropriate check set. Catalog-style listing pages are folded into the `mcp` kind by classifying the first validated embedded MCP URL. Returns score, grade, and detailed layer breakdown. The response includes an optional `urlKind` field indicating the detected kind ('domain', 'mcp', or 'mcp-app'). Scoring completes within the request, but deeper analysis can continue asynchronously afterwards: when the returned analysisStatus is 'partial', the response is a 202 Accepted with a Location header pointing at the polling endpoint for the remaining work; a 200 means analysis is already complete. For real-time progress updates, use GET /api/scan/stream which serves a text/event-stream. A complete stored result younger than the freshness window is returned as-is (servedFromCache, resultAgeSeconds, Age header) without running or persisting a scan - pass force: true, or widen/narrow maxAgeSeconds, to control that. Rate limited two ways: 10 requests per minute per IP (burst) and a durable daily scan budget shared with the other scan entry points - both return 429 with a Retry-After header. */ scanDomain: { parameters: { query?: { /** @description Pass 1 to include a `competitors` object in the response: the category top-5 leaders plus the neighbor window (2 above / self / 2 below) drawn from the leaderboard. Returned only for domains with a market category - unclassified domains (Community, or no leaderboard row) get `competitors: null`. Omitted by default so the plain response stays lean. */ competitors?: "1"; /** @description Pass `audit` to receive the versioned, allowlisted audit shape (AuditScanResult / AuditScoreResult) instead of the default body: every field is documented, carries a `contractVersion`, and internal fields are dropped. Omit it and the response is unchanged from previous releases. On GET /api/scan/stream, only the terminal `scan_complete` event's `result` is projected - all other events are identical to the default stream. */ format?: "audit"; /** @description Opt-in expansion list (comma-separated). `essentials` adds one response key, `essentials`: an alternate reading of the same scan carrying its own `score` (0-100 or null - required checks share 80 points, recommended 20, forward-looking signals upside-only), the required/recommended buckets, label copy, per-surface sub-scores, access signals, and a `checks` map keyed by check id. That map holds the essentials INTERPRETATION only (tier, bonus, fraction, occurrences, `essentialsGain`) - name, status, details, ora's recommendation, and `estScoreGain` for the same id stay in `layers[].checks[]`, so nothing serializes twice; join on the id. The pre-sorted `issues` list and `scoreEvidence` are arrays of ids resolving in that map. Omit the parameter and the response is byte-identical to previous releases. On GET /api/checks, `essentials` instead adds `essentialsTier`, `essentialsBonusOnly`, and `essentialsExcluded` to every catalog check (an excluded check never enters the essentials model; since 1.19.1 that is the two robots.txt policy checks). */ include?: "essentials"; }; }; requestBody: { content: { "application/json": { /** * @description The domain, MCP server URL, or MCP app URL to scan. The server detects which kind of input was provided and runs the appropriate check set. * @example stripe.com */ url: string; /** * @description Optional MCP server URL to inspect as the sole MCP target. A failed endpoint is never replaced by a discovered server * @example */ mcpUrl?: string; /** * @description How stale a stored result may be and still be returned instead of running a new scan. Defaults to 21600 (6 hours) and is clamped server-side to [3600, 86400] rather than rejected. See the 200 response's `servedFromCache` / `resultAgeSeconds` fields. * @example 21600 */ maxAgeSeconds?: number; /** @description Always run a live scan, whatever the age of the stored result. This is the only way to bypass the freshness window entirely. */ force?: boolean; /** @description Store the result as disposable: it is excluded from the leaderboard, the sites-scanned coverage count, research statistics, and score history, is served with Cache-Control: no-store, and is deleted after a few days. Intended for a local site exposed through a tunnel, or any host that will not exist tomorrow. Public tunnel hostnames (trycloudflare.com, ngrok, and similar) are stored this way whether or not the flag is set. Rejected with 400 EPHEMERAL_CLOBBER when the domain already has a real stored scan, since a disposable result would replace it. */ ephemeral?: boolean; }; }; }; responses: { /** @description Scan completed successfully and analysis is complete. With `?competitors=1`, also carries a `competitors` object (category leaders + neighbor window drawn from the leaderboard). With `?format=audit` the body is `#/components/schemas/AuditScanResult` instead of the shape below. A response served from the freshness window instead of a new scan additionally carries `servedFromCache: true` and `resultAgeSeconds` (on both body shapes) plus an `Age` header, and consumed no scan. */ 200: { headers: { /** @description Present only on a freshness-window hit: the age of the returned stored result in seconds. */ Age?: number; Deprecation: components["headers"]["Deprecation"]; Sunset: components["headers"]["Sunset"]; }; content: { "application/json": components["schemas"]["ScanResult"] & ({ /** @description Present only when the request passed ?competitors=1. Category leaders + neighbor window from the leaderboard, independent of analysis completeness. Null when the domain has no market category (unclassified / Community domains, or no leaderboard row) or when competitive data is temporarily unavailable. */ competitors?: components["schemas"]["CompetitorSet"] | null; }); }; }; /** @description Scan accepted and scored, but analysis is still in progress (analysisStatus is 'partial'). The body is the same shape as a 200 (including `competitors` when `?competitors=1` was passed, and `#/components/schemas/AuditScanResult` when `?format=audit` was passed). Poll the Location header URL (GET /api/score/{domain}) until analysisStatus is 'complete' and pendingChecks is empty. */ 202: { headers: { /** @description URL of the scan result resource to poll, e.g. /api/score/stripe.com */ Location?: string; }; content: { "application/json": components["schemas"]["ScanResult"] & ({ /** @description Present only when the request passed ?competitors=1. Category leaders + neighbor window from the leaderboard, independent of analysis completeness. Null when the domain has no market category (unclassified / Community domains, or no leaderboard row) or when competitive data is temporarily unavailable. */ competitors?: components["schemas"]["CompetitorSet"] | null; }); }; }; /** @description Invalid input, or `ephemeral: true` for a domain that already has a real stored scan (body carries `code: "EPHEMERAL_CLOBBER"`; storing a disposable result would replace the real one). */ 400: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description MCP authentication is required. No score or grade was produced. The failed attempt does not overwrite a previous measured scan. */ 422: { content: { "application/json": components["schemas"]["McpAuthRequiredResponse"]; }; }; /** @description Rate limit exceeded - either the 10-per-minute burst cap or the durable daily quota (30 scans per rolling 24h per IP; 6 per day for force=true). Cache-served responses never count against the daily quota. The response includes a Retry-After header indicating seconds until the next request is allowed, and a JSON body with error and retry_after_ms. */ 429: { headers: { /** @description Seconds until next allowed request */ "Retry-After"?: number; }; content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description Scan failed */ 500: { content: never; }; }; }; /** * Instant-trigger entry point for the v2 async scan lifecycle (rollout, gated) * @description Phase 1b of the scan lifecycle redesign (see docs/plans/scan-lifecycle-prd.md). Validates the input (rate-limit 10/min/IP, Zod, isValidUrl, reachability, URL-kind classification), de-dupes against any in-progress v2 row for the domain, INSERTs the scans row at status="pending" with is_current=true and flow_version='v2', and returns the scanId plus a pollUrl. The downstream pipeline (Stage 1 context + static checks, Stage 2 deep checks via the Fly worker, Stage 3 finalize) is NOT YET WIRED in this PR - rows created here stay at status="pending" until follow-up PRs ship Stage 1 and the recovery cron. The endpoint is gated behind the SCAN_V2_ENABLED env flag and returns 503 in environments where it is unset, so external callers must not rely on it before cutover. The v1 read paths (/api/score/[domain], the leaderboard, the sitemap) already filter out flow_version='v2' rows. */ scanDomainV2: { requestBody: { content: { "application/json": { /** * @description The domain, MCP server URL, or MCP App URL to scan. * @example stripe.com */ url: string; /** * @description Optional explicit MCP server URL. When set, drives URL-kind classification. * @example */ mcpUrl?: string; /** @description Same flag as POST /api/scan: store the row as disposable, excluded from the leaderboard, coverage counts, research statistics, and score history. Public tunnel hostnames are stored this way whether or not the flag is set. Unlike POST /api/scan, this endpoint does NOT refuse the flag with 400 EPHEMERAL_CLOBBER - the clobber guard rides on the freshness read, which v2 does not perform yet. */ ephemeral?: boolean; }; }; }; responses: { /** @description Duplicate hit - an in-progress v2 row already exists for the domain. Returns its scanId and current status so the client can poll. */ 200: { content: { "application/json": { /** @example 7 */ scanId: number; /** @example running */ status: string; /** @example /api/v2/scan/7 */ pollUrl: string; }; }; }; /** @description v2 scan row created. Client should poll pollUrl for progress. */ 201: { content: { "application/json": { /** @example 42 */ scanId: number; /** @example pending */ status: string; /** @example /api/v2/scan/42 */ pollUrl: string; }; }; }; /** @description Invalid input (schema or domain). */ 400: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description Domain unreachable or URL-kind classification failed. */ 422: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description Rate limit exceeded - either the 10-per-minute burst cap or the durable daily quota (30 scans per rolling 24h per IP), which this endpoint spends from the same budget as the v1 scan routes so a caller cannot drain it twice by switching endpoints. The response carries a Retry-After header; a durable deny also carries retry_after_ms in the body. */ 429: { headers: { /** @description Seconds until next allowed request */ "Retry-After"?: number; }; content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description Internal error. */ 500: { content: never; }; /** @description Endpoint disabled in this environment (SCAN_V2_ENABLED is not set). */ 503: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; }; }; /** * Stream an agent-readiness scan as Server-Sent Events * @description Runs a full agent-readiness scan on the given URL and streams progress as text/event-stream. Accepts a domain, MCP server URL, or MCP App URL (server that supports the MCP Apps extension `io.modelcontextprotocol/ui`) - the server auto-detects which kind of input was provided and selects the appropriate check set. Catalog-style listing pages are folded into the `mcp` kind by classifying the first validated embedded MCP URL. The stream emits a `kind_detecting` event immediately after the cheap reachability probe, followed by exactly one `kind_detected` event with payload `{ kind: 'domain' | 'mcp' | 'mcp-app', mcpUrl?: string, embeddedMcpUrls?: string[], hint?: string }` once URL-kind detection resolves. Subsequent events include `scan_init`, `layer_start`, `check_start`, `check_complete`, `layer_complete`, and finally `scan_complete` whose payload mirrors the ScanResult schema (including the optional `urlKind` field indicating the detected kind). The same freshness gate as POST /api/scan applies: a hit is a `kind_detected` frame followed by the terminal `scan_complete` event, rather than a full run. Rate limited two ways: 10 requests per minute per IP (burst) and a durable daily scan budget shared with the other scan entry points - both return 429 with a Retry-After header. */ scanDomainStream: { parameters: { query: { /** @description The domain, MCP server URL, or MCP app URL to scan. The server detects which kind of input was provided and runs the appropriate check set. */ domain: string; /** @description Optional MCP server URL to inspect as the sole MCP target; it is never replaced by a discovered server */ mcp?: string; /** @description Same freshness window as POST /api/scan: how stale a stored result may be and still be streamed back instead of running a new scan. Defaults to 21600 (6 hours), clamped to [3600, 86400]. */ maxAgeSeconds?: number; /** @description Pass 1 to always run a live scan, whatever the age of the stored result. */ force?: "1"; /** @description Pass 1 to store the result as disposable - the same flag POST /api/scan takes in its body, with the same 400 EPHEMERAL_CLOBBER refusal when the domain already has a real stored scan. */ ephemeral?: "1"; /** @description Pass `audit` to receive the versioned, allowlisted audit shape (AuditScanResult / AuditScoreResult) instead of the default body: every field is documented, carries a `contractVersion`, and internal fields are dropped. Omit it and the response is unchanged from previous releases. On GET /api/scan/stream, only the terminal `scan_complete` event's `result` is projected - all other events are identical to the default stream. */ format?: "audit"; }; }; responses: { /** @description Server-Sent Events stream of scan progress. If MCP authentication is required, the stream ends with an error event carrying code MCP_AUTH_REQUIRED, mcpAuthRequired: true, mcpUrl and urlKind, without scan_complete or a score. With `?format=audit`, the terminal `scan_complete` event's `result` is `#/components/schemas/AuditScanResult`; every other event is unchanged. When a stored result inside the freshness window answers the request, the stream is a `kind_detected` frame followed by a terminal `scan_complete` event carrying `servedFromCache: true` and `resultAgeSeconds`, and the response carries an `Age` header. */ 200: { content: { "text/event-stream": string; }; }; /** @description Missing or invalid domain parameter */ 400: { content: never; }; /** @description Rate limit exceeded - either the 10-per-minute burst cap or the durable daily quota (30 scans per rolling 24h per IP; 6 per day for force=1). The response carries a Retry-After header with the seconds until the caller's window frees up. */ 429: { headers: { /** @description Seconds until next allowed request */ "Retry-After"?: number; }; content: never; }; }; }; /** * Run a selected subset of checks against a URL * @description Runs only the checks you select against the given URL and returns per-check results - the re-verify step after shipping a fix, with check ids from GET /api/checks. The run always executes; results are never served from a cache, so a re-check reflects the fix you just deployed (allow for DNS and CDN caches clearing). For most or all of the catalog, use POST /api/scan instead: same budget unit, and it returns a score. The response carries no aggregate score; GET /api/score/{domain} is the score surface. Callers holding a scan API key also get stored-scan patching - see storedScanUpdated on the response. Rate limited two ways: 10 requests per minute per IP, and one run spends one unit of the daily scan budget shared with POST /api/scan, spent once the target has been probed and classified; requests rejected earlier (invalid input, unknown ids, unreachable) spend nothing. Scan API key callers are exempt from both - an exemption, not a larger allowance; keys are issued manually on request - contact ora. */ runChecks: { requestBody: { content: { "application/json": components["schemas"]["RunChecksRequest"]; }; }; responses: { /** @description Per-check results for the selection - at least one entry per requested id, including 'na' entries for ids that cannot apply to the detected kind. Always synchronous and complete: there is no 202 and no pending status. */ 200: { content: { "application/json": components["schemas"]["RunChecksResponse"]; }; }; /** @description Invalid input - either a schema failure (`{ error, details }` with the Zod detail, whose checkIds bounds messages point at POST /api/scan for full runs) or an id the catalog does not list (`code: "UNKNOWN_CHECK_IDS"` with the offending ids echoed in `details` and GET /api/checks as the pointer), or an invalid / unreachable domain. */ 400: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description Rate limit exceeded - either the 10-per-minute burst cap (body `{ error }`) or the durable daily scan budget shared with POST /api/scan (body also carries `retry_after_ms`). Both carry a Retry-After header with the seconds until the caller's window frees up. */ 429: { headers: { /** @description Seconds until next allowed request */ "Retry-After"?: number; }; content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description Selective run failed */ 500: { content: never; }; }; }; /** * Get cached score for a domain * @description Returns the most recent cached scan result for the given domain. Read-only: never triggers a scan. On miss (404) or when the previous scan got stuck mid-flight (200 with `analysisStatus: "stuck"`), the response carries a structured `next_action` envelope pointing at `POST /api/scan` so agent callers have a machine-parseable next step. Successful responses are cached for 1 hour; stuck, 404, and ephemeral (disposable, `urlKind: "ephemeral"`) responses are uncached (`Cache-Control: no-store`) so a successful re-scan is observable immediately and a deleted disposable row is never served from cache. Rate limited to 10 requests per minute per IP - returns 429 if exceeded. */ getScore: { parameters: { query?: { /** @description Pass 1 to include a `competitors` object in the response: the category top-5 leaders plus the neighbor window (2 above / self / 2 below) drawn from the leaderboard. Returned only for domains with a market category - unclassified domains (Community, or no leaderboard row) get `competitors: null`. Omitted by default so the plain response stays lean. */ competitors?: "1"; /** @description Pass `audit` to receive the versioned, allowlisted audit shape (AuditScanResult / AuditScoreResult) instead of the default body: every field is documented, carries a `contractVersion`, and internal fields are dropped. Omit it and the response is unchanged from previous releases. On GET /api/scan/stream, only the terminal `scan_complete` event's `result` is projected - all other events are identical to the default stream. */ format?: "audit"; /** @description Opt-in expansion list (comma-separated). `essentials` adds one response key, `essentials`: an alternate reading of the same scan carrying its own `score` (0-100 or null - required checks share 80 points, recommended 20, forward-looking signals upside-only), the required/recommended buckets, label copy, per-surface sub-scores, access signals, and a `checks` map keyed by check id. That map holds the essentials INTERPRETATION only (tier, bonus, fraction, occurrences, `essentialsGain`) - name, status, details, ora's recommendation, and `estScoreGain` for the same id stay in `layers[].checks[]`, so nothing serializes twice; join on the id. The pre-sorted `issues` list and `scoreEvidence` are arrays of ids resolving in that map. Omit the parameter and the response is byte-identical to previous releases. On GET /api/checks, `essentials` instead adds `essentialsTier`, `essentialsBonusOnly`, and `essentialsExcluded` to every catalog check (an excluded check never enters the essentials model; since 1.19.1 that is the two robots.txt policy checks). */ include?: "essentials"; }; path: { /** @description The domain to look up (e.g. stripe.com). URL-encoded full URLs are normalized to their hostname. */ domain: string; }; }; responses: { /** @description Cached scan result. With `?competitors=1`, also carries a `competitors` object (category leaders + neighbor window drawn from the leaderboard). When `analysisStatus` is `"stuck"`, the body also includes a `next_action` envelope. With `?format=audit` the body is `#/components/schemas/AuditScoreResult` and the recovery envelope is the camelCase `nextAction`. */ 200: { headers: { Deprecation: components["headers"]["Deprecation"]; Sunset: components["headers"]["Sunset"]; }; content: { "application/json": components["schemas"]["ScanResult"] & ({ /** @description Present only when the request passed ?competitors=1. Category leaders + neighbor window from the leaderboard, independent of analysis completeness. Null when the domain has no market category (unclassified / Community domains, or no leaderboard row) or when competitive data is temporarily unavailable. */ competitors?: components["schemas"]["CompetitorSet"] | null; }) & { /** @description Present only when analysisStatus is 'stuck' (a scan that stayed partial for over 30 minutes). Machine-parseable next step to recover the score. Under `?format=audit` this is the camelCase `nextAction` on AuditScoreResult instead. */ next_action?: components["schemas"]["NextAction"]; }; }; }; /** @description No cached score for this domain. Body includes `code: "DOMAIN_NOT_SCANNED"` and a `next_action` pointing at `POST /api/scan` (`nextAction`, camelCase, under `?format=audit`). */ 404: { content: { "application/json": components["schemas"]["NotScannedResponse"]; }; }; /** @description MCP authentication is required. No score or grade was produced. The failed attempt does not overwrite a previous measured scan. */ 422: { content: { "application/json": components["schemas"]["McpAuthRequiredResponse"]; }; }; /** @description Rate limit exceeded - max 10 requests per minute per IP. The response carries a Retry-After header with the seconds until the oldest request in the window ages out. */ 429: { headers: { /** @description Seconds until next allowed request */ "Retry-After"?: number; }; content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description Database unavailable */ 500: { content: never; }; }; }; /** * Get SVG badge for a domain * @description Returns an SVG badge showing the domain's ora score and grade. Embed in READMEs or websites. Cached for 1 hour. */ getBadge: { parameters: { path: { /** @description The domain to get a badge for */ domain: string; }; }; responses: { /** @description SVG badge image */ 200: { content: { "image/svg+xml": string; }; }; /** @description No score found for this domain */ 404: { content: never; }; }; }; /** * Get the complete catalog of scanner checks * @description Returns every check the ora scanner can run - stable id, scored layer, max score, applicability, eligible scan kinds, tier, maturity, and fix guidance per check, plus the four scored layers with their weights. Check ids are stable: gate CI on an explicit id list, not on tiers (the required set can grow on a minor version). Ids are also what POST /api/scan/checks takes, and every check carries a `beta` boolean for building check pickers - a beta check runs but cannot affect any score. The document is static and byte-stable between check-set changes, so diffing it detects catalog updates. One optional parameter: `?include=essentials` adds `essentialsTier`, `essentialsBonusOnly`, and `essentialsExcluded` (the essentials-model classification; excluded checks are ignored by that model outright) to every check; without it the body is byte-identical to previous releases. Sends Access-Control-Allow-Origin: * and is CDN-cached for 1 hour. Rate limited to 60 requests per minute per IP - returns 429 with a Retry-After header if exceeded. */ listChecks: { parameters: { query?: { /** @description Opt-in expansion list (comma-separated). `essentials` adds one response key, `essentials`: an alternate reading of the same scan carrying its own `score` (0-100 or null - required checks share 80 points, recommended 20, forward-looking signals upside-only), the required/recommended buckets, label copy, per-surface sub-scores, access signals, and a `checks` map keyed by check id. That map holds the essentials INTERPRETATION only (tier, bonus, fraction, occurrences, `essentialsGain`) - name, status, details, ora's recommendation, and `estScoreGain` for the same id stay in `layers[].checks[]`, so nothing serializes twice; join on the id. The pre-sorted `issues` list and `scoreEvidence` are arrays of ids resolving in that map. Omit the parameter and the response is byte-identical to previous releases. On GET /api/checks, `essentials` instead adds `essentialsTier`, `essentialsBonusOnly`, and `essentialsExcluded` to every catalog check (an excluded check never enters the essentials model; since 1.19.1 that is the two robots.txt policy checks). */ include?: "essentials"; }; }; responses: { /** @description The complete check catalog */ 200: { content: { "application/json": components["schemas"]["CheckCatalog"]; }; }; /** @description RATE_LIMIT_EXCEEDED - max 60 requests per minute per IP. */ 429: { headers: { /** @description Seconds until next allowed request */ "Retry-After"?: number; }; content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; }; }; /** * Discover agent-ready products by intent * @description Find the most agent-ready products for a given need. Describe what you're looking for and get products ranked by agent-readiness score. Cached for 5 minutes. */ discoverProducts: { parameters: { query: { /** @description What you need - describe the task or product category (e.g. 'send transactional emails', 'CRM with API') */ intent: string; /** @description Max results to return */ limit?: number; }; }; responses: { /** @description Matching products ranked by relevance and agent-readiness */ 200: { content: { "application/json": { intent?: string; results?: components["schemas"]["DiscoverResult"][]; total?: number; }; }; }; /** @description Missing intent parameter */ 400: { content: never; }; }; }; /** * Report an issue with a specific check result * @description Submit feedback about an inaccurate check result. Accepts both human and agent submissions. Agent submissions require HATCHA verification. Check state (score, status, details) is snapshotted server-side from the latest scan. */ reportCheckIssue: { requestBody: { content: { "application/json": { /** * @description Submission source. Agent submissions require HATCHA verification fields. * @enum {string} */ reporterType: "human" | "agent"; /** @description The product domain (e.g. stripe.com) */ domain: string; /** @description The check ID to report (e.g. openapi-spec) */ checkId: string; /** * @description Why the check result seems wrong * @enum {string} */ reason: "false_pass" | "false_fail" | "wrong_details" | "outdated" | "other"; /** @description Description of the issue */ message: string; /** @description Human only, optional. We'll notify you if we find and fix the issue. */ reporterEmail?: string; /** @description Agent only, required. Agent identifier (e.g. claude-code-a8f3b1e92d) */ agentId?: string; /** @description Agent only, required. Token from get_verification_challenge. */ verificationToken?: string; /** @description Agent only, required. Solved HATCHA challenge answer. */ verificationAnswer?: string; }; }; }; responses: { /** @description Feedback submitted successfully */ 200: { content: { "application/json": { ok?: boolean; /** @description The feedback record ID */ id?: number; }; }; }; /** @description Invalid payload or unknown checkId */ 400: { content: never; }; /** @description Agent verification failed */ 401: { content: never; }; /** @description No scan found for domain, or check not in latest scan */ 404: { content: never; }; /** @description Rate limit exceeded */ 429: { content: never; }; /** @description Agent verification unavailable */ 503: { content: never; }; }; }; /** * Send a message to the ora team * @description Submit a contact-form message. Open endpoint - no authentication required. Sends an inquiry email to the ora team and an auto-responder to the submitter. Rate limited to 3 submissions per IP per 10 minutes. Agents are welcome to use this endpoint, though email is the simpler path for most cases. */ submitContactInquiry: { requestBody: { content: { "application/json": { /** @description Sender's name */ name: string; /** * Format: email * @description Sender's email - used as Reply-To on the inquiry and as the destination for the auto-responder */ email: string; /** @description The message body */ message: string; }; }; }; responses: { /** @description Submission accepted */ 200: { content: { "application/json": { ok?: boolean; }; }; }; /** @description Invalid input */ 400: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description Too many submissions from this IP */ 429: { content: never; }; /** @description Failed to send the inquiry email */ 500: { content: never; }; }; }; /** * Search agentic resources (Agentic Resource Discovery) * @description Runs an Agentic Resource Discovery (ARD) search over ora's catalog of agent-ready resources. Returns resources ranked by relevance to a free-text query, with optional field filters and federation control. The per-result `score` is a readiness-weighted relevance score (0-100): match quality for the query (dominant), multiplied by a 0.6-1.0 factor from the domain's agent-readiness. It is distinct from the raw agent-readiness score returned by POST /api/scan and GET /api/score/{domain}. Rate limited to 30 requests per minute per IP - returns 429 if exceeded. */ ardSearch: { requestBody: { content: { "application/json": { query: { /** @description Free-text search query. May be omitted when filter is present (a filter-only browse); at least one of text/filter is required. */ text?: string; /** @description Optional field filters: a map of field name to an allowed value (a scalar string or an array of strings; OR within a key, AND across keys). */ filter?: { [key: string]: string | string[]; }; }; /** * @description Federation policy. 'none': ora's own index only. 'referrals': ora's results plus referrals[] pointers to upstream registries. 'auto': ora merges upstream registry results into results (each tagged with its upstream source); merging is off by default (server-gated) and degrades to own-results-only when disabled. * @default auto * @enum {string} */ federation?: "auto" | "referrals" | "none"; /** * @description Results per page (1-100, default 10). * @default 10 */ pageSize?: number; /** @description Opaque pagination token from a previous response. */ pageToken?: string; }; }; }; responses: { /** @description The ARD SearchResponse: { results, referrals, pageToken? }. pageToken is omitted when the result set is exhausted (never null). ora is non-federating, so referrals is []. Each result's trustManifest.attestations[0] is a reference { type, uri, mediaType } to GET /api/ard/attestation/{domain}, not inlined claims. Results from ora's own index also carry an `oraScorecard` vendor extension ({ score?, grade?, category?, checkedAt? }): agent-readiness score/grade only when ora has scored the domain (never a fabricated 0/F), category whenever ora has classified it - all distinct from the relevance `score`; the signed claim remains the attestation endpoint. */ 200: { content: never; }; /** @description INVALID_ARGUMENT - malformed query body. */ 400: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; /** @description RATE_LIMIT_EXCEEDED - max 30 requests per minute per IP. */ 429: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; /** @description INTERNAL_ERROR */ 500: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; }; }; /** * Faceted exploration of agentic resources (Agentic Resource Discovery) * @description Returns facet aggregations (counts per field value) for an Agentic Resource Discovery (ARD) result set. Use this to build filter UIs over the ARD catalog. An optional query narrows the set before faceting. Rate limited to 30 requests per minute per IP - returns 429 if exceeded. */ ardExplore: { requestBody: { content: { "application/json": { /** @description Optional query to narrow the set before faceting (same shape as POST /api/ard/search's query). */ query?: { /** @description Free-text search query. */ text?: string; filter?: { [key: string]: string[]; }; }; resultType: { facets: { /** @description The resource field to aggregate on. */ field: string; /** @description Max facet values to return for this field. */ limit?: number; /** @description Drop buckets whose count is below this (default 1). */ minCount?: number; }[]; }; }; }; }; responses: { /** @description Facet aggregations for the matched resource set. */ 200: { content: never; }; /** @description INVALID_ARGUMENT - malformed query body. */ 400: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; /** @description RATE_LIMIT_EXCEEDED - max 30 requests per minute per IP. */ 429: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; /** @description INTERNAL_ERROR */ 500: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; }; }; /** * List discoverable agentic resources (Agentic Resource Discovery) * @description Returns a paginated listing of the agentic resources ora publishes for Agentic Resource Discovery (ARD). Deterministic browsing via the spec EBNF `filter` expression and `orderBy`. Rate limited to 60 requests per minute per IP - returns 429 if exceeded. */ ardListAgents: { parameters: { query?: { /** @description Results per page (1-100, default 20). */ pageSize?: number; /** @description Opaque pagination token from a previous response. */ pageToken?: string; /** @description EBNF filter expression, e.g. "type = 'application/mcp-server-card+json' AND updatedAfter > '2026-01-01'". Fields: type, displayName, publisherId, tags, updatedAt. Operators: = != > < >= <=, conditions joined by AND. An unsupported field/operator/syntax returns 400 INVALID_ARGUMENT. */ filter?: string; /** @description Sort expression " [ASC|DESC]", e.g. "displayName DESC". Fields: displayName, updatedAt, identifier, type. */ orderBy?: string; }; }; responses: { /** @description The ARD ListResponse: { items, total, pageToken? } (pageToken omitted when exhausted). */ 200: { content: never; }; /** @description INVALID_ARGUMENT - unsupported or malformed filter/orderBy. */ 400: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; /** @description RATE_LIMIT_EXCEEDED - max 60 requests per minute per IP. */ 429: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; /** @description INTERNAL_ERROR */ 500: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; }; }; /** * Get a signed scorecard attestation for a domain (Agentic Resource Discovery) * @description Returns an Agentic Resource Discovery (ARD) scorecard attestation for the given domain. When ora has an attestation signing key configured, the payload is returned as an EdDSA detached JWS that verifies against the public JWK set at GET /api/ard/jwks (also served at /.well-known/jwks.json); without a configured key the attestation is returned unsigned. Rate limited to 60 requests per minute per IP - returns 429 if exceeded. */ ardGetAttestation: { parameters: { path: { /** @description The domain to attest (e.g. stripe.com). */ domain: string; }; }; responses: { /** @description The scorecard attestation - an EdDSA detached JWS when signing is configured, otherwise an unsigned payload. */ 200: { content: never; }; /** @description INVALID_ARGUMENT - malformed domain. */ 400: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; /** @description NOT_FOUND - no cached score for this domain. Body also carries `next` pointing at POST /api/scan. */ 404: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; /** @description RATE_LIMIT_EXCEEDED - max 60 requests per minute per IP. */ 429: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; /** @description INTERNAL_ERROR */ 500: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; }; }; /** * Get the ARD registry service descriptor (Agentic Resource Discovery) * @description Returns the application/ai-registry+json service descriptor at ora's registry base URL - the document ora advertises in its AI Catalog's application/ai-registry+json entry. Names the live search/explore/agents/catalog endpoints, the pageToken pagination model (max page size 100), the filter/orderBy/facet fields, the served media types, and the federation modes. A crawler that ingests /.well-known/ai-catalog.json and follows the registry entry lands here. */ ardGetRegistryDescriptor: { responses: { /** @description The ARD registry service descriptor. */ 200: { content: never; }; }; }; /** * Get ora's AI Catalog manifest (Agentic Resource Discovery) * @description Returns ora's AI Catalog manifest for Agentic Resource Discovery (ARD). Also served at /.well-known/ai-catalog.json via a rewrite, and advertised by a Link header with rel="ai-catalog" on the homepage. */ ardGetCatalog: { responses: { /** @description The ARD AI Catalog manifest. */ 200: { content: never; }; /** @description Catalog generation failed */ 500: { content: never; }; }; }; /** * Get the full ARD catalog dump (Agentic Resource Discovery) * @description Returns every indexed Agentic Resource Discovery (ARD) entry - ora's own products, their detected MCP server / skill resources, and crawled external catalogs - in one uncapped document { version, generatedAt, publisher, data }, for bulk ingest without paging through /api/ard/agents. Shares the same index pipeline as /agents and /explore. Sends Access-Control-Allow-Origin: * and is CDN-cached. Rate limited to 60 requests per minute per IP - returns 429 if exceeded. Also reachable at directory.ora.ai/api.json. */ ardGetCatalogDump: { responses: { /** @description The full-catalog dump: { version, generatedAt, publisher, data }. */ 200: { content: never; }; /** @description RATE_LIMIT_EXCEEDED - max 60 requests per minute per IP. */ 429: { content: { "application/json": components["schemas"]["ArdErrorResponse"]; }; }; }; }; /** * Get the public JWK set for verifying signed attestations (Agentic Resource Discovery) * @description Returns the public JSON Web Key Set used to verify ora's signed Agentic Resource Discovery (ARD) scorecard attestations. Also served at /.well-known/jwks.json via a rewrite. */ ardGetJwks: { responses: { /** @description The public JWK set ({ keys: [...] }). */ 200: { content: never; }; /** @description JWKS generation failed */ 500: { content: never; }; }; }; /** * Get the Web Bot Auth signature agent card (public keys for verifying ora's crawler) * @description Returns ora's Web Bot Auth signature agent card: the client name, contact, stated purpose, and the public Ed25519 keys that verify HTTP Message Signatures (RFC 9421) on requests from ora's scanner. Bot-management verifiers resolve a signed request's `keyid` against the `kid` of a key here. Also served at /.well-known/http-message-signatures-directory via a rewrite, which is the path the specification fixes and the one verifiers fetch. `keys` is an empty array when no signing key is configured, so the endpoint is always valid JSON and can be probed unconditionally. */ getWebBotAuthDirectory: { responses: { /** @description The signature agent card ({ client_name, homepage_uri, contact_email, purpose, keys: [...] }). */ 200: { content: never; }; }; }; /** * Run an agent journey against a domain * @description Triggers a real agent run: an AI agent (harness + model) attempts a task (an intent) against the given domain, and ora records the trajectory and derives insights. Two-step flow: this endpoint returns fast with a run record whose `stream_url` serves the live trajectory as Server-Sent Events; poll GET /api/journey/runs/{id} instead if you do not want the stream. Two caller tiers. Anonymous: curated intents only (see GET /api/journey/intents) - the server derives the actual agent prompt from the intent id, so no free-text prompt can reach the engine. Keyed: a caller presenting an ora-issued partner API key ('Authorization: Bearer ', issued manually - contact ora) may instead send bounded free text (`intent.custom`, 4 to 300 characters, with `intent.domain` required), which the server anchors to the requested domain before dispatch and echoes back on the 201. Free text without a recognized key is a 401 with code CUSTOM_INTENT_REQUIRES_KEY; a curated body with a missing or unrecognized key is never an error and simply runs on the anonymous tier. The keyed free-text tier is also reachable from the ora CLI (ax deep-journey --task, v0.5+). Only publicly runnable agents are accepted (see GET /api/journey/agents). Unknown body fields are rejected (strict schema). Anonymous rate limits, three ways: a 20-per-minute burst cap per IP; a per-target cap of 100 runs per rolling 24h per (domain, intent, harness, model) - when a target is over the cap the response is HTTP 200 with the most recent stored run for that target (`rate_limited: true`) plus Retry-After, the freshness analog: a denied trigger still returns the newest result and consumes nothing; and a durable per-caller cap of 200 runs per rolling 24h per IP - exceeded, that one is a real 429 with `retry_after_ms` (there is no cached result to serve for a caller). Keyed rate limit, one way: 1000 runs per rolling 24h per key, exhaustion being the same 429 with `retry_after_ms`. A keyed caller skips both the burst cap and the per-target cap - free text fragments the target key, so a per-target window over it could never fill. Successful and capped responses carry X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset headers describing exactly one window: the per-target window on an anonymous response, the per-key caller window on a keyed one. Note: journey insights use the 5-layer journey taxonomy (discovery, identity, access, payments, experience), deliberately distinct from the 4 scoring layers of the audit report (POST /api/scan) - never map between the two. */ createJourneyRun: { requestBody: { content: { "application/json": { /** @description The task to run, as exactly one of two arms. Curated: an `intent_id` the server owns a prompt template for, open to every caller. Custom: bounded free text, accepted only from a caller presenting a partner API key. A body carrying both arms, or neither, is a 400. */ intent: OneOf<[ { /** * @description Curated intent to execute (GET /api/journey/intents lists them with labels) * @enum {string} */ intent_id: "pricing" | "integrate" | "api-docs" | "signup" | "support" | "evaluate" | "discover-trust" | "agent-access" | "understand-offering" | "find-integration" | "inspect-integration" | "production-readiness" | "authenticate" | "transact" | "act-for-user" | "operate-site"; /** * @description Target domain (e.g. stripe.com). Validated like a scan target: IP literals, localhost, and malformed hosts are rejected with code INVALID_DOMAIN. * @example stripe.com */ domain?: string; }, { /** * @description Free-text task for the agent, 4 to 300 characters measured after trimming. REQUIRES an ora-issued partner API key on the request ('Authorization: Bearer '): sent without one, this arm is a 401 with code CUSTOM_INTENT_REQUIRES_KEY, so do not post free text keylessly - use the curated arm instead. The server anchors the text to `domain` before dispatch (that anchored prompt is what runs and what is stored); the 201 echoes back the text you sent, not the anchored form. * @example Sign up for a free account and deploy a sample project */ custom: string; /** * @description Target domain (e.g. stripe.com). Required on this arm (it is optional on the curated one) because free text is domain-agnostic and the server anchors the prompt to this target. Validated like a scan target: IP literals, localhost, and malformed hosts are rejected with code INVALID_DOMAIN. * @example stripe.com */ domain: string; } ]>; /** * @description Agent harness wire name. The (harness, model) pair must resolve to a publicly runnable agent from GET /api/journey/agents, else 400 UNSUPPORTED_AGENT. * @enum {string} */ harness: "claude-agent-sdk" | "openai-agents" | "ash"; /** * @description Model the harness drives (e.g. claude-haiku-4-5) * @example claude-haiku-4-5 */ model: string; }; }; }; responses: { /** @description Per-target cap hit - the freshness analog, not an error: the body is the most recent stored run for this exact (domain, intent, harness, model) target, marked `rate_limited: true`, so CI and agents get the newest result without spending a run. When the served run has finished, the body also carries `verdict` and `step_count`. Replay it via its `stream_url`. Anonymous tier only: a keyed caller skips the per-target cap and never receives this response. */ 200: { headers: { /** @description Seconds until the target's oldest in-window run ages out */ "Retry-After"?: number; /** @description Per-target window size (100 runs per rolling 24h) */ "X-RateLimit-Limit"?: number; /** @description Always 0 on a capped response */ "X-RateLimit-Remaining"?: number; /** @description Unix seconds when the next per-target slot frees */ "X-RateLimit-Reset"?: number; }; content: { "application/json": components["schemas"]["JourneyCappedRun"]; }; }; /** @description Run created and dispatched. Open `stream_url` (SSE) to watch the trajectory live, or poll GET /api/journey/runs/{id}. A keyed custom run gets its own free text back as `intent` here - the create response is the only place any intent text is published, so GET /api/journey/runs/{id} and the capped 200 never carry it, and a curated run has no `intent` at all. */ 201: { headers: { /** @description Size of the one window this response describes: the per-target window (100 runs per rolling 24h) for an anonymous caller, the per-key caller window (1000 runs per rolling 24h) for a keyed one */ "X-RateLimit-Limit"?: number; /** @description Runs left in that same window, including this one's consumption */ "X-RateLimit-Remaining"?: number; /** @description Unix seconds when the next slot in that same window frees. Absent when the window was empty. */ "X-RateLimit-Reset"?: number; }; content: { "application/json": components["schemas"]["JourneyCreatedRun"]; }; }; /** @description Invalid input: schema failure (including unknown body fields - the contract is strict, and a body matching neither intent arm or both of them lands here), `code: "INVALID_DOMAIN"` for a domain the scanner would reject, or `code: "UNSUPPORTED_AGENT"` for a (harness, model) pair that is not publicly runnable. */ 400: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description `code: "CUSTOM_INTENT_REQUIRES_KEY"` - the request sent a custom free-text intent without a recognized ora-issued partner API key. An absent, malformed, and unrecognized key all land here alike, so the status never doubles as a key-validity oracle. Two ways forward: run a curated intent instead (GET /api/journey/intents lists them, and that arm needs no key), or ask ora for a partner key - they are issued manually on request, so contact us at the address in this document's info.contact. */ 401: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description Caller limit exceeded - the 20-per-minute burst cap (anonymous callers only; a keyed caller skips it), or the durable per-caller budget (200 runs per rolling 24h per IP anonymous, 1000 per rolling 24h per key; body carries `retry_after_ms`). Per-TARGET saturation is the 200 above, not this. */ 429: { headers: { /** @description Seconds until next allowed request */ "Retry-After"?: number; }; content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description The run engine could not be reached; nothing was created or consumed. */ 503: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; }; }; /** * Get a journey run record (non-streaming) * @description Returns the run record by id. While the run executes, `status` is 'running' - open the stream or poll. Once finished and persisted, `status` is 'succeeded' and the body carries `verdict`, `step_count` (billable steps - the same counter run pricing uses), and the full `result` (trajectory + insight + signals). `status` 'failed' is terminal with no result. `result` is present iff status is 'succeeded'. */ getJourneyRun: { parameters: { path: { /** @description The run id from POST /api/journey/runs */ id: string; }; }; responses: { /** @description The run record; plus `result` once succeeded. */ 200: { content: { "application/json": components["schemas"]["JourneyRunDetail"]; }; }; /** @description Unknown run id */ 404: { content: never; }; }; }; /** * Get or create a domain's agent journey * @description Returns the agent journey for a domain, running one only if there is not one already. Send only the domain and no body: ora selects the intent and the agent. Requires an ora-issued partner API key ('Authorization: Bearer ', issued manually - contact ora); without one, 401 PARTNER_KEY_REQUIRED. 200 means nothing was dispatched and an existing run answered, either finished (carrying its `result`) or still running (open `stream_url` to watch it). 201 means a new run was dispatched. Branch on `dispatched`. Two response views (see the `view` parameter): `graph` (JourneyDomainRunGraph, the default - `result` narrowed to what drawing the journey needs) and `full` (JourneyDomainRun, the complete projection, on `?view=full`). A finished run is served indefinitely; `run_age_seconds` gives its age. There is no per-caller rate limit on this endpoint. Each domain is capped at 100 runs per rolling 24h, and one domain cannot start two runs at once - both are per-domain, neither limits how many domains you may ask for or how fast. */ getOrCreateDomainJourney: { parameters: { query?: { /** @description Response view. `graph` returns JourneyDomainRunGraph - the same record and envelope with `result` narrowed to what drawing the journey needs (the step tree, verdict, and insight summary; no run_signals, probes, usage aggregates, or agent_response). `full` returns the complete JourneyDomainRun projection. Omitted, the response is the graph view - pass `view=full` for the complete payload. An unrecognized value is a 400 with code INVALID_VIEW. */ view?: "graph" | "full"; }; path: { /** @description The domain, as a bare host (example.com). A full URL is accepted if percent-encoded, and is normalized to its apex. IP literals, localhost, and malformed hosts are rejected with 400 INVALID_DOMAIN. */ host: string; }; }; responses: { /** @description An existing run answered; nothing was dispatched (`dispatched` is false). Either a finished run with `result` and `run_age_seconds` (final: no backoff hint), one still running (carries `retry_after_ms` and a `Retry-After` header: poll no faster than that), or - when the domain is over its 100-per-24h cap - its newest stored run with `retry_after_ms`. The body is JourneyDomainRun, or JourneyDomainRunGraph when the effective view is `graph` (see the `view` parameter). */ 200: { content: { "application/json": components["schemas"]["JourneyDomainRun"] | components["schemas"]["JourneyDomainRunGraph"]; }; }; /** @description A new run was dispatched and is running (`dispatched` is true, no `result` yet). Open `stream_url` for the live trajectory, or poll GET /api/journey/runs/{id}. The record fields are identical across views, so this body is the same whichever view is in effect. */ 201: { content: { "application/json": components["schemas"]["JourneyDomainRun"] | components["schemas"]["JourneyDomainRunGraph"]; }; }; /** @description `code: "INVALID_DOMAIN"` - the host is not a scannable domain. Also returned when the request carries body parameters, which this endpoint does not accept. */ 400: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description `code: "PARTNER_KEY_REQUIRED"` - no recognized partner API key. Keys are issued manually; contact us at the address in this document's info.contact. To run a journey without a key, use POST /api/journey/runs with a curated intent. */ 401: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description Back off and retry the SAME DOMAIN; the body carries `retry_after_ms` and a `Retry-After` header. All three codes here are per-domain, never key-wide, so do not throttle your other traffic. `POLL_TOO_FAST`: more than 3 requests for this domain inside 2 seconds; back off by this response's `retry_after_ms`. The in-flight `200` also carries `retry_after_ms`, and a poller that honours it never sees this code. `DISPATCH_IN_PROGRESS`: another request is already starting this domain. `TARGET_CAPPED`: this domain is over its 100-per-24h cap. */ 429: { headers: { /** @description Seconds until next allowed request */ "Retry-After"?: number; }; content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; /** @description The agent gateway is unreachable. No run was created and no allowance consumed, but this domain's dispatch claim is briefly held, so an immediate retry of the same domain answers 429 DISPATCH_IN_PROGRESS. Other domains are unaffected. */ 503: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; }; }; /** * Stream a journey run's trajectory as Server-Sent Events * @description The live trajectory SSE for a run created by POST /api/journey/runs. Event names (stable): `run_id` ({ run_id }), then progressive `trajectory` frames (cumulative snapshots, each a { steps, ... } object of JourneyTrajectoryStep items), optionally `processing` ({ message }) while insights generate, and finally exactly one of `result` (a JourneyRunResult) or `error` ({ message }). Reopening the stream of a finished run replays the stored result instead of re-executing the agent (pass ?replay=1 for paced frames, plus &quick=1 to skip the startup delay); a plain reopen renders the finished journey in one frame. Partner keys on the graph stream tier receive graph-narrowed frames instead (the JourneyDomainRunGraph subset: trajectory frames carry only `steps`, and the result frame only verdict, finished_at, intent_id, the step tree, and the insight summary); event names are identical. Long-lived: a live run can hold the connection for many minutes (function ceiling 800s). Rate limited 20 per minute per IP. */ streamJourneyRun: { parameters: { query?: { /** @description Pass 1 to replay a finished run as paced progressive frames (the animated replay). */ replay?: "1"; /** @description With replay=1: skip the startup delay. */ quick?: "1"; }; path: { /** @description The run id */ id: string; }; }; responses: { /** @description Server-Sent Events stream: run_id -> trajectory* -> processing? -> result | error. */ 200: { content: { "text/event-stream": string; }; }; /** @description Unknown run id */ 404: { content: never; }; /** @description Rate limit exceeded - max 20 requests per minute per IP. A caller presenting a recognized partner API key (Authorization: Bearer) is exempt when the run was already dispatched, since opening it only reads it; a run with no stored record executes on this open and stays limited for every caller. */ 429: { content: { "application/json": components["schemas"]["ErrorResponse"]; }; }; }; }; /** * List the curated journey intents * @description The curated tasks a public journey run can execute. Each entry carries the stable `id` (what POST /api/journey/runs takes), a short `label`, a one-line `hint`, and the user-facing `template` phrasing. Cached statically. */ listJourneyIntents: { responses: { /** @description Curated intents plus the default id. */ 200: { content: { "application/json": { intents?: { /** @description Stable intent id */ id?: string; /** @description Short picker label */ label?: string; /** @description One-line description of the job to be done */ hint?: string; /** @description User-facing phrasing of the task */ template?: string; }[]; defaultId?: string; }; }; }; }; }; /** * List the publicly runnable journey agents * @description The agents an anonymous POST /api/journey/runs accepts - every listed (harness, model) pair is publicly runnable. Entries carry display metadata (label, variant, brand, blurb) plus the wire fields `harness` and `model`. Cached statically. */ listJourneyAgents: { responses: { /** @description Runnable agents plus the default picker id. */ 200: { content: { "application/json": { agents?: { /** @description Picker id */ id?: string; label?: string; variant?: string; brand?: string; /** @description Wire harness for POST /api/journey/runs */ harness?: string; /** @description Wire model for POST /api/journey/runs */ model?: string; agentLabel?: string; blurb?: string; badge?: string; }[]; defaultId?: string; }; }; }; }; }; /** * Get agent feedback for a product * @description Returns feedback submitted by AI agents about their experience using a product. Includes aggregate stats and individual reviews. */ getAgentFeedback: { parameters: { path: { /** @description The product domain (e.g. stripe.com) */ domain: string; }; }; responses: { /** @description Agent feedback with stats */ 200: { content: { "application/json": { domain?: string; stats?: components["schemas"]["FeedbackStats"]; feedback?: components["schemas"]["AgentFeedback"][]; }; }; }; }; }; } export {};