{"components":{"schemas":{"Error":{"description":"Every non-2xx response. `code` is the stable, machine-readable part (e.g. UNAUTHORIZED, BAD_REQUEST); `message` is for humans.","properties":{"code":{"type":"string"},"data":{"description":"Code-specific detail, e.g. validation issues for BAD_REQUEST."},"defined":{"type":"boolean"},"message":{"type":"string"}},"required":["code","message"],"type":"object"},"__schema0":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"},{"type":"array","items":{"$ref":"#/components/schemas/__schema0"}},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/__schema0"}}]},"JsonValue":{"$ref":"#/components/schemas/__schema0"}},"securitySchemes":{"apiKeyHeader":{"description":"An API key (`vg_…`) created with apiKeys.create or in the web app settings.","in":"header","name":"x-api-key","type":"apiKey"},"bearerAuth":{"description":"A session token from `vg login` (device-code flow), or an API key (`vg_…`). This is what `VG_TOKEN` holds.","scheme":"bearer","type":"http"},"sessionCookie":{"description":"better-auth session cookie, set by signing in at /auth/login.","in":"cookie","name":"__Secure-better-auth.session_token","type":"apiKey"}}},"externalDocs":{"description":"Developer docs","url":"https://vibedgames.com/docs"},"info":{"contact":{"email":"kai@kyh.io","url":"https://vibedgames.com/contact"},"description":"The Vibedgames platform API: deploy browser games, generate assets, run playtests. The `vg` CLI is the reference client.\n\nEvery operation is a POST with a JSON body under /api/v1; errors are JSON with a stable `code` and a `message`.\n\nThe same procedures are also served at `POST /api/orpc/<router>/<procedure>` in oRPC's own wire format (body wrapped as `{\"json\": …}`), which the CLI and web app use.\n\nOperations without a security requirement are public. The rest take a session cookie, a bearer token from `vg login`, or an API key (`vg_…`); a few refuse API keys and need a real session.","license":{"name":"MIT","url":"https://github.com/kyh/vibedgames/blob/main/LICENSE"},"title":"Vibedgames API","version":"1.0.0"},"servers":[{"url":"https://vibedgames.com/api/v1"}],"openapi":"3.1.1","paths":{"/admin/credits/balances":{"post":{"operationId":"admin.credits.balances","summary":"List credit balances","description":"Returns every user's credit balance in micro-USD. A user with no ledger rows is absent and holds nothing. Admin only.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"balances":{"type":"array","items":{"type":"object","properties":{"balanceMicro":{"type":"number"},"userId":{"type":"string"}},"required":["balanceMicro","userId"],"additionalProperties":false}}},"required":["balances"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/admin/credits/grant":{"post":{"operationId":"admin.credits.grant","summary":"Grant credits","description":"Grants (positive) or claws back (negative) credits in dollars. The client-minted `key` makes retries idempotent. Admin only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amountUsd":{"type":"number","minimum":-1000,"maximum":1000},"key":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"type":"string","maxLength":500},"userId":{"type":"string","minLength":1}},"required":["amountUsd","key","userId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"balanceMicro":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["balanceMicro"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/admin/users/create":{"post":{"operationId":"admin.users.create","summary":"Create a user","description":"Creates a user with an email and password. Admin only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"name":{"type":"string","minLength":1,"maxLength":100},"password":{"type":"string","minLength":8},"role":{"default":"user","type":"string","enum":["user","admin"]}},"required":["email","name","password"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"banExpires":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"banReason":{"type":["string","null"]},"banned":{"type":["boolean","null"]},"createdAt":{"type":"string","format":"date-time","x-native-type":"date"},"email":{"type":"string"},"emailVerified":{"type":"boolean"},"id":{"type":"string"},"image":{"type":["string","null"]},"name":{"type":"string"},"role":{"type":["string","null"]},"updatedAt":{"type":"string","format":"date-time","x-native-type":"date"}},"required":["createdAt","email","emailVerified","id","name","updatedAt"],"additionalProperties":false}},"required":["user"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/admin/users/list":{"post":{"operationId":"admin.users.list","summary":"List users","description":"Lists the 100 most recently created users. Admin only.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"number"},"offset":{"type":"number"},"total":{"type":"number"},"users":{"type":"array","items":{"type":"object","properties":{"banExpires":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"banReason":{"type":["string","null"]},"banned":{"type":["boolean","null"]},"createdAt":{"type":"string","format":"date-time","x-native-type":"date"},"email":{"type":"string"},"emailVerified":{"type":"boolean"},"id":{"type":"string"},"image":{"type":["string","null"]},"name":{"type":"string"},"role":{"type":["string","null"]},"updatedAt":{"type":"string","format":"date-time","x-native-type":"date"}},"required":["createdAt","email","emailVerified","id","name","updatedAt"],"additionalProperties":false}}},"required":["total","users"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/apiKeys/create":{"post":{"operationId":"apiKeys.create","summary":"Create an API key","description":"Mints an API key (`vg_…`) for CI and headless agents. The raw `key` is returned only in this response. Needs a real session; an API key cannot mint keys.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expiresInDays":{"default":null,"anyOf":[{"type":"integer","minimum":1,"maximum":3650},{"type":"null"}]},"name":{"type":"string","minLength":1,"maxLength":100}},"required":["name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","x-native-type":"date"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"id":{"type":"string"},"keyPrefix":{"type":"string"},"name":{"type":["string","null"]},"key":{"type":"string"}},"required":["createdAt","expiresAt","id","keyPrefix","name","key"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/apiKeys/list":{"post":{"operationId":"apiKeys.list","summary":"List API keys","description":"Lists the caller's API keys by prefix, never the raw key. Needs a real session.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","x-native-type":"date"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"id":{"type":"string"},"keyPrefix":{"type":"string"},"name":{"type":["string","null"]},"lastUsedAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]}},"required":["createdAt","expiresAt","id","keyPrefix","name","lastUsedAt"],"additionalProperties":false}}},"required":["keys"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/apiKeys/revoke":{"post":{"operationId":"apiKeys.revoke","summary":"Revoke an API key","description":"Deletes one of the caller's API keys. Needs a real session.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The referenced resource does not exist."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/auth/cliConfirm":{"post":{"operationId":"auth.cliConfirm","summary":"Confirm a CLI login code","description":"Approves a pending CLI device code from a signed-in browser, handing the CLI this session's token on its next cliPoll.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The referenced resource does not exist."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/auth/cliInit":{"post":{"operationId":"auth.cliInit","summary":"Start a CLI login","description":"Starts the CLI device-code login: returns a short code, valid for five minutes, for the person to confirm in a browser. Poll cliPoll with it.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}}}},"/auth/cliPoll":{"post":{"operationId":"auth.cliPoll","summary":"Poll a CLI login","description":"Polls a CLI login code. Returns `pending` until confirmed, then `confirmed` with a bearer token exactly once; `expired` after five minutes or once consumed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"status":{"type":"string","const":"expired"}},"required":["status"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","const":"pending"}},"required":["status"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","const":"confirmed"},"token":{"type":"string"}},"required":["status","token"],"additionalProperties":false}]}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}}}},"/auth/createInvites":{"post":{"operationId":"auth.createInvites","summary":"Create credit codes","description":"Creates one custom credit code or a batch of random ones, each worth `creditUsd` per redemption. Admin only; 409 when a custom code already exists.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"default":null,"anyOf":[{"type":"string","pattern":"^[A-Z0-9]{6}$","description":"6 letters or digits; surrounding whitespace is trimmed and letters are upper-cased first."},{"type":"null"}]},"count":{"default":1,"type":"integer","minimum":1,"maximum":100},"creditUsd":{"default":20,"type":"number","exclusiveMinimum":0,"maximum":1000},"expiresAt":{"default":null,"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"maxUses":{"default":1,"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]},"note":{"default":null,"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"codes":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"createdAt":{"type":"string","format":"date-time","x-native-type":"date"},"createdBy":{"type":["string","null"]},"creditMicro":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Credit one redemption grants, in micro-USD."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"id":{"type":"string"},"maxUses":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"null means unlimited uses."},"note":{"type":["string","null"]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"usedCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["code","createdAt","createdBy","creditMicro","expiresAt","id","maxUses","note","revokedAt","usedCount"],"additionalProperties":false}}},"required":["codes"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request conflicts with existing state."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/auth/listInvites":{"post":{"operationId":"auth.listInvites","summary":"List credit codes","description":"Lists every credit code, newest first, with its creator's email. Admin only.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"codes":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"createdAt":{"type":"string","format":"date-time","x-native-type":"date"},"createdBy":{"type":["string","null"]},"creditMicro":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Credit one redemption grants, in micro-USD."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"id":{"type":"string"},"maxUses":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"null means unlimited uses."},"note":{"type":["string","null"]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"usedCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"creatorEmail":{"type":["string","null"]}},"required":["code","createdAt","createdBy","creditMicro","expiresAt","id","maxUses","note","revokedAt","usedCount","creatorEmail"],"additionalProperties":false}}},"required":["codes"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/auth/me":{"post":{"operationId":"auth.me","summary":"Get the current user","description":"Returns the authenticated identity, whether the caller used a session cookie, a session token or an API key. Backs `vg whoami`.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"role":{"type":["string","null"]}},"required":["email","id","name","role"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/auth/updateInvite":{"post":{"operationId":"auth.updateInvite","summary":"Update a credit code","description":"Revokes, un-revokes, or changes the use limit or value of a credit code. Omitted fields are left untouched. Admin only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"creditUsd":{"type":"number","exclusiveMinimum":0,"maximum":1000},"id":{"type":"string"},"maxUses":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]},"revoked":{"type":"boolean"}},"required":["id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"object","properties":{"code":{"type":"string"},"createdAt":{"type":"string","format":"date-time","x-native-type":"date"},"createdBy":{"type":["string","null"]},"creditMicro":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Credit one redemption grants, in micro-USD."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"id":{"type":"string"},"maxUses":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"null means unlimited uses."},"note":{"type":["string","null"]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","x-native-type":"date"},{"type":"null"}]},"usedCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["code","createdAt","createdBy","creditMicro","expiresAt","id","maxUses","note","revokedAt","usedCount"],"additionalProperties":false}},"required":["code"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The referenced resource does not exist."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]}]}},"/credits/checkout":{"post":{"operationId":"credits.checkout","summary":"Buy credits","description":"Starts a card payment for `amountUsd` of generation credit and returns a hosted checkout URL for a person to open. The credit lands once the payment clears, usually within seconds of paying. 412 while card purchases aren't open (`purchasesEnabled` is false on `credits.me`).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amountUsd":{"type":"integer","minimum":5,"maximum":500,"description":"Whole dollars of credit to buy, 5–500. One dollar paid is one dollar of credit."}},"required":["amountUsd"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"412":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A server-side dependency this procedure needs is not configured."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"An upstream provider failed or returned an unusable response."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/credits/me":{"post":{"operationId":"credits.me","summary":"Get your credit balance","description":"Returns the caller's generation credit balance in micro-USD (1,000,000 = $1), their 100 most recent ledger entries, and whether card purchases are open.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"balanceMicro":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"entries":{"type":"array","items":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","x-native-type":"date"},"deltaMicro":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Signed micro-USD: positive is a grant or refund."},"endpointId":{"type":["string","null"]},"id":{"type":"string"},"kind":{"type":"string","enum":["signup_grant","admin_grant","code_redeem","purchase","generation_hold","generation_settle","generation_release"]},"note":{"type":["string","null"]},"requestId":{"type":["string","null"]}},"required":["createdAt","deltaMicro","endpointId","id","kind","note","requestId"],"additionalProperties":false}},"purchasesEnabled":{"type":"boolean","description":"Whether `credits.checkout` takes card payments; when false, credit comes from codes."}},"required":["balanceMicro","entries","purchasesEnabled"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/credits/redeem":{"post":{"operationId":"credits.redeem","summary":"Redeem a credit code","description":"Redeems a credit code for the caller, adding its value to their balance. Each account can redeem a given code once. 403 when the code is unknown, expired, revoked or used up; 409 when this account already redeemed it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":64}},"required":["code"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"balanceMicro":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"creditedMicro":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["balanceMicro","creditedMicro"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request conflicts with existing state."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/deploy/create":{"post":{"operationId":"deploy.create","summary":"Create a deployment","description":"Starts a deployment of a built game to `{slug}.vibedgames.com`, replacing the previous one. Returns a presigned PUT URL per file (and for the optional source archive); upload each, then call deploy.finalize. 403 when the slug belongs to someone else.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"files":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"object","properties":{"contentType":{"type":"string","minLength":1,"maxLength":127},"path":{"type":"string","minLength":1,"maxLength":512},"sha256":{"type":"string","minLength":64,"maxLength":64},"size":{"type":"integer","minimum":0,"maximum":10485760}},"required":["contentType","path","sha256","size"]}},"name":{"type":"string","maxLength":120},"slug":{"type":"string","minLength":3,"maxLength":40,"pattern":"^[a-z0-9][a-z0-9-]*[a-z0-9]$"},"source":{"type":"object","properties":{"bytes":{"type":"integer","exclusiveMinimum":0,"maximum":104857600},"sha256":{"type":"string","minLength":64,"maxLength":64}},"required":["bytes","sha256"]}},"required":["files","slug"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"deploymentId":{"type":"string"},"gameId":{"type":"string"},"sourceUpload":{"anyOf":[{"type":"object","properties":{"headers":{"type":"object","properties":{"content-type":{"type":"string"}},"required":["content-type"],"additionalProperties":false},"url":{"type":"string"}},"required":["headers","url"],"additionalProperties":false},{"type":"null"}]},"uploads":{"type":"array","items":{"type":"object","properties":{"headers":{"type":"object","properties":{"content-type":{"type":"string"}},"required":["content-type"],"additionalProperties":false},"url":{"type":"string"},"path":{"type":"string"}},"required":["headers","url","path"],"additionalProperties":false}}},"required":["deploymentId","gameId","sourceUpload","uploads"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/deploy/delete":{"post":{"operationId":"deploy.delete","summary":"Delete a game","description":"Permanently deletes one of the caller's games and all of its deployed files.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"gameId":{"type":"string"}},"required":["gameId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true}},"required":["success"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The referenced resource does not exist."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/deploy/finalize":{"post":{"operationId":"deploy.finalize","summary":"Finalize a deployment","description":"Marks an uploaded deployment ready and makes it the live version of its game. Returns the public URL.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"deploymentId":{"type":"string"}},"required":["deploymentId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string"},"url":{"type":"string"}},"required":["slug","url"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The referenced resource does not exist."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/deploy/getSource":{"post":{"operationId":"deploy.getSource","summary":"Get a game's source","description":"Returns a one-hour download URL for a game's forkable source archive. Any signed-in user may fork any game that shipped source. Backs `vg fork`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","minLength":3,"maxLength":40,"pattern":"^[a-z0-9][a-z0-9-]*[a-z0-9]$"}},"required":["slug"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"bytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"name":{"type":["string","null"]},"slug":{"type":"string"},"url":{"type":"string"}},"required":["bytes","name","slug","url"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The referenced resource does not exist."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/deploy/list":{"post":{"operationId":"deploy.list","summary":"List your games","description":"Lists the caller's games, most recently updated first, each with its live deployment.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"games":{"type":"array","items":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","x-native-type":"date"},"currentDeploymentId":{"type":["string","null"]},"deployment":{"anyOf":[{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","x-native-type":"date"},"fileCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"id":{"type":"string"},"status":{"type":"string","enum":["pending","ready","failed"]},"totalBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["createdAt","fileCount","id","status","totalBytes"],"additionalProperties":false},{"type":"null"}]},"id":{"type":"string"},"name":{"type":["string","null"]},"slug":{"type":"string"},"updatedAt":{"type":"string","format":"date-time","x-native-type":"date"},"userId":{"type":"string"}},"required":["createdAt","currentDeploymentId","deployment","id","name","slug","updatedAt","userId"],"additionalProperties":false}}},"required":["games"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/generate/forward":{"post":{"operationId":"generate.forward","summary":"Proxy a media-generation request","description":"Forwards one request to the fal media-generation API with the server's key: `target` picks the host (queue, platform, storage, docs), `path`/`query`/`body` the rest; a queue path holds only lowercase letters, digits and `-._~/`, so the endpoint billed is the one fal runs. Returns the upstream JSON body. Queue submits are billed on the result fetch, and need a positive credit balance (403 `insufficient_credits` otherwise) and an endpoint with a published price (400 `unknown_endpoint` otherwise) unless the caller is an admin, whose submits are billed but never refused. Backs `vg generate`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{},"method":{"type":"string","enum":["GET","POST","PUT","DELETE"]},"path":{"type":"string","minLength":1,"maxLength":512,"pattern":"^\\/[\\w.~!$&'()*+,;=:@%/-]*$"},"query":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}},"target":{"type":"string","enum":["queue","platform","storage","docs"]}},"required":["method","path","target"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"description":"The upstream response body, or null when it had none.","$ref":"#/components/schemas/JsonValue"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The caller is authenticated but may not perform this action."},"412":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A server-side dependency this procedure needs is not configured."},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request body or a field in it is over its size limit."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"An upstream provider failed or returned an unusable response."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/playtest/decide":{"post":{"operationId":"playtest.decide","summary":"Ask the playtest decision model","description":"Asks the decision model typed questions (yes/no, choice, score) about a JSON game state and returns its answers verbatim. Backs `vg playtest run`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"model":{"type":"string","pattern":"^[a-z0-9][a-z0-9.-]{0,63}$"},"questions":{"type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":64},"additionalProperties":{"oneOf":[{"type":"object","properties":{"criteria":{"type":"object","properties":{"false":{"type":"string","minLength":1,"maxLength":2000},"true":{"type":"string","minLength":1,"maxLength":2000}}},"instructions":{"type":"string","minLength":1,"maxLength":2000},"type":{"type":"string","const":"noul"}},"required":["instructions","type"]},{"type":"object","properties":{"criteria":{"type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":64},"additionalProperties":{"type":"string","minLength":1,"maxLength":2000}},"instructions":{"type":"string","minLength":1,"maxLength":2000},"type":{"type":"string","const":"choice"}},"required":["criteria","instructions","type"]},{"type":"object","properties":{"criteria":{"minItems":2,"maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":2000}},"instructions":{"type":"string","minLength":1,"maxLength":2000},"type":{"type":"string","const":"score"}},"required":["criteria","instructions","type"]}]}},"state":{"$ref":"#/components/schemas/JsonValue"}},"required":["questions","state"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"description":"The decision model's reply; `answers` is keyed by question label.","$ref":"#/components/schemas/JsonValue"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"412":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A server-side dependency this procedure needs is not configured."},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request body or a field in it is over its size limit."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limited; retry with backoff."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"An upstream provider failed or returned an unusable response."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/playtest/session":{"post":{"operationId":"playtest.session","summary":"Mint a playtest token","description":"Mints a short-lived token, bound to the caller, that an in-page playtester presents to /api/playtest/decide. It grants nothing else.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"expiresAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Unix epoch milliseconds."},"token":{"type":"string"}},"required":["expiresAt","token"],"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No valid session cookie, bearer token or API key."},"412":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A server-side dependency this procedure needs is not configured."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}},"security":[{"sessionCookie":[]},{"bearerAuth":[]},{"apiKeyHeader":[]}]}},"/waitlist/join":{"post":{"operationId":"waitlist.join","summary":"Join the waitlist","description":"Adds an email address to the waitlist. Public; attaches the signed-in user when a session is present.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}},"required":["email"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"waitlist":{"type":"object","properties":{"email":{"type":["string","null"]},"id":{"type":"string"},"source":{"type":["string","null"]},"userId":{"type":["string","null"]}},"required":["email","id","source","userId"],"additionalProperties":false}},"additionalProperties":false}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The input failed validation or the request cannot be carried out as sent."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unexpected server error."}}}}}}