{"openapi":"3.1.0","info":{"title":"Noa API","version":"0.4.0"},"servers":[{"url":"/"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"An API key (bt_...), the ADMIN_KEY, or a session token (bt_s_...) from POST /v1/auth/google. The dashboard may instead send the __Host-bt_session cookie; cookie POST/PATCH/DELETE requests need X-Requested-With: noa."}},"schemas":{"AccountSettings":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"reportEmailEnabled":{"type":"boolean"}},"required":["reportEmailEnabled"]},"MyUsageReport":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"month":{"type":"string"},"months":{"type":"array","items":{"type":"string"}},"period":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]},"periods":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]}},"totals":{"type":"object","properties":{"tasksRun":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tasksDone":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tasksFailed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tasksPaused":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"aiRequests":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"inputTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"outputTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"audioSeconds":{"type":"number"},"tokens":{"type":"number"}},"required":["tasksRun","tasksDone","tasksFailed","tasksPaused","aiRequests","inputTokens","outputTokens","tokens"]},"byModel":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["ai_messages","jev","transcribe","realtime","image","speak","phone"]},"model":{"type":"string"},"requests":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"inputTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"outputTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"audioSeconds":{"type":"number"},"tokens":{"type":"number"}},"required":["model","requests","inputTokens","outputTokens","tokens"]}},"byDay":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"tasksRun":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tokens":{"type":"number"},"charges":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["ai_messages","jev","transcribe","realtime","image","speak","phone"]},"model":{"type":"string"},"tokens":{"type":"number"}},"required":["kind","model","tokens"]}}},"required":["date","tasksRun","tokens"]}}},"required":["month","months","totals","byModel","byDay"]},"ShopBuyRequest":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"item":{"type":"string"}},"required":["item"]},"ShopEquipRequest":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"item":{"type":"string"}},"required":["item"]},"ShopState":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slot":{"type":"string","enum":["smoke","lighter"]},"name":{"type":"string"},"blurb":{"type":"string"},"price":{"type":"integer","minimum":0,"maximum":9007199254740991},"look":{"type":"object","properties":{"shape":{"type":"string","enum":["cigarette","slim","cigar","lighter","flip"]},"color":{"type":"string"},"trim":{"type":"string"},"smoke":{"type":"string"}},"required":["shape","color","trim"]}},"required":["id","slot","name","blurb","price","look"]}},"owned":{"type":"array","items":{"type":"string"}},"equipped":{"type":"object","properties":{"smoke":{"type":"string"},"lighter":{"type":"string"}},"required":["smoke","lighter"]},"tokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","owned","equipped","tokens"]},"BillingPlansResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["free","starter","plus","pro"]},"name":{"type":"string"},"priceCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"monthlyTokens":{"type":"integer","minimum":0,"maximum":9007199254740991},"usageMultiple":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"apiKeys":{"type":"boolean"},"voice":{"type":"boolean"},"todo":{"type":"boolean"},"sync":{"type":"boolean"},"calls":{"type":"boolean"}},"required":["id","name","priceCents","monthlyTokens","usageMultiple","apiKeys","voice","todo"]}},"topups":{"type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}}},"required":["plans","topups"]},"CheckoutInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"plan":{"type":"string","enum":["starter","plus","pro"]},"returnUrl":{"type":"string","minLength":1,"maxLength":2000}},"required":["plan","returnUrl"]},"CreditInfo":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"subscriptionCents":{"type":"number"},"topupCents":{"type":"number"},"totalCents":{"type":"number"},"periodGrantCents":{"type":"number"},"periodEnd":{"type":["string","null"]}},"required":["subscriptionCents","topupCents","totalCents","periodGrantCents","periodEnd"]},"DeleteAccountInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"confirm":{"type":"string","minLength":1}},"required":["confirm"]},"JevProxyInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"state":{},"questions":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["state","questions"]},"MeBillingResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string","enum":["free","starter","plus","pro"]},"status":{"type":"string","enum":["active","past_due","canceled","none"]},"currentPeriodEnd":{"type":["string","null"]},"cancelAtPeriodEnd":{"type":"boolean"},"source":{"type":"string","enum":["stripe","complimentary"]},"compExpiresAt":{"type":["string","null"]},"hasSubscription":{"type":"boolean"},"subscriptionPlan":{"type":"string","enum":["starter","plus","pro"]}},"required":["id","status","currentPeriodEnd","cancelAtPeriodEnd"]},"credit":{"type":"object","properties":{"subscriptionCents":{"type":"number"},"topupCents":{"type":"number"},"totalCents":{"type":"number"},"periodGrantCents":{"type":"number"},"periodEnd":{"type":["string","null"]}},"required":["subscriptionCents","topupCents","totalCents","periodGrantCents","periodEnd"]},"usage":{"type":"object","properties":{"tokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"monthTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"monthGrant":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"boughtTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"resetsAt":{"type":["string","null"]},"weekly":{"type":"boolean"},"weekPercentUsed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"weekResetsAt":{"type":["string","null"]},"extraWeeks":{"type":"number"},"extraWeeksOf":{"type":"string","enum":["starter","plus","pro"]}},"required":["tokens","monthTokens","monthGrant","boughtTokens","resetsAt"]},"stripeConfigured":{"type":"boolean"},"groq":{"type":"boolean"}},"required":["plan","credit","usage","stripeConfigured"]},"MeResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":["string","null"]},"pictureUrl":{"type":["string","null"]},"plan":{"type":"object","properties":{"id":{"type":"string","enum":["free","starter","plus","pro"]},"status":{"type":"string","enum":["active","past_due","canceled","none"]},"currentPeriodEnd":{"type":["string","null"]},"cancelAtPeriodEnd":{"type":"boolean"},"source":{"type":"string","enum":["stripe","complimentary"]},"compExpiresAt":{"type":["string","null"]},"hasSubscription":{"type":"boolean"},"subscriptionPlan":{"type":"string","enum":["starter","plus","pro"]}},"required":["id","status","currentPeriodEnd","cancelAtPeriodEnd"]},"credit":{"type":"object","properties":{"subscriptionCents":{"type":"number"},"topupCents":{"type":"number"},"totalCents":{"type":"number"},"periodGrantCents":{"type":"number"},"periodEnd":{"type":["string","null"]}},"required":["subscriptionCents","topupCents","totalCents","periodGrantCents","periodEnd"]},"usage":{"type":"object","properties":{"tokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"monthTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"monthGrant":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"boughtTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"resetsAt":{"type":["string","null"]},"weekly":{"type":"boolean"},"weekPercentUsed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"weekResetsAt":{"type":["string","null"]},"extraWeeks":{"type":"number"},"extraWeeksOf":{"type":"string","enum":["starter","plus","pro"]}},"required":["tokens","monthTokens","monthGrant","boughtTokens","resetsAt"]},"isAdmin":{"type":"boolean","const":true},"isTester":{"type":"boolean","const":true}},"required":["id","email","name","pictureUrl","plan","credit","usage"]},"MemorySearchInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"query":{"type":"string","minLength":1,"maxLength":1000},"taskKey":{"type":"string","minLength":1,"maxLength":64},"limit":{"type":"integer","minimum":1,"maximum":100}},"required":["query"]},"MemorySearchResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"model":{"type":"string"},"hits":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"score":{"type":"number"}},"required":["id","score"]}},"pending":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["model","hits","pending"]},"OutOfCreditError":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"string","const":"out_of_credit"},"message":{"type":"string"},"topupUrl":{"type":"string"},"balanceCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"neededCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["error","message","topupUrl"]},"PlanInfo":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","enum":["free","starter","plus","pro"]},"status":{"type":"string","enum":["active","past_due","canceled","none"]},"currentPeriodEnd":{"type":["string","null"]},"cancelAtPeriodEnd":{"type":"boolean"},"source":{"type":"string","enum":["stripe","complimentary"]},"compExpiresAt":{"type":["string","null"]},"hasSubscription":{"type":"boolean"},"subscriptionPlan":{"type":"string","enum":["starter","plus","pro"]}},"required":["id","status","currentPeriodEnd","cancelAtPeriodEnd"]},"PlanRequiredError":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"string","const":"plan_required"},"feature":{"type":"string","enum":["todo","sync","voice","calls","apiKeys"]},"message":{"type":"string"},"upgradeUrl":{"type":"string"}},"required":["error","feature","message","upgradeUrl"]},"PortalInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"returnUrl":{"type":"string","minLength":1,"maxLength":2000}},"required":["returnUrl"]},"RealtimeErrorEvent":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"type":{"type":"string","const":"noa.error"},"error":{"type":"string","enum":["unauthorized","plan_required","out_of_credit","realtime_unavailable","session_open","session_replaced","denied","idle_timeout","session_limit","message_too_big","upstream_error"]},"message":{"type":"string"}},"required":["type","error","message"],"additionalProperties":{}},"RedirectUrlResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string"}},"required":["url"]},"SessionList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string"},"lastUsedAt":{"type":"string"},"expiresAt":{"type":"string"},"current":{"type":"boolean"}},"required":["id","createdAt","lastUsedAt","expiresAt","current"]}}},"required":["sessions"]},"TopupInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"amountCents":{"type":"number","enum":[1000,2500,5000]},"returnUrl":{"type":"string","minLength":1,"maxLength":2000}},"required":["amountCents","returnUrl"]},"TranscribeResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"text":{"type":"string"},"seconds":{"type":"number"},"chargedCents":{"type":"number"}},"required":["text","seconds","chargedCents"]},"GenerateImageRequest":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":4000},"size":{"type":"string","enum":["1024x1024","1536x1024","1024x1536"]},"quality":{"type":"string","enum":["low","medium","high"]},"transparent":{"type":"boolean"},"model":{"type":"string","enum":["gpt-image-2","gpt-image-1-mini"]}},"required":["prompt"]},"GenerateImageResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"base64":{"type":"string","minLength":1},"mimeType":{"type":"string","const":"image/png"},"size":{"type":"string","enum":["1024x1024","1536x1024","1024x1536"]},"quality":{"type":"string","enum":["low","medium","high"]},"model":{"type":"string","enum":["gpt-image-2","gpt-image-1-mini"]},"chargedCents":{"type":"number"}},"required":["base64","mimeType","size","quality","model","chargedCents"]},"StartCallRequest":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"to":{"type":"string"},"goal":{"type":"string","minLength":1,"maxLength":4000},"callerName":{"type":"string","minLength":1,"maxLength":80},"language":{"type":"string","minLength":1,"maxLength":40},"maxMinutes":{"type":"integer","minimum":1,"maximum":9007199254740991},"noaHangsUp":{"type":"boolean"},"model":{"type":"string","enum":["realtime","gemini"]},"voice":{"anyOf":[{"type":"string","enum":["marin","cedar","alloy","ash","ballad","coral","echo","sage","shimmer","verse"]},{"type":"string","enum":["Puck","Kore","Charon","Aoede","Zephyr","Fenrir","Leda","Orus","Callirrhoe","Autonoe","Enceladus","Iapetus","Umbriel","Algieba","Despina","Erinome","Algenib","Rasalgethi","Laomedeia","Achernar","Alnilam","Schedar","Gacrux","Pulcherrima","Achird","Zubenelgenubi","Vindemiatrix","Sadachbia","Sadaltager","Sulafat"]}]},"callbackNumber":{"type":"string"},"voicemail":{"type":"string","enum":["message","hangup"]},"announceAi":{"type":"boolean"},"instructions":{"type":"string","maxLength":4000},"twilio":{"type":"object","properties":{"accountSid":{"type":"string","pattern":"^AC[0-9a-f]{32}$"},"authToken":{"type":"string","pattern":"^[0-9a-f]{32}$"},"from":{"type":"string"}},"required":["accountSid","authToken","from"]},"record":{"type":"boolean"}},"required":["to","goal"]},"PhoneCallView":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"to":{"type":"string"},"from":{"type":"string"},"line":{"type":"string","enum":["noa","twilio"]},"state":{"type":"string","enum":["dialing","ringing","in_progress","completed","busy","no_answer","failed","canceled"]},"createdAt":{"type":"string"},"answeredAt":{"type":["string","null"]},"endedAt":{"type":["string","null"]},"durationSec":{"type":"number"},"transcript":{"type":"array","items":{"type":"object","properties":{"who":{"type":"string","enum":["noa","them","user","you"]},"text":{"type":"string"}},"required":["who","text"]}},"outcome":{"type":["string","null"]},"endedBy":{"anyOf":[{"type":"string","enum":["noa","them","time_limit","user","error"]},{"type":"null"}]},"error":{"type":["string","null"]},"chargedCents":{"type":"number"},"takenOverAt":{"type":["string","null"]},"recording":{"anyOf":[{"type":"object","properties":{"state":{"type":"string","enum":["pending","ready","failed","deleted"]},"durationSec":{"type":"number"}},"required":["state","durationSec"]},{"type":"null"}]}},"required":["id","to","from","line","state","createdAt","answeredAt","endedAt","durationSec","transcript","outcome","endedBy","error","chargedCents"]},"PhoneCallsInfo":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"noaLine":{"type":"boolean"},"voice":{"type":"boolean"},"models":{"type":"object","properties":{"realtime":{"type":"boolean"},"gemini":{"type":"boolean"}},"required":["realtime","gemini"]},"agreed":{"type":"boolean"},"recordingAgreed":{"type":"boolean"}},"required":["noaLine","voice"]},"CallLiveTicket":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"ticket":{"type":"string","minLength":1}},"required":["ticket"]},"UsageReport":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"month":{"type":"string"},"months":{"type":"array","items":{"type":"string"}},"period":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]},"periods":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]}},"totals":{"type":"object","properties":{"tasksRun":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tasksDone":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tasksFailed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tasksPaused":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"aiRequests":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"inputTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"outputTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"chargedCents":{"type":"number"},"creditGrantedCents":{"type":"number"},"topupsCents":{"type":"number"},"audioSeconds":{"type":"number"}},"required":["tasksRun","tasksDone","tasksFailed","tasksPaused","aiRequests","inputTokens","outputTokens","chargedCents","creditGrantedCents","topupsCents"]},"byModel":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["ai_messages","jev","transcribe","realtime","image","speak","phone"]},"model":{"type":"string"},"requests":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"inputTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"outputTokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"chargedCents":{"type":"number"},"audioSeconds":{"type":"number"}},"required":["model","requests","inputTokens","outputTokens","chargedCents"]}},"byDay":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"tasksRun":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"chargedCents":{"type":"number"},"charges":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["ai_messages","jev","transcribe","realtime","image","speak","phone"]},"model":{"type":"string"},"chargedCents":{"type":"number"}},"required":["kind","model","chargedCents"]}}},"required":["date","tasksRun","chargedCents"]}}},"required":["month","months","totals","byModel","byDay"]},"VoiceEnginesResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"engines":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["realtime","realtime-mini","gemini","deepgram","standard"]},"name":{"type":"string"},"model":{"type":"string"},"approxCentsPerMinute":{"type":"number"},"assumption":{"type":"string"},"available":{"type":"boolean"}},"required":["id","name","model","approxCentsPerMinute","assumption","available"]}},"default":{"type":"string","enum":["realtime","realtime-mini","gemini","deepgram","standard"]},"transcribers":{"type":"array","items":{"type":"object","properties":{"model":{"type":"string","enum":["gpt-live-transcribe","gpt-realtime-whisper","gpt-transcribe"]},"approxCentsPerMinute":{"type":"number"},"available":{"type":"boolean"}},"required":["model","approxCentsPerMinute","available"]}}},"required":["engines","default"]},"ApiError":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]},"AuthGoogleInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"idToken":{"type":"string","minLength":1,"maxLength":8192},"cookie":{"type":"boolean"}},"required":["idToken"]},"AuthResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"token":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":["string","null"]},"pictureUrl":{"type":["string","null"]}},"required":["id","email","name","pictureUrl"]},"expiresAt":{"type":"string"}},"required":["token","user","expiresAt"]},"RedeemSignInCodeInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"code":{"type":"string","pattern":"^bt_c_[0-9a-f]{64}$"}},"required":["code"]},"RedeemSignInCodeResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":["string","null"]},"pictureUrl":{"type":["string","null"]}},"required":["id","email","name","pictureUrl"]},"expiresAt":{"type":"string"}},"required":["user","expiresAt"]},"SignInCodeResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"code":{"type":"string","pattern":"^bt_c_[0-9a-f]{64}$"},"expiresAt":{"type":"string"}},"required":["code","expiresAt"]},"BatchCreateInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"tasks":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","properties":{"instructions":{"type":"string","minLength":1,"maxLength":8000},"account":{"type":"string","minLength":1,"maxLength":100},"mediaIds":{"maxItems":10,"type":"array","items":{"type":"string","minLength":1}},"priority":{"type":"integer","minimum":-1000,"maximum":1000},"schedule":{"type":"object","properties":{"at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"repeat":{"anyOf":[{"type":"object","properties":{"cron":{"type":"string","maxLength":1000},"tz":{"type":"string","minLength":1,"maxLength":64},"start":{"type":"string"},"end":{"type":"string"},"interval":{"type":"object","properties":{"every":{"type":"integer","minimum":1,"maximum":99},"unit":{"type":"string","enum":["day","week","month"]}},"required":["every","unit"]},"count":{"type":"integer","minimum":1,"maximum":1000}},"required":["cron","tz"]},{"type":"null"}]}}},"notBefore":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"repeat":{"anyOf":[{"type":"object","properties":{"dailyAt":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"}}},"required":["dailyAt"]},{"type":"null"}]},"tz":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"agentAuthored":{"type":"boolean"},"chat":{"type":"object","properties":{"sessionId":{"type":"string","minLength":1,"maxLength":100},"mode":{"type":"string","enum":["resume","remind"]},"title":{"type":"string","maxLength":200}},"required":["sessionId","mode"]},"runnerId":{"type":"string","minLength":1,"maxLength":100}},"required":["instructions"]}}},"required":["tasks"]},"ClaimInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"runnerId":{"type":"string","minLength":1,"maxLength":100},"taskId":{"type":"string","minLength":1,"maxLength":100}},"required":["runnerId"]},"ClaimResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"task":{"type":"object","properties":{"id":{"type":"string"},"instructions":{"type":"string"},"account":{"type":["string","null"]},"mediaIds":{"type":"array","items":{"type":"string"}},"notBefore":{"type":["string","null"]},"priority":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"status":{"type":"string","enum":["pending","running","done","failed","paused","cancelled"]},"attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"leaseOwner":{"type":["string","null"]},"leaseExpiresAt":{"type":["string","null"]},"retryAfter":{"type":["string","null"]},"resultSummary":{"type":["string","null"]},"resultUrl":{"type":["string","null"]},"resultScreenshotId":{"type":["string","null"]},"pauseReason":{"type":["string","null"]},"failReason":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"schedule":{"anyOf":[{"type":"object","properties":{"at":{"type":["string","null"]},"repeat":{"anyOf":[{"type":"object","properties":{"cron":{"type":"string","maxLength":1000},"tz":{"type":"string","minLength":1,"maxLength":64},"start":{"type":"string"},"end":{"type":"string"},"interval":{"type":"object","properties":{"every":{"type":"integer","minimum":1,"maximum":99},"unit":{"type":"string","enum":["day","week","month"]}},"required":["every","unit"]},"count":{"type":"integer","minimum":1,"maximum":1000}},"required":["cron","tz"]},{"type":"null"}]}},"required":["at","repeat"]},{"type":"null"}]},"ownerId":{"type":["string","null"]},"seriesId":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"agentAuthored":{"type":"boolean"},"runnerId":{"type":["string","null"]},"chat":{"anyOf":[{"type":"object","properties":{"sessionId":{"type":"string","minLength":1,"maxLength":100},"mode":{"type":"string","enum":["resume","remind"]},"title":{"type":"string","maxLength":200}},"required":["sessionId","mode"]},{"type":"null"}]},"title":{"type":["string","null"]}},"required":["id","instructions","account","mediaIds","notBefore","priority","status","attempts","leaseOwner","leaseExpiresAt","retryAfter","resultSummary","resultUrl","resultScreenshotId","pauseReason","failReason","createdAt","updatedAt"]},"media":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"filename":{"type":"string"},"contentType":{"type":"string"},"size":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["id","filename","contentType","size"]}},"leaseExpiresAt":{"type":"string"}},"required":["task","media","leaseExpiresAt"]},"CreateKeyInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"role":{"type":"string","enum":["creator","runner"]}},"required":["name","role"]},"CreateOwnKeyInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"role":{"type":"string","enum":["creator","runner"]}},"required":["name","role"]},"CreateTaskInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"instructions":{"type":"string","minLength":1,"maxLength":8000},"account":{"type":"string","minLength":1,"maxLength":100},"mediaIds":{"maxItems":10,"type":"array","items":{"type":"string","minLength":1}},"priority":{"type":"integer","minimum":-1000,"maximum":1000},"schedule":{"type":"object","properties":{"at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"repeat":{"anyOf":[{"type":"object","properties":{"cron":{"type":"string","maxLength":1000},"tz":{"type":"string","minLength":1,"maxLength":64},"start":{"type":"string"},"end":{"type":"string"},"interval":{"type":"object","properties":{"every":{"type":"integer","minimum":1,"maximum":99},"unit":{"type":"string","enum":["day","week","month"]}},"required":["every","unit"]},"count":{"type":"integer","minimum":1,"maximum":1000}},"required":["cron","tz"]},{"type":"null"}]}}},"notBefore":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"repeat":{"anyOf":[{"type":"object","properties":{"dailyAt":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"}}},"required":["dailyAt"]},{"type":"null"}]},"tz":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"agentAuthored":{"type":"boolean"},"chat":{"type":"object","properties":{"sessionId":{"type":"string","minLength":1,"maxLength":100},"mode":{"type":"string","enum":["resume","remind"]},"title":{"type":"string","maxLength":200}},"required":["sessionId","mode"]},"runnerId":{"type":"string","minLength":1,"maxLength":100}},"required":["instructions"]},"HeartbeatInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"runnerId":{"type":"string","minLength":1}},"required":["runnerId"]},"MediaInfo":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"filename":{"type":"string"},"contentType":{"type":"string"},"size":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["id","filename","contentType","size"]},"LegacyRepeatRule":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"dailyAt":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"}}},"required":["dailyAt"]},"RepeatSchedule":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"cron":{"type":"string","maxLength":1000},"tz":{"type":"string","minLength":1,"maxLength":64},"start":{"type":"string"},"end":{"type":"string"},"interval":{"type":"object","properties":{"every":{"type":"integer","minimum":1,"maximum":99},"unit":{"type":"string","enum":["day","week","month"]}},"required":["every","unit"]},"count":{"type":"integer","minimum":1,"maximum":1000}},"required":["cron","tz"]},"ResultInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"runnerId":{"type":"string","minLength":1},"outcome":{"type":"string","enum":["done","failed","paused","retry"]},"summary":{"type":"string","maxLength":4000},"url":{"type":"string","maxLength":2000},"reason":{"type":"string","maxLength":4000},"screenshotId":{"type":"string"},"retryAfterMinutes":{"type":"integer","minimum":1,"maximum":1440}},"required":["runnerId","outcome"]},"ScheduleInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"repeat":{"anyOf":[{"type":"object","properties":{"cron":{"type":"string","maxLength":1000},"tz":{"type":"string","minLength":1,"maxLength":64},"start":{"type":"string"},"end":{"type":"string"},"interval":{"type":"object","properties":{"every":{"type":"integer","minimum":1,"maximum":99},"unit":{"type":"string","enum":["day","week","month"]}},"required":["every","unit"]},"count":{"type":"integer","minimum":1,"maximum":1000}},"required":["cron","tz"]},{"type":"null"}]}}},"Task":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"instructions":{"type":"string"},"account":{"type":["string","null"]},"mediaIds":{"type":"array","items":{"type":"string"}},"notBefore":{"type":["string","null"]},"priority":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"status":{"type":"string","enum":["pending","running","done","failed","paused","cancelled"]},"attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"leaseOwner":{"type":["string","null"]},"leaseExpiresAt":{"type":["string","null"]},"retryAfter":{"type":["string","null"]},"resultSummary":{"type":["string","null"]},"resultUrl":{"type":["string","null"]},"resultScreenshotId":{"type":["string","null"]},"pauseReason":{"type":["string","null"]},"failReason":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"schedule":{"anyOf":[{"type":"object","properties":{"at":{"type":["string","null"]},"repeat":{"anyOf":[{"type":"object","properties":{"cron":{"type":"string","maxLength":1000},"tz":{"type":"string","minLength":1,"maxLength":64},"start":{"type":"string"},"end":{"type":"string"},"interval":{"type":"object","properties":{"every":{"type":"integer","minimum":1,"maximum":99},"unit":{"type":"string","enum":["day","week","month"]}},"required":["every","unit"]},"count":{"type":"integer","minimum":1,"maximum":1000}},"required":["cron","tz"]},{"type":"null"}]}},"required":["at","repeat"]},{"type":"null"}]},"ownerId":{"type":["string","null"]},"seriesId":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"agentAuthored":{"type":"boolean"},"runnerId":{"type":["string","null"]},"chat":{"anyOf":[{"type":"object","properties":{"sessionId":{"type":"string","minLength":1,"maxLength":100},"mode":{"type":"string","enum":["resume","remind"]},"title":{"type":"string","maxLength":200}},"required":["sessionId","mode"]},{"type":"null"}]},"title":{"type":["string","null"]}},"required":["id","instructions","account","mediaIds","notBefore","priority","status","attempts","leaseOwner","leaseExpiresAt","retryAfter","resultSummary","resultUrl","resultScreenshotId","pauseReason","failReason","createdAt","updatedAt"]},"TaskEvent":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"taskId":{"type":"string"},"type":{"type":"string","enum":["created","updated","claimed","heartbeat","done","failed","paused","cancelled","retried","lease_expired"]},"detail":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","taskId","type","detail","createdAt"]},"TaskListResponse":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"instructions":{"type":"string"},"account":{"type":["string","null"]},"mediaIds":{"type":"array","items":{"type":"string"}},"notBefore":{"type":["string","null"]},"priority":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"status":{"type":"string","enum":["pending","running","done","failed","paused","cancelled"]},"attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"leaseOwner":{"type":["string","null"]},"leaseExpiresAt":{"type":["string","null"]},"retryAfter":{"type":["string","null"]},"resultSummary":{"type":["string","null"]},"resultUrl":{"type":["string","null"]},"resultScreenshotId":{"type":["string","null"]},"pauseReason":{"type":["string","null"]},"failReason":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"schedule":{"anyOf":[{"type":"object","properties":{"at":{"type":["string","null"]},"repeat":{"anyOf":[{"type":"object","properties":{"cron":{"type":"string","maxLength":1000},"tz":{"type":"string","minLength":1,"maxLength":64},"start":{"type":"string"},"end":{"type":"string"},"interval":{"type":"object","properties":{"every":{"type":"integer","minimum":1,"maximum":99},"unit":{"type":"string","enum":["day","week","month"]}},"required":["every","unit"]},"count":{"type":"integer","minimum":1,"maximum":1000}},"required":["cron","tz"]},{"type":"null"}]}},"required":["at","repeat"]},{"type":"null"}]},"ownerId":{"type":["string","null"]},"seriesId":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"agentAuthored":{"type":"boolean"},"runnerId":{"type":["string","null"]},"chat":{"anyOf":[{"type":"object","properties":{"sessionId":{"type":"string","minLength":1,"maxLength":100},"mode":{"type":"string","enum":["resume","remind"]},"title":{"type":"string","maxLength":200}},"required":["sessionId","mode"]},{"type":"null"}]},"title":{"type":["string","null"]}},"required":["id","instructions","account","mediaIds","notBefore","priority","status","attempts","leaseOwner","leaseExpiresAt","retryAfter","resultSummary","resultUrl","resultScreenshotId","pauseReason","failReason","createdAt","updatedAt"]}},"nextCursor":{"type":["string","null"]},"locked":{"type":"boolean"},"runners":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"platform":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"lastSeenAt":{"type":"string"}},"required":["id","platform","lastSeenAt"]}}},"required":["tasks","nextCursor","locked"]},"TaskSchedule":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"at":{"type":["string","null"]},"repeat":{"anyOf":[{"type":"object","properties":{"cron":{"type":"string","maxLength":1000},"tz":{"type":"string","minLength":1,"maxLength":64},"start":{"type":"string"},"end":{"type":"string"},"interval":{"type":"object","properties":{"every":{"type":"integer","minimum":1,"maximum":99},"unit":{"type":"string","enum":["day","week","month"]}},"required":["every","unit"]},"count":{"type":"integer","minimum":1,"maximum":1000}},"required":["cron","tz"]},{"type":"null"}]}},"required":["at","repeat"]},"TaskStatus":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","enum":["pending","running","done","failed","paused","cancelled"]},"UpdateTaskInput":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"instructions":{"type":"string","minLength":1,"maxLength":8000},"account":{"anyOf":[{"type":"string","minLength":1,"maxLength":100},{"type":"null"}]},"mediaIds":{"maxItems":10,"type":"array","items":{"type":"string","minLength":1}},"priority":{"type":"integer","minimum":-1000,"maximum":1000},"schedule":{"anyOf":[{"type":"object","properties":{"at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"repeat":{"anyOf":[{"type":"object","properties":{"cron":{"type":"string","maxLength":1000},"tz":{"type":"string","minLength":1,"maxLength":64},"start":{"type":"string"},"end":{"type":"string"},"interval":{"type":"object","properties":{"every":{"type":"integer","minimum":1,"maximum":99},"unit":{"type":"string","enum":["day","week","month"]}},"required":["every","unit"]},"count":{"type":"integer","minimum":1,"maximum":1000}},"required":["cron","tz"]},{"type":"null"}]}}},{"type":"null"}]},"notBefore":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"repeat":{"anyOf":[{"type":"object","properties":{"dailyAt":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"}}},"required":["dailyAt"]},{"type":"null"}]},"tz":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"agentAuthored":{"type":"boolean"},"chat":{"anyOf":[{"type":"object","properties":{"sessionId":{"type":"string","minLength":1,"maxLength":100},"mode":{"type":"string","enum":["resume","remind"]},"title":{"type":"string","maxLength":200}},"required":["sessionId","mode"]},{"type":"null"}]},"title":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}]}}},"User":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":["string","null"]},"pictureUrl":{"type":["string","null"]}},"required":["id","email","name","pictureUrl"]},"KeyInfo":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"role":{"type":"string","enum":["creator","runner"]},"createdAt":{"type":"string"},"revokedAt":{"type":["string","null"]},"ownerId":{"type":["string","null"],"description":"Owning user (GET /v1/keys only); null for legacy keys"}},"required":["id","name","role","createdAt","revokedAt"]}}},"paths":{"/v1/tasks":{"post":{"summary":"Create a task. `schedule`: `{ at, repeat: { cron, tz, start, end, interval, count } }` (see RepeatSchedule); the legacy `notBefore`, `repeat: { dailyAt }` and `tz` are still accepted and converted","description":"Roles: creator, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"404":{"description":"Unknown media id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskInput"}}}}},"get":{"summary":"List tasks, newest first. Works on every plan: `locked` is true when the plan does not include the TODO list (the kept tasks are then read-only)","description":"Roles: creator, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"A page of tasks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskListResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/TaskStatus"}},{"name":"series","in":"query","schema":{"type":"string"},"description":"Only the rows of one series (a task's seriesId): a repeating task's runs"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"nextCursor of the previous page"}]}},"/v1/tasks/batch":{"post":{"summary":"Create up to 100 tasks, all or nothing","description":"Roles: creator, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"tasks":{"type":"array","items":{"$ref":"#/components/schemas/Task"}}},"required":["tasks"]}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"404":{"description":"Unknown media id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchCreateInput"}}}}}},"/v1/tasks/{id}":{"get":{"summary":"Get a task with its events","description":"Roles: creator, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"Task and events","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"$ref":"#/components/schemas/Task"},"events":{"type":"array","items":{"$ref":"#/components/schemas/TaskEvent"}}},"required":["task","events"]}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]},"patch":{"summary":"Edit a pending or paused task","description":"Roles: creator, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not pending or paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaskInput"}}}}},"delete":{"summary":"Delete a task and its events (not while running)","description":"Roles: creator, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"204":{"description":"Deleted"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Running","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/tasks/{id}/cancel":{"post":{"summary":"Cancel a pending, paused or running task (a running one's lease is cleared: its runner's result is refused)","description":"Roles: creator, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not pending, paused or running","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/tasks/{id}/retry":{"post":{"summary":"Move a failed task back to pending (attempts reset), run a paused one now (attempts kept), or put a cancelled one back in the queue at its time","description":"Roles: creator, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"Pending again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not failed, paused or cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/tasks/{id}/pause":{"post":{"summary":"Pause a pending, paused or running task: it does not run until resumed (a running one's lease is cleared; optional body { reason }, default \"Paused by you\")","description":"Roles: creator, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"Paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not pending, paused or running","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/tasks/{id}/resume":{"post":{"summary":"Resume a paused task: pending at its time again (a repeating one whose time went by: its rule's next time)","description":"Roles: creator, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"Pending again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/media":{"post":{"summary":"Upload a file","description":"Roles: creator, runner, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"201":{"description":"Stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaInfo"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"413":{"description":"Too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}}}},"/v1/media/{id}":{"get":{"summary":"Download a file","description":"Roles: creator, runner, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"File bytes with its content type and Content-Disposition filename"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/runner/claim":{"post":{"summary":"Claim the next due task this runner may run (one on no runner, on this one, or on one gone 24 hours: Task.runnerId), or with `taskId` that task now (Run on its row; it moves to this runner)","description":"Roles: runner, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"Claimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimResponse"}}}},"204":{"description":"Nothing is due"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"404":{"description":"taskId: not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"taskId: not pending, paused or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimInput"}}}}}},"/v1/runner/tasks/{id}/heartbeat":{"post":{"summary":"Extend the lease","description":"Roles: runner, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"New lease expiry","content":{"application/json":{"schema":{"type":"object","properties":{"leaseExpiresAt":{"type":"string"}},"required":["leaseExpiresAt"]}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not running under this runner's lease","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeartbeatInput"}}}}}},"/v1/runner/tasks/{id}/result":{"post":{"summary":"Report done, failed, paused or retry. A repeating task that ends done or failed gets its next occurrence (once).","description":"Roles: runner, user. A signed-in user (session token) and keys it owns only see its own tasks and media; legacy keys (no owner) only see owner-less tasks and media; admin sees everything. Anything outside the caller's scope answers 404.","security":[{"bearer":[]}],"responses":{"200":{"description":"Final task","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not running under this runner's lease","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultInput"}}}}}},"/v1/keys":{"post":{"summary":"Create a legacy (owner-less) API key (shown once)","description":"Roles: admin.","security":[{"bearer":[]}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"role":{"type":"string"},"key":{"type":"string"}},"required":["id","name","role","key"]}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyInput"}}}}},"get":{"summary":"List all API keys","description":"Roles: admin.","security":[{"bearer":[]}],"responses":{"200":{"description":"Keys","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/KeyInfo"}}},"required":["keys"]}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/keys/{id}":{"delete":{"summary":"Revoke an API key","description":"Roles: admin.","security":[{"bearer":[]}],"responses":{"204":{"description":"Revoked"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/auth/google":{"post":{"summary":"Sign in with a Google ID token; returns a 60-day sliding session token","description":"Public. The ID token must be signed by Google, issued for GOOGLE_CLIENT_ID, unexpired and carry a verified email. Creates the user on first sign-in.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthGoogleInput"}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Invalid Google ID token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"cookie: true without the CSRF header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Google's signing keys could not be fetched","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Google sign-in is not configured on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/auth/code":{"post":{"summary":"A one-time code that signs the dashboard in to the caller's account (single use, valid for a minute)","description":"Roles: user (session token in the Authorization header; not the dashboard cookie).","security":[{"bearer":[]}],"responses":{"200":{"description":"The code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInCodeResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not a session token, or sent with the cookie","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/auth/code/redeem":{"post":{"summary":"Trade a sign-in code for a new dashboard session (sets the __Host-bt_session cookie)","description":"Public; needs X-Requested-With: noa. The code works once, until it expires, and only while the session that asked for it is still signed in. The new session's token is only in the HttpOnly cookie.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedeemSignInCodeInput"}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedeemSignInCodeResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"The code is unknown, used, expired, or its session ended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Missing the CSRF header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/auth/password":{"post":{"summary":"Email + password sign-in, for the accounts an admin gave a password (Google sign-in for everyone else)","description":"Public; needs X-Requested-With: noa. Answers with a one-time ticket shaped as a Google ID token for the given nonce (aud = the Google client ID), which works once, for ten minutes, at POST /v1/auth/google. No session is made here.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"},"nonce":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,128}$"}},"required":["email","password","nonce"]}}}},"responses":{"200":{"description":"The ticket","content":{"application/json":{"schema":{"type":"object","properties":{"idToken":{"type":"string"}},"required":["idToken"]}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"The email has no password, or the password is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Missing the CSRF header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many tries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/auth/logout":{"post":{"summary":"Revoke the calling session","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"204":{"description":"Signed out"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/me":{"get":{"summary":"The signed-in user with plan and usage","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"User, plan and usage","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"summary":"Delete the account: cancels any Stripe subscription immediately, deletes tasks, events, media, cloud files, keys, sessions, usage and the user","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"204":{"description":"Deleted"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Stripe could not cancel the subscription (nothing deleted)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAccountInput"}}}}}},"/v1/me/keys":{"post":{"summary":"Create an API key scoped to the signed-in user (shown once)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"role":{"type":"string"},"key":{"type":"string"}},"required":["id","name","role","key"]}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The plan does not include API keys","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRequiredError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOwnKeyInput"}}}}},"get":{"summary":"List the signed-in user's API keys","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Keys","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/KeyInfo"}}},"required":["keys"]}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/me/keys/{id}":{"delete":{"summary":"Revoke one of the signed-in user's API keys","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"204":{"description":"Revoked"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/me/billing":{"get":{"summary":"Plan, usage and whether Stripe is configured","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Billing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeBillingResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/me/usage":{"get":{"summary":"Usage report of a pay period or a UTC month (tasks by outcome, hosted AI usage in tokens)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyUsageReport"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"month","in":"query","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"description":"YYYY-MM (UTC): report this month instead of a pay period"},{"name":"period","in":"query","schema":{"type":"string"},"description":"An ISO date or instant within the pay period to report; default the current one"}]}},"/v1/me/usage.csv":{"get":{"summary":"The pay period's (or month's) hosted AI usage events as CSV","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"text/csv","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"month","in":"query","schema":{"type":"string"}},{"name":"period","in":"query","schema":{"type":"string"}}]}},"/v1/me/settings":{"get":{"summary":"Account settings (monthly report email opt-in)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountSettings"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"summary":"Change account settings","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountSettings"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountSettings"}}}}}},"/v1/memory":{"get":{"summary":"The agent's memory changed after `since` (entries and deletions) and the revision now; `locked` when the plan no longer syncs it","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Changes","content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer"},"entries":{"type":"array","items":{"type":"object"}},"deleted":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"at":{"type":"string"}}}},"refused":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"reason":{"type":"string"}}}},"locked":{"type":"boolean"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"since","in":"query","schema":{"type":"integer","minimum":0}}]},"delete":{"summary":"Forget everything the account keeps in memory (allowed on any plan)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Forgotten","content":{"application/json":{"schema":{"type":"object","properties":{"removed":{"type":"integer"},"rev":{"type":"integer"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/memory/sync":{"post":{"summary":"Send this browser's memory changes (newest wins; entries holding a secret are refused) and get the changes since `since`","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Changes","content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer"},"entries":{"type":"array","items":{"type":"object"}},"deleted":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"at":{"type":"string"}}}},"refused":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"reason":{"type":"string"}}}},"locked":{"type":"boolean"}}}}}},"400":{"description":"Invalid entries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["since","upserts","deletes"],"properties":{"since":{"type":"integer","minimum":0},"upserts":{"type":"array","items":{"type":"object"}},"deletes":{"type":"array","items":{"type":"object"}}}}}}}}},"/v1/files":{"get":{"summary":"The account's cloud files, newest first, with the bytes used and the quota","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Files","content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","required":["id","name","folder","contentType","size","createdAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"folder":{"type":"string","maxLength":60,"description":"Where it sits, as in the Noa folder (one level): \"\" its top, \"images\" generated pictures, or a folder the user or the agent chose (e.g. \"invoices\")"},"contentType":{"type":"string"},"size":{"type":"integer","minimum":0},"createdAt":{"type":"string"}}}},"usedBytes":{"type":"integer"},"quotaBytes":{"type":"integer"},"locked":{"type":"boolean","description":"The plan does not include cloud files: kept ones can be read and deleted, new ones not added"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"summary":"Store a file in the account (at most MAX_MEDIA_BYTES; the account's files at most CLOUD_FILES_QUOTA_BYTES)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"201":{"description":"Stored","content":{"application/json":{"schema":{"type":"object","required":["id","name","folder","contentType","size","createdAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"folder":{"type":"string","maxLength":60,"description":"Where it sits, as in the Noa folder (one level): \"\" its top, \"images\" generated pictures, or a folder the user or the agent chose (e.g. \"invoices\")"},"contentType":{"type":"string"},"size":{"type":"integer","minimum":0},"createdAt":{"type":"string"}}}}}},"400":{"description":"No file, or a folder name that is not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"413":{"description":"Too large, or the account's files are full","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many uploads","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"folder":{"type":"string","maxLength":60,"description":"\"\" (the default) or one folder name: letters, digits, spaces, - and _"}},"required":["file"]}}}}}},"/v1/files/{id}":{"get":{"summary":"Download a cloud file","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"File bytes with its content type and Content-Disposition filename"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]},"delete":{"summary":"Delete a cloud file (any plan)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"204":{"description":"Deleted"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/shop":{"get":{"summary":"The shop: its items, what this account owns and wears, and the tokens it has to spend","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Shop","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShopState"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/shop/buy":{"post":{"summary":"Buy an item with tokens (priced by the server; the month's tokens are spent first, then bought ones) and wear it. Buying an owned item only wears it","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Shop","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShopState"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"not_enough_tokens: the item costs more than the tokens left (nothing is charged)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No such item","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShopBuyRequest"}}}}}},"/v1/shop/equip":{"post":{"summary":"Wear an owned (or free) item","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Shop","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShopState"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"not_owned: the item has not been bought","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No such item","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShopEquipRequest"}}}}}},"/v1/rooftop/config.js":{"get":{"summary":"The roof page's window.ROOFTOP_CONFIG (text/javascript): the mesh, its public app key, the one door (noa), whether the staff can talk, money on, screen-share ICE servers","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"JavaScript","content":{"text/javascript":{"schema":{"type":"string"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/rooftop/auth/whoami":{"get":{"summary":"The account as the roof knows it","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Who","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string","description":"n:<Noa user id>"},"displayName":{"type":"string"},"avatarUrl":{"type":["string","null"]},"provider":{"const":"noa"},"login":{"type":"string"}}}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/rooftop/auth/me":{"get":{"summary":"The same as /auth/whoami","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Who","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string","description":"n:<Noa user id>"},"displayName":{"type":"string"},"avatarUrl":{"type":["string","null"]},"provider":{"const":"noa"},"login":{"type":"string"}}}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/rooftop/auth/logout":{"post":{"summary":"Nothing to do: signing out is Noa's","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"204":{"description":"Done"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/rooftop/api/money":{"get":{"summary":"The roof's wallet: the account's Noa tokens, the shop and wardrobe priced in tokens, what it owns and wears, achievements, loot boxes, the daily visit, the coming-back ladder, the table's limits and Noa's billing page","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Wallet","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/rooftop/api/money/buy":{"post":{"summary":"Buy a catalogue thing with tokens (the month's first, then bought ones; never below zero; ledger kind rooftop). The same `ref` again is the same purchase and is not charged twice","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Bought","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Not sold here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"not_enough_tokens, with needTokens: true and tokens (nothing is charged)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Already owned to the full","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["item"],"properties":{"item":{"type":"string"},"ref":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,64}$"}}}}}}}},"/v1/rooftop/api/money/used":{"post":{"summary":"A perishable thing was used up","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Pockets","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Not in the catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"string"}}}}}}}},"/v1/rooftop/api/money/wear":{"post":{"summary":"Put an owned (or free) thing on, or an empty key to take the slot back to its plain setting","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Worn","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"No such slot, or it does not go there","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"not_owned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slot":{"type":"string"},"key":{"type":"string"}}}}}}}},"/v1/rooftop/api/money/loot":{"post":{"summary":"Open a loot box: a thing, never tokens","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Prize","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"No boxes (boxes: 0), or nothing left to give","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/rooftop/api/money/earn":{"post":{"summary":"An achievement was done: its boxes, once ever (got: null when already had)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Earned","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"No such achievement","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"One at a time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string"}}}}}}}},"/v1/rooftop/api/money/visit":{"post":{"summary":"Still here: the daily box after five minutes on the server's clock, once every twenty hours; the coming-back ladder's next thing","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Visit","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/rooftop/api/money/table/buyin":{"post":{"summary":"Blackjack chips: 1 to 20,000 tokens charged into the room's escrow (a `ref` makes a retry the same buy-in)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Bought in","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Amount out of range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"not_enough_tokens, with needTokens: true and tokens (nothing is charged)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["room","amount"],"properties":{"room":{"type":"string"},"amount":{"type":"integer","minimum":1,"maximum":20000},"ref":{"type":"string"}}}}}}}},"/v1/rooftop/api/money/table/cashout":{"post":{"summary":"Chips off the table: `total` (everything taken off in this room) less what was already paid, from the room's escrow first and the house's top-up after (at most 20,000 tokens a person a UTC day), as bought tokens; only to somebody who bought in at that room","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Paid","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["room","total"],"properties":{"room":{"type":"string"},"total":{"type":"integer","minimum":0}}}}}}}},"/v1/rooftop/api/rooms":{"get":{"summary":"The public rooms (the fixed places first)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Rooms","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"summary":"Open a public room (or answer the one already open, already: true)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Room","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"No code, or a meeting code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"The list is full, or you opened enough","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string"},"name":{"type":"string"},"max":{"type":"integer"},"x":{"type":"boolean"},"map":{"type":"string"}}}}}}}},"/v1/rooftop/api/rooms/{code}":{"delete":{"summary":"Take a public room off the list: whoever opened it, or an admin","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not yours, or a fixed place","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No such room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/rooftop/api/rooms/mine":{"get":{"summary":"This account's rooms: the meetings it kept and the public rooms it opened, newest first","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Rooms","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"summary":"Keep a meeting code on this account's list","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Room","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Not a meeting code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string"},"map":{"type":"string"},"name":{"type":"string"}}}}}}}},"/v1/rooftop/api/rooms/mine/{code}":{"delete":{"summary":"Erase a meeting from this account's list","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No such room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/bookmarks":{"get":{"summary":"Noa Browser bookmarks changed after `since` (deletions included) and the revision now","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Changes","content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer"},"nodes":{"type":"array","items":{"type":"object","required":["guid","parentGuid","index","title","url","updatedAt","deleted"],"properties":{"guid":{"type":"string"},"parentGuid":{"type":"string","description":"A folder's guid, or root_bar / root_other / root_mobile"},"index":{"type":"integer","minimum":0},"title":{"type":"string"},"url":{"type":["string","null"],"description":"null: a folder"},"updatedAt":{"type":"string"},"deleted":{"type":"boolean"}}}}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"since","in":"query","schema":{"type":"integer","minimum":0}}]}},"/v1/bookmarks/sync":{"post":{"summary":"Send this computer's bookmark changes (newest wins per node) and get the changes since `since`; at most 20000 live nodes per account","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Changes","content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer"},"nodes":{"type":"array","items":{"type":"object","required":["guid","parentGuid","index","title","url","updatedAt","deleted"],"properties":{"guid":{"type":"string"},"parentGuid":{"type":"string","description":"A folder's guid, or root_bar / root_other / root_mobile"},"index":{"type":"integer","minimum":0},"title":{"type":"string"},"url":{"type":["string","null"],"description":"null: a folder"},"updatedAt":{"type":"string"},"deleted":{"type":"boolean"}}}}}}}}},"400":{"description":"Invalid nodes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Over the bookmark limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many syncs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["since","changes"],"properties":{"since":{"type":"integer","minimum":0},"changes":{"type":"array","items":{"type":"object","required":["guid","parentGuid","index","title","url","updatedAt","deleted"],"properties":{"guid":{"type":"string"},"parentGuid":{"type":"string","description":"A folder's guid, or root_bar / root_other / root_mobile"},"index":{"type":"integer","minimum":0},"title":{"type":"string"},"url":{"type":["string","null"],"description":"null: a folder"},"updatedAt":{"type":"string"},"deleted":{"type":"boolean"}}}}}}}}}}},"/v1/chats":{"get":{"summary":"The account's chats changed after `since` (deletions included), 200 at a time; any plan","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Changes","content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer"},"more":{"type":"boolean","description":"More changes wait after `rev`"},"chats":{"type":"array","items":{"type":"object","required":["sessionId","updatedAt","deleted","info"],"properties":{"sessionId":{"type":"string"},"updatedAt":{"type":"string"},"deleted":{"type":"boolean"},"info":{"oneOf":[{"type":"object","required":["sessionId","source","title","startedAt"],"description":"The chat's SessionInfo (other fields kept as sent)"},{"type":"null"}]}}}}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"since","in":"query","schema":{"type":"integer","minimum":0}}]}},"/v1/chats/{id}":{"put":{"summary":"Keep a chat (its SessionInfo and events) in the account; a copy older than the stored one is ignored. The account keeps its newest 2000 chats","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Written","content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer"},"stored":{"type":"boolean"}}}}}},"400":{"description":"Invalid chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Chat too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many saves","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["info","updatedAt","events"],"properties":{"info":{"type":"object","required":["sessionId","source","title","startedAt"],"description":"The chat's SessionInfo (other fields kept as sent)"},"updatedAt":{"type":"string"},"events":{"type":"array","items":{"type":"object"}}}}}}}},"delete":{"summary":"Delete a chat (a tombstone stays so other computers delete theirs)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"204":{"description":"Deleted"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/improve":{"get":{"summary":"Whether this account shares chats where Noa made a mistake (docs/BLUNDERS.md); off until the user agrees","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Status","content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"boolean"},"version":{"type":"string"},"givenAt":{"type":"string","nullable":true},"reports":{"type":"integer"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"put":{"summary":"Turn sharing on (the user read the words of this version) or off","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Status","content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"boolean"},"version":{"type":"string"},"givenAt":{"type":"string","nullable":true},"reports":{"type":"integer"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"The words changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["on"],"properties":{"on":{"type":"boolean"},"version":{"type":"string"},"source":{"type":"string","enum":["extension","dashboard","app"]}}}}}}}},"/v1/improve/check":{"post":{"summary":"Judge a stretch of a chat for blunders; a blunder files a report. Needs the user's agreement; costs them nothing","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"found":{"type":"boolean"},"id":{"type":"string"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not agreed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["sessionId","turn","earlier","latest","brain","source","appVersion"],"properties":{"sessionId":{"type":"string"},"turn":{"type":"integer"},"earlier":{"type":"string"},"latest":{"type":"string"},"brain":{"type":"string"},"source":{"type":"string"},"appVersion":{"type":"string"}}}}}}}},"/v1/improve/reports":{"delete":{"summary":"Delete every report of this account","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Status","content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"boolean"},"version":{"type":"string"},"givenAt":{"type":"string","nullable":true},"reports":{"type":"integer"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/improve/reports/{id}/raw":{"put":{"summary":"A report's raw copy of the chat, as JSON","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"204":{"description":"Kept"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not agreed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No such report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/shares/{id}":{"put":{"summary":"A chat's shared link: new (with its copy, sealed on the sharer's computer) or changed (who may read it; with a doc, a newer copy)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Written","content":{"application/json":{"schema":{"type":"object","required":["id","updatedAt"],"properties":{"id":{"type":"string"},"updatedAt":{"type":"string"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["sessionId","access"],"properties":{"sessionId":{"type":"string"},"access":{"type":"string","enum":["link","people"]},"people":{"type":"array","items":{"type":"string"}},"doc":{"type":"string"},"expiresAt":{"type":["string","null"],"description":"When the link stops working (ISO); null never; absent unchanged"}}}}}}},"delete":{"summary":"Ends the link and deletes its copy","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"204":{"description":"Ended"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/shared/{id}":{"get":{"summary":"A shared chat's sealed copy, when anyone with the link may read it (403 signIn: only some may). The key is only in the link","description":"Roles: none.","security":[{"bearer":[]}],"responses":{"200":{"description":"The copy","content":{"application/json":{"schema":{"type":"object","required":["doc","updatedAt"],"properties":{"doc":{"type":"string"},"updatedAt":{"type":"string"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/chats/{id}/events":{"get":{"summary":"A chat's events, oldest first","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Events","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object"}}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/e2ee":{"get":{"summary":"The key (its fingerprint, and the key locked with the Noa password) the account's chats and memory are end-to-end encrypted with; null: no key yet","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"State","content":{"application/json":{"schema":{"type":"object","required":["keyId"],"properties":{"keyId":{"type":["string","null"]},"hasProof":{"type":"boolean","description":"While on: the account keeps the hash of the key's proof (changing the locked key, or turning off, needs it)"},"wrapped":{"type":["object","null"],"required":["v","kdf","iterations","salt","data"],"properties":{"v":{"const":1},"kdf":{"const":"PBKDF2-SHA256"},"iterations":{"type":"integer","minimum":100000,"maximum":10000000},"salt":{"type":"string","description":"16 bytes, base64url"},"data":{"type":"string","description":"The 12-byte IV and the AES-GCM ciphertext of the key, base64url"}},"description":"The key locked with the user's Noa password (null: none set yet)"},"holds":{"type":"boolean","description":"The account keeps the key for the user's signed-in computers (signing in is all a computer needs)"},"held":{"type":["string","null"],"description":"The key's 16 bytes (base64url) the account keeps, sent only to the signed-in user (null: none given yet)"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"put":{"summary":"Encrypt the account's chats and memory end to end with this key (its fingerprint, its proof and the key locked with the Noa password are sent, never the key; from now on only values sealed with it are kept). On an account already encrypted, the locked key is set or changed only with the proof","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"State","content":{"application/json":{"schema":{"type":"object","required":["keyId"],"properties":{"keyId":{"type":["string","null"]},"hasProof":{"type":"boolean","description":"While on: the account keeps the hash of the key's proof (changing the locked key, or turning off, needs it)"},"wrapped":{"type":["object","null"],"required":["v","kdf","iterations","salt","data"],"properties":{"v":{"const":1},"kdf":{"const":"PBKDF2-SHA256"},"iterations":{"type":"integer","minimum":100000,"maximum":10000000},"salt":{"type":"string","description":"16 bytes, base64url"},"data":{"type":"string","description":"The 12-byte IV and the AES-GCM ciphertext of the key, base64url"}},"description":"The key locked with the user's Noa password (null: none set yet)"},"holds":{"type":"boolean","description":"The account keeps the key for the user's signed-in computers (signing in is all a computer needs)"},"held":{"type":["string","null"],"description":"The key's 16 bytes (base64url) the account keeps, sent only to the signed-in user (null: none given yet)"}}}}}},"400":{"description":"Invalid key id, proof or locked key; a locked key without the proof","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"A proof other than the one the account keeps","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Already encrypted with another key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["keyId"],"properties":{"keyId":{"type":"string"},"proof":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$","description":"Proof of the recovery words (only its SHA-256 is kept)"},"wrapped":{"type":"object","required":["v","kdf","iterations","salt","data"],"properties":{"v":{"const":1},"kdf":{"const":"PBKDF2-SHA256"},"iterations":{"type":"integer","minimum":100000,"maximum":10000000},"salt":{"type":"string","description":"16 bytes, base64url"},"data":{"type":"string","description":"The 12-byte IV and the AES-GCM ciphertext of the key, base64url"}}},"held":{"type":"string","description":"The key's 16 bytes (base64url), for the account to keep; checked against keyId"}}}}}}},"delete":{"summary":"Stop encrypting (computers with the recovery words send everything again, readable; needs the words' proof when the account keeps its hash); with forget=1 also delete every sealed chat, memory entry and link (no proof)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Off","content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["keyId"],"properties":{"keyId":{"type":["string","null"]},"hasProof":{"type":"boolean","description":"While on: the account keeps the hash of the key's proof (changing the locked key, or turning off, needs it)"},"wrapped":{"type":["object","null"],"required":["v","kdf","iterations","salt","data"],"properties":{"v":{"const":1},"kdf":{"const":"PBKDF2-SHA256"},"iterations":{"type":"integer","minimum":100000,"maximum":10000000},"salt":{"type":"string","description":"16 bytes, base64url"},"data":{"type":"string","description":"The 12-byte IV and the AES-GCM ciphertext of the key, base64url"}},"description":"The key locked with the user's Noa password (null: none set yet)"},"holds":{"type":"boolean","description":"The account keeps the key for the user's signed-in computers (signing in is all a computer needs)"},"held":{"type":["string","null"],"description":"The key's 16 bytes (base64url) the account keeps, sent only to the signed-in user (null: none given yet)"}}},{"type":"object","properties":{"chats":{"type":"integer"},"memories":{"type":"integer"},"links":{"type":"integer"}}}]}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"No proof, or a wrong one, while the account keeps its hash","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"forget","in":"query","schema":{"type":"string","enum":["1"]}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"proof":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$","description":"Proof of the recovery words (only its SHA-256 is kept)"}}}}}}}},"/v1/links":{"get":{"summary":"Link memory changed after `since` (links and deletions) and the revision now; `locked` when the plan no longer syncs it. An end-to-end encrypted account's links are sealed (only id and updatedAt readable)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Changes","content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer"},"entries":{"type":"array","items":{"type":"object"}},"deleted":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"at":{"type":"string"}}}},"refused":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"reason":{"type":"string"}}}},"locked":{"type":"boolean"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"since","in":"query","schema":{"type":"integer","minimum":0}}]},"delete":{"summary":"Forget every link the account keeps (allowed on any plan)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Forgotten","content":{"application/json":{"schema":{"type":"object","properties":{"removed":{"type":"integer"},"rev":{"type":"integer"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/links/sync":{"post":{"summary":"Send this computer's link memory changes (newest wins; an address of a kind never kept, or holding a secret, is refused) and get the changes since `since`. No vectors are stored","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Changes","content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer"},"entries":{"type":"array","items":{"type":"object"}},"deleted":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"at":{"type":"string"}}}},"refused":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"reason":{"type":"string"}}}},"locked":{"type":"boolean"}}}}}},"400":{"description":"Invalid links","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["since","upserts","deletes"],"properties":{"since":{"type":"integer","minimum":0},"upserts":{"type":"array","items":{"type":"object"}},"deletes":{"type":"array","items":{"type":"object"}}}}}}}}},"/v1/embeddings":{"post":{"summary":"Unit vectors (int8, base64) of up to 50 short texts, for a computer's own search of its links by meaning; nothing is kept. Not billed","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Vectors","content":{"application/json":{"schema":{"type":"object","properties":{"model":{"type":"string"},"dims":{"type":"integer"},"vectors":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"429":{"description":"Too many requests (rate limit)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Workers AI failed to embed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Workers AI is not bound on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["texts"],"properties":{"texts":{"type":"array","items":{"type":"string"},"maxItems":50}}}}}}}},"/v1/memory/search":{"post":{"summary":"The memory entries nearest in meaning to `query` (cosine similarity of embeddings), nearest first: the user's own memory and episodes, and the records of `taskKey` only (other tasks' records never). `limit` defaults to 50; a query over 1000 characters is cut. `pending`: entries not embedded yet (found by later searches). Not billed","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Hits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemorySearchResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed (ApiError), or the plan does not include the TODO list (PlanRequiredError, feature \"todo\")","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApiError"},{"$ref":"#/components/schemas/PlanRequiredError"}]}}}},"429":{"description":"Too many searches (rate limit)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Workers AI failed to embed the query","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Workers AI is not bound on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemorySearchInput"}}}}}},"/v1/me/sessions":{"get":{"summary":"Signed-in devices (unexpired sessions)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Sessions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionList"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"summary":"Sign out everywhere, including this device","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"204":{"description":"Signed out"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/me/sessions/{id}":{"delete":{"summary":"Sign out one device","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"204":{"description":"Signed out"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/config":{"get":{"summary":"Public runtime config for the dashboard: { googleClientId, stripeConfigured, dashboard: true }","security":[],"responses":{"200":{"description":"Config"}}}},"/v1/billing/plans":{"get":{"summary":"Plans (with their monthly tokens) and the token packs that can be bought","security":[],"responses":{"200":{"description":"Catalog","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingPlansResponse"}}}}}}},"/v1/billing/voice-engines":{"get":{"summary":"Voice engines (Realtime, Standard) with their approximate cost per minute of conversation and the default","security":[],"responses":{"200":{"description":"Engines","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceEnginesResponse"}}}}}}},"/v1/billing/checkout":{"post":{"summary":"Stripe Checkout for a paid plan (subscription)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Checkout page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedirectUrlResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Already on a paid plan: use the portal (body has portal: true)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Stripe error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Billing is not set up on this server yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutInput"}}}}}},"/v1/billing/topup":{"post":{"summary":"Stripe Checkout for a token pack (bought tokens never expire)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Checkout page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedirectUrlResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Stripe error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Billing is not set up on this server yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopupInput"}}}}}},"/v1/billing/portal":{"post":{"summary":"Stripe customer portal (change or cancel the plan, payment method, invoices)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Portal page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedirectUrlResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Stripe error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Billing is not set up on this server yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalInput"}}}}}},"/v1/billing/webhook":{"post":{"summary":"Stripe webhook (Stripe-Signature verified; idempotent on the event id)","security":[],"responses":{"200":{"description":"Received"},"400":{"description":"Bad signature or payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Webhook secret not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/ai/messages":{"post":{"summary":"Hosted Anthropic Messages, paid from tokens, as used; X-Noa-Charged-Cents on a whole answer (stream: true relays the server-sent events and charges when they end)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"Anthropic's response body (or its server-sent events, with stream: true), unchanged (max_tokens is lowered first when the credit left does not cover it)"},"400":{"description":"Unknown model, or a field, tool or anthropic-beta header hosted AI does not forward","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Out of credit, or too little for the request's input (then with balanceCents left and about neededCents)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutOfCreditError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Body over 20 MB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many concurrent requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Anthropic unreachable or refused the server's key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Hosted AI is not set up on this server yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Anthropic Messages API request"}}}}}},"/v1/ai/responses":{"post":{"summary":"Hosted OpenAI Responses (Noa AI on OpenAI), paid from tokens, as used; X-Noa-Charged-Cents on a whole answer (stream: true relays the server-sent events and charges when they end)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"OpenAI's response body (or its server-sent events, with stream: true), unchanged (max_output_tokens is lowered first when the credit left does not cover it)"},"400":{"description":"Unknown model, store not false, or a field or built-in tool hosted AI does not forward","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Out of credit, or too little for the request's input (then with balanceCents left and about neededCents)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutOfCreditError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Body over 20 MB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many concurrent requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"OpenAI unreachable or refused the server's key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Hosted AI is not set up on this server yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"OpenAI Responses API request"}}}}}},"/v1/ai/jev":{"post":{"summary":"Hosted Jev (TypeSafe systemOne), paid from tokens, as used","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"TypeSafe's response body, unchanged"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Out of credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutOfCreditError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"TypeSafe unreachable or refused the server's key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Hosted Jev is not set up on this server yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JevProxyInput"}}}}}},"/v1/ai/transcribe":{"post":{"summary":"Voice input: a WAV clip to text (Workers AI speech-to-text), for plans with voice; paid from tokens, as used per audio second","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"The text (empty for silence), the seconds billed and the charge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscribeResponse"}}}},"400":{"description":"Not a PCM WAV file, or a bad query parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Out of credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutOfCreditError"}}}},"403":{"description":"The plan does not include voice input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRequiredError"}}}},"413":{"description":"Over 2097152 bytes or 60 seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit or too many concurrent requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Workers AI failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"parameters":[{"name":"language","in":"query","required":false,"schema":{"type":"string"},"description":"Language hint, e.g. en or ko (default: detected)"},{"name":"speech_ms","in":"query","required":false,"schema":{"type":"number"},"description":"Milliseconds of speech the client heard; 0 skips the model and costs nothing"},{"name":"context","in":"query","required":false,"schema":{"type":"string"},"description":"Text said just before the clip (last 300 characters are used)"},{"name":"words","in":"query","required":false,"schema":{"type":"string"},"description":"Words the speech is likely to hold (names, emails), comma-separated; spelled for the model (up to 400 characters)"}],"requestBody":{"required":true,"content":{"audio/wav":{"schema":{"type":"string","format":"binary"}}}}}},"/v1/ai/speak":{"post":{"summary":"Deepgram's voice: text said in an Aura voice (Workers AI), as MP3, for plans with voice; paid from tokens, as used per character","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"The MP3 (audio/mpeg); the charge in X-Noa-Charged-Cents"},"400":{"description":"Empty text, over 1000 characters, or not one of Deepgram's voices","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Out of credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutOfCreditError"}}}},"403":{"description":"The plan does not include voice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRequiredError"}}}},"429":{"description":"Rate limit or too many concurrent requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Workers AI failed (nothing is charged)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text","voice"],"properties":{"text":{"type":"string","minLength":1,"maxLength":1000},"voice":{"type":"string","enum":["thalia","andromeda","helena","apollo","arcas","aries","amalthea","asteria","athena","atlas","aurora","callista","cora","cordelia","delia","draco","electra","harmonia","hera","hermes","hyperion","iris","janus","juno","jupiter","luna","mars","minerva","neptune","odysseus","ophelia","orion","orpheus","pandora","phoebe","pluto","saturn","theia","vesta","zeus"]}}}}}}}},"/v1/ai/images":{"post":{"summary":"Image generation (OpenAI's image model): a description in, one PNG out; paid from tokens, as used per the model's tokens","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"The picture (PNG, base64) and the charge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateImageResponse"}}}},"400":{"description":"A bad body, or a description the model refused (OpenAI's error passes through)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Out of credit, or too little for the most the picture can cost","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutOfCreditError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit or too many concurrent requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"OpenAI unreachable or refused the server's key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Image generation is not set up on this server yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateImageRequest"}}}}}},"/v1/calls/info":{"get":{"summary":"Whether this server places phone calls: on Noa's line, and with the voice set up","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"What is set up","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneCallsInfo"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/calls":{"post":{"summary":"A phone call (Twilio, with OpenAI's Realtime voice on the line): placed now, answered with the call in state dialing; poll GET /v1/calls/{id} until it ended. The voice is paid from tokens, as used; on Noa's line (no `twilio` account given) each started minute too, at Twilio's price for the number","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"201":{"description":"The call, placed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneCallView"}}}},"400":{"description":"A bad body, or Twilio refused the call (its message: an unverified number on a trial account, a country not allowed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Out of credit, or (Noa's line) too little for the longest call's minutes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutOfCreditError"}}}},"403":{"description":"The plan does not include phone calls (feature calls: Plus, Pro), on either line; or agreement_required: the Terms and call terms (and, with record, the recording terms) not agreed to","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRequiredError"}}}},"409":{"description":"Another call of this account has not ended (error: call_open)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Twilio unreachable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Phone calls are not set up on this server yet; or, without an own Twilio account: Noa's phone line is not set up on this server yet. Calls can go out on your own Twilio account (Settings > AI > Calls)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartCallRequest"}}}}}},"/v1/calls/{id}":{"get":{"summary":"A call as it is now: its state, what was said, and what came of it","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"The call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneCallView"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No such call of this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/calls/{id}/live-ticket":{"post":{"summary":"A single-use ticket (a minute) for the call's live WebSocket, GET /v1/telephony/{id}/live with subprotocols `noa, bt.<ticket>`: the call, its words as they are said, its sound while listening; it takes guidance for the voice, hang up, and take over (the voice stops; the socket's microphone sound, sent as speak, talks on the call)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"The ticket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallLiveTicket"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No such call of this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/calls/{id}/recording":{"get":{"summary":"A recorded call's audio (audio/mpeg), once Twilio had it after the call (the call's recording.state is ready)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"The recording","content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No such call of this user, or it has no recording","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"The recording is still being saved (error: recording_pending)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"summary":"Deletes a call's recording from the account","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"The call (recording.state deleted)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneCallView"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No such call of this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/calls/{id}/hangup":{"post":{"summary":"Ends the call (cancels it while it rings)","description":"Roles: user (session token).","security":[{"bearer":[]}],"responses":{"200":{"description":"The call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneCallView"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Role not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No such call of this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/ai/realtime":{"get":{"summary":"Realtime voice: a WebSocket relay to OpenAI's Realtime API, for plans with voice; each response counts toward usage","description":"With `Upgrade: websocket`: authenticate with the subprotocols `noa, bt.<session token>` (the server selects `noa`). OpenAI Realtime events pass verbatim both ways; the server's own events are RealtimeErrorEvent. A refusal is one RealtimeErrorEvent followed by a close: 1000 normal, 1009 tooBig, 4401 auth, 4402 credit, 4403 plan, 4408 idle, 4409 concurrent, 4410 sessionLimit, 4500 upstream, 4503 unavailable. Limits: 30 min per session, 5 min idle, 2097152 bytes per client message, 1 session per user. Without Upgrade (bearer session token): the same checks as JSON. docs/BILLING-CONTRACT.md has the full contract.","security":[{"bearer":[]}],"parameters":[{"name":"session","in":"query","required":false,"schema":{"type":"string"},"description":"Chat or run id recorded with the usage"},{"name":"takeover","in":"query","required":false,"schema":{"type":"string","enum":["1"]},"description":"With Upgrade: end this user's open session (its client gets session_replaced and close 4409) and take its place"},{"name":"tier","in":"query","required":false,"schema":{"type":"string","enum":["mini","gemini"]},"description":"mini: the smaller model (REALTIME_MINI_MODEL, the Realtime mini voice engine), billed at its prices. gemini: Gemini Live (GEMINI_LIVE_MODEL with Gemini's live transcription, the Gemini voice engine) behind the same events, billed at Gemini's prices; 503 realtime_unavailable without GEMINI_API_KEY"}],"responses":{"101":{"description":"WebSocket (also for refusals: see the description)"},"200":{"description":"Without Upgrade: a session would be accepted ({ ok, model, limits })"},"401":{"description":"Not signed in (session token missing, invalid or expired)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Out of credit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutOfCreditError"}}}},"403":{"description":"The plan does not include voice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRequiredError"}}}},"409":{"description":"Another realtime session of this user is open (error: session_open)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Not configured (error: realtime_unavailable): Realtime voice is not set up on this server yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/openapi.json":{"get":{"summary":"This document","security":[],"responses":{"200":{"description":"OpenAPI 3.1 document"}}}}}}