{"openapi":"3.0.3","info":{"title":"Amply Routing API","version":"3.1.13","description":"Machine-friendly vector routing: scores providers from catalog metrics (Supabase Postgres).\n\n**v1 scope:** Stable surface is **catalog + routing + diagnostics** (status, providers, route, optional outcome telemetry) plus machine-readable compliance profile (`GET /api/v1/compliance`) and route policy controls (`enterprise_policy`, `sandbox_mode`) documented under `paths`.\n\n**Production:** `POST /api/v1/route` requires `Authorization: Bearer` (see `GET /api/v1/status` `auth_mode`) unless `AMPLY_ALLOW_ANONYMOUS_ROUTE=1` on the server (preview only).\n\n**Autonomous agents:** `GET /api/v1/agent` returns an ordered workflow manifest: **credential steps (signup and/or dashboard) are listed before** `GET /api/v1/providers` so integrators mint a user-bound `amply_sk_*` key before routed traffic. When `AMPLY_AGENT_SIGNUP=1`, `POST /api/v1/signup/agent` mints that key (terms version required). **Prefer user-bound Bearer tokens** for `POST /api/v1/route`: server secrets in `AMPLY_API_KEYS` authenticate but do not attach a principal (`X-Amply-Principal-Bound: 0`).\n\n**Billing model:** prepaid usage wallet (deposit balance, then per-request debits). In production, user API keys typically require sufficient balance when wallet tables exist (`AMPLY_ROUTE_ENFORCE_WALLET_DEBIT`, default strict path, or `AMPLY_ROUTE_ENFORCE_WALLET_DEBIT_PILOT_PRINCIPALS` for cohort-only enforcement). Read `usage` and `estimated_charge_usd` in `POST /api/v1/route` responses. Daily per-principal quota: `AMPLY_ROUTE_QUOTA_ROUTES_PER_DAY_PER_PRINCIPAL`.\n\nLatency semantics: **Wall RTT** is full client round-trip (network + edge). **Server compute** is handler time only. Use JSON `compute_ms` and the `X-Amply-Compute-Ms` response header (and `compute_*` fields from scripts/synthetic-probe.mjs) for SLO tracking. The ~200ms product bar applies to server compute, not necessarily to wall RTT from arbitrary clients or CI runners."},"servers":[{"url":"https://www.flightmedia.app"}],"paths":{"/api/v1/openapi":{"get":{"summary":"OpenAPI document","operationId":"getOpenApi","responses":{"200":{"description":"This document (JSON)"}}}},"/api/v1/status":{"get":{"summary":"Health and catalog diagnostics","operationId":"getStatus","responses":{"200":{"description":"ok, data_mode, diagnostics (catalog freshness, measured catalog methodology, outcome_telemetry freshness_hours + rollup_freshness_hours + coverage, catalog_data_loop coverage aggregates for the data-collection plan including proprietary_signal_* coverage, rollout flags, agent_signup summary when present, principal_binding launch guidance + last_24h_metering_snapshot + adoption.recommended_machine_actions when present), catalog_freshness, auth_mode, rate_limit, request_id, compute_ms (handler time only). Headers: X-Amply-Request-Id, X-Amply-Compute-Ms (matches compute_ms). Wall RTT is not the same as compute_ms."}}}},"/api/v1/health":{"get":{"summary":"Latest live provider health states","operationId":"getProviderHealth","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"providers[] with provider_id, status (healthy|degraded|unknown|incident), probed_at, and p95_ms from the latest health-probe cycle."},"401":{"description":"Missing or invalid Bearer token"}}}},"/api/v1/outcome":{"post":{"summary":"Submit route outcome feedback and earn one credit","operationId":"postOutcome","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["route_id","success"],"properties":{"route_id":{"type":"string"},"success":{"type":"boolean"},"actual_latency_ms":{"type":"integer"},"error_code":{"type":"string"}}}}}},"responses":{"200":{"description":"received=true, credits_awarded=1, route_id"},"400":{"description":"Validation error"},"401":{"description":"Missing or invalid Bearer token"},"404":{"description":"route_id not found in route history"}}}},"/api/v1/report":{"post":{"summary":"Operator savings and cost report","operationId":"postReport","security":[{"bearerAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"period":{"type":"string","enum":["30d","90d"]}}}}}},"responses":{"200":{"description":"period, period_start, period_end, total_routes, total_estimated_cost_usd, naive_cost_usd, total_savings_usd, savings_pct, top_providers_used[], optional summary string."},"400":{"description":"Invalid period"},"401":{"description":"Missing or invalid Bearer token"},"403":{"description":"User-bound API key required"}}}},"/api/v1/agent":{"get":{"summary":"Agent workflow manifest (discover → credential → route → outcome → commercial)","operationId":"getAgentWorkflow","responses":{"200":{"description":"ok, request_id, autonomous_credential_path, oauth_fallback_always_available, tos_version_for_signup, workflow (steps[] with credential_* before catalog, credential_before_catalog, bootstrap_sequence, principal_binding_policy.recommended_machine_flow, commercial block, agent_signup flags). Single entrypoint for autonomous onboarding."}}}},"/api/v1/signup/agent":{"post":{"summary":"Mint API key for autonomous agents (when AMPLY_AGENT_SIGNUP=1)","operationId":"postAgentSignup","parameters":[{"name":"X-Amply-Agent-Signup-Token","in":"header","required":false,"schema":{"type":"string"},"description":"Required when AMPLY_AGENT_SIGNUP_SHARED_SECRET is set on the server."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accept_tos_version"],"properties":{"accept_tos_version":{"type":"string","description":"Must equal current terms from GET /api/v1/status diagnostics.agent_signup.tos_version"},"label":{"type":"string","maxLength":120,"description":"Optional human/agent-readable label"}}}}}},"responses":{"200":{"description":"api_key (once), id, key_prefix, principal_id, quickstart URLs, request_id. Use Authorization: Bearer api_key on POST /api/v1/route."},"400":{"description":"accept_tos_version mismatch or bad JSON"},"401":{"description":"Shared secret required and missing/invalid"},"403":{"description":"Agent signup disabled"},"429":{"description":"Per-IP hourly attempt limit"},"500":{"description":"Key store or schema error"},"503":{"description":"Supabase not configured for user keys"}}}},"/api/v1/providers":{"get":{"summary":"Public provider snapshot with default composite scores","operationId":"listProviders","responses":{"200":{"description":"providers[] (PublicProvider: … catalog_row_updated_at, measured_at, metrics_verified_at, measured_aggregated_at, outcome_aggregated_at, …), default_scoring_weights, catalog_freshness"}}}},"/api/v1/compliance":{"get":{"summary":"Machine-readable compliance and policy contract profile","operationId":"getComplianceProfile","responses":{"200":{"description":"spec_version, as_of, terms urls/version, data_policy summary, and routing_policy_contract support flags (enterprise_policy constraints, sandbox_mode)."}}}},"/api/v1/billing/status":{"get":{"summary":"Programmatic wallet status for calling principal","operationId":"getBillingStatus","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"ok, principal_id (when user key maps to principal), settlement_enabled, can_call, balance_usd, updated_at, request_id."},"401":{"description":"Missing or invalid Bearer token"},"503":{"description":"Wallet settlement migration not applied / unavailable"}}}},"/api/v1/billing/topup":{"post":{"summary":"Admin-programmatic wallet top-up for a principal","operationId":"postBillingTopup","parameters":[{"name":"X-Amply-Billing-Admin-Secret","in":"header","required":true,"schema":{"type":"string"},"description":"Must match server env AMPLY_BILLING_ADMIN_SECRET."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["principal_id","amount_usd"],"properties":{"principal_id":{"type":"string"},"amount_usd":{"type":"number","minimum":0.000001},"reference":{"type":"string"},"metadata":{"type":"object"}}}}}},"responses":{"200":{"description":"ok, credited_usd, balance_before_usd, balance_after_usd, ledger_request_id"},"400":{"description":"Validation error"},"401":{"description":"Invalid/missing admin secret"},"503":{"description":"Wallet settlement migration not applied / unavailable"}}}},"/api/v1/platform-metrics":{"get":{"summary":"Public routing telemetry aggregates (7d/30d, share to listed providers, by category)","operationId":"getPlatformMetrics","responses":{"200":{"description":"decisions_last_7d, decisions_last_30d, decisions_to_listed_providers_last_30d, pct, by_category_last_30d, by_provider_last_7d, by_provider_last_30d (after SQL migration), optional outcome_telemetry coverage/freshness, methodology, telemetry_ready"}}}},"/api/v1/route":{"post":{"summary":"Recommend a catalog provider for a task (vector retrieval or LLM inference). For wallet, quota, and audit, use a user-bound key (amply_sk_*) — not an AMPLY_API_KEYS server secret.","operationId":"postRoute","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["task"],"properties":{"task":{"type":"string","description":"Workload description (1 to 8000 chars)"},"budget_usd":{"type":"number","minimum":0},"latency_target_ms":{"type":"number","minimum":0},"budget_usd_per_1m_ops":{"type":"number","minimum":0,"description":"Optional max provider cost per 1M operations for constraint filtering."},"slo_p99_ms":{"type":"integer","minimum":1,"description":"Optional max provider p99 latency for constraint filtering."},"dimension":{"type":"integer","minimum":1,"maximum":65536},"filter_complexity":{"enum":["low","medium","high"]},"workload_type":{"enum":["insert_heavy","query_heavy","hybrid"]},"exploration_mode":{"enum":["off","measure"],"description":"Optional. `measure` enables bounded exploration when server env AMPLY_ROUTE_EXPLORATION_ENABLED=1 (used to gather multi-provider outcome evidence)."},"include_raw_metrics":{"type":"boolean","description":"Optional. Set false to skip raw_metrics payload construction (lower response size and compute for latency-sensitive agent paths)."},"sandbox_mode":{"type":"boolean","description":"Optional. When true, runs policy and scoring but suppresses telemetry writes (safe validation mode before full integration)."},"enterprise_policy":{"type":"object","description":"Optional policy constraints applied before scoring. Use to enforce enterprise allow/deny controls in routing.","properties":{"provider_allowlist":{"type":"array","items":{"type":"string"},"maxItems":64,"description":"If provided, only these provider ids are eligible for scoring."},"provider_denylist":{"type":"array","items":{"type":"string"},"maxItems":64,"description":"If provided, these provider ids are excluded from scoring."}}},"referral_tag":{"type":"string","description":"Optional 1–64 char attribution token (letters, digits, ._:@/-). Counted in telemetry for attribution and campaign breakdowns.","maxLength":64},"route_category":{"enum":["vector_retrieval","llm_inference","rerank","ocr_document_extract","speech_to_text","content_moderation","web_extract"],"description":"Optional. Default `vector_retrieval` scores retrieval listings. Additional categories are adapter-backed and return 503 until matching active listings exist in catalog. `llm_inference` interprets cost_per_1m_dims_usd as $/1M tokens."},"context_tokens":{"type":"integer","minimum":1,"maximum":2000000,"description":"Optional. When set, excludes LLM listings whose capabilities.max_context_tokens is below this value."}}}}}},"responses":{"200":{"description":"recommended + recommended_provider_id, score, confidence (0..1) + confidence_tier + confidence_factors, route_category, context_tokens, route_request_digest (SHA256 of canonical inputs), freshness block on winner + raw_metrics provider rows, rankings[] with confidence_sources, optional filtered_out[] and cost_analysis when budget_usd_per_1m_ops and/or slo_p99_ms constraints are provided, health {status, probed_at, note} for the selected provider, optional health_override=true when an incident winner is replaced with a better-health option, integration (when metering on: principal_attached, alerts when server-only key, links for billing/outcome/audit, outcome_telemetry hint), data_moat_signals, provenance, usage, settlement, concentration_control, alternates, raw_metrics.all_providers.*, catalog_source/catalog_backend_used. Headers: X-Amply-Request-Id, X-Amply-Compute-Ms, X-Amply-Principal-Bound (0|1), X-Amply-Auth-Source (user_key|server_key|anonymous).","content":{"application/json":{"schema":{"type":"object","description":"Partial schema: documents `integration` when present. Full route payload includes many additional fields; clients should tolerate unknown keys.","additionalProperties":true,"properties":{"integration":{"description":"Omitted in sandbox_mode or when usage metering is off. Otherwise principal, billing, outcome, and audit links.","allOf":[{"$ref":"#/components/schemas/RouteIntegrationHints"}]}}}}}},"400":{"description":"Validation error; body includes request_id when applicable"},"401":{"description":"When AMPLY_API_KEYS is set: missing/invalid Bearer token + request_id"},"402":{"description":"Insufficient prepaid wallet balance when strict debit is enabled. Body: error=insufficient_balance, shortfall_usd, required_usd, balance_usd, top_up_url (hint)."},"429":{"description":"Rate limit (AMPLY_V1_RATE_LIMIT_PER_MIN) or daily per-principal route quota (error=quota_exceeded, quota_limit, quota_used, quota_resets_at)."},"503":{"description":"No scorable providers (stale/missing metrics), no listings for route_category, or no provider passes catalog capabilities (body may include capability_exclusions)."}}}},"/api/v1/route/audit":{"get":{"summary":"Audit/replay proof bundle for a route request_id (scoped to caller principal)","operationId":"getRouteAudit","security":[{"bearerAuth":[]}],"parameters":[{"name":"request_id","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"request_id from POST /api/v1/route response"}],"responses":{"200":{"description":"ok, audit bundle with decision row, usage event, outcome rows, and proof_sha256 deterministic hash."},"400":{"description":"Invalid request_id format"},"401":{"description":"Missing or invalid Bearer token"},"403":{"description":"User API key cannot read another principal’s bundle, or legacy decision without caller_principal_id."},"404":{"description":"request_id not found in route decisions"}}}},"/api/v1/route/outcome":{"post":{"summary":"Record optional post-route outcomes for telemetry","operationId":"postRouteOutcome","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["request_id","provider_id","outcome"],"properties":{"request_id":{"type":"string","format":"uuid","description":"request_id from an earlier POST /api/v1/route response"},"provider_id":{"type":"string"},"outcome":{"enum":["success","failure","timeout","cancelled"]},"latency_ms":{"type":"number","minimum":0},"cost_usd":{"type":"number","minimum":0},"error_code":{"type":"string","maxLength":120},"workload_key":{"type":"string","maxLength":120},"probe_version":{"type":"string","maxLength":64},"quality_score":{"type":"number","minimum":0,"maximum":1,"description":"Optional quality scalar for downstream calibration (0..1)."},"execution_hash":{"type":"string","maxLength":128,"description":"Optional stable execution fingerprint for dedupe/correlation."},"source":{"enum":["agent_outcome","amply_probe"]},"metadata":{"type":"object"}}}}}},"responses":{"200":{"description":"ok, request_id (Amply telemetry write id), compute_ms trace header"},"400":{"description":"Validation error"},"401":{"description":"Missing or invalid Bearer token"},"404":{"description":"request_id not found in route decisions"},"409":{"description":"provider_id does not match decision winner for request_id"},"500":{"description":"Telemetry write failed"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Optional. Required only if AMPLY_API_KEYS is set on the server."}},"schemas":{"RouteIntegrationHints":{"type":"object","description":"Integrator hints from POST /api/v1/route when metering is on (non-sandbox). Aligns with server `buildRouteIntegrationHints`.","properties":{"principal_attached":{"type":"boolean"},"auth_source":{"type":"string","nullable":true},"settlement_eligible":{"type":"boolean"},"recommendation":{"type":"string","nullable":true,"description":"Guidance when no user-bound principal (e.g. server-only AMPLY_API_KEYS)."},"alerts":{"type":"array","nullable":true,"description":"Structured warnings; present when principal_attached is false under metering. When Bearer matched AMPLY_API_KEYS, first alert typically uses code server_env_key_no_principal.","items":{"type":"object","properties":{"code":{"type":"string"},"severity":{"type":"string","enum":["high","medium","low"]},"summary":{"type":"string"},"remediation":{"type":"string"}}}},"links":{"type":"object","properties":{"create_api_key":{"type":"string","format":"uri"},"agent_signup":{"type":"string","format":"uri"},"workflow_manifest":{"type":"string","format":"uri"},"billing_status":{"type":"string","format":"uri"},"route_outcome":{"type":"string","format":"uri"},"route_audit":{"type":"string","format":"uri"},"docs_outcomes":{"type":"string","format":"uri"}}},"outcome_telemetry":{"type":"object","properties":{"why":{"type":"string"},"post_body":{"type":"object","properties":{"request_id":{"type":"string"},"provider_id":{"type":"string"},"outcome":{"type":"string"}}}}}}},"CatalogMetricsDiagnostics":{"type":"object","description":"Hybrid measured catalog transparency (GET /api/v1/status diagnostics.catalog_metrics).","properties":{"freshness_hours":{"type":"object","properties":{"cost":{"type":"number","nullable":true,"description":"Hours since last history row with cost"},"latency":{"type":"number","nullable":true},"success":{"type":"number","nullable":true}}},"methodology":{"type":"string"}}},"PublicProvider":{"type":"object","properties":{"id":{"type":"string"},"display_name":{"type":"string"},"category":{"type":"string"},"metrics_status":{"type":"string"},"routing_metrics_source":{"type":"string","nullable":true,"enum":["attested","measured_7d"]},"metrics_source":{"type":"string","nullable":true,"enum":["attested_catalog","amply_measured_7d"],"description":"Stable public label for comparable raw_metrics"},"measured_at":{"type":"string","format":"date-time","nullable":true,"description":"ISO time for attested (metrics_verified_at) or measured (measured_aggregated_at) path"},"metrics_verified_at":{"type":"string","format":"date-time","nullable":true},"measured_aggregated_at":{"type":"string","format":"date-time","nullable":true},"catalog_row_updated_at":{"type":"string","format":"date-time","nullable":true,"description":"Last amply_route_providers upsert (daily catalog-refresh moves all active rows)"},"metrics_provenance":{"type":"object","nullable":true},"p99_latency_ms":{"type":"number","nullable":true},"cost_per_1m_dims_usd":{"type":"number","nullable":true},"success_rate_last_24h":{"type":"number","nullable":true},"success_rate_last_7d":{"type":"number","nullable":true},"win_rate":{"type":"number","nullable":true},"revenue_captured_usd":{"type":"number","nullable":true},"missed_opportunity_usd":{"type":"number","nullable":true},"outcome_success_rate_30d":{"type":"number","nullable":true},"outcome_timeout_rate_30d":{"type":"number","nullable":true},"outcome_sample_size_30d":{"type":"integer","nullable":true},"outcome_aggregated_at":{"type":"string","format":"date-time","nullable":true},"outcome_blended":{"type":"boolean"},"catalog_listing":{"type":"string","description":"Catalog representation tier (e.g. organic vs extended metadata). Editorial taxonomy; not a pay-for-placement product."},"cost_model_review_pending":{"type":"boolean"},"placement_disclosure":{"type":"string","description":"Short plain-language note on how this row participates in routing disclosure."},"live_composite_score":{"type":"number","nullable":true}}},"RouteRawMetricsProvider":{"type":"object","description":"Per-provider entry under raw_metrics.all_providers from POST /api/v1/route","properties":{"composite_score":{"type":"number"},"components":{"type":"object","additionalProperties":{"type":"number"}},"p99_latency_ms":{"type":"number","nullable":true},"cost_per_1m_dims_usd":{"type":"number","nullable":true},"success_rate_last_24h":{"type":"number","nullable":true},"success_rate_last_7d":{"type":"number","nullable":true},"win_rate":{"type":"number","nullable":true},"revenue_captured_usd":{"type":"number","nullable":true},"missed_opportunity_usd":{"type":"number","nullable":true},"outcome_success_rate_30d":{"type":"number","nullable":true},"outcome_timeout_rate_30d":{"type":"number","nullable":true},"outcome_sample_size_30d":{"type":"integer","nullable":true},"outcome_blended":{"type":"boolean"},"metrics_source":{"type":"string","nullable":true,"enum":["attested_catalog","amply_measured_7d"]},"measured_at":{"type":"string","format":"date-time","nullable":true},"data_moat_signals":{"type":"object","nullable":true,"description":"Amply-only confidence signals from internal telemetry depth/freshness. Includes internal_signal_score (0..1), tier, and proprietary_ready flag."},"risk_signals":{"type":"object","nullable":true,"description":"Adversarial-quality risk model from observed timeout/success patterns and sample depth (risk_score, risk_tier, recommended_restriction)."}}}}}}