{"openapi":"3.1.0","info":{"title":"looot API","version":"0.1.0","description":"Discover, inspect, run, and pay for real (or fixture) provider operations through one bearer token.","termsOfService":"https://looot.ai/terms","contact":{"name":"looot","url":"https://looot.ai/contact"}},"servers":[{"url":"https://api.looot.ai","description":"Public API (default)"}],"security":[{"bearerAuth":[]}],"paths":{"/v1/discover":{"get":{"summary":"Discover operations","responses":{"200":{"description":"Success"}}}},"/v1/catalog/search":{"get":{"summary":"Ranked full-text catalog search, with capability expansion and a stemmed-token fallback so a long natural-language query is never empty","description":"The same search core answers MCP `search` (10 title rows by default) and MCP search_catalog (50 full rows by default, like this route). A title row is a subset of this route's row: endpointId, provider, name, capability, category, estimatedPrice, priceBasis, access, executionMode, costPerSuccessUsd (facts.costPerSuccessUsd), async (facts.async), works, sourceCapability and credential, in the same order for the same q, limit and offset for a customer token. A platform operator token differs: this route always includes unavailable rows, while MCP honors an explicit filters.includeUnavailable false.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"offset","in":"query","description":"Where the page starts in the ranked list; pass the previous page's nextOffset (null on the last page).","schema":{"type":"integer","minimum":0}},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"platform","in":"query","schema":{"type":"string"}},{"name":"provider","in":"query","schema":{"type":"string"}},{"name":"capability","in":"query","schema":{"type":"string"}},{"name":"keyless","in":"query","schema":{"type":"boolean"}},{"name":"verified","in":"query","schema":{"type":"boolean"}},{"name":"mock","in":"query","description":"true/false excludes the other kind; omitted keeps both but real providers still rank above mocks","schema":{"type":"boolean"}},{"name":"maxPriceMicros","in":"query","schema":{"type":"integer","minimum":0}},{"name":"hidden","in":"query","schema":{"type":"boolean"}},{"name":"prefer","in":"query","description":"How providers are ordered inside a job: balanced (default), cheapest, reliable or fastest. Any other value is a 400 validation_error. Every item's facts carry it and the body echoes it; it changes the order only once the gateway serves the shared ranking.","schema":{"type":"string","enum":["cheapest","reliable","fastest","balanced"]}}],"responses":{"200":{"description":"Ranked catalog items (each carrying mock, verified, keyless, runnableNow, stats, access, matchedBy, facts, works, credential and sourceCapability; capability is the resolved job id), same shape as the listing, plus the prefer value used. An empty page may carry warnings (no_supply_for_job) or priceHint (price_above_max: maxPriceMicros removed every endpoint that does the named job; it names the cheapest)"},"400":{"description":"q is required"},"403":{"description":"catalog:read scope required"}}}},"/v1/catalog/endpoints":{"get":{"summary":"Paged, filtered, faceted catalog listing","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":5000}},{"name":"cursor","in":"query","schema":{"type":"string","maxLength":4096}},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"platform","in":"query","schema":{"type":"string"}},{"name":"provider","in":"query","schema":{"type":"string"}},{"name":"capability","in":"query","schema":{"type":"string"}},{"name":"keyless","in":"query","schema":{"type":"boolean"}},{"name":"verified","in":"query","schema":{"type":"boolean"}},{"name":"maxPriceMicros","in":"query","schema":{"type":"integer","minimum":0}},{"name":"hidden","in":"query","schema":{"type":"boolean"}},{"name":"facets","in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Catalog listing page with nextCursor and, when facets=true, category/platform/provider/capability counts for the filtered set"},"304":{"description":"Catalog unchanged since If-None-Match"},"403":{"description":"catalog:read scope required"}}}},"/v1/operations/{endpointId}":{"get":{"summary":"Inspect an operation","responses":{"200":{"description":"Version-pinned operation contract and safe usage hints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InspectOutput"}}}}}}},"/v1/runs":{"post":{"summary":"Create a run -- idempotencyKey replay returns the existing run (any status) with replayed:true instead of creating a new one","description":"customer-test friction #4 (docs/product/reviews/2026-09-04-customer-test): dispatch is woken immediately for the run's workspace rather than left for the next poll tick, and the run always returns immediately unless `wait` asks otherwise (either as this query parameter or as a body field of the same name -- the query parameter wins if both are given). `wait=true` (or a bare `?wait`) waits up to the default of 20 s; a number is seconds, clamped to [0, 60]. Returns the result inline, in the same run shape this endpoint already returns, once it reaches a terminal status within `wait` seconds. If it is still queued/running when the window ends, returns that queued/running run instead, exactly as with no `wait` at all -- poll it with GET /v1/runs/{runId} or runs_get. A 201 can carry status:\"failed\" (a pre-dispatch denial such as endpoint_not_found or a validation_error) -- check the run's own `status` and `error` fields, never the HTTP status code alone, to know whether it actually ran.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunRequest"}}}},"parameters":[{"name":"wait","in":"query","schema":{"oneOf":[{"type":"boolean"},{"type":"number","minimum":0,"maximum":60}]},"description":"Seconds to wait for a terminal result inline (default 20 when true/bare, max 60). Omit entirely for the pre-existing immediate-return behavior."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1},"description":"Alternative to the request body's idempotencyKey field. Either satisfies the (still mandatory) requirement; if both are present, they must be equal or the request is a 400."}],"responses":{"201":{"description":"Run created (or the existing run replayed, any status, with replayed:true). A 201 is not itself success -- a pre-dispatch denial (endpoint_not_found, validation_error) also answers 201 with status:\"failed\" and a populated error; read the body, not the status code."},"402":{"description":"Out of balance -- the run was blocked before dispatch, never billed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","additionalProperties":false,"required":["code","message","requestId"],"properties":{"code":{"type":"string","const":"insufficient_balance"},"message":{"type":"string"},"requestId":{"type":"string"}}},"balanceMicros":{"type":"integer"},"estimatedCostMicros":{"type":"integer"},"topUpUrl":{"type":"string","nullable":true},"topUp":{"type":"object","properties":{"minimumUsd":{"type":"number"},"suggestedUsd":{"type":"number"},"checkoutUrl":{"type":"string","nullable":true},"dashboardUrl":{"type":"string"},"message":{"type":"string"},"reason":{"type":"string","enum":["billing_disabled","payment_processor_not_configured","operator_workspace","read_only_token","rate_limited","checkout_failed","stripe_timeout","amount_out_of_range","balance_low","balance_sufficient"]}},"required":["minimumUsd","suggestedUsd","checkoutUrl","dashboardUrl","message"]}},"required":["error","balanceMicros","estimatedCostMicros","topUpUrl","topUp"]}}}},"409":{"description":"Idempotency-key conflict -- a request already exists for this key that this one does not match"},"429":{"description":"Rate limit exceeded (execute class, too_many_inflight_runs) -- see Retry-After and X-RateLimit-* response headers","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","additionalProperties":false,"required":["code","message","requestId"],"properties":{"code":{"type":"string","const":"too_many_inflight_runs"},"message":{"type":"string"},"requestId":{"type":"string"}}},"inflight":{"type":"integer"},"limit":{"type":"integer"},"retryAfterSeconds":{"type":"integer"}},"required":["error","inflight","limit","retryAfterSeconds"]}}}}}},"get":{"summary":"List runs -- bounded summaries (runId, endpointId, providerId, status, providerResponseStatus, createdAt, completedAt, actualCost, error, resultBytes, and servedEndpointId/servedProviderId (the endpoint and provider that answered; absent when none did)); the full result payload stays on GET /v1/runs/{runId}; includeResult=true opts back in","responses":{"200":{"description":"Success"}}}},"/v1/runs/{runId}":{"get":{"summary":"Get a run -- on a failed run, error carries { code, providerStatus, message, requestId, whoseError, retryable, retryHint }, never secrets or raw provider headers","responses":{"200":{"description":"Success"}},"description":"2026-09-27 (additive): a completed run can carry an optional `normalized` object next to `result`: { job, endpointId, fields, missing, ambiguous, unmapped, providerValues, verdict, verdictReason, mapVerified }, with fixed field names per job and one email status vocabulary (valid, invalid, catch_all, risky, unknown). Present only when the gateway has it turned on and the answering endpoint has an output map; absent, never null, otherwise. `result` and `outcome` are unchanged."}},"/v1/runs/{runId}/cancel":{"post":{"summary":"Cancel a run","responses":{"200":{"description":"Success"}}}},"/v1/runs/{runId}/attempts":{"get":{"summary":"Inspect durable attempts and receipts","responses":{"200":{"description":"Success"}}}},"/v1/balance":{"get":{"summary":"Get workspace balance. topUpLink includes purchase minimum, dashboard and any pending checkout. Compare funds with each run estimate; the purchase minimum is not a wallet threshold. Reading balance never creates checkout","responses":{"200":{"description":"Success"}}}},"/v1/top-ups":{"get":{"summary":"List prepaid top-ups","responses":{"200":{"description":"Success"}}},"post":{"summary":"Request a prepaid top-up (creates a Stripe Checkout Session when Stripe is configured)","responses":{"200":{"description":"Success"}}}},"/v1/connections":{"get":{"summary":"List connection metadata","responses":{"200":{"description":"Tenant connection metadata"},"403":{"description":"connections:read scope required"}}},"post":{"summary":"Register a write-only API credential","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterConnection"}}}},"responses":{"201":{"description":"Connection metadata; secrets and internal references are excluded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Connection"}}}},"403":{"description":"connections:write scope required"},"409":{"description":"Connection already exists"}}}},"/v1/public-catalog":{"get":{"security":[],"summary":"Search stable redacted public catalog pages by text, capability, category, or provider","description":"Publicly browsable without a bearer token; the catalog is not tenant-scoped.","parameters":[{"name":"text","in":"query","schema":{"type":"string","minLength":1,"maxLength":200},"description":"At most 800 UTF-8 bytes; control characters rejected"},{"name":"q","in":"query","schema":{"type":"string","minLength":1,"maxLength":200},"description":"Alias for text; cannot be combined with text"},{"name":"capability","in":"query","schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"}},{"name":"category","in":"query","schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"}},{"name":"provider","in":"query","schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"}},{"name":"operationId","in":"query","schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"signals","in":"query","schema":{"enum":["1"]},"description":"1 also returns unsuppliedJobs and warnings when no public endpoint does the query's job; without it that page is items: [] with the three base keys"}],"responses":{"200":{"description":"Stable public catalog page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCatalogPage"}}}},"400":{"description":"Invalid, repeated, or malformed query/cursor"},"401":{"description":"Only returned for a malformed request; no bearer token is required"},"403":{"description":"Catalog read scope required"},"409":{"description":"catalog_cursor_stale: cursor no longer matches the query or catalog snapshot"}}}},"/v1/public-catalog/{catalogItemId}":{"get":{"security":[],"summary":"Inspect one public published catalog item; hidden and missing ids share 404","description":"Publicly browsable without a bearer token; the catalog is not tenant-scoped.","parameters":[{"name":"catalogItemId","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"}}],"responses":{"200":{"description":"Public catalog item","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCatalogItem"}}}},"401":{"description":"Only returned for a malformed request; no bearer token is required"},"403":{"description":"Catalog read scope required"},"404":{"description":"Catalog item not found"}}}},"/v1/public-catalog/compare":{"post":{"security":[],"summary":"Compare 2 to 20 explicit public catalog items without inferring missing price semantics","description":"Publicly browsable without a bearer token; the catalog is not tenant-scoped.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["catalogItemIds"],"properties":{"catalogItemIds":{"type":"array","minItems":2,"maxItems":20,"uniqueItems":true,"items":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"}}}}}}},"responses":{"200":{"description":"Conservative catalog comparison","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCatalogComparison"}}}},"400":{"description":"Invalid selection"},"401":{"description":"Only returned for a malformed request; no bearer token is required"},"403":{"description":"Catalog read scope required"},"404":{"description":"One or more catalog items not found"}}}},"/v1/session-context":{"get":{"summary":"Get bounded authenticated local session context","parameters":[{"name":"include","in":"query","required":false,"description":"expiresAt adds the token's own expiry to the response; organizationName adds the organization's display name. Comma-separated or repeated.","schema":{"type":"string","examples":["expiresAt","expiresAt,organizationName"]}}],"description":"Returns only the server-derived local organization, authentication class, canonical granted scopes, and local mode. It never returns tokens, token hashes, principal identifiers, vault references, or session identifiers.","responses":{"200":{"description":"Authenticated local session context","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionContext"}}}},"401":{"description":"Authentication required"},"403":{"description":"local-product:read scope required"},"405":{"description":"Method not allowed"},"503":{"description":"Session context unavailable"}}}},"/v1/whoami":{"get":{"summary":"Current principal's membership, organization, tool grants and machine sessions","responses":{"200":{"description":"Organization, membership, active tool grants and active machine sessions; never includes a tokenHash"},"403":{"description":"organizations:read scope required"},"404":{"description":"No membership in the token's organization"},"503":{"description":"Control plane unavailable"}}}},"/v1/catalog/overview":{"get":{"security":[],"summary":"What the catalog covers: categories > platforms > jobs with provider, endpoint and runnable counts","description":"Publicly readable without a bearer token; the same answer for every caller, rebuilt only when the catalog revision (or a platform key or fee) changes. Counts the rows the live public catalog lists (GET /v1/public-catalog when it serves the live catalog): the same row rules and public record checks (hidden slugs, prohibited, mock, unpriced, inactive and disabled rows, and records the public schema refuses, are left out). A category is the vocabulary category of the job's platform, `other` when the platform is not in the vocabulary. Every level (category, platform, job) carries providerCount (distinct providers), endpointCount, runnableCount (runnable without your own key: keyless, or a platform key exists), cheapestPerCall and cheapestPerResult (lowest prices, the price you pay, null when nothing is priced that way; never compared with each other). The default answer is depth summary (totals plus one entry per category, a few KB); depth=full is the whole document (about 490 KB). category or platform narrows to one branch (default depth platforms; depth=jobs lists its jobs) and then answers filter instead of totals. topic answers the matching jobs, at most 25. Each view has its own ETag; revalidate with If-None-Match.","parameters":[{"name":"depth","in":"query","required":false,"schema":{"enum":["summary","platforms","jobs","full"]},"description":"summary (default): totals and per category counts. platforms: adds each category's platforms. jobs: adds each platform's jobs (a tree). full: each category's platforms[] and flat jobs[] with job.platform. Default platforms when category or platform is given; pass jobs to list the branch jobs. Not allowed with topic."},{"name":"category","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"A category id: answers only that category. Unknown: 404 unknown_category."},{"name":"platform","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"A platform id: answers the category holding it with only that platform. Unknown (or not in the given category): 404 unknown_platform."},{"name":"topic","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"Words such as email or phone: answers the jobs whose id, title, platform or category match (the job-first search matcher), best first, at most 25, with truncated. Combines with category and platform."},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Catalog overview view: a depth view, or a topic view when topic is given","headers":{"ETag":{"schema":{"type":"string"},"description":"W/\"rev-<revision>-<digest of this view>\""}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CatalogOverview"},{"$ref":"#/components/schemas/CatalogOverviewTopic"}]}}}},"304":{"description":"View unchanged since If-None-Match"},"400":{"description":"validation_error: error.field names the argument (depth, category, platform or topic) and error.message says what to send"},"404":{"description":"unknown_category (error.categories lists the ids) or unknown_platform; error.field names the argument"},"503":{"description":"catalog_loading: no catalog snapshot is loaded yet; retry after retry-after seconds. production_runtime_unavailable: this deployment serves no v2 catalog"}}}},"/v1/ledger":{"get":{"summary":"List ledger entries","responses":{"200":{"description":"Success"}}}},"/v1/capability-requests":{"get":{"summary":"List tenant capability requests","responses":{"200":{"description":"Success"}}},"post":{"summary":"Create an idempotent tenant capability request","responses":{"200":{"description":"Success"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"CatalogOverview":{"type":"object","additionalProperties":false,"required":["revision","builtAt","depth","categories"],"properties":{"revision":{"type":"string"},"builtAt":{"type":"string","format":"date-time"},"depth":{"enum":["summary","platforms","jobs","full"]},"filter":{"type":"object","additionalProperties":false,"description":"Present only on a narrowed view (category or platform given); such a view has no totals","properties":{"category":{"type":"string"},"platform":{"type":"string"}}},"totals":{"type":"object","additionalProperties":false,"description":"Whole catalog; only on a view that is not narrowed","required":["categories","platforms","jobs","providers","endpoints","runnableEndpoints"],"properties":{"categories":{"type":"integer"},"platforms":{"type":"integer"},"jobs":{"type":"integer"},"providers":{"type":"integer"},"endpoints":{"type":"integer"},"runnableEndpoints":{"type":"integer"}}},"categories":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["id","title","platformCount","jobCount","providerCount","endpointCount","runnableCount","cheapestPerCall","cheapestPerResult"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"platformCount":{"type":"integer"},"jobCount":{"type":"integer"},"providerCount":{"type":"integer","description":"Distinct providers with at least one listed endpoint at this level"},"endpointCount":{"type":"integer"},"runnableCount":{"type":"integer","description":"Endpoints runnable without your own key (keyless, or a platform key exists)"},"cheapestPerCall":{"type":["number","null"],"description":"Lowest per-call price in dollars at this level (the price you pay); null when nothing is priced per call"},"cheapestPerResult":{"type":["number","null"],"description":"Lowest per-result price in dollars at this level (the price you pay); null when nothing is priced per result"},"platforms":{"type":"array","description":"Depth platforms, jobs and full, and any view narrowed by platform","items":{"type":"object","additionalProperties":false,"required":["id","title","jobCount","providerCount","endpointCount","runnableCount","cheapestPerCall","cheapestPerResult"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"jobCount":{"type":"integer"},"providerCount":{"type":"integer","description":"Distinct providers with at least one listed endpoint at this level"},"endpointCount":{"type":"integer"},"runnableCount":{"type":"integer","description":"Endpoints runnable without your own key (keyless, or a platform key exists)"},"cheapestPerCall":{"type":["number","null"],"description":"Lowest per-call price in dollars at this level (the price you pay); null when nothing is priced per call"},"cheapestPerResult":{"type":["number","null"],"description":"Lowest per-result price in dollars at this level (the price you pay); null when nothing is priced per result"},"jobs":{"type":"array","description":"Depth jobs only: this platform's jobs","items":{"type":"object","additionalProperties":false,"required":["id","title","providerCount","endpointCount","runnableCount","cheapestPerCall","cheapestPerResult"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"providerCount":{"type":"integer","description":"Distinct providers with at least one listed endpoint at this level"},"endpointCount":{"type":"integer"},"runnableCount":{"type":"integer","description":"Endpoints runnable without your own key (keyless, or a platform key exists)"},"cheapestPerCall":{"type":["number","null"],"description":"Lowest per-call price in dollars at this level (the price you pay); null when nothing is priced per call"},"cheapestPerResult":{"type":["number","null"],"description":"Lowest per-result price in dollars at this level (the price you pay); null when nothing is priced per result"}}}}}}},"jobs":{"type":"array","description":"Depth full only: the category's jobs, each naming its platform","items":{"type":"object","additionalProperties":false,"required":["id","title","platform","providerCount","endpointCount","runnableCount","cheapestPerCall","cheapestPerResult"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"platform":{"type":"string"},"providerCount":{"type":"integer","description":"Distinct providers with at least one listed endpoint at this level"},"endpointCount":{"type":"integer"},"runnableCount":{"type":"integer","description":"Endpoints runnable without your own key (keyless, or a platform key exists)"},"cheapestPerCall":{"type":["number","null"],"description":"Lowest per-call price in dollars at this level (the price you pay); null when nothing is priced per call"},"cheapestPerResult":{"type":["number","null"],"description":"Lowest per-result price in dollars at this level (the price you pay); null when nothing is priced per result"}}}}}}}}},"CatalogOverviewTopic":{"type":"object","additionalProperties":false,"required":["revision","builtAt","topic","matchCount","truncated","jobs"],"properties":{"revision":{"type":"string"},"builtAt":{"type":"string","format":"date-time"},"topic":{"type":"string","description":"The topic as matched: lowercased, punctuation folded to spaces"},"filter":{"type":"object","additionalProperties":false,"description":"Present only on a narrowed view (category or platform given); such a view has no totals","properties":{"category":{"type":"string"},"platform":{"type":"string"}}},"matchCount":{"type":"integer","description":"Jobs that match before the cap"},"truncated":{"type":"boolean","description":"True when matchCount is over the 25 jobs answered"},"jobs":{"type":"array","maxItems":25,"items":{"type":"object","additionalProperties":false,"required":["id","title","providerCount","endpointCount","runnableCount","cheapestPerCall","cheapestPerResult","category","platform"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"providerCount":{"type":"integer","description":"Distinct providers with at least one listed endpoint at this level"},"endpointCount":{"type":"integer"},"runnableCount":{"type":"integer","description":"Endpoints runnable without your own key (keyless, or a platform key exists)"},"cheapestPerCall":{"type":["number","null"],"description":"Lowest per-call price in dollars at this level (the price you pay); null when nothing is priced per call"},"cheapestPerResult":{"type":["number","null"],"description":"Lowest per-result price in dollars at this level (the price you pay); null when nothing is priced per result"},"category":{"type":"string"},"platform":{"type":"string"}}}}}},"Connection":{"type":"object","additionalProperties":false,"required":["id","providerId","label","status","auth","scopes","lastProbe","cooldownUntil","credentialExpiresAt","consent","pendingCredentialRoles","createdAt","updatedAt","testState"],"properties":{"id":{"type":"string"},"providerId":{"type":"string"},"label":{"type":"string"},"status":{"enum":["draft","authorization_pending","connected","degraded","expired","action_required","revoked"]},"auth":{"type":"object","additionalProperties":false,"required":["kind"],"properties":{"kind":{"enum":["none","api_key","oauth","service_account","credential_file","signed_request","cli_auth"]},"placement":{"enum":["header","query"]},"name":{"type":"string"},"authorizationUrl":{"type":"string"},"tokenUrl":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"tokenPlacement":{"enum":["header","query"]},"algorithm":{"const":"hmac-sha256"},"signedHeaders":{"type":"array","items":{"type":"string"}}}},"scopes":{"type":"array","items":{"type":"string"},"description":"Granted scopes, populated after oauth callback or a probe that reports scopes"},"lastProbe":{"anyOf":[{"$ref":"#/components/schemas/ConnectionHealthReceipt"},{"type":"null"}]},"cooldownUntil":{"type":["string","null"],"format":"date-time"},"credentialExpiresAt":{"type":["string","null"],"format":"date-time"},"consent":{"type":"object","additionalProperties":false,"required":["expiresAt","warning"],"properties":{"expiresAt":{"type":["string","null"],"format":"date-time"},"warning":{"enum":["expiring_soon","expired",null]}}},"pendingCredentialRoles":{"type":"array","items":{"type":"string"},"description":"Declared secondary credential roles not yet supplied; non-empty implies status action_required"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"testState":{"enum":["not_tested","verified"],"description":"verified requires at least one persisted fixture_tested receipt; never set from CRUD alone"},"probeConfigured":{"type":"boolean","description":"true when POST /v1/connections/{connectionId}/probe has a live health check to run; false for a pasted api_key with no probe (the probe then answers 400 validation_error with reason connection_probe_not_configured)"},"latestTestReceipt":{"anyOf":[{"$ref":"#/components/schemas/ConnectionFixtureReceipt"},{"type":"null"}],"description":"Present only in the GET /v1/connections list response (absent, not null, on register/rotate/revoke/etc. responses); the same object GET /v1/connections/{connectionId}/test-receipts returns as `latest`"}}},"ConnectionFixtureReceipt":{"type":"object","additionalProperties":false,"required":["receiptId","connectionId","verification","providerCalled","credentialState","evidence","testedAt"],"properties":{"receiptId":{"type":"string","pattern":"^cfr_[a-f0-9]{32}$"},"connectionId":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"verification":{"const":"fixture_tested"},"providerCalled":{"type":"boolean"},"credentialState":{"const":"available"},"evidence":{"const":"local_fixture_credential_access"},"testedAt":{"type":"string","format":"date-time"}}},"ConnectionHealthReceipt":{"type":"object","additionalProperties":false,"required":["receiptId","connectionId","providerId","outcome","status","identity","scopes","resources","providerRequestId","remediation","checkedAt"],"properties":{"receiptId":{"type":"string","pattern":"^chr_[a-f0-9]{32}$"},"connectionId":{"type":"string"},"providerId":{"type":"string"},"outcome":{"enum":["healthy","invalid_credentials","insufficient_scope","unavailable","rate_limited","missing_resource"]},"status":{"type":["integer","null"]},"identity":{"type":["string","null"]},"scopes":{"type":"array","items":{"type":"string"}},"resources":{"type":"array","items":{"type":"string"},"description":"Accessible provider resource ids discovered by this probe (CA-CONN-03)"},"providerRequestId":{"type":["string","null"]},"remediation":{"type":["string","null"],"description":"Safe human-readable next step; never credential material"},"checkedAt":{"type":"string","format":"date-time"}}},"ConnectionProbeDefinition":{"type":"object","additionalProperties":false,"required":["host","method","path","expectedStatuses"],"properties":{"host":{"type":"string"},"method":{"enum":["GET","POST"]},"path":{"type":"string"},"expectedStatuses":{"type":"array","items":{"type":"integer"}},"identityPointer":{"type":"string"},"scopesPointer":{"type":"string"},"resourcesPointer":{"type":"string"}}},"InspectOutput":{"type":"object","additionalProperties":false,"required":["endpoint","estimatedMaxCost","usageHints"],"properties":{"endpoint":{"type":"object","description":"Exact registered endpoint metadata"},"estimatedMaxCost":{"type":"number","minimum":0,"description":"The price you pay: the total a run through the platform key would charge"},"inputSchema":{"type":["object","null"],"description":"Draft 2020-12 input schema resolved from fixture, activation bundle, or direct registration; null when the endpoint declares none"},"requestBinding":{"type":["object","null"],"description":"How validated input fields become the HTTP request (path/query/header/body); null for a fixture or bundle-published endpoint that declares none"},"uiSchema":{"type":["object","null"],"description":"Presentation hints for a generated form; never validation"},"outputFormat":{"type":"object","additionalProperties":false,"required":["schema","summary","provenance","note"],"description":"The endpoint's stored output format, present only when one is stored. Check provenance.verified before relying on it: false means an expected shape not yet checked against real answers","properties":{"schema":{"type":"object","description":"Draft 2020-12 output schema, structure only; its $comment says where it came from"},"summary":{"type":"string","description":"One-line summary of the schema"},"provenance":{"type":"object","additionalProperties":false,"required":["source","validatedAnswers","checkedAnswers","verified"],"properties":{"source":{"enum":["own-answers","treg-shape","both","unknown"]},"validatedAnswers":{"type":"integer","minimum":0,"description":"Real saved answers that matched the schema"},"checkedAnswers":{"type":"integer","minimum":0,"description":"Real saved answers the schema was checked against"},"verified":{"type":"boolean","description":"True only when at least one real answer was checked and every one matched"}}},"note":{"type":"string","description":"One-line label, e.g. \"validated on 12 real answers\" or \"expected shape, not yet checked against real answers\""}}},"usageHints":{"type":"object","additionalProperties":false,"required":["endpointId","operationVersion","contractDigest","authentication","requiredInputFields","outputSchema","examples"],"properties":{"endpointId":{"type":"string"},"operationVersion":{"type":"string"},"contractDigest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"authentication":{"type":"object","description":"Bearer placeholder only; never contains a credential"},"requiredInputFields":{"type":"array","items":{"type":"string"}},"outputSchema":{"type":"object","description":"Pinned Draft 2020-12 output schema"},"examples":{"type":"object","description":"Equivalent REST, MCP, and CLI requests using placeholders only"}}}}},"PublicCatalogComparison":{"type":"object","additionalProperties":false,"required":["items","priceComparison","comparisonDigest"],"properties":{"items":{"type":"array","minItems":2,"maxItems":20,"items":{"$ref":"#/components/schemas/PublicCatalogItem"}},"priceComparison":{"oneOf":[{"type":"object","additionalProperties":false,"required":["state","currency","unit","amounts","lowestAmountMicros","lowestCatalogItemIds"],"properties":{"state":{"const":"comparable"},"currency":{"type":"string","pattern":"^[A-Z]{3}$"},"unit":{"type":"string","minLength":1,"maxLength":80},"amounts":{"type":"array","minItems":2,"maxItems":20,"items":{"type":"object","additionalProperties":false,"required":["catalogItemId","amountMicros"],"properties":{"catalogItemId":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"},"amountMicros":{"type":"integer","minimum":0,"maximum":9007199254740991}}}},"lowestAmountMicros":{"type":"integer","minimum":0,"maximum":9007199254740991},"lowestCatalogItemIds":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"}}}},{"type":"object","additionalProperties":false,"required":["state","reason"],"properties":{"state":{"const":"not_comparable"},"reason":{"enum":["price_state_unknown_or_uncomputable","capability_or_contract_mismatch","price_semantics_unverified","currency_or_unit_mismatch"]}}}]},"comparisonDigest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"}}},"PublicCatalogItem":{"type":"object","additionalProperties":false,"required":["catalogItemId","revision","providerId","operationId","title","description","categories","capabilities","inputSchemaDigest","outputSchemaDigest","method","pathTemplate","parameters","verification","references","price","publishedAt","itemDigest","provenanceTier"],"properties":{"catalogItemId":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"},"revision":{"type":"integer","minimum":1,"maximum":9007199254740991},"providerId":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"},"operationId":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"},"title":{"type":"string","minLength":1,"maxLength":160},"description":{"type":"string","minLength":1,"maxLength":2000},"categories":{"type":"array","minItems":1,"maxItems":30,"items":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"}},"capabilities":{"type":"array","maxItems":30,"items":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"}},"inputSchemaDigest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"outputSchemaDigest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"method":{"type":["string","null"],"enum":["GET","POST","PUT","PATCH","DELETE",null]},"pathTemplate":{"type":["string","null"],"minLength":1,"maxLength":1024},"parameters":{"type":"array","maxItems":100,"items":{"type":"object","additionalProperties":false,"required":["name","location","required"],"properties":{"name":{"type":"string","minLength":1,"maxLength":120},"location":{"enum":["query","path","header","body"]},"required":{"type":"boolean"},"description":{"type":"string","minLength":1,"maxLength":500},"schemaDigest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"}}}},"verification":{"type":"object","additionalProperties":false,"required":["state","verifiedAt","responseCapture"],"properties":{"state":{"enum":["unverified","fixture_verified","authenticated_verified","stale"]},"verifiedAt":{"type":["string","null"],"format":"date-time"},"responseCapture":{"type":"object","additionalProperties":false,"required":["available","digest"],"properties":{"available":{"type":"boolean"},"digest":{"type":["string","null"],"pattern":"^sha256:[a-f0-9]{64}$"}}}}},"references":{"type":"array","maxItems":20,"items":{"type":"object","additionalProperties":false,"required":["kind","url"],"properties":{"kind":{"enum":["documentation","pricing"]},"url":{"type":"string","format":"uri","maxLength":2048}}}},"price":{"$ref":"#/components/schemas/PublicCatalogPrice"},"publishedAt":{"type":"string","format":"date-time"},"itemDigest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"provenanceTier":{"enum":["official","verified_partner","community","private","unverified"]}}},"PublicCatalogPage":{"type":"object","additionalProperties":false,"required":["items","catalogDigest","nextCursor"],"properties":{"items":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/PublicCatalogItem"}},"catalogDigest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"nextCursor":{"type":["string","null"],"minLength":1,"maxLength":2000},"unsuppliedJobs":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200},"description":"Sent only with signals=1 and together with warnings: best-matching job ids no public endpoint is filed under"},"warnings":{"type":"array","minItems":1,"maxItems":5,"description":"Sent only with signals=1, when no public endpoint does the query's job; items is then empty","items":{"type":"object","additionalProperties":false,"required":["code","jobId","jobTitle","message"],"properties":{"code":{"const":"no_supply_for_job"},"jobId":{"type":"string","minLength":1,"maxLength":200},"jobTitle":{"type":"string","minLength":1,"maxLength":300},"message":{"type":"string","minLength":1,"maxLength":1000}}}}}},"PublicCatalogPrice":{"oneOf":[{"type":"object","additionalProperties":false,"required":["state","amountMicros","currency","unit","effectiveAt"],"properties":{"state":{"const":"known"},"amountMicros":{"type":"integer","minimum":0,"maximum":9007199254740991},"currency":{"type":"string","pattern":"^[A-Z]{3}$"},"unit":{"type":"string","minLength":1,"maxLength":80},"effectiveAt":{"type":"string","format":"date-time"},"comparisonBasis":{"type":"object","additionalProperties":false,"required":["canonicalMeter","billingBasis","tier","minimumMicros","rounding"],"properties":{"canonicalMeter":{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"},"billingBasis":{"enum":["per_call","per_result","per_unit"]},"tier":{"type":"string","minLength":1,"maxLength":80},"minimumMicros":{"type":"integer","minimum":0,"maximum":9007199254740991},"rounding":{"enum":["exact","ceil","floor"]}}}}},{"type":"object","additionalProperties":false,"required":["state","reason"],"properties":{"state":{"enum":["unknown","uncomputable"]},"reason":{"type":"string","minLength":1,"maxLength":300}}}]},"RegisterConnection":{"type":"object","description":"One of api_key (default when kind is omitted), oauth, service_account, credential_file, signed_request, or cli_auth, discriminated by kind","additionalProperties":true,"required":["id","providerId","label"],"properties":{"kind":{"enum":["api_key","oauth","service_account","credential_file","signed_request","cli_auth"]},"id":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"providerId":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"label":{"type":"string"},"placement":{"enum":["header","query"]},"name":{"type":"string"},"format":{"type":"string","description":"e.g. \"Bearer {secret}\""},"secret":{"type":"string","writeOnly":true},"authorizationUrl":{"type":"string","format":"uri"},"tokenUrl":{"type":"string","format":"uri"},"scopes":{"type":"array","items":{"type":"string"}},"tokenPlacement":{"enum":["header","query"]},"clientId":{"type":"string","writeOnly":true},"keyFile":{"type":"string","writeOnly":true},"file":{"type":"string","writeOnly":true},"keyId":{"type":"string"},"algorithm":{"const":"hmac-sha256"},"signedHeaders":{"type":"array","items":{"type":"string"}},"blob":{"type":"string","writeOnly":true},"credentialExpiresAt":{"type":["string","null"],"format":"date-time"},"probe":{"anyOf":[{"$ref":"#/components/schemas/ConnectionProbeDefinition"},{"type":"null"}]},"secondaryCredentialRoles":{"type":"array","items":{"type":"string"},"description":"Secondary credential roles this provider requires beyond the primary auth"}}},"RunRequest":{"type":"object","additionalProperties":false,"required":["endpointId","input"],"properties":{"endpointId":{"type":"string","minLength":1,"description":"The catalog endpoint id to run"},"input":{"type":"object","description":"Arbitrary key/value input, validated against the endpoint's own input schema"},"idempotencyKey":{"type":"string","minLength":1,"description":"Required, unless supplied instead as the Idempotency-Key request header (either satisfies the requirement; supplying both requires them to match, or the request is a 400). A replay with the same key and the same body returns the original run (replayed:true); the same key with a different body is a 409. Never auto-generated by the gateway -- see the report's decision options for why."},"output":{"type":"object","description":"Optional output selection/mapping pin; same shape as an inspected endpoint's outputMappingPin"},"wait":{"oneOf":[{"type":"boolean"},{"type":"number","minimum":0,"maximum":60}],"description":"Optional alternative to the ?wait= query parameter (the query parameter wins if both are given)"},"fallback":{"description":"Optional. Let the run move to the next provider of the same job when one misses or fails, within ONE hold; only attempts that ran are charged. true takes every default. The run then carries `route` {servedBy, outcome, chargedUsd, capped, attempts[], skipped[], summary}.","oneOf":[{"type":"boolean"},{"type":"object","additionalProperties":false,"properties":{"enabled":{"type":"boolean","description":"false turns it off (the same as omitting fallback)"},"maxAttempts":{"type":"integer","minimum":1,"maximum":10,"description":"Dispatched attempts, default 3"},"maxCostUsd":{"type":"number","exclusiveMinimum":0,"maximum":100,"description":"The route's cap; default the sum of the first maxAttempts prices; can only lower it"},"prefer":{"enum":["cheapest","reliable","fastest","balanced"],"description":"Order of the job's other providers, default balanced"},"exclude":{"type":"array","maxItems":50,"items":{"type":"string","minLength":1,"maxLength":200},"description":"Endpoint or provider ids never tried"},"stopAtFirstMiss":{"type":"boolean","description":"Stop at the first empty answer, default false"}}}]}}},"SessionContext":{"type":"object","additionalProperties":false,"required":["mode","authenticationClass","organizationId","scopes","deploymentMode","role"],"properties":{"mode":{"const":"local_alpha"},"authenticationClass":{"enum":["local_static_token","machine_session"]},"organizationId":{"oneOf":[{"type":"string","pattern":"^[a-z][a-z0-9_-]{2,127}$"},{"type":"string","pattern":"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"}]},"scopes":{"type":"array","maxItems":100,"uniqueItems":true,"items":{"type":"string","minLength":1,"maxLength":128,"pattern":"^(?:\\*|[a-z][a-z0-9-]{0,62}(?::(?:\\*|[a-z][a-z0-9-]{0,62}))?)$"}},"deploymentMode":{"description":"Operator-facing deployment mode: fixture_demo/authenticated_sandbox in local_alpha, approved_production in production, degraded whenever live readiness fails regardless of mode.","enum":["fixture_demo","authenticated_sandbox","approved_production","degraded"]},"role":{"description":"The resolved principal's organization role; null for a resolver that never set one.","enum":["owner","admin","member","viewer",null]},"expiresAt":{"description":"Present only for ?include=expiresAt: the presenting token's own expiry (ISO 8601 UTC), or null when it has none.","type":["string","null"],"format":"date-time"},"organizationName":{"description":"Present only for ?include=organizationName: the organization's display name, or null when the gateway could not read one.","type":["string","null"],"minLength":1,"maxLength":120}}}}}}