{"components":{"schemas":{"Accepted":{"description":"收下了几条。被形状挡掉的不算在里面，但整批不会因为一条坏的就全退。","properties":{"accepted":{"format":"uint","minimum":0,"type":"integer"}},"required":["accepted"],"type":"object"},"Access":{"description":"谁能玩（REWRITE §3.3）。三档都在门禁页上完成，玩家不需要注册。","oneOf":[{"const":"link","description":"任何拿到链接的人。默认。","type":"string"},{"const":"password","description":"要口令。","type":"string"},{"const":"invite","description":"只允许邀请名单（链接带一次性令牌，或玩家填邀请码）。","type":"string"}]},"AnonSessionResponse":{"properties":{"expires_at":{"description":"RFC 3339。到期后令牌与它创建的作品一起失效。","type":"string"},"token":{"type":"string"}},"required":["token","expires_at"],"type":"object"},"Boost":{"properties":{"created_at":{"type":"string"},"ends_at":{"type":["string","null"]},"granted":{"default":false,"description":"运营者赠送的，不是买的。广场上的标签一样是「推广」，不区分。","type":"boolean"},"id":{"format":"int64","type":"integer"},"kind":{"$ref":"#/components/schemas/BoostKind"},"order_id":{"description":"支付订单号；赠送的没有。","type":["string","null"]},"reason":{"description":"人工没放行时给开发者看的理由；只在 `Rejected` 时有。","type":["string","null"]},"slug":{"type":"string"},"starts_at":{"description":"RFC 3339。`Pending` 时是排到的那一天；周报那一项是那一期发出的时间。","type":"string"},"status":{"$ref":"#/components/schemas/BoostStatus"}},"required":["id","slug","kind","status","starts_at","created_at"],"type":"object"},"BoostKind":{"description":"两个 SKU 加一个附加项（DESIGN §6）。","oneOf":[{"const":"days3","description":"推广 3 天。","type":"string"},{"const":"days7","description":"推广 7 天。","type":"string"},{"const":"digest","description":"进本周周报（不占广场推广位）。","type":"string"}]},"BoostList":{"items":{"$ref":"#/components/schemas/Boost"},"type":"array"},"BoostStatus":{"oneOf":[{"const":"pending","description":"付了钱（或被赠送），等人工看一眼。","type":"string"},{"const":"live","description":"在推广位上（或已排入本周周报）。","type":"string"},{"const":"ended","description":"窗口过了。","type":"string"},{"const":"rejected","description":"人工没放行。付了钱的全额退。","type":"string"}]},"Capabilities":{"properties":{"email":{"default":false,"description":"配了发信实现（`resend` / `smtp` / 本机的 `log`）。为假时门禁页与广场不出现邮箱输入框。","type":"boolean"},"generated_at":{"default":"","type":"string"},"push_public_key":{"description":"Web Push 的 VAPID 公钥（base64url）。没有就不出现「用浏览器通知」按钮。","type":["string","null"]},"schema":{"default":0,"format":"uint32","minimum":0,"type":"integer"}},"type":"object"},"ChapterEntry":{"description":"连载小说的有序章节（DESIGN §3.17）。","properties":{"hash":{"description":"章节生成的安全展示产物哈希。","type":"string"},"id":{"description":"稳定章节标识（如 `c1`、`c2`），在作品生命周期内不可变。","type":"string"},"path":{"description":"原稿文件在 `files` 里的路径（如 `01-wake.md`）。","type":"string"},"size":{"format":"uint64","minimum":0,"type":"integer"},"title":{"description":"章节标题（如「第一章：沉睡的三百年」）。","type":"string"}},"required":["id","title","path","hash","size"],"type":"object"},"Collection":{"properties":{"blocked_slugs":{"items":{"type":"string"},"type":"array"},"closes_at":{"type":["string","null"]},"created_at":{"type":"string"},"creator":{"type":"string"},"entries":{"default":[],"items":{"$ref":"#/components/schemas/CollectionEntry"},"type":"array"},"hidden":{"type":"boolean"},"kind":{"$ref":"#/components/schemas/CollectionKind"},"prompt":{"type":"string"},"public":{"type":"boolean"},"rules":{"type":"string"},"slug":{"type":"string"},"summary":{"type":"string"},"title":{"type":"string"},"updated_at":{"type":"string"}},"required":["slug","title","summary","kind","prompt","rules","public","hidden","creator","created_at","updated_at"],"type":"object"},"CollectionDraft":{"properties":{"closes_at":{"default":null,"type":["string","null"]},"kind":{"$ref":"#/components/schemas/CollectionKind","default":"collection"},"prompt":{"default":"","type":"string"},"public":{"default":false,"type":"boolean"},"rules":{"default":"","type":"string"},"slug":{"default":null,"type":["string","null"]},"summary":{"default":"","type":"string"},"title":{"type":"string"}},"required":["title"],"type":"object"},"CollectionEntry":{"properties":{"note":{"default":"","type":"string"},"slug":{"type":"string"},"submitted_at":{"type":"string"},"submitted_version":{"format":"uint32","minimum":0,"type":"integer"},"title":{"type":"string"}},"required":["slug","title","submitted_version","submitted_at"],"type":"object"},"CollectionKind":{"enum":["collection","challenge"],"type":"string"},"CollectionList":{"items":{"$ref":"#/components/schemas/Collection"},"type":"array"},"CommitUploadResponse":{"properties":{"expires_at":{"type":["string","null"]},"slug":{"type":"string"},"url":{"type":"string"},"version":{"format":"uint32","minimum":0,"type":"integer"}},"required":["slug","version","url"],"type":"object"},"ConfirmRequest":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"},"ConfirmResponse":{"properties":{"me":{"$ref":"#/components/schemas/MeView"},"me_token":{"description":"种进 `pt_me` 的值。长期有效，撤销靠退订。","type":"string"}},"required":["me_token","me"],"type":"object"},"Cover":{"description":"封面（DESIGN §3.3）。**不在 `files` 里**：它不是开发者目录的一部分，是清单单独的一条引用，\n边缘在 `/_playtest/cover` 提供。这样开发者目录的字节一个不多一个不少（§3.7 最后一条）。","properties":{"hash":{"type":"string"},"mime":{"description":"`image/png`、`image/jpeg` 或 `image/webp`，见 [`COVER_MIMES`]。","type":"string"},"size":{"format":"uint64","minimum":0,"type":"integer"}},"required":["hash","size","mime"],"type":"object"},"CreateSiteRequest":{"properties":{"slug":{"description":"想要的 slug；匿名用户忽略此项，随机分配。","type":["string","null"]},"title":{"description":"作品名；没给就在上传时用目录名。","type":["string","null"]}},"type":"object"},"DeliveryMode":{"description":"这个作品的字节从哪来（REWRITE §2.1「交付」）。切换对玩家透明，同一个 slug。","oneOf":[{"const":"upload","description":"上传的静态目录，按清单分发。","type":"string"},{"const":"tunnel","description":"开发者机器上跑着的进程，经隧道转发。","type":"string"},{"const":"hybrid","description":"清单里有的从边缘给，清单外的走隧道到后端（`--backend`）。","type":"string"}]},"DeviceLoginPoll":{"properties":{"device_code":{"type":"string"}},"required":["device_code"],"type":"object"},"DeviceLoginStart":{"description":"平台设备授权的第一步：CLI 显示 `user_code` 和 `verification_uri`，然后拿 `device_code` 轮询。","properties":{"device_code":{"type":"string"},"expires_in":{"description":"这个码还能用几秒。","format":"uint32","minimum":0,"type":"integer"},"interval":{"description":"两次轮询之间至少隔几秒；GitHub 说 `slow_down` 时控制面会把它加大。","format":"uint32","minimum":0,"type":"integer"},"user_code":{"description":"给人在浏览器里输入的那串，形如 `WDJB-MJHT`。","type":"string"},"verification_uri":{"description":"一般是 `https://github.com/login/device`。","type":"string"}},"required":["device_code","user_code","verification_uri","expires_in","interval"],"type":"object"},"EntryDraft":{"properties":{"note":{"default":"","type":"string"},"slug":{"type":"string"}},"required":["slug"],"type":"object"},"ErrorBody":{"description":"所有非 2xx 响应的体。`code` 给程序判断，`message` 给人看（英文，第一次用的人看得懂）。","properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string"}},"required":["code","message"],"type":"object"},"ErrorCode":{"oneOf":[{"enum":["unauthorized","token_expired","not_found","internal"],"type":"string"},{"const":"invalid","description":"请求体不合法，`message` 说明哪一项。","type":"string"},{"const":"quota_exceeded","description":"超出档位配额（体积、文件数、作品数），`message` 说明数字。体积与文件数用 413，作品数用 403。","type":"string"},{"const":"hash_mismatch","description":"上传的字节哈希与 URL 里的哈希不一致。","type":"string"},{"const":"blobs_missing","description":"提交时还有哈希没传上来，`message` 列出前几个。","type":"string"},{"const":"slug_unavailable","description":"slug 被占或保留。","type":"string"},{"const":"tunnel_replaced","description":"这条隧道令牌对应的会话已被同一作品更新的隧道挤掉（DESIGN §4.3「新的挤掉旧的」）；\n拿着它重连会一直得到这个码，CLI 应退出而不是重试。","type":"string"},{"const":"backend_offline","description":"混合模式（`playtest ./dist --backend 3000`）里走隧道的那几条路径，在开发者的电脑\n不在线时由边缘回这个码（503）。页面本身照常能开——静态文件是上传过的。\n对端是游戏里的 `fetch`，不是浏览器导航，所以是 JSON 不是一页 HTML。","type":"string"},{"const":"login_unavailable","description":"这个控制面没配 GitHub 登录（自托管、或本机开发没设 client_id）。`message` 说明匿名链接照常能用。","type":"string"},{"const":"login_failed","description":"GitHub 那边没走完：设备码过期、用户在授权页点了拒绝、授权码用过了或 state 对不上。\n重新 `playtest login` 一次即可；`message` 会说是哪一种。","type":"string"}]},"ErrorSummary":{"description":"错误按 fingerprint 归堆之后的样子。","properties":{"distinct":{"description":"去重之后有几种。","format":"uint32","minimum":0,"type":"integer"},"top":{"description":"撞得最多的几条，最多 [`TOP_ERRORS`] 条。","items":{"$ref":"#/components/schemas/ErrorTally"},"type":"array"},"total":{"description":"一共撞了几次。","format":"uint32","minimum":0,"type":"integer"}},"required":["distinct","total"],"type":"object"},"ErrorTally":{"properties":{"count":{"format":"uint32","minimum":0,"type":"integer"},"fingerprint":{"description":"SDK 报上来的 `name`，通常是「错误类型 + 出错的那一行」。","type":"string"}},"required":["fingerprint","count"],"type":"object"},"Event":{"properties":{"data":true,"kind":{"description":"[`kind`] 里的一个。","type":"string"},"name":{"type":["string","null"]},"ts":{"description":"客户端的 RFC 3339 时间。客户端的钟不可信，服务端只在它落在合理窗口里时采信。","type":"string"}},"required":["ts","kind"],"type":"object"},"EventBatch":{"properties":{"events":{"default":[],"items":{"$ref":"#/components/schemas/Event"},"type":"array"},"session":{"description":"会话 id，来自 [`Me::sid`]；SDK 拿不到时是它自己在 localStorage 里生成的匿名 id。","type":"string"},"slug":{"type":"string"}},"required":["session","slug"],"type":"object"},"FeedbackAccepted":{"properties":{"remaining":{"description":"这个会话还能再提几条。","format":"uint32","minimum":0,"type":"integer"}},"required":["remaining"],"type":"object"},"FeedbackItem":{"description":"一条反馈：一句话 + 让这句话能被定位的上下文（DESIGN §3.4）。","properties":{"browser":{"type":["string","null"]},"device":{"type":["string","null"]},"id":{"format":"int64","type":"integer"},"name":{"description":"说这句话的人在门禁页留的名字（DESIGN §3.5）。","type":["string","null"]},"public":{"default":false,"description":"这一条在门禁页上公开着（作品开了公开反馈、且开发者没把它单独藏起来）。","type":"boolean"},"screenshot_hash":{"description":"截图 v0.2 才做，现在永远是 `None`（DESIGN §3.5 说的「附截图」还没实现）。","type":["string","null"]},"seconds_in":{"description":"进入多久说的这句话。","format":"uint32","minimum":0,"type":["integer","null"]},"session_id":{"type":"string"},"status":{"$ref":"#/components/schemas/FeedbackStatus"},"text":{"type":"string"},"ts":{"type":"string"},"version":{"format":"uint32","minimum":0,"type":"integer"}},"required":["id","session_id","version","ts","text","status"],"type":"object"},"FeedbackList":{"properties":{"items":{"description":"时间倒序，最新的在最前面。","items":{"$ref":"#/components/schemas/FeedbackItem"},"type":"array"},"slug":{"type":"string"}},"required":["slug","items"],"type":"object"},"FeedbackRequest":{"properties":{"seconds_in":{"description":"玩家进来多少秒了。SDK 自己算，用来在点名册里显示「进入 47 秒」。","format":"uint32","minimum":0,"type":["integer","null"]},"session":{"type":"string"},"slug":{"type":"string"},"source":{"description":"反馈来源：如 \"gate\"（门禁页交流舱）。SDK 发送时为 None。","type":["string","null"]},"text":{"type":"string"}},"required":["session","slug","text"],"type":"object"},"FeedbackStatus":{"oneOf":[{"const":"new","description":"还没看。","type":"string"},{"const":"seen","description":"看过了。","type":"string"},{"const":"done","description":"处理完了。","type":"string"}]},"FileEntry":{"description":"清单里的一个文件。","properties":{"hash":{"description":"内容哈希，见 [`crate::hash`]。","type":"string"},"path":{"description":"相对于上传目录的路径。正斜杠分隔，不以斜杠开头，没有 `.` / `..` / 空段。\n例：`index.html`、`Build/game.wasm.br`。","type":"string"},"size":{"format":"uint64","minimum":0,"type":"integer"}},"required":["path","hash","size"],"type":"object"},"FollowChannel":{"oneOf":[{"description":"要双重确认：先发确认信，点了才算关注。","properties":{"email":{"type":"string"},"kind":{"const":"email","type":"string"}},"required":["kind","email"],"type":"object"},{"description":"浏览器已经问过用户了，直接生效。","properties":{"kind":{"const":"push","type":"string"},"subscription":{"$ref":"#/components/schemas/PushSubscription"}},"required":["kind","subscription"],"type":"object"},{"description":"这台设备已经有 `pt_me`：一下点击，不再要邮箱。","properties":{"kind":{"const":"me","type":"string"},"me_token":{"type":"string"}},"required":["kind","me_token"],"type":"object"}]},"FollowRequest":{"properties":{"channel":{"$ref":"#/components/schemas/FollowChannel"},"from":{"description":"见 [`form::FROM`]。","type":["string","null"]},"target":{"$ref":"#/components/schemas/FollowTarget"}},"required":["target","channel"],"type":"object"},"FollowResponse":{"oneOf":[{"description":"邮箱路径：确认信已经在路上（或在队列里——邮件服务商不可用时也是这个，玩家不该看到报错）。","properties":{"status":{"const":"confirm_sent","type":"string"}},"required":["status"],"type":"object"},{"description":"推送或 `pt_me` 路径：已经生效。","properties":{"status":{"const":"subscribed","type":"string"}},"required":["status"],"type":"object"},{"description":"早就关注着了。","properties":{"status":{"const":"already_following","type":"string"}},"required":["status"],"type":"object"}]},"FollowTarget":{"description":"一个作品，或广场本身。","oneOf":[{"properties":{"kind":{"const":"site","type":"string"},"slug":{"type":"string"}},"required":["kind","slug"],"type":"object"},{"properties":{"kind":{"const":"collection","type":"string"},"slug":{"type":"string"}},"required":["kind","slug"],"type":"object"},{"properties":{"kind":{"const":"plaza","type":"string"}},"required":["kind"],"type":"object"}]},"FollowView":{"properties":{"since":{"description":"RFC 3339。","type":"string"},"target":{"$ref":"#/components/schemas/FollowTarget"},"title":{"description":"作品名；广场那一项没有。","type":["string","null"]},"url":{"description":"作品链接；广场那一项没有。","type":["string","null"]}},"required":["target","since"],"type":"object"},"GateMode":{"description":"门禁页出现的策略（DESIGN §3.3）。","oneOf":[{"const":"once","description":"一次点开在 24 小时内不再重复出现。默认。","type":"string"},{"const":"always","description":"每次都出，正式测试要认人。","type":"string"},{"const":"never","description":"不出。","type":"string"}]},"GrantBoostRequest":{"properties":{"kind":{"$ref":"#/components/schemas/BoostKind"},"review":{"default":false,"description":"赠送的默认直接 `Live`（运营者自己就是审核的人）；传 `true` 让它走 `Pending`。","type":"boolean"},"slug":{"type":"string"},"starts_at":{"description":"不给就是「最早能排上的那一天」。RFC 3339。","type":["string","null"]}},"required":["slug","kind"],"type":"object"},"JobList":{"items":{"$ref":"#/components/schemas/JobStatus"},"type":"array"},"JobStatus":{"description":"控制面一件后台活的现状（清过期作品、回收 blob、周报、发通知、重写广场与 `live.json`）。","properties":{"every_secs":{"format":"uint64","minimum":0,"type":"integer"},"failures":{"format":"uint64","minimum":0,"type":"integer"},"last_count":{"format":"uint","minimum":0,"type":["integer","null"]},"last_error":{"type":["string","null"]},"last_ok":{"type":["boolean","null"]},"last_run_at":{"description":"RFC 3339；还没跑过是 `None`。","type":["string","null"]},"name":{"type":"string"},"runs":{"format":"uint64","minimum":0,"type":"integer"}},"required":["name","every_secs","runs","failures"],"type":"object"},"Limits":{"description":"一个档位的全部上限。边缘拿到的是这一份（跟着 `current.json` 下来），不认识档位这个词。","properties":{"active_projects":{"format":"uint32","minimum":0,"type":"integer"},"concurrent_players":{"description":"同一个作品同时在玩的人数上限。","format":"uint32","minimum":0,"type":"integer"},"hourly_bytes":{"description":"每作品每小时出网上限，边缘本地判定、不回源。\n\n月配额挡长期滥用，每小时熔断挡分钟级的 DDoS——SIMMER.io 2025-04 是被分钟级账单\n打死的，月配额在那个尺度上完全无效，它一个月才结算一次。两个尺度缺一不可。","format":"uint64","minimum":0,"type":"integer"},"kept_versions":{"format":"uint32","minimum":0,"type":"integer"},"max_version_bytes":{"description":"一个版本所有文件之和的上限。","format":"uint64","minimum":0,"type":"integer"},"results_days":{"description":"会话、事件、反馈保留多少天。","format":"uint32","minimum":0,"type":"integer"},"traffic_bytes":{"description":"窗口内出网字节上限。到顶硬停，不产生账单。","format":"uint64","minimum":0,"type":"integer"},"traffic_window":{"$ref":"#/components/schemas/TrafficWindow"}},"required":["traffic_bytes","traffic_window","hourly_bytes","active_projects","kept_versions","concurrent_players","results_days","max_version_bytes"],"type":"object"},"Listing":{"description":"一个作品在广场（DESIGN §3.8）上的状态。","properties":{"boost":{"anyOf":[{"$ref":"#/components/schemas/Boost"},{"type":"null"}],"description":"推广状态（DESIGN §3.11）：`None` 没有；否则是当前或排队中的那一段。"},"community_url":{"description":"开发者的群（`--community`）。","type":["string","null"]},"feedback_public":{"default":false,"description":"「让玩家看到彼此的反馈」（DESIGN §3.5）。","type":"boolean"},"followers":{"default":0,"description":"关注这个作品的人数（DESIGN §3.6）。开发者只看到数字。","format":"uint32","minimum":0,"type":"integer"},"has_cover":{"default":false,"description":"最新版本有没有封面。","type":"boolean"},"hidden":{"default":false,"description":"被举报到阈值、或我们手工撤下了：`public` 仍是开发者的意愿，但广场上不出现。\n控制台要把这件事告诉开发者，不能让他以为自己在广场上。","type":"boolean"},"joined":{"default":0,"format":"uint32","minimum":0,"type":"integer"},"public":{"default":false,"description":"开发者勾了「放到广场上」。默认不公开。","type":"boolean"},"seats":{"description":"想找几位试玩者（`--seats`，DESIGN §3.3）；`joined` 是留了名字的人数。","format":"uint32","minimum":0,"type":["integer","null"]},"seek_note":{"description":"想让来的人重点看什么，最多 [`crate::limits::MAX_SEEK_NOTE_CHARS`] 字。","type":["string","null"]},"seeking":{"default":false,"description":"「正在找人测」。只在 `public` 时有意义。","type":"boolean"},"summary":{"description":"一句话介绍，最多 [`crate::limits::MAX_SUMMARY_CHARS`] 字。随最新版本的清单走。","type":["string","null"]}},"type":"object"},"LoginPollResponse":{"description":"轮询的结果。`pending` 继续等；`ok` 里就是登录结果。","oneOf":[{"description":"人还没在浏览器里输完码。`interval` 是下一次至少隔几秒。","properties":{"interval":{"format":"uint32","minimum":0,"type":"integer"},"status":{"const":"pending","type":"string"}},"required":["status","interval"],"type":"object"},{"$ref":"#/components/schemas/LoginResponse","properties":{"status":{"const":"ok","type":"string"}},"required":["status"],"type":"object"}]},"LoginResponse":{"description":"登录成功。`token` 长期有效，之后所有请求都带它。","properties":{"display_name":{"description":"玩家在门禁页和广场上看到的名字：GitHub 上的显示名，没有就是用户名。","type":"string"},"login":{"description":"GitHub 用户名（不带 @）。","type":"string"},"migrated_sites":{"description":"这次顺带归入账号的匿名作品数（请求带了匿名令牌才会大于 0）。","format":"uint32","minimum":0,"type":"integer"},"token":{"type":"string"}},"required":["token","login","display_name","migrated_sites"],"type":"object"},"Me":{"description":"`GET /v1/me`。","properties":{"avatar_url":{"description":"GitHub 头像；匿名没有。广场卡片与门禁页上显示（DESIGN §3.9）。","type":["string","null"]},"display_name":{"type":"string"},"expires_at":{"description":"匿名身份的到期时间；登录用户没有。","type":["string","null"]},"kind":{"description":"`anon` 或 `github`。","type":"string"},"login":{"description":"GitHub 用户名；匿名没有。","type":["string","null"]}},"required":["kind","display_name"],"type":"object"},"MeRequest":{"properties":{"me_token":{"type":"string"}},"required":["me_token"],"type":"object"},"MeView":{"description":"关注页（DESIGN §3.10）：一个抽屉，不是一个 profile。","properties":{"email_masked":{"description":"打码显示的邮箱，如 `z***@example.com`；只用浏览器通知、没留邮箱的人没有。","type":["string","null"]},"follows":{"default":[],"items":{"$ref":"#/components/schemas/FollowView"},"type":"array"},"push":{"default":false,"description":"这个人开了浏览器通知。","type":"boolean"}},"type":"object"},"ModerateCollectionRequest":{"properties":{"hidden":{"type":"boolean"}},"required":["hidden"],"type":"object"},"NotificationQueue":{"properties":{"dead":{"description":"进了死信的（DESIGN §4.10：失败三次）。","format":"uint32","minimum":0,"type":"integer"},"failed_24h":{"format":"uint32","minimum":0,"type":"integer"},"next_digest_at":{"description":"下一期周报什么时候发，RFC 3339。","type":["string","null"]},"pending":{"format":"uint32","minimum":0,"type":"integer"},"sent_24h":{"format":"uint32","minimum":0,"type":"integer"}},"required":["pending","sent_24h","failed_24h","dead"],"type":"object"},"Owner":{"description":"作品的主人。匿名开发者也是一个 owner，只是没有名字和头像。","properties":{"avatar_url":{"type":["string","null"]},"display_name":{"description":"门禁页与邀请卡上「某某 邀请你」的那个某某。匿名是「匿名开发者」。","type":"string"},"kind":{"$ref":"#/components/schemas/OwnerKind","default":"anon"},"login":{"description":"GitHub 用户名；匿名没有。","type":["string","null"]}},"required":["display_name"],"type":"object"},"OwnerKind":{"enum":["anon","github"],"type":"string"},"Plan":{"description":"三档。匿名也是一档——它有自己的上限，不是「Free 的特例」。","oneOf":[{"const":"anon","description":"没登录就发的链接：24 小时，额度更紧。","type":"string"},{"const":"free","description":"GitHub 登录之后。","type":"string"},{"const":"pro","description":"$15 / 月或 $150 / 年。","type":"string"}]},"Plaza":{"properties":{"club_followers":{"default":0,"description":"关注广场本身的人数（DESIGN §3.6 的周报收件人）。页面上不写，给周报用。","format":"uint32","minimum":0,"type":"integer"},"collections":{"default":[],"items":{"$ref":"#/components/schemas/Collection"},"type":"array"},"generated_at":{"description":"RFC 3339，这份是什么时候整理的。排查用，页面上不显示。","type":"string"},"items":{"description":"已经按广场的默认顺序排好：推广中的在最前（最多 [`crate::boost::MAX_SLOTS`] 个），\n然后正在找人测的，其余按最近更新。边缘按这个顺序铺一面网格，不再分段。","items":{"$ref":"#/components/schemas/ProjectCard"},"type":"array"},"schema":{"format":"uint32","minimum":0,"type":"integer"}},"required":["schema","generated_at","items"],"type":"object"},"PrepareUploadRequest":{"properties":{"chapters":{"description":"连载小说的章节列表（DESIGN §3.17）。","items":{"$ref":"#/components/schemas/ChapterEntry"},"type":"array"},"cover":{"anyOf":[{"$ref":"#/components/schemas/Cover"},{"type":"null"}],"description":"封面。CLI 先把它当普通 blob 传上来（`PUT /v1/blobs/{hash}`），提交时服务端检查它在不在。\n没给就沿用上一版的封面。"},"engine":{"description":"CLI 上传时认出来的引擎，见 [`crate::manifest::Manifest::engine`]。认不出来就没有。","type":["string","null"]},"entry":{"description":"文章原稿或视频文件在 `files` 里的路径；网页没有固定入口。","type":["string","null"]},"files":{"items":{"$ref":"#/components/schemas/FileEntry"},"type":"array"},"gate":{"$ref":"#/components/schemas/GateMode","default":"once"},"isolated":{"default":false,"type":"boolean"},"kind":{"$ref":"#/components/schemas/WorkKind","default":"web","description":"单文件发布明确告诉控制面怎样验证和展示；旧 CLI 没有此字段，按网页处理。"},"note":{"type":["string","null"]},"spa":{"default":false,"type":"boolean"},"summary":{"description":"一句话介绍（DESIGN §3.8）。没给就沿用这个作品上一版的。","type":["string","null"]},"title":{"type":["string","null"]}},"required":["files"],"type":"object"},"PrepareUploadResponse":{"properties":{"missing":{"description":"服务端没有的哈希，CLI 只传这些。去重后、按清单顺序。","items":{"type":"string"},"type":"array"},"missing_bytes":{"description":"这次要传的字节数之和，给进度条用。","format":"uint64","minimum":0,"type":"integer"},"upload_id":{"type":"string"}},"required":["upload_id","missing","missing_bytes"],"type":"object"},"Project":{"description":"一个作品的全部事实。控制面从库里读一次、拼一次，其余都是投影。\n\n六组属性的分法见 REWRITE §2.1：身份、交付、呈现、访问、上架、社会。","properties":{"access":{"$ref":"#/components/schemas/Access","default":"link"},"boost":{"anyOf":[{"$ref":"#/components/schemas/Boost"},{"type":"null"}],"description":"当前或排队中的推广（REWRITE §4.1）。"},"community_url":{"description":"开发者的群（`--community`）。去哪是开发者的事，我们对去向不承诺。","type":["string","null"]},"cover_hash":{"description":"最新版本封面的内容哈希；没有封面就没有。\n\n存哈希而不是一个 `bool`，是为了让封面地址带上 `?v=`：换了封面地址就变，\n边缘那边才能放心让浏览器缓存一天。","type":["string","null"]},"created_at":{"type":"string"},"current_version":{"description":"还没上传过版本时为 `None`。","format":"uint32","minimum":0,"type":["integer","null"]},"engine":{"description":"上传时认出来的引擎，小写标识符；认不出来就没有。","type":["string","null"]},"expires_at":{"description":"匿名作品、或开发者设了 `--ttl` 的作品的到期时间。长期作品没有。","type":["string","null"]},"feedback_public":{"default":false,"description":"「让玩家看到彼此的反馈」。","type":"boolean"},"followers":{"default":0,"description":"关注这个作品的人数。开发者只看到数字，看不到名单（REWRITE §3.3）。","format":"uint32","minimum":0,"type":"integer"},"gate":{"$ref":"#/components/schemas/GateMode","default":"once"},"hidden":{"default":false,"description":"被举报到阈值、或运营者手工撤下了。`public` 仍是开发者的意愿，但广场上不出现——\n控制台要如实告诉他这件事，不能让他以为自己在广场上。","type":"boolean"},"isolated":{"default":false,"type":"boolean"},"joined":{"default":0,"description":"已加入 = 点「开始」时留了名字的去重会话数（REWRITE §3.3）。","format":"uint32","minimum":0,"type":"integer"},"kind":{"$ref":"#/components/schemas/WorkKind","default":"web"},"last_seen":{"description":"隧道上一次在线是什么时候，RFC 3339。离线页上那一行。","type":["string","null"]},"mode":{"$ref":"#/components/schemas/DeliveryMode","default":"upload"},"note":{"description":"**这一版**的话（`--note`）：改了什么、想让人看什么。跟着版本走。\n\n一个字段不是两个：对开发者「这版改了什么」和「想让人看什么」是同一句话，\n对玩家在门禁页上、在通知正文里、在广场卡上看到的也是同一句话（REWRITE §9.6）。","type":["string","null"]},"owner":{"$ref":"#/components/schemas/Owner"},"plan":{"$ref":"#/components/schemas/Plan","default":"anon","description":"跟着 owner 走，决定 [`crate::plan::Limits`]。"},"players":{"default":0,"description":"[`crate::plaza::PLAYERS_WINDOW_DAYS`] 天内点了「开始」的去重人数。","format":"uint32","minimum":0,"type":"integer"},"public":{"default":false,"description":"开发者勾了「放到广场上」。默认不公开。","type":"boolean"},"public_feedback":{"description":"公开的反馈里最近几条，新的在前。`feedback_public` 为假时为空。","items":{"$ref":"#/components/schemas/PublicNote"},"type":"array"},"seats":{"description":"想找几位试玩者（`--seats`）。","format":"uint32","minimum":0,"type":["integer","null"]},"seeking":{"default":false,"description":"「正在找人测」。只在 `public` 时有意义。","type":"boolean"},"slug":{"type":"string"},"spa":{"default":false,"type":"boolean"},"summary":{"description":"一句话介绍，最多 [`crate::limits::MAX_SUMMARY_CHARS`] 字。跟着作品走，换版本不变。","type":["string","null"]},"title":{"type":"string"},"tunnel_online":{"default":false,"description":"隧道此刻连着（只有边缘知道，控制面从边缘的上报里得到）。","type":"boolean"},"updated_at":{"description":"最近一次提交版本的时间，RFC 3339。广场默认顺序的依据。","type":["string","null"]},"url":{"description":"玩家点开的完整链接，例如 `https://brisk-otter-41.playtest.run`。","type":"string"}},"required":["slug","url","owner","created_at","title"],"type":"object"},"ProjectCard":{"description":"广场那面墙上、控制台作品墙上的一张卡（REWRITE §3.3）。\n\n两处用同一个类型不是省事：开发者从广场点进控制台，看到的应该是同一张卡、\n同一件事实。各写一份的话，同一个作品在两个地方会说不同的话。","properties":{"avatar_url":{"type":["string","null"]},"boosted":{"default":false,"description":"在推广位上。渲染时永远标「推广」。","type":"boolean"},"cover_hash":{"description":"封面的内容哈希；没有就排一张字卡。见 [`ProjectCard::cover_url`]。","type":["string","null"]},"developer":{"type":"string"},"engine":{"type":["string","null"]},"expires_at":{"type":["string","null"]},"followers":{"default":0,"format":"uint32","minimum":0,"type":"integer"},"is_game":{"description":"说「试玩」还是「体验」。控制面算好，边缘不再判。","type":"boolean"},"joined":{"default":0,"format":"uint32","minimum":0,"type":"integer"},"kind":{"$ref":"#/components/schemas/WorkKind","default":"web"},"note":{"description":"这一版的话。正在找人测时卡上写它（「这次想测：…」），否则写 `summary`——\n哪一句由 [`ProjectCard::blurb`] 一处决定。","type":["string","null"]},"players":{"format":"uint32","minimum":0,"type":"integer"},"seats":{"format":"uint32","minimum":0,"type":["integer","null"]},"seeking":{"default":false,"type":"boolean"},"slug":{"type":"string"},"summary":{"type":["string","null"]},"title":{"type":"string"},"updated_at":{"description":"RFC 3339。","type":"string"},"url":{"type":"string"},"version":{"format":"uint32","minimum":0,"type":"integer"}},"required":["slug","url","title","developer","is_game","version","updated_at","players"],"type":"object"},"ProjectLive":{"description":"一个作品**会变的那些**：控制面写、边缘只读、短缓存。\n\n清单不可变、每版一份；但门禁页上还有几样东西随时在变，又不值得为它们发一个版本。\n控制面挂了门禁页照常出，只是数字旧几分钟——所以没有这份文件的作品一律按\n[`ProjectLive::default`] 解析：没名额、没人关注、没有群、反馈不公开，\n门禁页上对应的那几行不出现，不报错。","properties":{"avatar_url":{"type":["string","null"]},"community_url":{"type":["string","null"]},"feedback_public":{"default":false,"type":"boolean"},"followers":{"default":0,"format":"uint32","minimum":0,"type":"integer"},"generated_at":{"default":"","description":"RFC 3339，这份是什么时候整理的。","type":"string"},"joined":{"default":0,"format":"uint32","minimum":0,"type":"integer"},"listed":{"default":false,"description":"现在在广场上。门禁页的「分享」只给这样的作品。","type":"boolean"},"public_feedback":{"description":"最多 [`PUBLIC_NOTES_ON_GATE`] 条，新的在前。","items":{"$ref":"#/components/schemas/PublicNote"},"type":"array"},"schema":{"default":0,"format":"uint32","minimum":0,"type":"integer"},"seats":{"format":"uint32","minimum":0,"type":["integer","null"]},"seeking":{"default":false,"type":"boolean"},"slug":{"default":"","type":"string"}},"type":"object"},"PublicNote":{"description":"门禁页上显示的一条公开反馈。开发者看到的那份（`results::FeedbackItem`）字段多得多，\n带设备、浏览器、会话号——那些不该给玩家，所以这里是裁剪过的投影，不是同一个类型。","properties":{"at":{"description":"RFC 3339。","type":"string"},"name":{"description":"留下的名字；没留就显示「一位试玩者」，由渲染方决定，这里是 `None`。","type":["string","null"]},"text":{"type":"string"},"version":{"format":"uint32","minimum":0,"type":"integer"}},"required":["text","version","at"],"type":"object"},"PushKeys":{"properties":{"auth":{"type":"string"},"p256dh":{"type":"string"}},"required":["p256dh","auth"],"type":"object"},"PushSubscription":{"description":"浏览器 `PushSubscription.toJSON()` 的形状。原样存、原样用，我们不解读里面的字节。","properties":{"endpoint":{"type":"string"},"keys":{"$ref":"#/components/schemas/PushKeys"}},"required":["endpoint","keys"],"type":"object"},"ReviewBoostRequest":{"properties":{"approve":{"type":"boolean"},"reason":{"type":["string","null"]}},"required":["approve"],"type":"object"},"RosterSort":{"description":"点名册的排法。","oneOf":[{"const":"dwell","description":"停留最短的排最前面——排在最前面的人就是你要看的人（DESIGN §3.4）。默认。","type":"string"},{"const":"time","description":"最近打开的排最前面。","type":"string"}]},"SendLinkRequest":{"properties":{"email":{"type":"string"}},"required":["email"],"type":"object"},"SessionEvent":{"properties":{"data":true,"kind":{"description":"[`crate::ingest::kind`] 或 [`crate::ingest::edge_kind`] 里的一个。","type":"string"},"name":{"type":["string","null"]},"source":{"description":"edge / sdk。","type":"string"},"ts":{"type":"string"}},"required":["ts","source","kind"],"type":"object"},"SessionRow":{"description":"点名册里的一个人。每一列都要能回答「我下一步该看谁」，回答不了的列不加。","properties":{"at":{"description":"打开的时间。","type":"string"},"browser":{"type":["string","null"]},"device":{"type":["string","null"]},"dwell_s":{"description":"停留秒数：最后一次看见减第一次看见。","format":"uint32","minimum":0,"type":"integer"},"entered":{"description":"进到游戏了没。","type":"boolean"},"errors":{"format":"uint32","minimum":0,"type":"integer"},"events":{"description":"展开这一行看到的：这个会话的事件与错误，时间正序，最多 [`MAX_EVENTS_PER_SESSION`] 条。","items":{"$ref":"#/components/schemas/SessionEvent"},"type":"array"},"feedback":{"description":"留了几句话。","format":"uint32","minimum":0,"type":"integer"},"first_frame":{"description":"报过首帧。要 SDK，没接的作品这一列永远是 false。","type":"boolean"},"id":{"type":"string"},"is_return":{"type":"boolean"},"last_input_after_s":{"description":"最后一次输入距「进入」多久。没接 SDK、或者一次都没动过是 `None`。","format":"uint32","minimum":0,"type":"integer"},"more_events":{"description":"事件多到被截断了。","type":"boolean"},"name":{"description":"点「开始」时留的名字（DESIGN §3.3 第 5 条）。没留就没有，点名册显示会话 id 的头几位。","type":["string","null"]},"os":{"type":["string","null"]},"reached":{"description":"玩到哪：最后一个自定义事件的名字。","type":["string","null"]},"referrer_kind":{"description":"[`crate::ingest::source`] 里的一个：card / notice / plaza / wechat / discord / direct / other，尽力而为。","type":["string","null"]},"started":{"description":"点过门禁页的「开始」。","type":"boolean"},"wechat":{"type":"boolean"}},"required":["id","at","wechat","started","first_frame","entered","dwell_s","last_input_after_s","errors","feedback","is_return"],"type":"object"},"Site":{"properties":{"created_at":{"type":"string"},"current_version":{"description":"还没上传过版本时为 `None`。","format":"uint32","minimum":0,"type":["integer","null"]},"expires_at":{"description":"匿名作品的到期时间。","type":["string","null"]},"kind":{"$ref":"#/components/schemas/WorkKind","default":"web","description":"网页 / 文章 / 视频。还没有版本以及旧控制面返回的作品按网页显示。"},"listing":{"$ref":"#/components/schemas/Listing","default":{"feedback_public":false,"followers":0,"has_cover":false,"hidden":false,"joined":0,"public":false,"seeking":false},"description":"广场上的状态（DESIGN §3.8）。旧控制面不返回这一段，按「不公开」解析。"},"slug":{"type":"string"},"title":{"type":"string"},"url":{"description":"玩家点开的完整链接，例如 `https://brisk-otter-41.playtest.run`。","type":"string"}},"required":["slug","url","title","created_at"],"type":"object"},"SiteList":{"items":{"$ref":"#/components/schemas/Site"},"type":"array"},"SiteResults":{"description":"作品时间线：一屏能看完的全部（DESIGN §3.4「点开是这一版的会话列表」的上一层）。","properties":{"current_version":{"format":"uint32","minimum":0,"type":["integer","null"]},"followers":{"default":0,"description":"关注这个作品的人数（DESIGN §3.6）——「下一版发出去他们会收到通知」那一句的数字。","format":"uint32","minimum":0,"type":"integer"},"slug":{"type":"string"},"title":{"type":"string"},"versions":{"description":"版本倒序，最新的在最前面。","items":{"$ref":"#/components/schemas/VersionResults"},"type":"array"}},"required":["slug","title","versions"],"type":"object"},"SourceTally":{"properties":{"count":{"format":"uint32","minimum":0,"type":"integer"},"kind":{"type":"string"}},"required":["kind","count"],"type":"object"},"TrafficWindow":{"description":"流量额度算在哪个窗口上。","oneOf":[{"const":"link_lifetime","description":"这条链接一辈子（匿名链接只活 24 小时）。","type":"string"},{"const":"monthly","description":"自然月，每月 1 日 00:00 UTC 重置。","type":"string"}]},"TunnelGrant":{"description":"`POST /v1/projects/{slug}/tunnel` 的响应：CLI 拿它去连边缘。","properties":{"connect_url":{"description":"握手地址，例如 `wss://brisk-otter-41.playtest.run/_playtest/tunnel`（本机开发是 `ws://…localhost:8443/…`）。","type":"string"},"expires_at":{"description":"RFC 3339，CLI 在此之前换新。","type":"string"},"site_expires_at":{"type":["string","null"]},"slug":{"type":"string"},"token":{"type":"string"},"url":{"description":"玩家链接，例如 `https://brisk-otter-41.playtest.run`。","type":"string"}},"required":["slug","url","connect_url","token","expires_at"],"type":"object"},"TunnelRequest":{"description":"`POST /v1/projects/{slug}/tunnel` 的请求体。","properties":{"gate":{"$ref":"#/components/schemas/GateMode","default":"once"},"hybrid":{"description":"见 [`Claims::hybrid`]。控制面只在作品已有上传过的版本时才签它。","type":"boolean"},"isolated":{"default":false,"type":"boolean"},"title":{"type":["string","null"]}},"type":"object"},"UnfollowRequest":{"properties":{"me_token":{"type":"string"},"target":{"$ref":"#/components/schemas/FollowTarget"}},"required":["me_token","target"],"type":"object"},"UnsubscribeRequest":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"},"UpdateFeedbackRequest":{"description":"`PATCH /v1/projects/{slug}/feedback/{id}`：只改带了的字段。\n`public: Some(false)` 是「把这一条藏起来」——作品级的开关在 [`crate::api::UpdateSiteRequest::feedback_public`]。","properties":{"public":{"type":["boolean","null"]},"status":{"anyOf":[{"$ref":"#/components/schemas/FeedbackStatus"},{"type":"null"}]}},"type":"object"},"UpdateSiteRequest":{"description":"`PATCH /v1/projects/{slug}`：只改带了的字段。\n`seek_note` / `community_url` 传空字符串表示清掉；`seats` 传 0 表示清掉。","properties":{"community_url":{"type":["string","null"]},"feedback_public":{"type":["boolean","null"]},"public":{"type":["boolean","null"]},"seats":{"format":"uint32","minimum":0,"type":["integer","null"]},"seek_note":{"type":["string","null"]},"seeking":{"type":["boolean","null"]},"summary":{"type":["string","null"]},"title":{"type":["string","null"]}},"type":"object"},"VersionFile":{"properties":{"hash":{"description":"内容哈希（SHA-256 小写十六进制）。同样的哈希就是同样的字节，不用再传一遍。","type":"string"},"path":{"type":"string"},"size":{"format":"uint64","minimum":0,"type":"integer"},"url":{"description":"直接能打开的地址。","type":"string"}},"required":["path","size","hash","url"],"type":"object"},"VersionFiles":{"description":"一个版本里的文件清单（`GET /v1/projects/{slug}/versions/{version}/files`）。\n\n只有清单和哈希，没有字节：文件本身在作品自己的域上按路径取就行。","properties":{"current":{"description":"玩家现在看到的就是这一版。","type":"boolean"},"files":{"description":"按路径排序，和清单里一致。","items":{"$ref":"#/components/schemas/VersionFile"},"type":"array"},"slug":{"type":"string"},"total_bytes":{"format":"uint64","minimum":0,"type":"integer"},"version":{"format":"uint32","minimum":0,"type":"integer"}},"required":["slug","version","current","total_bytes","files"],"type":"object"},"VersionInfo":{"description":"一个已发布的版本。","properties":{"created_at":{"type":"string"},"current":{"description":"玩家现在看到的就是这一版。","type":"boolean"},"file_count":{"format":"uint32","minimum":0,"type":"integer"},"note":{"type":["string","null"]},"total_bytes":{"format":"uint64","minimum":0,"type":"integer"},"version":{"format":"uint32","minimum":0,"type":"integer"}},"required":["version","created_at","file_count","total_bytes","current"],"type":"object"},"VersionList":{"properties":{"current_version":{"format":"uint32","minimum":0,"type":["integer","null"]},"slug":{"type":"string"},"versions":{"description":"新的在前。","items":{"$ref":"#/components/schemas/VersionInfo"},"type":"array"}},"required":["slug","versions"],"type":"object"},"VersionResults":{"description":"一个版本的全部数字。控制台按这些拼那段话，数为 0 的句子不说。","properties":{"created_at":{"description":"这一版上传的时间。只在会话里见过、版本表里没有的版本是 `None`。","type":["string","null"]},"dropped_before_first_frame":{"description":"L7：点了「开始」却没等到首帧的人数——门禁到首帧之间掉的那几个（DESIGN §3.4）。\n\n`None` 表示**这一版我们不知道**：没有任何会话报过首帧，多半是没接 SDK。\n不知道就说不知道，不拿 0 冒充「一个都没掉」（AGENTS 第 4 条）。","format":"uint32","minimum":0,"type":["integer","null"]},"dwell_median_s":{"description":"停留秒数的中位数，见 [`median_seconds`]。没有会话时是 `None`。","format":"uint32","minimum":0,"type":["integer","null"]},"entered":{"description":"多少人真的进到游戏：点过「开始」，或者 SDK 报过 `load`。","format":"uint32","minimum":0,"type":"integer"},"errors":{"$ref":"#/components/schemas/ErrorSummary"},"feedback_count":{"format":"uint32","minimum":0,"type":"integer"},"first_at":{"description":"这一版第一次和最后一次被打开的时间。","type":["string","null"]},"last_at":{"type":["string","null"]},"load_failures":{"description":"边缘报的 `resource_fail` 条数：404、下到一半断了、MIME 不对。","format":"uint32","minimum":0,"type":"integer"},"named":{"default":0,"description":"留了名字的人数。","format":"uint32","minimum":0,"type":"integer"},"note":{"description":"上传时附的那句「这版改了什么」（DESIGN §3.5）。","type":["string","null"]},"opened":{"description":"多少人打开。一次打开就是一个会话。","format":"uint32","minimum":0,"type":"integer"},"played_5min_plus":{"description":"停留超过 [`LONG_PLAY_SECONDS`] 的人数。","format":"uint32","minimum":0,"type":"integer"},"returned":{"description":"回头再来一次的人数。","format":"uint32","minimum":0,"type":"integer"},"sources":{"description":"来自哪里，按人数降序（DESIGN §3.5「来自：邀请卡 4 · 广场 2 · 微信 2」）。\n键是 [`crate::ingest::source`] 里的值。0 的不列。","items":{"$ref":"#/components/schemas/SourceTally"},"type":"array"},"version":{"format":"uint32","minimum":0,"type":"integer"}},"required":["version","opened","entered","dropped_before_first_frame","returned","dwell_median_s","played_5min_plus","errors","load_failures","feedback_count"],"type":"object"},"VersionSessions":{"properties":{"sessions":{"items":{"$ref":"#/components/schemas/SessionRow"},"type":"array"},"slug":{"type":"string"},"sort":{"$ref":"#/components/schemas/RosterSort"},"version":{"format":"uint32","minimum":0,"type":"integer"}},"required":["slug","version","sort","sessions"],"type":"object"},"WebLoginExchange":{"description":"网页授权码流程的最后一步：控制台把 GitHub 回传的 `code` 与 `state` 交给控制面换令牌。","properties":{"code":{"type":"string"},"state":{"type":"string"}},"required":["code","state"],"type":"object"},"WorkKind":{"description":"作品怎样被体验。旧清单没有这一项，必须继续按网页读取。","enum":["web","article","video"],"type":"string"}},"securitySchemes":{"admin":{"description":"the operator token; not for developers","scheme":"bearer","type":"http"},"bearer":{"description":"a developer token from `playtest login` or POST /v1/anon/sessions","scheme":"bearer","type":"http"}}},"info":{"description":"Publish a built web directory and get a link players can open with no account and no install. Static files only — we never run user code. See /llms-full.txt for the three-step publish flow.","summary":"Put a playable web build in front of real people.","title":"playtest","version":"0.1.0"},"openapi":"3.1.0","paths":{"/admin/boosts":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoostList"}}},"description":"every boost, newest first"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"admin":[]}],"summary":"every boost, newest first"},"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantBoostRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Boost"}}},"description":"grant a boost without payment"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"admin":[]}],"summary":"grant a boost without payment"}},"/admin/boosts/{id}":{"delete":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"204":{"description":"end a boost early"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"admin":[]}],"summary":"end a boost early"}},"/admin/boosts/{id}/review":{"post":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewBoostRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Boost"}}},"description":"approve or reject a pending boost"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"admin":[]}],"summary":"approve or reject a pending boost"}},"/admin/jobs":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobList"}}},"description":"background jobs: when each last ran and how it went"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"admin":[]}],"summary":"background jobs: when each last ran and how it went"}},"/admin/notifications":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationQueue"}}},"description":"how the notification queue looks"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"admin":[]}],"summary":"how the notification queue looks"}},"/admin/plaza/{slug}/hide":{"post":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"take a project off the plaza (or put it back)"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"admin":[]}],"summary":"take a project off the plaza (or put it back)"}},"/healthz":{"get":{"responses":{"200":{"description":"is this control plane up"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"is this control plane up"}},"/v1/admin/collections/{slug}":{"put":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerateCollectionRequest"}}},"required":true},"responses":{"204":{"description":"hide or restore a collection"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"admin":[]}],"summary":"hide or restore a collection"}},"/v1/anon/sessions":{"post":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnonSessionResponse"}}},"description":"an anonymous token; the links it makes expire"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"an anonymous token; the links it makes expire"}},"/v1/blobs/{hash}":{"put":{"parameters":[{"in":"path","name":"hash","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}},"required":true},"responses":{"204":{"description":"upload one file by content hash (raw bytes, not JSON)"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"upload one file by content hash (raw bytes, not JSON)"}},"/v1/collections":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionList"}}},"description":"the caller's collections"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"the caller's collections"},"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionDraft"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Collection"}}},"description":"create a collection or a creative challenge"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"create a collection or a creative challenge"}},"/v1/collections/open":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionList"}}},"description":"published collections and challenges"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"published collections and challenges"}},"/v1/collections/{slug}":{"delete":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"remove a collection, not its projects"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"remove a collection, not its projects"},"get":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Collection"}}},"description":"view a collection"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"view a collection"},"put":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionDraft"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Collection"}}},"description":"update collection details and visibility"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"update collection details and visibility"}},"/v1/collections/{slug}/blocks/{site}":{"delete":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}},{"in":"path","name":"site","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"allow a moderated author to submit again"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"allow a moderated author to submit again"}},"/v1/collections/{slug}/entries":{"post":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntryDraft"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Collection"}}},"description":"submit an owned public project with self-reported creation notes"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"submit an owned public project with self-reported creation notes"}},"/v1/collections/{slug}/entries/{site}":{"delete":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}},{"in":"path","name":"site","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"withdraw or moderate a submission"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"withdraw or moderate a submission"}},"/v1/follow":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowResponse"}}},"description":"a player follows a project or a developer (the gate page calls this)"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"a player follows a project or a developer (the gate page calls this)"}},"/v1/follow/confirm":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmResponse"}}},"description":"confirm an e-mail follow with the token from the mail"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"confirm an e-mail follow with the token from the mail"}},"/v1/follow/unsubscribe":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnsubscribeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeView"}}},"description":"one-click unsubscribe from the mail footer"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"one-click unsubscribe from the mail footer"}},"/v1/ingest/events":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventBatch"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Accepted"}}},"description":"the in-page SDK reports errors and milestones here"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"the in-page SDK reports errors and milestones here"}},"/v1/ingest/feedback":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackAccepted"}}},"description":"the in-page SDK submits a player's note here"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"the in-page SDK submits a player's note here"}},"/v1/login/device":{"post":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceLoginStart"}}},"description":"start a GitHub device login; show the user the code"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"start a GitHub device login; show the user the code"}},"/v1/login/device/poll":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceLoginPoll"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginPollResponse"}}},"description":"poll until the user has entered the code"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"poll until the user has entered the code"}},"/v1/login/github/exchange":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebLoginExchange"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}},"description":"trade the code GitHub sent the browser back with for a token"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"trade the code GitHub sent the browser back with for a token"}},"/v1/me":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}},"description":"who this token belongs to"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"who this token belongs to"}},"/v1/me/push-off":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeView"}}},"description":"turn browser notifications off, keep the follows"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"turn browser notifications off, keep the follows"}},"/v1/me/send-link":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowResponse"}}},"description":"mail the player a link to their own page"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"mail the player a link to their own page"}},"/v1/me/token":{"delete":{"responses":{"204":{"description":"revoke only this API token; keep projects and other devices"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"revoke only this API token; keep projects and other devices"}},"/v1/me/unfollow":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnfollowRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeView"}}},"description":"stop following one thing"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"stop following one thing"}},"/v1/me/view":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeView"}}},"description":"what a player follows (by the player's own token)"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[],"summary":"what a player follows (by the player's own token)"}},"/v1/projects":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteList"}}},"description":"the caller's projects"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"the caller's projects"},"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSiteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}},"description":"create a project"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"create a project"}},"/v1/projects/{slug}":{"delete":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"delete it; the link stops working"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"delete it; the link stops working"},"get":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}},"description":"one project as it is right now"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"one project as it is right now"},"patch":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSiteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}},"description":"change plaza listing, seats, community link, public feedback"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"change plaza listing, seats, community link, public feedback"}},"/v1/projects/{slug}/feedback":{"get":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackList"}}},"description":"what players wrote"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"what players wrote"}},"/v1/projects/{slug}/feedback/{id}":{"patch":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFeedbackRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackItem"}}},"description":"mark a note seen/done, or hide it"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"mark a note seen/done, or hide it"}},"/v1/projects/{slug}/results":{"get":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteResults"}}},"description":"per-version roster: opened, started, stayed, where they came from"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"per-version roster: opened, started, stayed, where they came from"}},"/v1/projects/{slug}/tunnel":{"post":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TunnelRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TunnelGrant"}}},"description":"a signed token for the tunnel (the CLI uses this)"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"a signed token for the tunnel (the CLI uses this)"}},"/v1/projects/{slug}/uploads":{"post":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrepareUploadRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrepareUploadResponse"}}},"description":"declare the files, learn which hashes are missing"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"declare the files, learn which hashes are missing"}},"/v1/projects/{slug}/uploads/{upload_id}/commit":{"post":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}},{"in":"path","name":"upload_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommitUploadResponse"}}},"description":"make the uploaded files the live version"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"make the uploaded files the live version"}},"/v1/projects/{slug}/versions":{"get":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionList"}}},"description":"every version, newest first"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"every version, newest first"}},"/v1/projects/{slug}/versions/{version}/activate":{"post":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}},{"in":"path","name":"version","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}},"description":"roll back: point players at an older version"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"roll back: point players at an older version"}},"/v1/projects/{slug}/versions/{version}/files":{"get":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}},{"in":"path","name":"version","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionFiles"}}},"description":"what is actually in a version: path, size, sha256, direct url"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"what is actually in a version: path, size, sha256, direct url"}},"/v1/projects/{slug}/versions/{version}/sessions":{"get":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}},{"in":"path","name":"version","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionSessions"}}},"description":"one row per player for a version"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}},"description":"an error: `code` is for programs, `message` for people"}},"security":[{"bearer":[]}],"summary":"one row per player for a version"}}},"security":[{"bearer":[]}],"servers":[{"url":"https://playtest.run"}]}