{"openapi":"3.1.0","info":{"title":"Tokenwise Public API","version":"1.0.0","description":"Read-only access to your workspace's LLM observability data. Authenticate with a tw_api_* key from Settings → API Keys. Rate-limited per plan: 1,000/hour on Indie, 10,000/hour on Pro.","contact":{"name":"Tokenwise","email":"hi@tokenwisehq.com","url":"https://tokenwisehq.com"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://tokenwisehq.com","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"tw_api_*","description":"Pass your tw_api_* key as `Authorization: Bearer tw_api_...`. Mint keys in Settings → API Keys → Public REST."}},"schemas":{"Request":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspaceId":{"type":"string","format":"uuid"},"timestamp":{"type":"string","format":"date-time"},"provider":{"type":"string","example":"openai"},"model":{"type":"string","example":"gpt-4o"},"inputTokens":{"type":"integer","nullable":true},"outputTokens":{"type":"integer","nullable":true},"totalTokens":{"type":"integer","nullable":true},"cost":{"type":"number","nullable":true},"latencyMs":{"type":"integer","nullable":true},"status":{"type":"string","enum":["success","error"]},"statusCode":{"type":"integer","nullable":true},"errorMessage":{"type":"string","nullable":true},"cacheHit":{"type":"boolean"},"metadata":{"type":"object","nullable":true},"inputPayload":{"type":"object","nullable":true,"description":"Original request body (decrypted server-side). Omitted when workspace.payloadStorageEnabled = false."},"outputPayload":{"type":"object","nullable":true,"description":"Upstream response body. Same opt-out as inputPayload."}},"required":["id","workspaceId","timestamp","provider","model","status","cacheHit"]},"Metrics":{"type":"object","properties":{"totalRequests":{"type":"integer"},"totalCost":{"type":"number"},"avgLatencyMs":{"type":"integer"},"errorRate":{"type":"number","description":"0–1"},"byModel":{"type":"array","items":{"type":"object","properties":{"model":{"type":"string"},"requests":{"type":"integer"},"cost":{"type":"number"}}}}}},"Eval":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"promptGroupId":{"type":"string","format":"uuid"},"requestId":{"type":"string","format":"uuid","nullable":true},"overallScore":{"type":"number","description":"0–100"},"scores":{"type":"object","additionalProperties":{"type":"number"},"description":"Per-criterion score map (0–100)."},"reasoning":{"type":"string","nullable":true},"judgeModel":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"Workspace":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"ProxyRuleType":{"type":"string","enum":["model_override","max_tokens_cap","context_trim","cache_enable","fallback_chain","compress"],"description":"Discriminator for the `config` blob. Adding a value here is a non-breaking spec change."},"CompressRuleConfig":{"type":"object","description":"Configuration for a `compress` proxy rule. Runs the in-house, cache-safe, sync compressor on system prompts and tool output BEFORE the cache lookup. Watchdog auto-pauses the rule on a >10% quality drop.","properties":{"strength":{"type":"string","enum":["light","medium","aggressive"],"description":"How hard to compress. `light` only touches tool output. `medium` adds prose-aware passes. `aggressive` adds tool-definition + polite-filler stripping and requires the watchdog to be enabled."},"profile":{"type":"integer","const":1,"description":"Compressor profile version pinned for cache safety. Always 1 today; bumped when the upstream pipeline changes."},"tags":{"type":"array","items":{"type":"string"},"description":"Optional tag scope. Empty = applies workspace-wide."}},"required":["strength","profile"]},"Error":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message"}},"required":["error"]}},"responses":{"Unauthorized":{"description":"Missing, malformed, or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized"}}}},"RateLimited":{"description":"Plan rate limit hit. Inspect X-RateLimit-* headers to see your quota.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer","description":"Epoch seconds"}},"Retry-After":{"schema":{"type":"integer","description":"Seconds"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Rate limit exceeded"}}}}},"parameters":{"RangeQuery":{"name":"range","in":"query","schema":{"type":"string","enum":["24h","7d","30d","90d","180d"]},"description":"Time window. Default: 7d."},"ModelQuery":{"name":"model","in":"query","schema":{"type":"string"},"description":"Filter by model name (exact match)."},"StatusQuery":{"name":"status","in":"query","schema":{"type":"string","enum":["success","error"]}},"FromQuery":{"name":"from","in":"query","schema":{"type":"string","format":"date-time"},"description":"ISO 8601 timestamp. Overrides `range` lower bound."},"ToQuery":{"name":"to","in":"query","schema":{"type":"string","format":"date-time"}},"PageQuery":{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},"LimitQuery":{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}}},"paths":{"/api/v1/public/requests":{"get":{"summary":"List logged LLM requests","description":"Paginated list of every LLM call routed through the proxy in your active workspace. Payloads (input/output) are decrypted on read and only included when `payloadStorageEnabled` is on for the workspace.","tags":["Requests"],"parameters":[{"$ref":"#/components/parameters/ModelQuery"},{"$ref":"#/components/parameters/StatusQuery"},{"$ref":"#/components/parameters/FromQuery"},{"$ref":"#/components/parameters/ToQuery"},{"$ref":"#/components/parameters/PageQuery"},{"$ref":"#/components/parameters/LimitQuery"}],"responses":{"200":{"description":"Paginated request log","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Request"}},"total":{"type":"integer"},"page":{"type":"integer"},"limit":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/public/metrics":{"get":{"summary":"Aggregate cost / latency / error metrics","description":"Roll-up of workspace traffic for the requested window. Useful for piping into Grafana / Datadog / your own dashboards.","tags":["Metrics"],"parameters":[{"$ref":"#/components/parameters/RangeQuery"}],"responses":{"200":{"description":"Metrics summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Metrics"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/public/evals":{"get":{"summary":"List recent eval-run scores","description":"LLM-as-judge quality scores from the eval engine. Each row corresponds to one (promptGroup, criteria) scoring run.","tags":["Evals"],"parameters":[{"$ref":"#/components/parameters/RangeQuery"}],"responses":{"200":{"description":"Eval runs","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Eval"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"tags":[{"name":"Requests","description":"The raw log of every LLM call. One row per request, includes upstream cost + latency + cache hit + retry/fallback info."},{"name":"Metrics","description":"Aggregate roll-ups suitable for dashboards."},{"name":"Evals","description":"LLM-as-judge quality scores from the eval engine."}]}