{"openapi":"3.1.0","info":{"title":"Waymark HTTP API","version":"0.9.2","summary":"Read API for the Waymark agent procedural-knowledge network.","description":"Public HTTP/JSON API for Waymark — the shared, self-verifying route map for AI agents. The primary agent interface is the MCP server (streamable-HTTP at /mcp; tools waymark_query, waymark_register, waymark_contribute, waymark_attest). These REST endpoints expose the same underlying data for tools, crawlers, and codegen. Trust model: a route's `verification.status` (provenance/fact-check axis) is distinct from its attestation consensus (success/failure counts); no route is blanket \"verified\".","contact":{"name":"Waymark security","url":"https://waymark.network/trust","email":"security@waymark.network"},"license":{"name":"Apache-2.0","identifier":"Apache-2.0"}},"servers":[{"url":"https://mcp.waymark.network"}],"externalDocs":{"description":"Integration guide + MCP setup","url":"https://waymark.network/docs"},"paths":{"/search":{"get":{"operationId":"search","summary":"Semantic route search","description":"Returns up to 5 task routes matching a natural-language query, ranked by embedding similarity with a confidence floor (returns an empty list rather than a low-confidence wrong route).","parameters":[{"name":"q","in":"query","required":true,"description":"Natural-language task description.","schema":{"type":"string","maxLength":256}},{"name":"probe","in":"query","required":false,"description":"Set to `1` only by Waymark's own smoke/eval/build tooling so demand telemetry can separate internal probes from organic queries. Omit for normal use.","schema":{"type":"string","enum":["1"]}},{"name":"verified_only","in":"query","required":false,"description":"Pro (prepaid credits): return only individually fact-checked (verification:'verified') routes. Requires a Pro contributor key with credits>0, passed as `Authorization: Bearer wmk_…`, `x-api-key`, or `api_key` query param. 1 credit per served query; zero-result queries are free. Without entitlement the endpoint answers 402 with upgrade instructions. Normal queries (omit this) stay free and keyless.","schema":{"type":"string","enum":["1","true"]}}],"responses":{"200":{"description":"Ranked routes (possibly empty on a low-confidence/no-match query). Zero-result responses additionally carry `note` and `request_route` — the paid path to commission a fact-checked route for the unmatched task. When routes ARE returned but none are independently verified and the best decayed trust is still near the neutral prior, the response additionally carries `verify_cta` — the $25 bounty to get a verified route for the caller's exact stack.","headers":{"X-Search-Cache":{"description":"`hit` if the retrieval result was served from edge cache, else `miss`.","schema":{"type":"string","enum":["hit","miss"]}}},"content":{"application/json":{"schema":{"type":"object","required":["routes"],"properties":{"routes":{"type":"array","items":{"$ref":"#/components/schemas/RouteResult"}},"note":{"type":"string","description":"Present only when `routes` is empty: why nothing was served (Waymark refuses low-confidence matches rather than guess)."},"request_route":{"type":"object","description":"Present only when `routes` is empty: how to commission a fact-checked route for this exact task ($25 bounty).","properties":{"what":{"type":"string"},"price_usd":{"type":"number","const":25},"url":{"type":"string","format":"uri"},"checkout":{"type":"string","format":"uri"}}},"verify_cta":{"type":"object","description":"Present only when routes were returned but none are independently verified and top decayed trust < 0.7: the $25 bounty to commission a live-tested verified route for this exact task.","properties":{"what":{"type":"string"},"price_usd":{"type":"number","const":25},"url":{"type":"string","format":"uri"},"checkout":{"type":"string","format":"uri"}}}}}}}},"402":{"description":"verified_only was requested without a credited Pro key. Body explains how to upgrade (https://waymark.network/pricing); free keyless search remains available without verified_only.","content":{"application/json":{"schema":{"type":"object","properties":{"routes":{"type":"array","maxItems":0},"error":{"type":"string","const":"pro_required"},"detail":{"type":"string"},"how":{"type":"string"},"pricing":{"type":"string","format":"uri"},"checkout":{"type":"string","format":"uri"}}}}}}}},"post":{"operationId":"searchPost","summary":"Semantic route search (JSON body)","description":"Same retrieval as `GET /search?q=` for callers that prefer to POST a body. Send `{\"query\":\"…\"}` (aliases: `q`, `task`). Zero-result responses carry the same `note` + `request_route` ($25 bounty) fields. `verified_only`/`probe` still go in the query string.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["query"],"properties":{"query":{"type":"string","maxLength":256,"description":"Natural-language task description. Aliases: `q`, `task`."}}}}}},"responses":{"200":{"description":"Same shape as GET /search — ranked routes, or an empty list plus the $25 bounty CTA on a no-match query.","content":{"application/json":{"schema":{"type":"object","required":["routes"],"properties":{"routes":{"type":"array","items":{"$ref":"#/components/schemas/RouteResult"}},"note":{"type":"string"},"request_route":{"type":"object","properties":{"what":{"type":"string"},"price_usd":{"type":"number","const":25},"url":{"type":"string","format":"uri"},"checkout":{"type":"string","format":"uri"}}}}}}}}}}},"/r/{id}.json":{"get":{"operationId":"getRoute","summary":"Full route record","parameters":[{"name":"id","in":"path","required":true,"description":"Route UUID.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The route record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteRecord"}}}},"404":{"description":"No route with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}}}}},"/stats":{"get":{"operationId":"getStats","summary":"Corpus + traffic counters","responses":{"200":{"description":"Aggregate counters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stats"}}}}}}},"/freshness":{"get":{"operationId":"getFreshness","summary":"Route freshness + re-verify priority","description":"Time-decayed trust per route (60-day half-life), bucket counts, and the stalest-first queue canary re-verification runs work from.","responses":{"200":{"description":"Freshness report.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Freshness"}}}}}}},"/drift.json":{"get":{"operationId":"getDrift","summary":"API drift feed","description":"Real, observed API changes that silently break agents running on stale knowledge. RSS 2.0 variant at /drift.xml.","responses":{"200":{"description":"Drift events, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DriftFeed"}}}}}}},"/contributors.json":{"get":{"operationId":"getContributors","summary":"Contributor leaderboard","responses":{"200":{"description":"Contributors by route count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contributors"}}}}}}},"/v1/keys":{"post":{"operationId":"issueContributorKey","summary":"Mint a free contributor key (no auth)","description":"One call returns a contributor API key used as `api_key` in waymark_contribute. Handles are unique — the first registration claims a handle. Per-IP and network-wide hourly rate limits apply.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["handle"],"properties":{"handle":{"type":"string","minLength":2,"maxLength":60,"description":"Contributor handle: letters, digits, space, _ . - / @."}}}}}},"responses":{"200":{"description":"Key issued (shown once).","content":{"application/json":{"schema":{"type":"object","required":["api_key","handle"],"properties":{"api_key":{"type":"string","description":"Contributor key (prefix wmk_). Store it now — not retrievable later."},"handle":{"type":"string"},"note":{"type":"string"}}}}}},"400":{"description":"Invalid or reserved handle.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Handle already registered (handles are unique, first come).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (per-IP or network-wide).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/usage":{"get":{"operationId":"getUsage","summary":"Key-holder usage + prepaid credit balance","description":"Self-service usage for a contributor key: tier (free|pro), prepaid credit balance, and verified-only queries served. Pass the key as `Authorization: Bearer wmk_…` or `x-api-key`.","responses":{"200":{"description":"Usage for the presented key.","content":{"application/json":{"schema":{"type":"object","required":["handle","tier","credits","queries_used"],"properties":{"handle":{"type":"string"},"tier":{"type":"string","enum":["free","pro"]},"credits":{"type":"integer"},"queries_used":{"type":"integer"},"contributions":{"type":"integer"},"hourly_contribute_cap":{"type":"integer"}}}}}},"401":{"description":"Missing, invalid, or revoked key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/drift-alerts":{"post":{"operationId":"createDriftAlert","summary":"Sign up for drift alerts on APIs you depend on (intake only)","description":"Records an email + optional domain watchlist for the API Drift Tracker (/drift). Intake only: alert delivery has not launched yet — signups are stored double-opt-in-ready and honored from day one when sending goes live. Repeat signups with the same email merge the domain watchlist. Per-IP daily rate limit applies.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","maxLength":120},"domains":{"description":"API domains to watch — array of strings or one comma-separated string; max 20. Empty = all drifted APIs.","oneOf":[{"type":"array","items":{"type":"string","maxLength":80}},{"type":"string"}]}}}}}},"responses":{"200":{"description":"Signup recorded.","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"watching":{"description":"Watched domains, or the string \"all drifted APIs\".","oneOf":[{"type":"array","items":{"type":"string"}},{"type":"string"}]},"note":{"type":"string"}}}}}},"400":{"description":"Invalid email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (per-IP daily cap).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"components":{"schemas":{"Verification":{"type":"object","description":"Provenance/fact-check axis — distinct from attestation consensus.","properties":{"status":{"type":"string","example":"sampled"},"method":{"type":"string"}}},"RouteResult":{"type":"object","description":"A route as returned by /search (trimmed for ranking responses).","required":["id","task","domain","steps","url"],"properties":{"id":{"type":"string","format":"uuid"},"task":{"type":"string"},"domain":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}},"gotchas":{"type":"array","items":{"type":"string"}},"success":{"type":"integer"},"failure":{"type":"integer"},"verification":{"$ref":"#/components/schemas/Verification"},"url":{"type":"string","format":"uri"}}},"RouteRecord":{"type":"object","description":"Full route record from /r/{id}.json.","required":["id","task","domain","steps","created","attestations","url"],"properties":{"id":{"type":"string","format":"uuid"},"task":{"type":"string"},"domain":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}},"gotchas":{"type":"array","items":{"type":"string"}},"contributor":{"type":"string"},"created":{"type":"string","format":"date-time"},"attestations":{"type":"object","properties":{"success":{"type":"integer"},"failure":{"type":"integer"},"keyed_success":{"type":"integer","description":"Attestations attributed to a validated contributor key. A subset of success, not a separate tally."},"keyed_failure":{"type":"integer","description":"Keyed subset of failure."},"last_attested":{"type":["string","null"],"format":"date-time"}}},"success_rate":{"type":["number","null"],"description":"success / (success+failure), or null if never attested."},"effective_trust":{"type":"number","description":"Time-decayed trust: 0.5 + (laplace(success,failure) - 0.5) * 0.5^(days_since_last_attestation/60). Reverts toward the 0.5 neutral prior as evidence ages; this is the number retrieval ranking weighs."},"evidence_age_days":{"type":["integer","null"],"description":"Days since the last attestation, or null if never attested."},"trust_half_life_days":{"type":"integer","description":"Half-life of attestation weight in days (60 — matches /drift)."},"verification":{"$ref":"#/components/schemas/Verification"},"url":{"type":"string","format":"uri"}}},"Stats":{"type":"object","properties":{"routes":{"type":"integer"},"attestations":{"type":"integer"},"queries":{"type":"integer"},"events_30d":{"type":"integer"}}},"Freshness":{"type":"object","properties":{"total":{"type":"integer"},"half_life_days":{"type":"integer"},"buckets":{"type":"object","properties":{"fresh":{"type":"integer"},"aging":{"type":"integer"},"stale":{"type":"integer"},"never_attested":{"type":"integer"}}},"reverify_priority":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"task":{"type":"string"},"domain":{"type":"string"},"attestations":{"type":"integer"},"last_days":{"type":"integer"},"trust":{"type":"number"},"freshness":{"type":"number"},"decayed_trust":{"type":"number"}}}}}},"DriftFeed":{"type":"object","required":["count","drift"],"properties":{"count":{"type":"integer"},"drift":{"type":"array","items":{"type":"object","properties":{"t":{"type":"string","format":"date-time"},"domain":{"type":"string"},"api":{"type":"string"},"what":{"type":"string"},"impact":{"type":"string"},"fix":{"type":"string"},"route_id":{"type":"string","format":"uuid"}}}}}},"Contributors":{"type":"object","required":["count","contributors"],"properties":{"count":{"type":"integer"},"contributors":{"type":"array","items":{"type":"object","properties":{"handle":{"type":"string"},"routes":{"type":"integer"},"domains":{"type":"integer"},"top_domains":{"type":"array","items":{"type":"string"}},"attestations":{"type":"integer"},"success":{"type":"integer"},"failure":{"type":"integer"},"success_rate":{"type":["number","null"]},"verified_routes":{"type":"integer"},"last_active":{"type":["string","null"],"format":"date-time"}}}}}},"NotFound":{"type":"object","required":["error"],"properties":{"error":{"type":"string","example":"not_found"},"id":{"type":"string"}}},"ApiError":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}