{"openapi":"3.1.0","info":{"title":"WAVE Dispatch","version":"rolling","summary":"Local-first AI router — routes each prompt to the cheapest capable model.","description":"WAVE Dispatch returns a routing DECISION ({route, probability, margin}); your keys, data, and inference stay on your infra (see /transparency). Agents may pay-per-use with NO account via an HTTP-402 challenge settled over x402 / MPP / ACP / Tempo / Privy / Bridge / Coinbase CDP. Capabilities advertised here are derived live from the worker's wired rails.","contact":{"name":"WAVE","url":"https://wave.online"},"license":{"name":"open edge worker + SDKs","url":"https://dispatch.wave.online/transparency"}},"servers":[{"url":"https://dispatch.wave.online"}],"x-payment":{"pay_per_use_enabled":true,"protocols":["x402","mpp","acp","tempo","privy","bridge","cdp","card"],"networks":["base"],"x402":{"network":"base","asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","payTo":"0x7543c839B9a96d1305d3A85464a5C6b0Cfcac406","price_atomic":"1000"},"discovery":"https://dispatch.wave.online/.well-known/x402","mpp_version":"1.0-draft","mpp_discovery":"https://dispatch.wave.online/v1/mpp/discovery","capability_doc":"https://dispatch.wave.online/payments","note":"Agents pay via a protocol (x402/mpp/acp/tempo/privy/bridge/cdp); WAVE settles on a rail. card = Stripe checkout (humans)."},"paths":{"/":{"post":{"operationId":"route","summary":"Route a prompt (and optionally execute) to the cheapest capable model.","description":"With a Bearer license: returns the routing decision. With NO auth and pay-per-use enabled: returns a 402 challenge — pay via a listed protocol and retry the same body with the payment header.","security":[{"licenseAuth":[]},{}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteRequest"}}}},"responses":{"200":{"description":"Routing decision","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteDecision"}}}},"402":{"description":"Payment Required — x402/MPP/ACP/… challenge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentChallenge"}}}}}}},"/classify":{"post":{"operationId":"classify","summary":"Classify a prompt (route + confidence) without doing any work.","security":[{"licenseAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClassifyRequest"}}}},"responses":{"200":{"description":"Decision","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteDecision"}}}}}}},"/extract":{"post":{"operationId":"extract","summary":"Attested extraction — grounded (s,p,o) triples from text, each backed by verbatim evidence, with a signed context-attestation proving what the model saw.","description":"Execution-enabled plans only (flag-gated). Every returned triple's evidence is a literal substring of the input — hallucinated triples are dropped and counted (dropped_hallucinated). The response carries a context-attestation (chunk_sha == prompt_sha ⇒ whole input seen; ≠ ⇒ truncated) verifiable offline.","security":[{"licenseAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractRequest"}}}},"responses":{"200":{"description":"Grounded triples + context-attestation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractResponse"}}}},"402":{"description":"Execution-enabled plan required","content":{"application/json":{}}}}}},"/attest/verify":{"post":{"operationId":"attestVerify","summary":"Verify a WAVE receipt. PUBLIC + free — Ed25519 records are self-describing (no secret needed). Verifies BOTH a context-attestation (what a model saw) AND a Payment-Receipt (that a settlement was verified); the kind is auto-detected by `v`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ContextAttestation"},{"$ref":"#/components/schemas/PaymentReceipt"}]}}}},"responses":{"200":{"description":"Verdict + public metadata (VerifyResult.kind distinguishes the two)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyResult"}}}}}}},"/attest/keys":{"get":{"operationId":"attestKeys","summary":"The trusted-key registry — dispatch publishes its OWN armed signing pubkey(s) so any verifier can confirm a receipt/attestation was signed by WAVE (not just by some valid key). PUBLIC, free, cacheable; pubkeys only, never a secret. Also served at /.well-known/wave-keys.json.","responses":{"200":{"description":"Published key set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaveKeys"}}}}}}},"/frontier":{"post":{"operationId":"frontier","summary":"Execute on a frontier model via OpenRouter (300+ models, one integration) — with a signed context-attestation over the call.","description":"Execution-enabled plans only (flag-gated). Two lanes: BYOK (pass `openrouter_key` — you pay OpenRouter, WAVE bills a flat fee) or WAVE-supplied tokens (resale, usage-metered). The response carries a context-attestation proving what the frontier model saw — provenance OpenRouter does not offer.","security":[{"licenseAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FrontierRequest"}}}},"responses":{"200":{"description":"Frontier answer + usage + context-attestation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FrontierResponse"}}}},"402":{"description":"Execution-enabled plan required","content":{"application/json":{}}},"413":{"description":"Input exceeds the model's context budget — reduce the prompt or choose a larger-context model (never silently truncated). Body: {model, input_chars, budget_chars, window_tokens}.","content":{"application/json":{}}},"503":{"description":"No OpenRouter key, or resale pricing not configured (fail-closed)","content":{"application/json":{}}}}}},"/payments":{"get":{"operationId":"payments","summary":"Machine-readable payment capability (protocols + rails + x402 terms).","responses":{"200":{"description":"Capability doc","content":{"application/json":{}}}}}},"/v1/mpp/discovery":{"get":{"operationId":"mppDiscovery","summary":"MPP machine-discovery — service info, discovery surfaces, and live paid endpoints (mppVersion 1.0-draft).","responses":{"200":{"description":"MPP discovery document","content":{"application/json":{}}}}}},"/pricing.json":{"get":{"operationId":"pricing","summary":"Machine-readable pricing (plans + pay-per-use).","responses":{"200":{"description":"Pricing","content":{"application/json":{}}}}}},"/transparency":{"get":{"operationId":"transparency","summary":"Trust contract — what dispatch sees, logs, and never logs.","responses":{"200":{"description":"Trust contract (JSON with ?format=json)","content":{"application/json":{}}}}}},"/llms.txt":{"get":{"operationId":"llmsTxt","summary":"LLM-readable description of the service.","responses":{"200":{"description":"text/plain","content":{"text/plain":{}}}}}}},"components":{"securitySchemes":{"licenseAuth":{"type":"http","scheme":"bearer","bearerFormat":"license","description":"Authorization: Bearer <license key>"}},"schemas":{"RouteRequest":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","description":"The request to route."},"execute":{"type":"boolean","description":"If true, run the routed model and return its answer.","default":false},"vector":{"type":"array","items":{"type":"number"},"minItems":768,"maxItems":768,"description":"Optional precomputed 768-d embedding (cheapest path; skips the edge embed)."}}},"ClassifyRequest":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string"}}},"ExtractRequest":{"type":"object","required":["text"],"properties":{"text":{"type":"string","description":"Source text to extract grounded triples from (capped at 8000 chars; truncation is proven in the attestation)."}}},"FrontierRequest":{"type":"object","required":["prompt","model"],"properties":{"prompt":{"type":"string","description":"The request to send to the frontier model. Bounded by the MODEL's context budget (E10 — e.g. ~600k chars for a 200K-token model), not a flat cap; over-budget returns 413 (never silently truncated)."},"model":{"type":"string","description":"OpenRouter model id, e.g. \"anthropic/claude-3.5-sonnet\"."},"openrouter_key":{"type":"string","description":"Optional BYOK OpenRouter key — you pay OpenRouter directly. Sent only in the upstream Authorization header; never logged or echoed."}}},"FrontierResponse":{"type":"object","required":["answer","model","context_attestation"],"properties":{"answer":{"type":"string"},"model":{"type":"string"},"finish_reason":{"type":"string","nullable":true},"usage":{"type":"object","nullable":true,"properties":{"prompt_tokens":{"type":"integer"},"completion_tokens":{"type":"integer"},"total_tokens":{"type":"integer"}}},"byok":{"type":"boolean","description":"true if your own OpenRouter key was used (you paid OpenRouter)."},"ai_tokens_usd":{"type":"number","description":"WAVE-metered amount for this call."},"context_budget_chars":{"type":"integer","description":"E10 — the model-aware input budget this call was bounded by (the whole prompt was sent when within it)."},"context_attestation":{"$ref":"#/components/schemas/ContextAttestation"}}},"ExtractResponse":{"type":"object","required":["triples","count","dropped_hallucinated","context_attestation"],"properties":{"triples":{"type":"array","items":{"$ref":"#/components/schemas/Triple"}},"count":{"type":"integer"},"dropped_hallucinated":{"type":"integer","description":"Triples rejected because their evidence was not a verbatim substring of the source."},"model":{"type":"string"},"context_attestation":{"$ref":"#/components/schemas/ContextAttestation"},"truncated":{"type":"boolean","description":"True when input_chars exceeds 8000: the tail was NOT processed. Declared up front, never silently."},"input_chars":{"type":"integer"},"processed_chars":{"type":"integer","description":"Chars actually seen (== input_chars unless truncated)."}}},"Triple":{"type":"object","required":["s","p","o","evidence"],"properties":{"s":{"type":"string"},"p":{"type":"string"},"o":{"type":"string"},"evidence":{"type":"string","description":"Verbatim span from the source supporting the triple."}}},"ContextAttestation":{"type":"object","description":"Signed proof of what the model saw (verify per docs/attest-hotpath.md).","properties":{"v":{"type":"string"},"ts":{"type":"integer"},"model":{"type":"string"},"source":{"type":"string"},"chunk_sha":{"type":"string","description":"SHA-256 of the slice the model saw."},"prompt_sha":{"type":"string","description":"SHA-256 of the full input; != chunk_sha ⇒ truncated."},"chunk_chars":{"type":"integer"},"num_ctx":{"type":"integer"},"kept":{"type":"integer"},"dropped_hallucinated":{"type":"integer"},"alg":{"type":"string","enum":["none","hmac-sha256","ed25519"]},"sig":{"type":"string","nullable":true},"pubkey":{"type":"string"},"key_id":{"type":"string"}}},"PaymentReceipt":{"type":"object","description":"Signed proof dispatch verified a settlement for a resource — the MONEY half of the two receipts (the context-attestation is the provenance half). Appears on a paid response and verifies offline via /attest/verify.","properties":{"v":{"type":"string","description":"Always starts \"wave.payment-receipt/\" (how /attest/verify detects the kind)."},"ts":{"type":"integer"},"protocol":{"type":"string","description":"x402 | mpp | acp | tempo | privy | bridge | cdp."},"mode":{"type":"string","description":"The settlement path verifyPayment took (e.g. wave-x402, x402-facilitator)."},"resource":{"type":"string"},"network":{"type":"string"},"asset":{"type":"string","nullable":true},"amount_atomic":{"type":"string"},"pay_to":{"type":"string","nullable":true},"tx_hash":{"type":"string","nullable":true},"verified":{"type":"boolean","description":"HONEST: true only on a real facilitator/WAVE settlement path — never a stub."},"alg":{"type":"string","enum":["none","hmac-sha256","ed25519"]},"sig":{"type":"string","nullable":true},"pubkey":{"type":"string"},"key_id":{"type":"string"}}},"VerifyResult":{"type":"object","required":["valid","alg","checked_at"],"properties":{"valid":{"type":"boolean","nullable":true,"description":"true/false when signed & checkable; null when unsigned or HMAC without our key."},"trusted":{"type":"boolean","nullable":true,"description":"true/false when the signer is/ isn't one of THIS dispatch's published keys (see /attest/keys); null when undecidable (unarmed edge, unsigned, or HMAC). Orthogonal to `valid`: a record can be valid:true, trusted:false (a real signature from a key WAVE never published)."},"kind":{"type":"string","enum":["payment-receipt"],"description":"Present when the verified record is a Payment-Receipt; absent for a context-attestation."},"alg":{"type":"string","enum":["none","hmac-sha256","ed25519"]},"key_id":{"type":"string","nullable":true},"model":{"type":"string","nullable":true},"source":{"type":"string","nullable":true},"protocol":{"type":"string","nullable":true},"verified":{"type":"boolean","nullable":true},"network":{"type":"string","nullable":true},"resource":{"type":"string","nullable":true},"tx_hash":{"type":"string","nullable":true},"num_ctx":{"type":"integer","nullable":true},"chunk_chars":{"type":"integer","nullable":true},"kept":{"type":"integer","nullable":true},"dropped_hallucinated":{"type":"integer","nullable":true},"truncated":{"type":"boolean","nullable":true,"description":"chunk_sha != prompt_sha ⇒ model saw less than the full input."},"checked_at":{"type":"integer"}}},"WaveKeys":{"type":"object","required":["v","keys","updated"],"properties":{"v":{"type":"string","description":"Schema version, always \"wave.keys/v0\"."},"keys":{"type":"array","description":"The edge's currently-armed signing keys (empty when unarmed — we publish nothing we cannot sign with).","items":{"type":"object","required":["key_id","pubkey","alg"],"properties":{"key_id":{"type":"string","description":"sha256(pubkey)[:16] — the short id echoed on receipts/attestations and at /attest/verify."},"pubkey":{"type":"string","description":"Ed25519 public key (hex). Feed it to the SDK trusted registry (makeRegistry) to verify offline."},"alg":{"type":"string","enum":["ed25519"]}}}},"updated":{"type":"integer","description":"Unix seconds when this document was generated."}}},"RouteDecision":{"type":"object","required":["route","probability","margin"],"properties":{"route":{"type":"string","enum":["local_code","local_search","local_summarize","direct","reason"]},"probability":{"type":"number","description":"Confidence in the chosen route (0–1)."},"margin":{"type":"number","description":"Gap to the next route; <0.20 escalates to your frontier."},"exec_skipped":{"type":"boolean","description":"execute:true but the prompt exceeded the 8000-char edge limit: the model was NOT run (never truncated) and ai_tokens was NOT billed. Use /frontier for large context."},"exec_error":{"type":"string","description":"Why an executed run was skipped or failed (the routing decision still succeeds)."},"context":{"type":"object","nullable":true,"description":"Over-budget detail (counts only, no prompt content).","properties":{"limit_chars":{"type":"integer"},"input_chars":{"type":"integer"},"over_by":{"type":"integer"},"advice":{"type":"string"},"local_window_chars":{"type":"integer","description":"The run model's actual context budget in chars. The 8000 limit is a pricing bracket, not this."},"fits_local_window":{"type":"boolean","description":"True when the prompt fits the model's window — only the pricing bracket stopped it, not capability, so it could have run locally."}}},"payment_receipt":{"$ref":"#/components/schemas/PaymentReceipt"},"context_attestation":{"$ref":"#/components/schemas/ContextAttestation"}}},"PaymentChallenge":{"type":"object","properties":{"x402Version":{"type":"integer"},"error":{"type":"string"},"accepts":{"type":"array","items":{"type":"object"},"description":"One entry per accepted protocol; the agent picks."},"rails":{"type":"object","description":"WAVE-side settlement infrastructure summary."}}}}}}