{"openapi":"3.1.0","info":{"title":"Rotor Town decision API","version":"1.0.0","description":"Decision idempotency is scoped to API key and program. Identical retries return the original result for 24 hours without new inference or billing. Inputs and external IDs are not retained for retries. After expiry or an uncertain provider response, admission remains reserved; no automatic paid retry occurs."},"servers":[{"url":"https://rotor.town"}],"security":[{"scopedKey":[]}],"paths":{"/api/v1/models/{model}/decisions":{"post":{"operationId":"decidePinnedVersion","x-required-scope":"decision","summary":"Call an independently enabled, pinned version.","parameters":[{"name":"model","in":"path","required":true,"schema":{"type":"string","maxLength":96,"pattern":"^[a-z][a-z0-9]*(-[a-z0-9]+)+$"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","minLength":1,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["input"],"properties":{"input":{"type":"object"},"external_id":{"type":"string","minLength":1,"maxLength":256}},"example":{"input":{"message":"Please refund the duplicate charge."}}}}}},"responses":{"200":{"description":"Includes the exact resolved version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionResponse"}}}},"400":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plan_quota_exceeded, improvement_quota_exceeded, compile_quota_exceeded, provider_key_required, endpoint_quota_exceeded, plan_feature_required or retention_limit_exceeded. Compile spend is always capped; other plan limits need enforcement.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"idempotency_conflict for changed requests; decision_in_progress for an admitted concurrent request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"inference_refused: the hosted model declined this input. Retrying it will not help; review it by hand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"424":{"description":"provider_key_invalid: TypeSafe rejected the workspace's own key. Never retried on Rotor's key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Opaque service failure. decision_unconfirmed or decision_expired must not be retried with a new key without reviewing provider spend.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/decisions":{"post":{"operationId":"decideNamedModel","x-required-scope":"decision","summary":"Run a named version.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","minLength":1,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["model","input"],"properties":{"model":{"type":"string","maxLength":96,"pattern":"^[a-z][a-z0-9]*(-[a-z0-9]+)+$"},"input":{"type":"object"},"external_id":{"type":"string","minLength":1,"maxLength":256}},"example":{"model":"support-v1","input":{"message":"Please refund the duplicate charge."}}}}}},"responses":{"200":{"description":"Includes requested model and resolved_model version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionResponse"}}}},"400":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plan_quota_exceeded, improvement_quota_exceeded, compile_quota_exceeded, provider_key_required, endpoint_quota_exceeded, plan_feature_required or retention_limit_exceeded. Compile spend is always capped; other plan limits need enforcement.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"idempotency_conflict for changed requests; decision_in_progress for an admitted concurrent request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"inference_refused: the hosted model declined this input. Retrying it will not help; review it by hand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"424":{"description":"provider_key_invalid: TypeSafe rejected the workspace's own key. Never retried on Rotor's key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Opaque service failure. decision_unconfirmed or decision_expired must not be retried with a new key without reviewing provider spend.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/programs/{program_id}/decisions":{"post":{"operationId":"decide","x-required-scope":"decision","summary":"Run the active version of an immutable contract.","parameters":[{"name":"program_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","minLength":1,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionRequest"}}}},"responses":{"200":{"description":"Typed decision.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionResponse"}}}},"400":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plan_quota_exceeded, improvement_quota_exceeded, compile_quota_exceeded, provider_key_required, endpoint_quota_exceeded, plan_feature_required or retention_limit_exceeded. Compile spend is always capped; other plan limits need enforcement.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"idempotency_conflict for changed requests; decision_in_progress for an admitted concurrent request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"inference_refused: the hosted model declined this input. Retrying it will not help; review it by hand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"424":{"description":"provider_key_invalid: TypeSafe rejected the workspace's own key. Never retried on Rotor's key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Opaque service failure. decision_unconfirmed or decision_expired must not be retried with a new key without reviewing provider spend.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace_id}/plan":{"get":{"operationId":"readPlanUsage","security":[{"session":[]}],"summary":"Read the workspace plan, this month's usage, and key source.","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Counts are for the current UTC month.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanUsage"}}}},"400":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/programs/{program_id}/decisions/{decision_id}/feedback":{"post":{"operationId":"reviewDecision","x-required-scope":"outcome","summary":"Confirm or correct a recorded result.","parameters":[{"name":"program_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"decision_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionFeedback"}}}},"responses":{"200":{"description":"Requires capture. With manifest_version, signed early votes remain pending until capture completes. Disagreement alone supplies no correct label.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["id","state","request_id"],"properties":{"id":{"type":"string"},"state":{"enum":["pending","mature","disputed"]},"request_id":{"type":"string"}}}}}},"400":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/programs/{program_id}/actions/batch":{"post":{"operationId":"recordActions","x-required-scope":"outcome","parameters":[{"name":"program_id","in":"path","required":true,"schema":{"type":"string"}}],"summary":"Record customer actions separately from correct answers.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["manifest_version","items"],"properties":{"manifest_version":{"type":"string"},"items":{"type":"array","minItems":1,"maxItems":50,"items":{"$ref":"#/components/schemas/Action"}}}}}}},"responses":{"200":{"description":"Check each item before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}},"400":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/programs/{program_id}/outcomes/batch":{"post":{"operationId":"recordOutcomes","x-required-scope":"outcome","parameters":[{"name":"program_id","in":"path","required":true,"schema":{"type":"string"}}],"summary":"Record independently retryable outcomes.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["manifest_version","items"],"additionalProperties":false,"properties":{"manifest_version":{"type":"string"},"items":{"type":"array","minItems":1,"maxItems":50,"items":{"$ref":"#/components/schemas/Outcome"}}}}}}},"responses":{"200":{"description":"Check each item before retrying.","content":{"application/json":{"schema":{"type":"object","required":["results","request_id"],"properties":{"request_id":{"type":"string"},"results":{"type":"array","items":{"oneOf":[{"type":"object","additionalProperties":false,"required":["index","ok","id"],"properties":{"index":{"type":"integer","minimum":0},"ok":{"const":true},"id":{"type":"string"}}},{"type":"object","additionalProperties":false,"required":["index","ok","error"],"properties":{"index":{"type":"integer","minimum":0},"ok":{"const":false},"error":{"type":"string"}}}]}}}}}}},"400":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Stable error; no provider details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"scopedKey":{"type":"http","scheme":"bearer","bearerFormat":"rotor_v1_*"},"session":{"type":"apiKey","in":"cookie","name":"better-auth.session_token"}},"schemas":{"PlanUsage":{"type":"object","additionalProperties":false,"required":["plan","limits","period","usage","key_source","provider_key","enforcement","request_id"],"properties":{"plan":{"type":"object","additionalProperties":false,"required":["id","name","price","features"],"properties":{"id":{"enum":["build","team","scale","enterprise"]},"name":{"type":"string"},"price":{"type":"string"},"features":{"type":"array","items":{"type":"string"}}}},"limits":{"type":"object","additionalProperties":false,"required":["endpoints","decisions","improvement_runs","rotor_decisions","compile_nano_usd","retention_days"],"properties":{"endpoints":{"type":["integer","null"],"minimum":0,"description":"null means no fixed limit."},"decisions":{"type":["integer","null"],"minimum":0,"description":"null means no fixed limit."},"improvement_runs":{"type":["integer","null"],"minimum":0,"description":"null means no fixed limit."},"rotor_decisions":{"type":["integer","null"],"minimum":0,"description":"Decisions per month on Rotor's TypeSafe key."},"compile_nano_usd":{"type":["integer","null"],"minimum":0,"description":"Improvement compile spend per month, in nano-USD. Always enforced."},"retention_days":{"type":["integer","null"],"minimum":0,"description":"null means no fixed limit."}}},"period":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"usage":{"type":"object","additionalProperties":false,"required":["decisions","rotor_decisions","customer_decisions","improvement_runs","compile_nano_usd","input_tokens","endpoints"],"properties":{"decisions":{"type":"integer","minimum":0},"rotor_decisions":{"type":"integer","minimum":0},"customer_decisions":{"type":"integer","minimum":0},"improvement_runs":{"type":"integer","minimum":0},"compile_nano_usd":{"type":"integer","minimum":0},"input_tokens":{"type":"integer","minimum":0},"endpoints":{"type":"integer","minimum":0}}},"key_source":{"enum":["rotor","customer"],"description":"Which TypeSafe key new Jev calls use."},"provider_key":{"oneOf":[{"type":"null"},{"type":"object","additionalProperties":false,"required":["provider","last4","checked_at"],"properties":{"provider":{"const":"typesafe"},"last4":{"type":"string"},"checked_at":{"type":"integer"}}}]},"enforcement":{"enum":["warn","enforce"]},"request_id":{"type":"string"}}},"ChoiceAnswer":{"type":"object","additionalProperties":false,"required":["type","choice","probabilities","confidence"],"properties":{"type":{"const":"choice"},"choice":{"type":"string"},"probabilities":{"type":"object","additionalProperties":{"type":"number","minimum":0,"maximum":1}},"confidence":{"type":"number","minimum":0,"maximum":1}}},"ScoreAnswer":{"type":"object","additionalProperties":false,"required":["type","score","legend","probabilities","confidence"],"properties":{"type":{"const":"score"},"score":{"type":"number","minimum":0,"maximum":19},"legend":{"type":"object","additionalProperties":{"type":"string"}},"probabilities":{"type":"object","additionalProperties":{"type":"number","minimum":0,"maximum":1}},"confidence":{"type":"number","minimum":0,"maximum":1}}},"NoulAnswer":{"type":"object","additionalProperties":false,"required":["type","noul"],"properties":{"type":{"const":"noul"},"noul":{"type":"number","minimum":0,"maximum":1}}},"DecisionFeedback":{"type":"object","additionalProperties":false,"required":["target","vote","idempotency_key","occurred_at"],"properties":{"manifest_version":{"type":"string","description":"Required before capture completes. Must match the signed decision identity."},"target":{"type":"string"},"vote":{"enum":["up","down"]},"correction":{"type":["string","number","boolean"]},"explanation":{"type":"string","maxLength":2000},"idempotency_key":{"type":"string","minLength":1,"maxLength":128},"occurred_at":{"type":"integer","minimum":0},"supersedes":{"type":"string","minLength":1,"description":"Your earlier feedback on this decision and target."}}},"Answer":{"oneOf":[{"$ref":"#/components/schemas/ChoiceAnswer"},{"$ref":"#/components/schemas/ScoreAnswer"},{"$ref":"#/components/schemas/NoulAnswer"}],"discriminator":{"propertyName":"type"}},"DecisionRequest":{"type":"object","additionalProperties":false,"required":["input","manifest_version"],"properties":{"input":{"type":"object","description":"Validated against the exact stored API contract."},"manifest_version":{"type":"string"},"external_id":{"type":"string","minLength":1,"maxLength":256}},"example":{"input":{"message":"Please refund the duplicate charge."},"manifest_version":"manifest_example","external_id":"ticket-42"}},"DecisionResponse":{"type":"object","additionalProperties":false,"required":["decision_id","request_id","program_id","manifest_version","pipeline_version","readiness","external_id","answers","routing","usage"],"properties":{"model":{"type":"string"},"resolved_model":{"type":"string"},"decision_id":{"type":"string"},"request_id":{"type":"string"},"program_id":{"type":"string"},"manifest_version":{"type":"string"},"pipeline_version":{"type":"string"},"readiness":{"enum":["pilot","live"]},"external_id":{"type":["string","null"]},"answers":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Answer"}},"routing":{"type":"object","additionalProperties":{"enum":["yes","no","review","automatic"]}},"usage":{"type":"object","additionalProperties":false,"required":["input_tokens","output_tokens","billed_tokens","price_plan","cost_nano_usd","key_source"],"properties":{"input_tokens":{"type":"integer","minimum":0,"maximum":51200},"output_tokens":{"const":0},"billed_tokens":{"type":"integer","minimum":256},"price_plan":{"type":"string","description":"v1-openai-2026-10-08 for OpenAI Luna versions; v1-2026-09-20 otherwise."},"cost_nano_usd":{"type":"integer","minimum":0,"description":"Token estimate for display. Plans, not tokens, set charges."},"key_source":{"enum":["rotor","customer"],"description":"Whether Rotor's or the workspace's own provider key paid."}}}}},"Action":{"type":"object","additionalProperties":false,"required":["target_id","action","value","occurred_at","idempotency_key"],"oneOf":[{"required":["decision_id"]},{"required":["external_id"]}],"properties":{"decision_id":{"type":"string","maxLength":128},"external_id":{"type":"string","minLength":1,"maxLength":256},"target_id":{"type":"string"},"action":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9_.:-]{0,79}$"},"value":{"oneOf":[{"type":"string","maxLength":512},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"occurred_at":{"type":"integer","minimum":0,"description":"Unix milliseconds."},"idempotency_key":{"type":"string","minLength":1,"maxLength":128},"supersedes":{"type":"string","description":"Prior action for this decision and target."}}},"BatchResponse":{"type":"object","additionalProperties":false,"required":["request_id","results"],"properties":{"request_id":{"type":"string"},"results":{"type":"array","items":{"oneOf":[{"type":"object","additionalProperties":false,"required":["index","ok","id"],"properties":{"index":{"type":"integer","minimum":0},"ok":{"const":true},"id":{"type":"string"}}},{"type":"object","additionalProperties":false,"required":["index","ok","error"],"properties":{"index":{"type":"integer","minimum":0},"ok":{"const":false},"error":{"type":"string"}}}]}}}},"Outcome":{"type":"object","additionalProperties":false,"required":["idempotency_key","state","source","occurred_at","labels"],"oneOf":[{"required":["decision_id"]},{"required":["external_id"]}],"properties":{"decision_id":{"type":"string","maxLength":128},"external_id":{"type":"string","minLength":1,"maxLength":256},"idempotency_key":{"type":"string","minLength":1,"maxLength":128},"state":{"enum":["missing","immature","censored","provisional","mature","disputed","rejected","superseded","deleted"]},"supersedes":{"type":"string","description":"Prior outcome ID explicitly replaced by a mature correction."},"source":{"enum":["outcome","correction"]},"occurred_at":{"type":"integer","minimum":0,"description":"Unix milliseconds."},"labels":{"type":"object","additionalProperties":{"type":["string","number","boolean"]},"description":"Target IDs map to Choice strings, Score numbers, or Noul booleans. Missing outcomes use an empty object."}}},"Error":{"type":"object","additionalProperties":false,"required":["error","request_id"],"properties":{"request_id":{"type":"string"},"error":{"type":"object","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}