{"swagger":"2.0","info":{"version":"0.9.9","title":"Ludo.ai API"},"basePath":"/api","tags":[{"name":"Authentication"},{"name":"Games"},{"name":"Explore"},{"name":"Gamestorming"},{"name":"Misc"},{"name":"Guide"},{"name":"Projects"},{"name":"Payments"},{"name":"Team"},{"name":"Assets"}],"schemes":["https"],"securityDefinitions":{"ApiKey":{"type":"apiKey","name":"Authorization","in":"header","description":"For accessing the API a valid API Key token must be passed in all the queries in\nthe 'Authorization' header.\nThe following syntax must be used in the 'Authorization' header: ApiKey xxxxxx.yyyyyyy.zzzzzz\n"},"Bearer":{"type":"apiKey","name":"Authorization","in":"header","description":"For accessing the API a valid JWT token must be passed in all the queries in\nthe 'Authorization' header.\nA valid JWT token is generated by the API and retourned as answer of a call\nto the route /login giving a valid user & password.\nThe following syntax must be used in the 'Authorization' header: Bearer xxxxxx.yyyyyyy.zzzzzz\n"}},"consumes":["application/json"],"produces":["application/json"],"paths":{"/register/{email}":{"get":{"summary":"Validates the email for the registration process. (Roles: user, none)","tags":["Authentication"],"operationId":"startRegistration","security":[{"Bearer":[]}],"x-security-scopes":["none","user"],"parameters":[{"name":"email","in":"path","required":true,"type":"string"},{"name":"captcha_token","in":"query","required":false,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/register":{"post":{"summary":"Registers a user. (Roles: user, none)","tags":["Authentication"],"operationId":"register","security":[{"Bearer":[]}],"x-security-scopes":["none","user"],"parameters":[{"name":"auth","in":"body","required":true,"schema":{"$ref":"#/definitions/RegistrationPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/check-email":{"post":{"summary":"Checks if an email is valid for registration. (Roles: user, none)","tags":["Authentication"],"operationId":"checkEmailValid","security":[{"Bearer":[]}],"x-security-scopes":["none","user"],"parameters":[{"name":"email","in":"body","required":true,"schema":{"$ref":"#/definitions/StringValue"}}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/register-paywall2":{"post":{"summary":"Registers a user with a subscription. (Roles: user, none)","tags":["Authentication"],"operationId":"registerPaywall2","security":[{"Bearer":[]}],"x-security-scopes":["none","user"],"parameters":[{"name":"auth","in":"body","required":true,"schema":{"$ref":"#/definitions/RegistrationPaywall2Payload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/register-paywall3":{"post":{"summary":"Registers a user without a subscription. (Roles: user, none)","tags":["Authentication"],"operationId":"registerPaywall3","security":[{"Bearer":[]}],"x-security-scopes":["none","user"],"parameters":[{"name":"auth","in":"body","required":true,"schema":{"$ref":"#/definitions/RegistrationPaywall3Payload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/register-paywall/session/success":{"post":{"summary":"Registers a user from a stripe checkout session. (Roles: user, none)","tags":["Authentication"],"operationId":"successPaywallSession","security":[{"Bearer":[]}],"x-security-scopes":["none","user"],"parameters":[{"name":"session","in":"body","required":true,"schema":{"$ref":"#/definitions/LoginPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/register-paywall/team":{"post":{"summary":"Registers a user and joins a team. (Roles: user, none)","tags":["Authentication"],"operationId":"registerPaywallTeam","security":[{"Bearer":[]}],"x-security-scopes":["none","user"],"parameters":[{"name":"auth","in":"body","required":true,"schema":{"$ref":"#/definitions/RegistrationPaywallTeamPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/login":{"post":{"summary":"Allow a user to log in and receive a JWT Token. (Roles: user, none)","tags":["Authentication"],"operationId":"login","security":[{"Bearer":[]}],"x-security-scopes":["none","user"],"parameters":[{"name":"auth","in":"body","description":"Username and password data","required":true,"schema":{"$ref":"#/definitions/Authentication"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/verify-magic-link":{"get":{"summary":"Verify magic link code and authenticate user","tags":["Authentication"],"operationId":"verifyMagicLink","security":[{"Bearer":[]}],"x-security-scopes":["none"],"parameters":[{"name":"code","in":"query","description":"Magic link verification code","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/discord/link-start":{"post":{"summary":"Starts the Discord account link flow and returns the OAuth url","tags":["Authentication"],"operationId":"startDiscordLink","consumes":["application/octet-stream"],"security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/DiscordLinkStartResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"503":{"description":"Discord linking is not configured on this environment","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/facebook":{"post":{"summary":"Authenticates with facebook (Roles: user, none)","tags":["Authentication"],"operationId":"authenticateFacebook","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"parameters":[{"name":"token","in":"body","description":"Facebook access token","required":true,"schema":{"$ref":"#/definitions/StringValue"}},{"name":"register","in":"query","type":"boolean"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/google":{"post":{"summary":"Authenticates with google (Roles: user, none)","tags":["Authentication"],"operationId":"authenticateGoogle","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"parameters":[{"name":"token","in":"body","description":"Google access token","required":true,"schema":{"$ref":"#/definitions/StringValue"}},{"name":"register","in":"query","type":"boolean"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/refresh-token":{"get":{"summary":"Refreshes auth token (Roles: user)","tags":["Authentication"],"operationId":"refreshToken","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/referral":{"get":{"summary":"Get referral info for the authenticated user (Roles: user)","tags":["Authentication"],"operationId":"getReferralInfo","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/forgot-password":{"post":{"summary":"Send password recovery email (Roles: none)","tags":["Authentication"],"operationId":"forgotPassword","security":[{"Bearer":[]}],"x-security-scopes":["none"],"parameters":[{"name":"email","in":"body","required":true,"schema":{"$ref":"#/definitions/StringValue"}}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/reset-password":{"post":{"summary":"Sets a new password (Roles: none)","tags":["Authentication"],"operationId":"resetPassword","security":[{"Bearer":[]}],"x-security-scopes":["none"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/ResetPasswordPayload"}}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/2fa/verify":{"post":{"summary":"Exchange a 2FA challenge ticket plus one proof for a full login payload. (Roles: none)","tags":["Authentication"],"operationId":"twoFactorVerify","security":[{"Bearer":[]}],"x-security-scopes":["none"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/TwoFactorVerifyPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"410":{"description":"The challenge ticket has expired or was already used","schema":{"$ref":"#/definitions/ErrorResponse"}},"429":{"description":"Too many failed second-factor attempts","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/2fa/webauthn/login-options":{"post":{"summary":"WebAuthn request options for a login challenge (ticket) or a step-up (Bearer). (Roles: user, none)","tags":["Authentication"],"operationId":"twoFactorWebauthnLoginOptions","security":[{"Bearer":[]}],"x-security-scopes":["none","user"],"parameters":[{"name":"data","in":"body","required":false,"schema":{"$ref":"#/definitions/TwoFactorLoginOptionsPayload"}}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"410":{"description":"The challenge ticket has expired or was already used","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/2fa/totp/setup":{"post":{"summary":"Begin authenticator-app enrolment; returns the secret and a QR code. (Roles: user)","tags":["Authentication"],"operationId":"twoFactorTotpSetup","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":false,"schema":{"$ref":"#/definitions/TwoFactorStepUpPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/TwoFactorSetupResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"429":{"description":"Too many failed second-factor attempts","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/2fa/totp/confirm":{"post":{"summary":"Confirm authenticator enrolment with a code; returns a fresh token and recovery codes. (Roles: user)","tags":["Authentication"],"operationId":"twoFactorTotpConfirm","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/TwoFactorTotpConfirmPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/TwoFactorEnrollResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"429":{"description":"Too many failed second-factor attempts","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/2fa/webauthn/register-options":{"post":{"summary":"WebAuthn creation options for enrolling a passkey. (Roles: user)","tags":["Authentication"],"description":"Requires NO step-up proof, even on an account that already has 2FA: this call mutates nothing, and the only thing it exposes that the session cannot already read is excludeCredentials, whose ids GET /auth/2fa/status returns to the same session. The single proof for adding a passkey accompanies POST /auth/2fa/webauthn/register. Asking at both would cost two distinct proofs per passkey, since every proof type is single-use.","operationId":"twoFactorWebauthnRegisterOptions","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":false,"schema":{"$ref":"#/definitions/TwoFactorStepUpPayload"}}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"429":{"description":"Too many failed second-factor attempts","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/2fa/webauthn/register":{"post":{"summary":"Finish passkey enrolment with the authenticator's attestation. (Roles: user)","tags":["Authentication"],"description":"The mutating half of the ceremony, and the ONLY place a step-up proof is required when adding a passkey to an account that already has 2FA: send exactly one of totp_code, recovery_code or webauthn_assertion alongside the attestation. First-time enrolment on an unprotected account needs no proof.","operationId":"twoFactorWebauthnRegister","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/TwoFactorRegisterPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/TwoFactorRegisterResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"429":{"description":"Too many failed second-factor attempts","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/2fa/disable":{"post":{"summary":"Turn two-factor authentication off. Requires a step-up proof. (Roles: user)","tags":["Authentication"],"operationId":"twoFactorDisable","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":false,"schema":{"$ref":"#/definitions/TwoFactorStepUpPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/TwoFactorDisableResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"429":{"description":"Too many failed second-factor attempts","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/2fa/webauthn/{credentialId}":{"delete":{"summary":"Remove a registered passkey. Requires a step-up proof. (Roles: user)","tags":["Authentication"],"operationId":"twoFactorRemoveCredential","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"credentialId","in":"path","required":true,"type":"string"},{"name":"data","in":"body","required":false,"schema":{"$ref":"#/definitions/TwoFactorStepUpPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/TwoFactorRemoveCredentialResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"404":{"description":"No such passkey on this account","schema":{"$ref":"#/definitions/ErrorResponse"}},"429":{"description":"Too many failed second-factor attempts","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/2fa/recovery-codes":{"post":{"summary":"Regenerate the ten recovery codes. Requires a step-up proof. (Roles: user)","tags":["Authentication"],"operationId":"twoFactorRegenerateRecoveryCodes","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":false,"schema":{"$ref":"#/definitions/TwoFactorStepUpPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/TwoFactorRecoveryCodesResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"429":{"description":"Too many failed second-factor attempts","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/2fa/status":{"get":{"summary":"Current two-factor state for the authenticated user. (Roles: user)","tags":["Authentication"],"operationId":"twoFactorStatus","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/TwoFactorStatusResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/user/info":{"post":{"summary":"Updates the user info (Roles: user)","tags":["Authentication"],"operationId":"updateUserInfo","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/UserInfoPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/User"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"delete":{"summary":"Deletes the user (Roles: user)","tags":["Authentication"],"operationId":"deleteUser","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/user/api-key":{"get":{"summary":"Generates a new API key (Roles: user)","tags":["Authentication"],"operationId":"generateApiKey","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/User"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/auth/validate-api-key":{"get":{"description":"Validates an API key. Returns 200 if valid, 403 if invalid.","tags":["Authentication"],"operationId":"validateApiKeyEndpoint","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Valid API key"},"403":{"description":"Invalid API key","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/teams":{"post":{"summary":"Adds a team. (Roles: user)","tags":["Team"],"operationId":"addTeam","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"team","in":"body","required":true,"schema":{"$ref":"#/definitions/Team"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Team"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"get":{"summary":"Gets a user's teams. (Roles: user)","tags":["Team"],"operationId":"getTeams","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/Team"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/teams/{id}/join":{"get":{"summary":"Joins a team. (Roles: user)","tags":["Team"],"operationId":"joinTeam","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/LoginPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/teams/{id}/invite/{email}":{"get":{"summary":"Invite email to team. (Roles: user)","tags":["Team"],"operationId":"inviteToTeam","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"email","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Team"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"delete":{"summary":"Remove invitation to team. (Roles: user)","tags":["Team"],"operationId":"removeInvite","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"email","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Team"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/teams/{id}/users":{"get":{"summary":"Get team users. (Roles: user)","tags":["Team"],"operationId":"getTeamUsers","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/TeamUserInfo"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/teams/{id}/users/{userId}":{"delete":{"summary":"Remove team user. (Roles: user)","tags":["Team"],"operationId":"removeTeamUser","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"userId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Team"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/teams/{id}/shared":{"get":{"summary":"Gets a team's shared content. (Roles: user)","tags":["Team"],"operationId":"getSharedItems","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/SharedItem"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/{id}":{"get":{"summary":"Get a game's information (Roles: user)","tags":["Games"],"operationId":"getGameInformation","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Game"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/":{"post":{"summary":"Get multiple games' information (Roles: user)","tags":["Games"],"operationId":"getGamesInformation","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GameIds"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Game"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/search/games/title/{query}":{"get":{"summary":"Searches a game based on title (Roles: user)","tags":["Games"],"operationId":"searchGamesByTitle","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"query","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/Game"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/utils/genres":{"get":{"summary":"Get all game genres (Roles: user, none)","tags":["Games"],"operationId":"getGenres","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"type":"string"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/utils/generation-styles":{"get":{"summary":"Get all generatiokn styles (Roles: user, none)","tags":["Games"],"operationId":"getGenerationStyles","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"type":"string"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/utils/platforms":{"get":{"summary":"Get all game platforms (Roles: user, none)","tags":["Games"],"operationId":"getPlatforms","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"type":"string"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/utils/sprite-poses":{"get":{"summary":"Get supported sprite poses (Roles: user, none)","tags":["Games"],"operationId":"getSpritePoses","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/SpritePose"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/utils/animation-presets":{"get":{"summary":"Get animation presets (Roles: user, none)","tags":["Games"],"operationId":"getAnimationPresets","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"parameters":[{"name":"id","in":"query","description":"Animation preset ID. If provided, returns the full preset with perspectives. If omitted, returns summaries without perspectives.","required":false,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/AnimationPreset"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"404":{"description":"Animation preset not found","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/suggest/project/{id}/assets":{"post":{"summary":"Suggests assets based on a project (Roles: user)","tags":["Gamestorming"],"operationId":"suggestProjectAssets","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/SuggestPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/ThreeDAsset"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/suggest/project/{id}/videos":{"post":{"summary":"Suggests videos based on a project (Roles: user)","tags":["Gamestorming"],"operationId":"suggestProjectVideos","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/SuggestPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GeneratedVideo"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/suggest/project/{id}/audio":{"post":{"summary":"Suggests audio based on a project (Roles: user)","tags":["Gamestorming"],"operationId":"suggestProjectAudio","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/SuggestPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GeneratedAudio"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/suggest/project/{id}/sections-to-add":{"get":{"summary":"Suggests sections to add (Roles: user)","tags":["Gamestorming"],"operationId":"suggestSectionsToAdd","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"selected_element","in":"query","required":false,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"type":"string"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/suggest/project/{id}/generated-images":{"post":{"summary":"Suggests generated images based on a project (Roles: user)","tags":["Gamestorming"],"operationId":"suggestProjectGeneratedImages","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/SuggestPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GameImage"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/suggest/project/{id}/text/{type}":{"post":{"summary":"Suggests text based on a project (Roles: user)","tags":["Gamestorming"],"operationId":"suggestProjectText","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"type","in":"path","required":true,"type":"string"},{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GeneratedText"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/suggest/project/{id}/followup":{"post":{"summary":"Suggests text based on a project (Roles: user)","tags":["Gamestorming"],"operationId":"suggestProjectFollowUp","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/SuggestPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GeneratedText"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/suggest/project/{id}/titles":{"get":{"summary":"Suggests titles based on a project (Roles: user)","tags":["Gamestorming"],"operationId":"suggestProjectTitles","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GeneratedText"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/{generationId}":{"delete":{"summary":"Cancels a game generation (Roles: user)","tags":["Gamestorming"],"operationId":"cancelGeneration","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"generationId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/refined-3d-images":{"post":{"summary":"Generate 3D model images based on existing models (Roles: user)","tags":["Gamestorming"],"operationId":"generateRefined3DImages","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/Refined3DPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/ThreeDAsset"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/3d-images":{"post":{"summary":"Generates image suitable for 3d models (Roles: user)","tags":["Gamestorming"],"operationId":"generate3DImages","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}},{"name":"force3D","in":"query","required":false,"type":"boolean"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/ThreeDAsset"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/image-to-3d":{"post":{"summary":"Generates 3D models from image (Roles: user)","tags":["Gamestorming"],"operationId":"imageTo3D","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/ImageTo3DPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AssetGeneration"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/download-3d":{"post":{"summary":"Downloads a 3D model (Roles: user)","tags":["Gamestorming"],"operationId":"export3D","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/Download3DPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AssetGeneration"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/retopology":{"post":{"summary":"Retopologizes a 3D model (Roles: user)","tags":["Gamestorming"],"operationId":"retopology","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/RetopologyPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AssetGeneration"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/animations":{"get":{"summary":"Lists accepted animation clips for a 3D model (Roles: user)","tags":["Gamestorming"],"operationId":"listAnimations","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"model_asset_id","in":"query","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/Animation3DClip"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/animations/accept":{"post":{"summary":"Persists a chosen animation candidate against its 3D model (Roles: user)","tags":["Gamestorming"],"operationId":"acceptAnimation","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/AcceptAnimationPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Animation3DClip"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/animations/{id}":{"delete":{"summary":"Deletes an accepted animation clip (Roles: user)","tags":["Gamestorming"],"operationId":"deleteAnimation","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/images":{"post":{"summary":"Generates images (Roles: user)","tags":["Gamestorming"],"operationId":"generateImages","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}},{"name":"skipGenerationHistory","in":"query","required":false,"type":"boolean"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GameImage"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/images-inpainting":{"post":{"summary":"Generates images inpainting (Roles: user)","tags":["Gamestorming"],"operationId":"generateImagesInpainting","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}},{"name":"skipGenerationHistory","in":"query","required":false,"type":"boolean"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GameImage"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/align-images":{"post":{"summary":"Aligns two images and returns alignment factors (Roles: user)","tags":["Gamestorming"],"operationId":"alignImages","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/AlignImagesPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AlignImagesResult"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/segmentation":{"post":{"summary":"Segments an image into a mask (Roles: user)","tags":["Gamestorming"],"operationId":"segmentImage","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/SegmentImagePayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/StringValue"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/videos":{"post":{"summary":"Generates videos (Roles: user)","tags":["Gamestorming"],"operationId":"generateVideo","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateVideoPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GeneratedVideo"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/videos/first-frame":{"post":{"summary":"Generates a video's first frame (Roles: user)","tags":["Gamestorming"],"operationId":"generateVideoFirstFrame","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GeneratedVideo"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/video-audio":{"post":{"summary":"Generates video audio (Roles: user)","tags":["Gamestorming"],"operationId":"generateVideoAudio","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateVideoAudioPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GeneratedVideo"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/audio":{"post":{"summary":"Generates audio assets (Roles: user)","tags":["Gamestorming"],"operationId":"generateAudio","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateAudioPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GeneratedAudio"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/sprite-image":{"post":{"summary":"Generates spritesheet from text (Roles: user)","tags":["Gamestorming"],"operationId":"generateSprite","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/Spritesheet"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/image-to-spritesheet":{"post":{"summary":"Generates spritesheet from image (Roles: user)","tags":["Gamestorming"],"operationId":"generateImageToSpritesheet","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Spritesheet"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/image":{"post":{"summary":"Generate game-art images from a text prompt alone, selecting an image_type (e.g. sprite) and optionally art_style, perspective, and aspect_ratio. Synchronous until September 10, 2026: the call blocks until generation finishes and returns an array of image results, each with a url; request n (1-8) to control how many variations come back. Because it generates purely from text it takes no source image, so there is no upload size limit to trip. Credits are charged only on success, scaled to the number of images produced. Use createImage to make new images from scratch; use generateWithStyle to match a reference image's art style, editImage to modify an existing image, and removeBackground to cut out a subject. Pass an optional request_id to tag the results so you can retrieve them later via `GET /assets/images/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Images"],"operationId":"createImage","x-credit-action":"GENERATE_IMAGE","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateImagePayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/ImageResult"}}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/image/style":{"post":{"summary":"Generate new images that match the visual style of a reference image: supply a style_image (URL or base64) plus a text prompt describing what to create and an image_type (defaults to sprite). Synchronous until September 10, 2026: the call blocks and returns an array of image results, each with a url; request n (1-4) to control the number of variations. The style_image is uploaded and validated, and an image larger than 15MB is rejected with HTTP 400. Credits are charged only on success, scaled to the number of images produced. Use this instead of createImage when style consistency with an existing asset matters; use editImage to alter the content of a specific image rather than borrow its style, and removeBackground to isolate a subject. Pass an optional request_id to tag the results so you can retrieve them later via `GET /assets/images/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Images"],"operationId":"generateWithStyle","x-credit-action":"GENERATE_IMAGE","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateWithStylePayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/ImageResult"}}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/image/edit":{"post":{"summary":"Modify an existing image according to text instructions: supply a source image (URL or base64) and a prompt describing the changes (e.g. \"add clouds\", \"warmer color scheme\"), with an optional reference_image for extra style or content guidance. Synchronous until September 10, 2026: the call blocks and returns an array of image results, each with a url; request n (1-4) to control the number of edited variations. Provided images are uploaded and validated, and any image larger than 15MB is rejected with HTTP 400. Credits are charged only on success, scaled to the number of images produced. Use editImage to transform a specific existing image; use createImage to generate from text alone, generateWithStyle to borrow a reference's art style, and removeBackground for the dedicated background-removal case. Pass an optional request_id to tag the results so you can retrieve them later via `GET /assets/images/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Images"],"operationId":"editImage","x-credit-action":"GENERATE_IMAGE","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/EditImagePayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/ImageResult"}}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/sprite/animate":{"post":{"summary":"Animate a static sprite into a spritesheet driven by a motion text prompt (image-to-spritesheet): supply an initial_image (URL or base64) plus a motion_prompt like \"walking\" or \"attack slash\", and optionally a final_image to interpolate between a start and end frame. For animations driven by up to three keyframes (including a middle frame), use animateSpriteKeyframes instead. Synchronous until September 10, 2026: the call blocks until the spritesheet is rendered and returns a single sprite result directly (spritesheet URL, frame layout, and optionally a GIF or individual frame URLs when requested) with no separate polling step. The chosen model must support sprite animation and the duration must be valid for it; incompatible model/duration combinations return HTTP 400. Credits are charged only on success, based on the produced duration and never more than the duration you requested. For best results, call generatePose first to put the character into the desired pose, then animate it here; use listAnimationPresets to discover named presets, and prefer transferMotion when you already have a reference video or preset to copy motion from rather than describing it in text. Pass an optional request_id to tag the result so you can locate it later via `GET /assets/sprites/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Spritesheets"],"operationId":"animateSprite","x-credit-action":"ANIMATE_SPRITE","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/AnimateSpritePayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/SpriteResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/sprite/animate-keyframes":{"post":{"summary":"Animate a sprite through up to three fixed keyframes - initial_image, middle_image and final_image (each a URL or base64) - producing a spritesheet that interpolates through the provided frames in order. At least one of initial_image or middle_image is required (a final_image alone has nothing to anchor the animation); any keyframe may be omitted. The motion_prompt is optional here - when omitted, the motion is derived purely from the keyframes. Runs on the Forge family, the only models supporting middle keyframes: Forge (default) or Forge Pixel for pixel-art sprites; both share the same durations and pricing. Synchronous until September 10, 2026: the call blocks until the spritesheet is rendered and returns a single sprite result directly (spritesheet URL, frame layout, and optionally a GIF or individual frame URLs when requested) with no separate polling step. Credits are charged only on success, based on the produced duration and never more than the duration you requested. Use animateSprite instead for the classic single-image + text-prompt animation with model choice. Pass an optional request_id to tag the result so you can locate it later via `GET /assets/sprites/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Spritesheets"],"operationId":"animateSpriteKeyframes","x-credit-action":"ANIMATE_SPRITE","x-credit-models":["forge","forge-pixel"],"security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/AnimateSpriteKeyframesPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/SpriteResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/sprite/pose":{"post":{"summary":"Re-pose an existing sprite into a new target pose while preserving the character, taking a source image (URL or base64), a pose name (or \"Other\" with a free-text description), and an optional n (1-4) for how many variations to produce. Only works with sprite image types (not icons, screenshots, etc.). Synchronous until September 10, 2026: the call blocks and returns an array of pose results, each containing the generated image url, the pose and description used, and a suggested motion_prompt tuned for that pose. Credits are charged only on success, scaled by the number of images generated. This is typically the first step before animating: call generatePose to set the character's pose, then feed the result (and its suggested motion_prompt) into animateSprite for the best animation quality; use rotateSprite instead when you want to change the camera angle rather than the pose. Pass an optional request_id to tag the results so you can locate them later via `GET /assets/sprites/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Spritesheets"],"operationId":"generatePose","x-credit-action":"GENERATE_IMAGE","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GeneratePosePayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/PoseResult"}}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/sprite/rotate":{"post":{"summary":"Re-render an existing sprite from a different camera viewpoint while keeping the same character and pose, taking a source image (URL or base64), a required camera_rotation azimuth (one of 0, 45, 90, 135, 180, -135, -90, -45 degrees), an optional camera_elevation (0, 30, or 60 degrees; omit to keep the current elevation), and an optional n (1-4) for the number of variations. Only works with sprite image types (not icons, screenshots, etc.). Synchronous until September 10, 2026: the call blocks and returns an array of rotate-sprite results, each with the generated image url and the camera_rotation and camera_elevation that were applied. Credits are charged only on success, scaled by the number of images generated. Use this to produce alternate view angles of a character; use generatePose instead to change the character's pose rather than the camera, and animateSprite or transferMotion to bring a sprite to life. Pass an optional request_id to tag the results so you can retrieve them later via `GET /assets/sprites/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Spritesheets"],"operationId":"rotateSprite","x-credit-action":"GENERATE_IMAGE","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/RotateSpritePayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/RotateSpriteResult"}}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/sprite/transfer-motion":{"post":{"summary":"Transfer motion from a reference video or a named animation preset onto a static sprite image, producing an animated spritesheet that mimics the reference movement. Provide the sprite as image (URL or base64) plus either a video URL or a preset_id together with perspective and direction. Synchronous until September 10, 2026: the call blocks and returns a single sprite result directly (spritesheet URL, plus optional GIF, individual frames, or with-background spritesheet when requested) with no polling step. It returns HTTP 400 if neither a video nor a complete preset_id/perspective/direction triple is supplied, if the named preset, perspective, or direction cannot be resolved, or if the model/duration combination is invalid. Credits are charged only on success, based on the spritesheet's actual produced duration (which may be shorter than requested if the reference video is shorter). Use this when you have an existing motion clip or preset to copy; prefer animateSprite to generate animation purely from a text prompt, and call listAnimationPresets first to get valid preset_id, perspective, and direction values. Pass an optional request_id to tag the result so you can retrieve it later via `GET /assets/sprites/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Spritesheets"],"operationId":"transferMotion","x-credit-action":"TRANSFER_SPRITE_MOTION","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/TransferMotionPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/SpriteResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/sprite/animation-presets":{"get":{"description":"List the available animation presets along with their perspectives and the eight supported compass directions (N, NE, E, SE, S, SW, W, NW). Synchronous GET with no request body: it returns an animations array (each with id, name, category, description, duration, preview_url, and - when the preset can be retargeted onto a rigged 3D model - clip_url), a deduplicated perspectives array, and the directions list. This is a free discovery endpoint and does not charge credits. Use it to obtain the preset_id, perspective, and direction values that transferMotion needs, and to find motion preset names you can reference when animating; pair it with transferMotion (to apply a preset onto a sprite), animateSprite (text-prompt animation), or animate3DModelPreset (apply a clip_url-backed preset to a rigged 3D model). Requires an API key (user scope).","tags":["Spritesheets"],"operationId":"listAnimationPresets","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AnimationPresetsResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/sprite/edit":{"post":{"summary":"Edit a previously generated spritesheet: re-prompt its underlying animation (edit_mode \"prompt\"), extend the frame beyond its borders (\"outpaint\"), or repair a bad loop (\"fix_loop\"). Pass the `spritesheet_url` you received from `animateSprite`, `transferMotion`, or an earlier edit - it must be a spritesheet you generated within the last 7 days; arbitrary external images are not accepted. A `prompt` is required for edit_mode \"prompt\", optional for \"outpaint\", and not accepted for \"fix_loop\"; optionally add up to 5 reference `images` (URL or base64) to guide the edit. Synchronous until September 10, 2026: the call blocks until the edited spritesheet is rendered and returns the same result shape as animateSprite (spritesheet URL, frame layout, optional GIF or individual frames). Credits are charged only on success, based on the produced duration and never more than the duration you requested. Pass an optional `request_id` to tag the result for later retrieval via `GET /assets/sprites/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Spritesheets"],"operationId":"editSpritesheet","x-credit-action":"EDIT_SPRITESHEET","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/EditSpritesheetPayloadPublic"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/SpriteResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/video":{"post":{"summary":"Generate a short video clip from a source image and a motion text prompt (image-to-video). Synchronous until September 10, 2026: the call blocks until rendering finishes and returns the video URL and its actual duration in seconds - there is no separate polling step. Optionally pass `final_image` to interpolate between a start and end frame. The chosen `model` and `duration` must be compatible (incompatible combinations return HTTP 400); see the `model` and `duration` fields for the values each model accepts. Credits are charged only on success, based on the produced duration and never more than the duration you requested. Pass an optional `request_id` to tag the result so you can locate it later via `GET /assets/videos/results`. Related tools: use `createImage` for static images, `animateSprite` for sprite-sheet animation, and `GET /assets/videos/results` to list videos you generated earlier. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Videos"],"operationId":"createVideo","x-credit-action":"GENERATE_VIDEO","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateVideoPayloadPublic"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/VideoResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/video/references":{"post":{"summary":"Generate a video from 1-5 reference images and a text prompt (references-to-video). Unlike createVideo, which animates a single source image, this composes a new scene that borrows characters, objects, and style from the reference images. Each image can be a URL or base64. Synchronous until September 10, 2026: the call blocks until rendering finishes and returns the video URL and its actual duration in seconds. Choose the output shape with `aspect_ratio` (\"default\" lets the model decide). The chosen `model` and `duration` must be compatible (incompatible combinations return HTTP 400). Credits are charged only on success, based on the produced duration and never more than the duration you requested. Pass an optional `request_id` to tag the result so you can locate it later via `GET /assets/videos/results`. Related tools: `createVideo` for image-to-video, `editVideo` to modify a generated video. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Videos"],"operationId":"createVideoFromReferences","x-credit-action":"REFERENCES_TO_VIDEO","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/ReferencesToVideoPayloadPublic"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/VideoResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/video/edit":{"post":{"summary":"Edit a previously generated video with a text prompt and optional reference images (video-to-video). Pass the video `url` you received from `createVideo`, `createVideoFromReferences`, or an earlier edit - it must be a video you generated within the last 7 days; arbitrary external videos are not accepted. Optionally add up to 5 reference `images` (URL or base64) to guide the edit. Synchronous until September 10, 2026: the call blocks until rendering finishes and returns the new video URL and its actual duration in seconds. Credits are charged only on success, based on the produced duration and never more than the duration you requested. Pass an optional `request_id` to tag the result so you can locate it later via `GET /assets/videos/results`. Related tools: `createVideo` to generate the source clip, `createVideoFromReferences` for reference-driven generation. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Videos"],"operationId":"editVideo","x-credit-action":"VIDEO_EDIT","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/EditVideoPayloadPublic"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/VideoResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/video/upscale":{"post":{"summary":"Upscale a previously generated video to twice its resolution (2x). Pass the video `url` you received from `createVideo`, `createVideoFromReferences`, or `editVideo` - it must be a video you generated within the last 7 days; arbitrary external videos are not accepted. Only videos below 960x960 pixels can be upscaled (larger sources are rejected). Synchronous until September 10, 2026: the call blocks until upscaling finishes and returns the new video URL and its duration in seconds. Billed per second of video, independent of model, charged only on success. Pass an optional `request_id` to tag the result so you can locate it later via `GET /assets/videos/results`. Related tools: `createVideo` for image-to-video, `editVideo` to modify a generated video. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Videos"],"operationId":"upscaleVideo","x-credit-action":"UPSCALE_VIDEO","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/UpscaleVideoPayloadPublic"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/VideoResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/image/remove-background":{"post":{"summary":"Remove the background from a single image, returning the subject isolated on a transparent background. Supply the source image (URL or base64); optionally set crop to trim the result to the content, and creative_edit (default true) for higher-quality output that may not match the input pixel-for-pixel. Synchronous until September 10, 2026: the call blocks and returns a single image result with a url (not an array). The image is uploaded and validated, and an image larger than 15MB is rejected with HTTP 400. Credits are charged only on success. Use removeBackground for this dedicated cutout task; editImage can also remove backgrounds via a prompt but is better for broader edits, while createImage and generateWithStyle produce new images rather than process an existing one. Pass an optional request_id to tag the result so you can retrieve it later via `GET /assets/images/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Images"],"operationId":"removeBackground","x-credit-action":"GENERATE_IMAGE","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/RemoveBackgroundPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/ImageResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/3d-model":{"post":{"summary":"Convert a single source image into a textured 3D model (image-to-3D). Synchronous until September 10, 2026: the call blocks while the mesh is generated, decompressed, and re-uploaded, then returns a downloadable GLB model_url plus an array of snapshot image URLs rendered from different angles (handy for previews). Accepts optional mesh controls: target_num_faces (max triangle count, 1000-200000, default 50000), texture_size (1024 or 2048, default 2048), and texture_type (\"pbr\", \"simple\", or \"none\", default \"pbr\"). Credits are charged only on success. Pass an optional request_id to tag the result so you can locate it later via `GET /assets/3d-models/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["3D Models"],"operationId":"create3DModel","x-credit-action":"GENERATE_3D_MODEL","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/Create3DModelPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Model3DResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/3d-model/rig":{"post":{"summary":"Rig a 3D model: generate a skeleton and skin weights for an existing GLB so it can be animated. Accepts a URL or base64-encoded GLB in `model`. Synchronous until September 10, 2026: the call blocks while the rig is generated, then returns a downloadable `model_url` for the rigged GLB. rig_type selects the skeleton prior - general (default, any asset), humanoid (anime-style characters), game (classic game-character rigs), or the pinned humanoid templates for two-armed, two-legged characters: humanoid_template (standard 22-joint skeleton with named joints, required for animating from the preset library) and humanoid_template_hands (52 joints, five fingers per hand). joint_naming relabels the identified joints to a convention - smpl (default), mixamo, humanik, unreal, godot, rigify, or vroid - without changing the skeleton. Credits are charged only on success. Rigging is non-destructive to geometry but replaces any prior skeleton, so animations made against an old rig no longer apply. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["3D Models"],"operationId":"rigModel","x-credit-action":"RIG_3D_MODEL","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/Rig3DModelPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Rig3DModelResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/3d-model/animate":{"post":{"summary":"Generate text-driven skeletal animations for an already-rigged 3D model. Pass the rigged GLB in `model` (URL or base64) and a motion `prompt` (e.g. \"walking\", \"swinging its axe\"). The model must already have a skeleton - rig it first via the rig endpoint if not. Synchronous until September 10, 2026: returns num_variants candidate animations (default 4), each a standalone animation-only GLB (skeleton + one clip, no mesh) in `glb_url` plus an mp4 `preview_url`, so you can pick the best one and fuse it with your model in a game engine or three.js. mode selects the representation - rot_trans (default, most faithful) or rot_only (for retargeting). Animation quality is hit-or-miss, which is why multiple candidates are returned. Credits are charged once per call regardless of variant count, only on success. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["3D Models"],"operationId":"animate3DModel","x-credit-action":"ANIMATE_3D_MODEL","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/Animate3DModelPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AnimationCandidates"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/3d-model/animate-preset":{"post":{"summary":"Apply a curated animation preset to an already-rigged 3D model (retargeting). Pass the rigged GLB in `model` (URL or base64) and a `preset_id` from the animation presets list (see the spritesheet animation-presets endpoint) - only presets that expose a `clip_url` can be applied to a 3D model. The model must have a humanoid-template rig (rig it with rig_type humanoid_template or humanoid_template_hands). Synchronous until September 10, 2026: returns one retargeted animation - a standalone animation-only GLB in `glb_url` plus an mp4 `preview_url` - in the same `animations` envelope as the animate endpoint. crop_loop trims the clip to its seamlessly-looping span (omit to follow the preset's own loop flag); in_place removes net travel so the character moves on the spot, as game-engine locomotion expects (omit to follow crop_loop). Credits are charged only on success. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["3D Models"],"operationId":"animate3DModelPreset","x-credit-action":"ANIMATE_3D_PRESET","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/Animate3DPresetModelPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AnimationCandidates"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/audio/sound-effect":{"post":{"summary":"Produce a short sound effect (SFX) from a text description, such as \"laser gun firing\" or \"footsteps on gravel\". Synchronous until September 10, 2026: the call blocks until generation finishes and returns a single audio result containing a URL; there is no separate polling step. The description field is required, duration is capped at 10 seconds (0 means auto-pick based on the description), and you may set loop to true for a seamlessly looping effect. Credits are charged on success. Use this for short, discrete sounds; use createAmbiance for a continuous looping background soundscape, createMusic for musical pieces, and createAudioTransform to remix an existing audio sample. Pass an optional request_id to tag the result so you can locate it later via `GET /assets/audio/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Audio"],"operationId":"createSoundEffect","x-credit-action":"GENERATE_AUDIO_SFX","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateSoundEffectPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AudioResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/audio/ambiance":{"post":{"summary":"Produce a looping background ambiance soundscape from a text description, such as \"windy forest at dusk\" or \"busy tavern interior\". Synchronous until September 10, 2026: the call blocks until generation finishes and returns a single audio result containing a URL; there is no separate polling step. The description field is required and duration is capped at 10 seconds (0 means auto-pick based on the description). Credits are charged on success. Use this for continuous, atmospheric background loops; use createSoundEffect for short discrete sound effects, createMusic for musical pieces, and createAudioTransform to remix an existing audio sample. Pass an optional request_id to tag the result so you can locate it later via `GET /assets/audio/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Audio"],"operationId":"createAmbiance","x-credit-action":"GENERATE_AUDIO_SFX","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateAmbiancePayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AudioResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/audio/music":{"post":{"summary":"Produce a piece of music from a text description, such as \"epic orchestral battle theme\" or \"calm piano melody\", with optional lyrics. Synchronous until September 10, 2026: the call blocks until generation finishes and returns a single audio result containing a URL; there is no separate polling step. The description field is required; duration must be one of the allowed values (0 means auto, otherwise multiples of 10 up to 180 seconds) and out-of-range values return HTTP 400. Credits are charged on success. Use this for songs and musical scores; use createSoundEffect for short sound effects, createAmbiance for looping background soundscapes, and createAudioTransform to remix an existing audio sample. Pass an optional request_id to tag the result so you can locate it later via `GET /assets/audio/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Audio"],"operationId":"createMusic","x-credit-action":"GENERATE_AUDIO_MUSIC","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateMusicPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AudioResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/audio/transform":{"post":{"summary":"Remix an existing audio sample (a sound effect, ambiance, or music clip) into a variation guided by a text prompt, for example turning a track into an 80s synthwave or metal version. Both the sample and the prompt are required; the sample is uploaded as a URL or base64 audio and must be at most 15MB or the call returns HTTP 400, and duration must be one of the allowed values (0 means match the source, otherwise multiples of 10 up to 180 seconds). Synchronous until September 10, 2026: the call blocks until generation finishes and returns a single audio result containing a URL; there is no separate polling step. The optional modification_strength (0 to 1, default 0.5) controls how far the result departs from the original. Credits are charged on success. Use this to transform existing audio you already have; use createSoundEffect, createAmbiance, or createMusic to generate audio from scratch. Pass an optional request_id to tag the result so you can locate it later via `GET /assets/audio/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Audio"],"operationId":"createAudioTransform","x-credit-action":"GENERATE_AUDIO_TRANSFORM","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateAudioTransformPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AudioResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/audio/voice":{"post":{"summary":"Design a new voice from a character description (such as \"deep-voiced warrior\" or \"cheerful young girl\") and have it speak a short line of text, returning a sample of that newly created voice. Both voice_description and text are required, the spoken text is limited to 200 characters or the call returns HTTP 400, and type selects \"human\" or \"non-human\" voices. Synchronous until September 10, 2026: the call blocks until generation finishes and returns a single audio result containing a URL; there is no separate polling step. Credits are charged on success. Use this to invent and audition a voice from a description; use createSpeech for text-to-speech that clones a specific voice from an audio sample, and createSpeechPreset for text-to-speech using a named preset voice. Pass an optional request_id to tag the result so you can locate it later via `GET /assets/audio/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Audio"],"operationId":"createVoice","x-credit-action":"GENERATE_AUDIO_SPEECH","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateVoicePayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AudioResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/audio/speech":{"post":{"summary":"Convert text to speech by cloning the voice from an audio sample you provide (voice-cloning text-to-speech). Both text and sample are required; the text is limited to 1000 characters and the sample is supplied as a URL or base64 audio that must be at most 15MB, with violations returning HTTP 400. Synchronous until September 10, 2026: the call blocks until generation finishes and returns a single audio result containing a URL; there is no separate polling step. Credits are charged on success. Use this when you have a reference voice sample to clone; use createSpeechPreset to speak with a built-in named preset voice instead, and createVoice to design a brand-new voice from a text description rather than cloning one. Pass an optional request_id to tag the result so you can locate it later via `GET /assets/audio/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Audio"],"operationId":"createSpeech","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"x-credit-action":"GENERATE_AUDIO_SPEECH","parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateSpeechPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AudioResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/audio/speech-preset":{"post":{"summary":"Convert text to speech using a named built-in preset voice, with optional emotion and language settings. Both text and voice_preset_id are required and the text is limited to 1000 characters; invalid input returns HTTP 400. Synchronous until September 10, 2026: the call blocks until generation finishes and returns a single audio result containing a URL; there is no separate polling step. Credits are charged on success. Use this when you want a ready-made catalog voice and do not need to supply your own sample; use createSpeech to clone a voice from an audio sample instead, and createVoice to design a new voice from a text description. Pass an optional request_id to tag the result so you can locate it later via `GET /assets/audio/results`. Requires an API key (user scope). Async is the intended integration pattern: pass `async: true` and the call returns 202 with a job id - poll `GET /assets/jobs/{id}` (`getApiJob` in MCP), honouring poll_after_ms or long-polling with wait=30, until status is succeeded, whose result field is exactly the response documented for this operation. Synchronous responses are deprecated: they remain the default until September 10, 2026, after which requests default to async and return 202 with a job id; set `async: false` explicitly to keep synchronous behavior during and after the transition. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).","tags":["Audio"],"operationId":"createSpeechPreset","x-credit-action":"GENERATE_AUDIO_SPEECH","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateSpeechPresetPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AudioResult"}},"202":{"description":"Accepted - the generation was queued (async: true, or a synchronous call still running after 15 minutes). Poll GET /assets/jobs/{id}.","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/generations":{"get":{"description":"List your generation history - everything you have generated, through the API or in the Ludo web studio - most recent first. This is a free read-only lookup (no credits, no generation); it does not create anything. Pick a media `type` (image, spritesheet, video, audio, or 3d) and optionally narrow by free-text `search` (matched against tags, prompts and the other text on the asset), `source`, a `date_from`/`date_to` range (unix seconds), or the `request_id` you passed when you generated. Results are paginated with `page_size` (default 20, max 100) and 1-based `page_number`, and come back as an object: `items` (this page), plus `page`, `page_size` and `has_more`. Page until `has_more` is false rather than until you get a short page. `has_more` is computed by reading one row beyond the page, so it costs no extra query; there is deliberately no total count.\n\n`source` selects the surface an item came from: `api` returns only your API/MCP generations, `web` only your web-studio ones, `all` (the default) both. API-originated items expire 7 days after creation - their files live under a lifecycle-pruned storage prefix - so `source=api` never returns anything older than 7 days, while web items are kept indefinitely and are not time-bounded. API-originated results expire after 7 days - download anything you want to retain. (Assets can be kept permanently from the Ludo web app.) Each item carries an `id`, its generation-history id, which identifies the item across these listings. Every item also carries `type`, echoing the media type you asked for; on audio items the kind of audio (sfx, music, speech) is reported as `audio_type` so it does not collide with it.\n\nThis lists work that has already FINISHED. To follow a generation that is still queued or running, poll getApiJob with the job id you were given instead. Requires an API key (user scope).","x-mcp-description":"List your generation history across the API and the web app - filter by type, source (api|web|all), text search, and date range. Returns {items, page, page_size, has_more}; keep paging while has_more is true. API-originated results expire after 7 days - download anything you want to retain. For an in-flight job's status use getApiJob instead.","tags":["Generations"],"operationId":"listGenerations","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"type","in":"query","description":"Which kind of generation to list","required":true,"type":"string","enum":["image","spritesheet","video","audio","3d"]},{"name":"search","in":"query","description":"Free-text search. Every whitespace-separated term must match (case-insensitive substring) the item's tags or one of its text fields (prompt, hints, style, label, ...), so \"dwarf axe\" narrows to items matching both.","required":false,"type":"string"},{"name":"source","in":"query","description":"Which surface the items were generated from: api (your API/MCP generations, last 7 days only), web (your Ludo web studio generations, no time limit), or all (default, both)","required":false,"type":"string","enum":["api","web","all"],"default":"all"},{"name":"date_from","in":"query","description":"Only return items generated at or after this time (unix seconds)","required":false,"type":"integer"},{"name":"date_to","in":"query","description":"Only return items generated at or before this time (unix seconds)","required":false,"type":"integer"},{"name":"page_size","in":"query","description":"Items per page (1-100)","required":false,"type":"integer","default":20},{"name":"page_number","in":"query","description":"1-based page number","required":false,"type":"integer","default":1},{"name":"request_id","in":"query","description":"Only return items tagged with this request_id when you generated them","required":false,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GenerationList"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/images/results":{"get":{"description":"**Deprecated:** prefer `GET /assets/generations` (all types, paginated, same source filter). This endpoint continues to work; no removal is scheduled.\nList images you previously generated through the API, most recent first. This is a free read-only history lookup (no credits, no generation); it does not create anything. Pass an optional request_id query parameter to return only the results tagged with that id when you originally called createImage, editImage, generateWithStyle, generatePose, rotateSprite, or removeBackground. Requires an API key (user scope).","tags":["Images"],"operationId":"getImageResults","deprecated":true,"x-mcp-exclude":true,"security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"request_id","in":"query","description":"Filter results by request_id","required":false,"type":"string"},{"name":"source","in":"query","description":"Which surface the results were generated from: api (default, your API/MCP generations from the last 7 days), web (your Ludo web studio generations, no time limit), or all","required":false,"type":"string","enum":["api","web","all"],"default":"api"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/ImageResult"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/sprites/results":{"get":{"description":"**Deprecated:** prefer `GET /assets/generations` (all types, paginated, same source filter). This endpoint continues to work; no removal is scheduled.\nList spritesheets you previously generated through the API, most recent first. This is a free read-only history lookup (no credits, no generation); it does not create anything. Pass an optional request_id query parameter to return only the results tagged with that id when you originally called animateSprite or transferMotion. Requires an API key (user scope).","tags":["Spritesheets"],"operationId":"getSpriteResults","deprecated":true,"x-mcp-exclude":true,"security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"request_id","in":"query","description":"Filter results by request_id","required":false,"type":"string"},{"name":"source","in":"query","description":"Which surface the results were generated from: api (default, your API/MCP generations from the last 7 days), web (your Ludo web studio generations, no time limit), or all","required":false,"type":"string","enum":["api","web","all"],"default":"api"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/SpriteResult"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/videos/results":{"get":{"description":"**Deprecated:** prefer `GET /assets/generations` (all types, paginated, same source filter). This endpoint continues to work; no removal is scheduled.\nList videos you previously generated through the API, most recent first. This is a free read-only history lookup (no credits, no generation); it does not create anything. Pass an optional request_id query parameter to return only the results tagged with that id when you originally called createVideo. Requires an API key (user scope).","tags":["Videos"],"operationId":"getVideoResults","deprecated":true,"x-mcp-exclude":true,"security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"request_id","in":"query","description":"Filter results by request_id","required":false,"type":"string"},{"name":"source","in":"query","description":"Which surface the results were generated from: api (default, your API/MCP generations from the last 7 days), web (your Ludo web studio generations, no time limit), or all","required":false,"type":"string","enum":["api","web","all"],"default":"api"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/VideoResult"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/audio/results":{"get":{"description":"**Deprecated:** prefer `GET /assets/generations` (all types, paginated, same source filter). This endpoint continues to work; no removal is scheduled.\nList audio clips you previously generated through the API, most recent first. This is a free read-only history lookup (no credits, no generation); it does not create anything. Pass an optional request_id query parameter to return only the results tagged with that id when you originally called createSoundEffect, createAmbiance, createMusic, createAudioTransform, createVoice, createSpeech, or createSpeechPreset. Requires an API key (user scope).","tags":["Audio"],"operationId":"getAudioResults","deprecated":true,"x-mcp-exclude":true,"security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"request_id","in":"query","description":"Filter results by request_id","required":false,"type":"string"},{"name":"source","in":"query","description":"Which surface the results were generated from: api (default, your API/MCP generations from the last 7 days), web (your Ludo web studio generations, no time limit), or all","required":false,"type":"string","enum":["api","web","all"],"default":"api"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/AudioResult"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/3d-models/results":{"get":{"description":"**Deprecated:** prefer `GET /assets/generations` (all types, paginated, same source filter). This endpoint continues to work; no removal is scheduled.\nList 3D models you previously generated through the API, most recent first. This is a free read-only history lookup (no credits, no generation); it does not create anything. Pass an optional request_id query parameter to return only the results tagged with that id when you originally called create3DModel. Requires an API key (user scope).","tags":["3D Models"],"operationId":"get3DModelResults","deprecated":true,"x-mcp-exclude":true,"security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"request_id","in":"query","description":"Filter results by request_id","required":false,"type":"string"},{"name":"source","in":"query","description":"Which surface the results were generated from: api (default, your API/MCP generations from the last 7 days), web (your Ludo web studio generations, no time limit), or all","required":false,"type":"string","enum":["api","web","all"],"default":"api"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/ThreeDModelResult"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/jobs":{"get":{"description":"List the generation jobs you started through the API or MCP, most recent first. Web-app jobs are not included. This is a free read-only lookup (no credits, no generation). Filter with a comma-separated status list; limit defaults to 50 and is capped at 100. Requires an API key (user scope).","tags":["Jobs"],"operationId":"listApiJobs","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"status","in":"query","description":"Comma-separated statuses to include (queued, running, succeeded, failed, canceled). Defaults to all.","required":false,"type":"string"},{"name":"limit","in":"query","description":"Maximum number of jobs to return. Defaults to 50, capped at 100.","required":false,"type":"integer","format":"int32"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/PublicJob"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/assets/jobs/{id}":{"get":{"description":"Fetch one job you started through the API or MCP. While the job is queued or running the response carries poll_after_ms - wait at least that long before asking again. Better still, pass wait=<seconds> (up to 60) to long-poll: the request is held until the job reaches a terminal state, so one call replaces a polling loop. Once status is succeeded, `result` holds exactly what the synchronous form of the operation named by task_type would have returned. This is a free read-only lookup (no credits). Requires an API key (user scope).","tags":["Jobs"],"operationId":"getApiJob","x-mcp-description":"Poll the status of a generation job started by any tool. Every tool returns `{id, status}`; call getApiJob with that id until status is `succeeded` (then read `result`, shaped exactly like the tool's documented output) or `failed` (read `error`). Poll every few seconds, or pass `wait: 30` to long-poll - the call is held up to 30 seconds (max 60) and returns as soon as the job finishes.","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"wait","in":"query","description":"Seconds to long-poll for a terminal state (0-60). Defaults to 0 (return immediately).","required":false,"type":"integer","format":"int32"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"404":{"description":"No such job for this API key","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"delete":{"description":"Cancel a queued job you started through the API or MCP and refund its credits. Jobs that are already running cannot be canceled (409). Returns the canceled job. This is free (no credits). Requires an API key (user scope).","tags":["Jobs"],"operationId":"cancelApiJob","security":[{"ApiKey":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/PublicJob"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"404":{"description":"No such job for this API key","schema":{"$ref":"#/definitions/ErrorResponse"}},"409":{"description":"Job is not in a cancelable state (e.g. already running)","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/video-to-spritesheet":{"post":{"summary":"Generates spritesheet from video (Roles: user)","tags":["Gamestorming"],"operationId":"generateVideoToSpritesheet","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Spritesheet"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/spritesheet-to-audio":{"post":{"summary":"Generates audio for spritesheet (Roles: user)","tags":["Gamestorming"],"operationId":"generateSpritesheetToAudio","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateSpritesheetAudioPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Spritesheet"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/spritesheet-motion-transfer":{"post":{"summary":"Generates spritesheet by transferring motion from video or spritesheet to image (Roles: user)","tags":["Gamestorming"],"operationId":"generateSpritesheetMotionTransfer","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateSpritesheetMotionTransferPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Spritesheet"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/render-model":{"post":{"summary":"Renders 3D model to video (Roles: user)","tags":["Gamestorming"],"operationId":"renderModel","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/StringValue"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/StringValue"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/game-concept-header/images/{projectId}":{"delete":{"summary":"Cancels request to generate images for the game concept header (Roles: user)","tags":["Gamestorming"],"operationId":"cancelGameConceptHeaderImages","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/game-concept-header/images-new/{projectId}":{"post":{"summary":"Generates images for the game concept header (Roles: user)","tags":["Gamestorming"],"operationId":"generateGameConceptHeaderImagesNew","consumes":["application/octet-stream"],"security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"},{"name":"extract","in":"query","required":false,"type":"boolean"}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/image-prompts":{"post":{"summary":"Image prompts based on current trends (Roles: user)","tags":["Gamestorming"],"operationId":"getImagePrompts","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"isVideo","in":"query","required":false,"type":"boolean"},{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/TrendKeywords"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/images/generations":{"post":{"summary":"Generates images for generations (Roles: user)","tags":["Gamestorming"],"operationId":"generateImagesForGenerations","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GameImage"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/lite":{"post":{"summary":"Generates lite game concepts (Roles: user, none)","tags":["Gamestorming"],"operationId":"generateLite","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}},{"name":"user_id","in":"query","required":false,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GeneratedGame"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/step-by-step":{"post":{"summary":"Step by step game generator (Roles: user)","tags":["Gamestorming"],"operationId":"generateStepByStep","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/StepByStepPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/StepValue"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/ideas-from-steps":{"post":{"summary":"Step by step game generator (Roles: user)","tags":["Gamestorming"],"operationId":"generateIdeasFromSteps","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/StepByStepPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GeneratedGame"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/games/generate/new":{"post":{"summary":"Generates game ideas (Roles: user)","tags":["Gamestorming"],"operationId":"generateNew","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GeneratedGame"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/generate/new-map":{"post":{"summary":"Generates new map (Roles: user)","tags":["Gamestorming"],"operationId":"generateNewMap","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewMapPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GeneratedMap"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/generate/map-tiles":{"post":{"summary":"Generates map tiles (Roles: user)","tags":["Gamestorming"],"operationId":"generateMapTiles","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateMapTilesPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GeneratedMap"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/generate/inpaint-map":{"post":{"summary":"Inpaints map elements (Roles: user)","tags":["Gamestorming"],"operationId":"generateInpaintMap","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"object"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/generate/extract-map-element":{"post":{"summary":"Extracts map element (Roles: user)","tags":["Gamestorming"],"operationId":"generateExtractMapElement","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"object"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/generation-history/{type}":{"get":{"summary":"Get user's generation history by type (Roles: user)","tags":["Misc"],"operationId":"getGenerationHistory","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"type","in":"path","required":true,"type":"string","description":"Type of generation history to retrieve"},{"name":"pageSize","in":"query","required":false,"type":"number","description":"Number of items per page (default: 20, max: 100)"},{"name":"pageNumber","in":"query","required":false,"type":"number","description":"Page number (1-based, default: 1)"},{"name":"search","in":"query","required":false,"type":"string","description":"Free-text search; whitespace-separated terms are AND-ed over tags, prompts, user inputs (hints), art style, perspective, and label"},{"name":"outputsOnly","in":"query","required":false,"type":"boolean","default":false,"description":"When true, return only items that have a finished generated output for this type (drops image-only/intermediate items). Applied before pagination so number_pages reflects the filtered count. Combines with search as AND. No-op for types without outputs (audio, generated_image)."},{"name":"dateFrom","in":"query","required":false,"type":"number","description":"Inclusive lower bound (unix seconds) on the item's creation date. Combines with search/outputsOnly as AND; applied before pagination so number_pages reflects the filtered count."},{"name":"dateTo","in":"query","required":false,"type":"number","description":"Inclusive upper bound (unix seconds) on the item's creation date. Callers pass end-of-day to keep a single-day range inclusive."},{"name":"source","in":"query","required":false,"type":"string","enum":["web","api","all"],"default":"web","description":"Which surface the items were generated from: web (default, studio generations only), api (public API/MCP generations only - these expire 7 days after creation unless kept), or all."}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GenerationHistoryResults"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/generation-history/{type}/tags":{"get":{"summary":"Get distinct tags (with counts) for a generation-history type (Roles: user)","tags":["Misc"],"operationId":"getGenerationHistoryTags","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"type","in":"path","required":true,"type":"string","description":"Type of generation history"},{"name":"source","in":"query","required":false,"type":"string","enum":["web","api","all"],"default":"web","description":"Which surface the items were generated from: web (default), api, or all."}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GenerationHistoryTags"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/generation-history":{"post":{"summary":"Updates a user's generation history (Roles: user)","tags":["Misc"],"operationId":"updateGenerationHistory","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerationHistory"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GenerationHistory"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"delete":{"summary":"Removes a user's generation history (Roles: user)","tags":["Misc"],"operationId":"removeGenerationHistory","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"}}}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GenerationHistory"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/generation-history/keep":{"post":{"summary":"Promote an API-generated history item to a permanent web asset (Roles: user)","description":"API/MCP generations expire 7 days after creation (their files live under the api/ storage prefix, which is lifecycle-pruned). Keeping an item copies its files out of the api/ prefix, rewrites the stored URLs, and clears the api flag - after which it behaves exactly like a studio generation: permanent, visible in the default library view, and usable in projects.","tags":["Misc"],"operationId":"keepGenerationHistory","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","description":"GenerationHistory id of the API-generated item to keep"}}}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GenerationHistory"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/chat/gdd/{id}":{"post":{"summary":"Chat with a GDD(Roles: user)","tags":["Misc"],"operationId":"chatGdd","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/ChatRequest"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/ChatMessage"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/chat/assistant":{"post":{"summary":"Chat with the Ludo assistant (Roles: user)","tags":["Misc"],"operationId":"chatAssistant","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/AssistantRequest"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AssistantMessage"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/chat/help":{"post":{"summary":"Chat with the Ludo assistant -- focused on docs (Roles: user)","tags":["Misc"],"operationId":"chatHelp","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/AssistantRequest"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AssistantMessage"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/chat/assistant/logs":{"get":{"summary":"Gets the chat logs with the Ludo assistant (Roles: user)","tags":["Misc"],"operationId":"getChatAssistantLogs","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AssistantLog"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"post":{"summary":"Persists updates to an existing assistant conversation, e.g. generated media (Roles: user)","tags":["Misc"],"operationId":"saveChatAssistantConversation","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/SaveAssistantConversationRequest"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AssistantLog"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/chat/assistant/archive":{"get":{"summary":"Gets the Ludo assistant chat archives (Roles: user)","tags":["Misc"],"operationId":"getChatAssistantArchives","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/AssistantLog"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/chat/assistant/archive/{id}":{"delete":{"summary":"Removes an archive and returns the current archives (Roles: user)","tags":["Misc"],"operationId":"removeChatAssistantArchive","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/AssistantLog"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/cron/{type}":{"get":{"summary":"Runs a cronjob (Roles: none)","tags":["Misc"],"operationId":"runCron","security":[{"Bearer":[]}],"x-security-scopes":["none"],"parameters":[{"name":"type","in":"path","required":true,"type":"string"},{"name":"id","in":"query","type":"string"}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/showcase":{"get":{"summary":"Gets items for a showcase gallery (Roles: none)","tags":["Misc"],"operationId":"getShowcaseGallery","security":[{"Bearer":[]}],"x-security-scopes":["none"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/ShowcaseGallery"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/showcase/unity":{"get":{"summary":"Gets items for a showcase gallery in unity (Roles: none)","tags":["Misc"],"operationId":"getShowcaseGalleryUnity","security":[{"Bearer":[]}],"x-security-scopes":["none"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/ShowcaseGallery"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/feedback":{"post":{"summary":"Submits user feedback (Roles: user)","tags":["Misc"],"operationId":"submitFeedback","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/UserFeedback"}}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/health-check":{"get":{"summary":"Checks if the service is running (Roles: user, none)","tags":["Misc"],"operationId":"healthCheck","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/credits/costs":{"get":{"summary":"Gets all credit costs for platform features (Roles: none)","tags":["Misc"],"operationId":"getCreditCosts","security":[{"Bearer":[]}],"x-security-scopes":["none"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/CreditCosts"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/initial-payload":{"get":{"summary":"Gets the user the inital data necessary to use the platform (Roles: user)","tags":["Misc"],"operationId":"getInitialPayload","security":[{"Bearer":[]}],"x-security-scopes":["none"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/UserRefreshPayload"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/checkout":{"post":{"summary":"Proceeds to the Stripe Checkout page. (Roles: user, none)","tags":["Payments"],"operationId":"goToCheckout","security":[{"Bearer":[]}],"x-security-scopes":["none","user"],"parameters":[{"name":"auth","in":"body","required":true,"schema":{"$ref":"#/definitions/StripeCheckoutPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/StringValue"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/checkout-session":{"post":{"summary":"Creates a Stripe Checkout session for subscription. (Roles: user)","tags":["Payments"],"operationId":"checkoutSession","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"auth","in":"body","required":true,"schema":{"$ref":"#/definitions/CheckoutSessionPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/StringValue"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/billing":{"get":{"summary":"Proceeds to the Stripe Billing portal. (Roles: user)","tags":["Payments"],"operationId":"goToBillingPortal","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/StringValue"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/plans":{"get":{"summary":"Gets available payment plans (Roles: user, none)","tags":["Payments"],"operationId":"getPaymentPlans","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/PaymentPlan"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/subscribe/{planId}":{"post":{"summary":"Subscribes to a payment plan (Roles: user)","tags":["Payments"],"operationId":"subscribe","security":[{"Bearer":[]}],"consumes":["application/octet-stream"],"x-security-scopes":["user"],"parameters":[{"name":"planId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Subscription"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/confirm/setup-intent":{"post":{"summary":"Confirms a setup intent (Roles: user)","tags":["Payments"],"operationId":"confirmSetupIntent","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/StringValue"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/CreditCard"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/confirm/payment-intent":{"post":{"summary":"Confirms a payment intent (Roles: user)","tags":["Payments"],"operationId":"confirmPaymentIntent","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/StringValue"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Subscription"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/subscription/renew-credits":{"get":{"summary":"Renews credits immediately for active subscribers (Roles: user)","tags":["Payments"],"operationId":"renewCredits","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Subscription"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/topup":{"post":{"summary":"Purchases extra credits via slider top-up (Roles: user)","tags":["Payments"],"operationId":"purchaseTopup","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/TopupPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/TopupResult"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/topup/config":{"get":{"summary":"Returns slider top-up pricing brackets (Roles: user)","tags":["Payments"],"operationId":"getTopupConfig","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/TopupConfig"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/topup/auto":{"get":{"summary":"Returns the user's auto top-up settings (Roles: user)","tags":["Payments"],"operationId":"getAutoTopup","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AutoTopupSettings"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"put":{"summary":"Updates the user's auto top-up settings (Roles: user)","tags":["Payments"],"operationId":"setAutoTopup","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/AutoTopupPayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/AutoTopupSettings"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/subscription/pause":{"post":{"summary":"Pauses subscription for specified duration (Roles: user)","tags":["Payments"],"operationId":"pauseSubscription","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/PauseSubscriptionRequest"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Subscription"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/subscription/unpause":{"post":{"summary":"Resumes paused subscription immediately (Roles: user)","tags":["Payments"],"operationId":"unpauseSubscription","security":[{"Bearer":[]}],"x-security-scopes":["user"],"consumes":["application/octet-stream"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Subscription"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/subscription/reactivate":{"post":{"summary":"Reverses a scheduled cancellation (Roles: user)","tags":["Payments"],"operationId":"reactivateSubscription","security":[{"Bearer":[]}],"x-security-scopes":["user"],"consumes":["application/octet-stream"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Subscription"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/credit-card/default":{"post":{"summary":"Sets a default credit card (Roles: user)","tags":["Payments"],"operationId":"makeCardDefault","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/StringValue"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/CreditCard"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/credit-card":{"post":{"summary":"Adds a credit card (Roles: user)","tags":["Payments"],"operationId":"addCreditCard","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/StringValue"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/CreditCard"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"get":{"summary":"Gets the user's credit card (Roles: user)","tags":["Payments"],"operationId":"getCreditCards","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/CreditCard"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"delete":{"summary":"Removes a credit card (Roles: user)","tags":["Payments"],"operationId":"removeCreditCard","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/StringValue"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/CreditCard"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/invoices":{"get":{"summary":"Gets the user's invoices (Roles: user)","tags":["Payments"],"operationId":"getInvoices","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/Invoice"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/subscription/retry-payment":{"get":{"summary":"Retries the failed payment for a subscription (Roles: user)","tags":["Payments"],"operationId":"retryPayment","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Subscription"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/subscription":{"get":{"summary":"Gets the user's subscription (Roles: user)","tags":["Payments"],"operationId":"getSubscription","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Subscription"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"delete":{"summary":"Unsubscribes (Roles: user)","tags":["Payments"],"operationId":"unsubscribe","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Subscription"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/promo":{"get":{"summary":"Gets the active promo code (Roles: user)","tags":["Payments"],"operationId":"getPromoCode","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Coupon"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/payment/promo/{code}":{"get":{"summary":"Adds a promo code (Roles: user)","tags":["Payments"],"operationId":"addPromoCode","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"code","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/StringValue"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/stripe-event":{"post":{"tags":["Payments"],"operationId":"receiveStripeEvent","summary":"Receives a stripe event (Role: none)","security":[{"Bearer":[]}],"x-security-scopes":["none"],"parameters":[{"name":"event","in":"body","required":true,"schema":{"$ref":"#/definitions/StripeEvent"}}],"responses":{"204":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/images/{id}":{"get":{"summary":"Gets an image (Roles: user)","tags":["Misc"],"operationId":"getImage","security":[{"Bearer":[]}],"x-security-scopes":["none"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"format","in":"query","required":false,"type":"string"}],"produces":["image/png","image/jpeg","image/gif","image/webp"],"responses":{"200":{"description":"Success","schema":{"type":"string","format":"binary"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/images/upload":{"post":{"summary":"Uploads user images (Roles: user)","tags":["Misc"],"operationId":"uploadImage","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/UploadFilesPayload"}}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GameImage"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/images/remove-background":{"post":{"summary":"Removes the background of an image (Roles: user)","tags":["Misc"],"operationId":"removeImageBackground","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"crop","in":"query","type":"boolean"},{"name":"image","in":"body","required":true,"schema":{"$ref":"#/definitions/GameImage"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GameImage"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/images/original/{filename}":{"get":{"summary":"Gets an original image (Roles: user)","tags":["Misc"],"operationId":"getOriginalImage","security":[{"Bearer":[]}],"x-security-scopes":["none"],"parameters":[{"name":"filename","in":"path","required":true,"type":"string"}],"produces":["image/png","image/jpeg","image/gif"],"responses":{"200":{"description":"Success","schema":{"type":"string","format":"binary"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/images/proxy":{"post":{"summary":"Proxies an image (Roles: user)","tags":["Misc"],"operationId":"proxyImage","security":[{"Bearer":[]}],"x-security-scopes":["none"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GameImage"}}],"produces":["image/png","image/jpeg","image/gif"],"responses":{"200":{"description":"Success","schema":{"type":"string","format":"binary"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/{projectId}":{"delete":{"summary":"Archives the project (Roles: user)","tags":["Misc"],"operationId":"archiveProject","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Project"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"get":{"summary":"Gets a project (Roles: user)","tags":["Misc"],"operationId":"getProject","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Project"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/favorites":{"get":{"summary":"Gets all of the user's active favorites (Roles: user)","tags":["Misc"],"operationId":"getAllActiveFavorites","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/Favorite"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/{projectId}/gdd3/history":{"get":{"summary":"Gets GDD3 version history (Roles: user)","tags":["Misc"],"operationId":"getGDD3History","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"},{"name":"limit","in":"query","required":false,"type":"integer","description":"Number of history entries to return (default: 50)"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GDD3Version"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/{projectId}/restore":{"get":{"summary":"Restores GDD3 to specific version (Roles: user)","tags":["Misc"],"operationId":"restoreGddVersion","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"},{"name":"version","in":"query","required":true,"type":"integer","description":"Version to restore to"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Project"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/{projectId}/restore/preview":{"get":{"summary":"Previews GDD3 state if restored to specific version (Roles: user)","tags":["Misc"],"operationId":"previewGddVersionRestore","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"},{"name":"version","in":"query","required":true,"type":"integer","description":"Version to preview restoration to"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Project"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/{projectId}/generation/cancel":{"get":{"summary":"Cancels GDD3 generation and clears loading states (Roles: user)","tags":["Misc"],"operationId":"cancelGdd3Generation","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Project"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/{projectId}/favorite":{"post":{"summary":"Adds a favorite item (unified endpoint) (Roles: user)","tags":["Misc"],"operationId":"addFavorite","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"},{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/AddFavoritePayload"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Favorite"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/playables":{"get":{"summary":"Gets the user's playables (Roles: user)","tags":["Projects"],"operationId":"getPlayables","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/Playable"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/playable-templates":{"get":{"summary":"Gets playable templates (Roles: user)","tags":["Projects"],"operationId":"getPlayableTemplates","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/SharedPlayable"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/playables/{id}/remix":{"get":{"summary":"Remixes a playable (Roles: user)","tags":["Projects"],"operationId":"remixPlayable","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Playable"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/playables/{id}/share":{"get":{"summary":"Shares a playable (Roles: user)","tags":["Projects"],"operationId":"getSharedPlayable","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/SharedPlayable"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"post":{"summary":"Shares a playable (Roles: user)","tags":["Projects"],"operationId":"sharePlayable","security":[{"Bearer":[]}],"x-security-scopes":["user"],"consumes":["application/json"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"allowPublic","in":"query","required":false,"type":"boolean"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/Playable"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/SharedPlayable"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/playables/showcase":{"get":{"summary":"Gets all shared playables (Roles: user)","tags":["Projects"],"operationId":"getPlayablesShowcase","security":[{"Bearer":[]}],"x-security-scopes":["user","none"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/SharedPlayable"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/playables/{id}":{"post":{"summary":"Generates or iterates on a Playable prototype (Roles: user)","tags":["Projects"],"operationId":"promptPlayable","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"streamingKey","in":"query","required":false,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/PlayableRequest"}},{"name":"disableAssets","in":"query","type":"boolean","required":false}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Playable"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"put":{"summary":"Saves a playable (Roles: user)","tags":["Projects"],"operationId":"savePlayable","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"playable","in":"body","required":true,"schema":{"$ref":"#/definitions/Playable"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Playable"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"delete":{"summary":"Removes a playable (Roles: user)","tags":["Projects"],"operationId":"removePlayable","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"object","properties":{"success":{"type":"boolean"}}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/playables/{id}/revert-code/{messageIndex}":{"post":{"summary":"Reverts the code to a previous version (Roles: user)","tags":["Projects"],"operationId":"revertPlayableCode","security":[{"Bearer":[]}],"x-security-scopes":["user"],"consumes":["application/octet-stream"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"messageIndex","in":"path","required":true,"type":"number"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Playable"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/playables/{id}/image":{"post":{"summary":"Adds an image to a playable (Roles: user)","tags":["Projects"],"operationId":"updatePlayableIcon","security":[{"Bearer":[]}],"x-security-scopes":["user"],"consumes":["application/json"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/GameImage"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Playable"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/playables/{id}/title":{"post":{"summary":"Updates the title of a playable (Roles: user)","tags":["Projects"],"operationId":"updatePlayableTitle","security":[{"Bearer":[]}],"x-security-scopes":["user"],"consumes":["application/json"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/StringValue"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Playable"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/{projectId}/generate-code":{"get":{"summary":"Generates code for the project (Roles: user)","tags":["Projects"],"operationId":"generateProjectCode","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"},{"name":"streamingKey","in":"query","required":false,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/ProjectCode"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/{projectId}/generate-code/prompt":{"post":{"summary":"Generates script for the project with a prompt (Roles: user)","tags":["Projects"],"operationId":"generateScriptPrompt","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"},{"name":"scriptId","in":"query","required":false,"type":"string"},{"name":"prompt","in":"body","required":true,"schema":{"$ref":"#/definitions/StringValue"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/ProjectCode"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/project/{projectId}/code":{"get":{"summary":"Gets the project's code (Roles: user)","tags":["Projects"],"operationId":"getProjectCode","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/ProjectCode"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/favorites/{id}":{"delete":{"summary":"Removes a favorite (Roles: user)","tags":["Misc"],"operationId":"removeFavorite","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/guide/project/{projectId}":{"get":{"summary":"Gets the current guide content for a project (Roles: user)","tags":["Guide"],"operationId":"getProjectGuideContent","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"projectId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GuideContent"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/guide/content":{"post":{"summary":"Gets guide content for a game concept (Roles: user)","tags":["Guide"],"operationId":"getGuideContent","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GuideContentPayload"}},{"name":"regenerate","in":"query","required":false,"type":"boolean"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/GuideContent"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/game-concept/import":{"post":{"summary":"Imports a game concept from a PDF (Roles: user)","tags":["Misc"],"operationId":"importGameConceptPDF","security":[{"Bearer":[]}],"x-security-scopes":["user"],"consumes":["multipart/form-data"],"parameters":[{"name":"file","in":"formData","description":"The uploaded file data","required":true,"type":"file"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Project"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/game-concept/import/text":{"post":{"summary":"Imports a game concept from text (Roles: user)","tags":["Misc"],"operationId":"importGameConceptText","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"gdd","in":"body","required":true,"schema":{"$ref":"#/definitions/GDD3"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Project"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/game-concept/import/{id}":{"post":{"summary":"Imports a game concept from an existing game (Roles: user)","tags":["Misc"],"operationId":"importGameConceptGame","security":[{"Bearer":[]}],"x-security-scopes":["user"],"consumes":["application/octet-stream"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Project"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/projects":{"get":{"summary":"Gets the user projects (Roles: user)","tags":["Misc"],"operationId":"getProjects","security":[{"Bearer":[]}],"x-security-scopes":["user"],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/Project"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}},"post":{"summary":"Add a project (Roles: user)","tags":["Misc"],"operationId":"addProject","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"project","in":"body","required":true,"schema":{"$ref":"#/definitions/Project"}}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Project"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/slack/share/{team}/{project}":{"post":{"summary":"Shares a resource to slack (Roles: user)","tags":["Team"],"operationId":"shareToSlack","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"team","in":"path","required":true,"type":"string"},{"name":"project","in":"path","required":true,"type":"string"},{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/SharePayload"}}],"responses":{"200":{"description":"Success"},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/slack/auth/url/{team}":{"get":{"summary":"Gets the Slack auth url for the user (Roles: user)","tags":["Team"],"operationId":"getSlackAuthUrl","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"team","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/StringValue"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/slack/auth/code/{code}/team/{team}":{"get":{"summary":"Completes the Slack authentication (Roles: user)","tags":["Team"],"operationId":"completeSlackAuth","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"team","in":"path","required":true,"type":"string"},{"name":"code","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Team"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/slack/remove/{team}":{"get":{"summary":"Removes the Slack authentication (Roles: user)","tags":["Team"],"operationId":"removeSlackAuth","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"team","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/Team"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs":{"get":{"summary":"List the user's active generation jobs (Roles: user)","tags":["Jobs"],"operationId":"listJobs","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"status","in":"query","required":false,"type":"string","description":"Comma-separated statuses to include. Defaults to queued,running,failed."},{"name":"includeAcknowledged","in":"query","required":false,"type":"boolean","description":"Include jobs the user has dismissed. Defaults to false."}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/GenerationJob"}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/dismiss-all":{"post":{"summary":"Dismiss all the user's terminal-state jobs (failed/canceled/succeeded). (Roles: user)","tags":["Jobs"],"operationId":"dismissAllJobs","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"body","in":"body","required":false,"schema":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"string"},"description":"Statuses to dismiss. Defaults to [failed,canceled,succeeded]."}}}}],"responses":{"200":{"description":"Success","schema":{"type":"object","properties":{"count":{"type":"integer"}}}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/{id}":{"delete":{"summary":"Cancel a queued job. Running jobs cannot be canceled. (Roles: user)","tags":["Jobs"],"operationId":"cancelJob","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"409":{"description":"Job is not in a cancelable state (e.g. already running)","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/{id}/dismiss":{"post":{"summary":"Dismiss a terminal-state job (failed/canceled/succeeded) so it stops appearing in the queue panel. (Roles: user)","tags":["Jobs"],"operationId":"dismissJob","consumes":["application/octet-stream"],"security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"404":{"description":"Job not found","schema":{"$ref":"#/definitions/ErrorResponse"}},"409":{"description":"Job is not in a terminal state","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/{id}/retry":{"post":{"summary":"Retry a failed job. Re-enqueues with the original task type and payload, dismisses the failed doc. (Roles: user)","tags":["Jobs"],"operationId":"retryJob","consumes":["application/octet-stream"],"security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"id","in":"path","required":true,"type":"string"}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"404":{"description":"Job not found","schema":{"$ref":"#/definitions/ErrorResponse"}},"409":{"description":"Job is not in a retryable state","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-images":{"post":{"summary":"Enqueue an image generation job (Roles: user)","tags":["Jobs"],"operationId":"generateImagesQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-3d-images":{"post":{"summary":"Enqueue a 3D image generation job (Roles: user)","tags":["Jobs"],"operationId":"generate3DImagesQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-refined-3d-images":{"post":{"summary":"Enqueue a refined 3D image generation job (Roles: user)","tags":["Jobs"],"operationId":"generateRefined3DImagesQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/Refined3DPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/rig-model":{"post":{"summary":"Enqueue a 3D model rigging job (Roles: user)","tags":["Jobs"],"operationId":"rigModelQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/RigModelPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}},"409":{"description":"Conflict - model has existing animations that must be deleted before re-rigging","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/animate-3d":{"post":{"summary":"Enqueue a text-driven 3D animation job (Roles: user)","tags":["Jobs"],"operationId":"animate3DQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/Animate3DPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/animate-3d-preset":{"post":{"summary":"Enqueue a 3D animation-preset retargeting job (Roles: user)","tags":["Jobs"],"operationId":"animate3DPresetQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/Animate3DPresetPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-images-inpainting":{"post":{"summary":"Enqueue an image inpainting job (Roles: user)","tags":["Jobs"],"operationId":"generateImagesInpaintingQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-video":{"post":{"summary":"Enqueue a video generation job (Roles: user)","tags":["Jobs"],"operationId":"generateVideoQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateVideoPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-video-first-frame":{"post":{"summary":"Enqueue a video first-frame generation job (Roles: user)","tags":["Jobs"],"operationId":"generateVideoFirstFrameQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-audio":{"post":{"summary":"Enqueue an audio generation job (Roles: user)","tags":["Jobs"],"operationId":"generateAudioQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateAudioPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-image-to-spritesheet":{"post":{"summary":"Enqueue an image-to-spritesheet job (Roles: user)","tags":["Jobs"],"operationId":"generateImageToSpritesheetQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-sprite":{"post":{"summary":"Enqueue a sprite generation job (Roles: user)","tags":["Jobs"],"operationId":"generateSpriteQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateNewPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-spritesheet-motion-transfer":{"post":{"summary":"Enqueue a spritesheet motion transfer job (Roles: user)","tags":["Jobs"],"operationId":"generateSpritesheetMotionTransferQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/GenerateSpritesheetMotionTransferPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-edit-spritesheet":{"post":{"summary":"Enqueue a spritesheet-edit job (Roles: user)","tags":["Jobs"],"operationId":"editSpritesheetQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/EditSpritesheetPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-references-to-video":{"post":{"summary":"Enqueue a references-to-video job (Roles: user)","tags":["Jobs"],"operationId":"referencesToVideoQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/ReferencesToVideoPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-video-edit":{"post":{"summary":"Enqueue a video-edit job (Roles: user)","tags":["Jobs"],"operationId":"videoEditQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/VideoEditPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/generate-upscale-video":{"post":{"summary":"Enqueue a video-upscale (2x) job. Only for videos below 960x960 pixels. (Roles: user)","tags":["Jobs"],"operationId":"upscaleVideoQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/UpscaleVideoPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/test-wait":{"post":{"summary":"Enqueue a no-op test job that sleeps for a random duration. Disabled in production unless JOB_TEST_TASK_ENABLED=true. (Roles: user)","tags":["Jobs"],"operationId":"testWaitQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/JobTestWaitPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"503":{"description":"Test task type disabled","schema":{"$ref":"#/definitions/ErrorResponse"}}}}},"/jobs/remove-image-background":{"post":{"summary":"Enqueue a background-removal job (Roles: user)","tags":["Jobs"],"operationId":"removeImageBackgroundQueued","security":[{"Bearer":[]}],"x-security-scopes":["user"],"parameters":[{"name":"data","in":"body","required":true,"schema":{"$ref":"#/definitions/QueueRemoveImageBackgroundPayload"}}],"responses":{"202":{"description":"Accepted","schema":{"$ref":"#/definitions/JobAcceptedResponse"}},"400":{"description":"Error","schema":{"$ref":"#/definitions/ErrorResponse"}},"402":{"description":"Insufficient credits","schema":{"$ref":"#/definitions/ErrorResponse"}}}}}},"definitions":{"User":{"type":"object","required":["_id","email","role"],"properties":{"_id":{"type":"string"},"name":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"teams":{"type":"array","items":{"type":"string"}},"email":{"type":"string"},"stripped_email":{"type":"string"},"password":{"type":"string"},"role":{"type":"string","enum":["user"]},"stripe_id":{"type":"string"},"selected_project":{"type":"string"},"number_generations":{"type":"integer","format":"int64"},"registration_date":{"type":"integer","format":"int64"},"whats_new_date":{"type":"integer","format":"int64"},"trial_end":{"type":"integer","format":"int64"},"tax":{"$ref":"#/definitions/Tax"},"address":{"$ref":"#/definitions/Address"},"tutorials_closed":{"type":"array","items":{"type":"string"}},"missions_completed":{"type":"array","items":{"type":"string"}},"credits":{"$ref":"#/definitions/GenerationCreditsInfo"},"auto_topup":{"$ref":"#/definitions/AutoTopupSettings"},"country":{"type":"string"},"genres":{"type":"array","items":{"type":"string"}},"platform":{"type":"string"},"welcome_form":{"type":"boolean"},"paywall":{"type":"boolean"},"video_generator":{"type":"boolean"},"subscription_stats":{"$ref":"#/definitions/SubscriptionStats"},"date_whats_new":{"type":"integer","format":"int64"},"api_key":{"type":"string"},"cookie_consent":{"type":"boolean"},"double_optin_sent":{"type":"boolean"},"credits_exhausted_at":{"type":"integer","format":"int64"},"credits_exhausted_synced_at":{"type":"integer","format":"int64"},"risk_hold":{"type":"boolean"},"marketing_consent":{"$ref":"#/definitions/MarketingConsent"},"fbp":{"type":"string"},"fbc":{"type":"string"},"fbclid":{"type":"string"},"rdt_cid":{"type":"string"},"last_login_ip":{"type":"string","description":"IP address of last login"},"purchase_ips":{"type":"array","description":"Capped history of client IPs captured at purchase actions (subscribe, checkout, card/payment confirmation, renewal). Used as chargeback evidence to tie a disputed transaction to the rightful cardholder.","items":{"$ref":"#/definitions/PurchaseIp"}},"preferred_content_type":{"type":"string"},"questionnaire":{"$ref":"#/definitions/Questionnaire"},"rate_limits_override":{"type":"integer","format":"int64"},"referral_code":{"type":"string"},"referred_by":{"type":"string","description":"User ID of the referrer"},"referral_credited":{"type":"boolean","description":"Whether referral bonus credits have been issued for this user"},"subscriber_welcome_sent_at":{"type":"integer","format":"int64"},"referral_announce_sent_at":{"type":"integer","format":"int64"},"discord_link_id":{"type":"string","description":"Opaque uuid shown on the user's Discord profile as their Ludo connection. Rotatable. Never the user id."},"discord_user_id":{"type":"string","description":"Discord snowflake of the linked account."},"discord_linked_at":{"type":"string","format":"date-time","description":"When the Discord account was last linked."},"totp":{"$ref":"#/definitions/UserTotp"},"webauthn_credentials":{"type":"array","description":"Registered passkeys (WebAuthn credentials) for this user.","items":{"$ref":"#/definitions/UserWebauthnCredential"}},"recovery_codes":{"type":"array","description":"Single-use 2FA recovery codes, stored hashed. Regenerating replaces the whole array.","items":{"$ref":"#/definitions/UserRecoveryCode"}},"sessions_version":{"type":"integer","default":0,"description":"Bumped whenever every existing session must be invalidated (2FA enable/disable, password change). Tokens carry it as the `sv` claim and are rejected when it no longer matches."}}},"UserTotp":{"type":"object","properties":{"secret_enc":{"type":"string","description":"TOTP shared secret, encrypted at rest (encryptionUtils.encryptSecret)."},"confirmed":{"type":"boolean","description":"True once the user proved possession of the secret with a code. An unconfirmed enrolment does not count as 2FA being enabled."},"last_timestep":{"type":"integer","format":"int64","description":"Timestep of the last accepted code. Anti-replay: a code whose timestep is not greater than this is rejected."},"created_at":{"type":"string","format":"date-time"}}},"UserWebauthnCredential":{"type":"object","properties":{"credential_id":{"type":"string","description":"base64url credential id returned by the authenticator."},"public_key":{"type":"string","description":"base64url COSE public key used to verify assertions."},"counter":{"type":"integer","format":"int64","description":"Signature counter of the last accepted assertion (clone detection)."},"transports":{"type":"array","items":{"type":"string"}},"name":{"type":"string","description":"User-visible label for the passkey."},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":"string","format":"date-time"}}},"UserRecoveryCode":{"type":"object","properties":{"hash":{"type":"string","description":"bcrypt hash of the recovery code; the plaintext is shown once."},"used_at":{"type":"string","format":"date-time","description":"When the code was redeemed. Unset while the code is still usable."}}},"PurchaseIp":{"type":"object","properties":{"ip":{"type":"string"},"date":{"type":"integer","format":"int64","description":"Unix epoch seconds when this IP was captured at a purchase action"}}},"MarketingConsent":{"type":"object","properties":{"granted":{"type":"boolean"},"date":{"type":"integer","format":"int64"},"source":{"type":"string"}}},"Questionnaire":{"type":"object","properties":{"date":{"type":"integer","format":"int64"},"questions":{"type":"array","items":{"$ref":"#/definitions/Question"}}}},"Question":{"type":"object","properties":{"question":{"type":"string"},"answer":{"type":"string"}}},"SubscriptionStats":{"type":"object","properties":{"revenue_year":{"type":"number","format":"float"},"revenue_12months":{"type":"number","format":"float"},"revenue_total":{"type":"number","format":"float"},"plan_name":{"type":"string"},"plan_interval":{"type":"string"},"is_customer":{"type":"boolean"},"became_customer_date":{"type":"integer","format":"int64"}}},"UserInfoPayload":{"type":"object","properties":{"name":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":"string"},"number_generations":{"type":"integer","format":"int64"},"tax":{"$ref":"#/definitions/Tax"},"address":{"$ref":"#/definitions/Address"},"questionnaire":{"$ref":"#/definitions/Questionnaire"},"preferred_content_type":{"type":"string"},"tutorials_closed":{"type":"array","items":{"type":"string"}},"missions_completed":{"type":"array","items":{"type":"string"}},"selected_project":{"type":"string"},"password":{"type":"string"},"genres":{"type":"array","items":{"type":"string"}},"platform":{"type":"string"},"whats_new_date":{"type":"integer","format":"int64"},"welcome_form":{"type":"boolean"},"cookie_consent":{"type":"boolean"},"marketing_consent":{"type":"boolean"},"marketing_consent_source":{"type":"string"},"fbc":{"type":"string"},"fbp":{"type":"string"},"fbclid":{"type":"string"},"rdt_cid":{"type":"string"},"referral_code":{"type":"string"}}},"TeamSlackToken":{"type":"object","properties":{"_id":{"type":"string"},"token":{"type":"string"}}},"Team":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"subscription_id":{"type":"string"},"owner_id":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/definitions/TeamUser"}},"invitations":{"type":"array","items":{"$ref":"#/definitions/TeamInvitation"}},"slack_token":{"type":"boolean"}}},"TeamInvitation":{"type":"object","properties":{"email":{"type":"string"},"role":{"enum":["editor"],"type":"string"}}},"TeamUser":{"type":"object","properties":{"user_id":{"type":"string"},"role":{"enum":["owner","editor"],"type":"string"},"subscription_id":{"type":"string"}}},"TeamUserInfo":{"type":"object","properties":{"user_id":{"type":"string"},"role":{"enum":["owner","editor"],"type":"string"},"email":{"type":"string"},"subscription_id":{"type":"string"},"subscription_status":{"type":"string"}}},"Tax":{"type":"object","properties":{"value":{"type":"string"},"type":{"type":"string"}}},"Address":{"type":"object","properties":{"line1":{"type":"string"},"line2":{"type":"string"},"city":{"type":"string"},"postal_code":{"type":"string"},"state":{"type":"string"},"country":{"type":"string"}}},"LoginPayload":{"type":"object","properties":{"token":{"type":"string"},"user":{"$ref":"#/definitions/User"},"subscription":{"$ref":"#/definitions/Subscription"},"registered":{"type":"boolean"},"magic_link_sent":{"type":"boolean"},"magic_link_message":{"type":"string"},"two_factor_required":{"type":"boolean","description":"Credentials were accepted but the account has 2FA enabled, so no token is issued. Exchange the challenge_ticket for a full payload via /auth/2fa/verify."},"challenge_ticket":{"type":"string","description":"Single-use, short-lived ticket identifying the pending 2FA challenge."},"two_factor_methods":{"type":"array","description":"Second factors the user can present for this challenge.","items":{"type":"string"}}}},"Authentication":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string"},"password":{"type":"string"}}},"TwoFactorVerifyPayload":{"type":"object","required":["challenge_ticket"],"properties":{"challenge_ticket":{"type":"string","description":"The ticket returned by the login endpoint that was challenged."},"totp_code":{"type":"string"},"recovery_code":{"type":"string"},"webauthn_assertion":{"type":"object","description":"The authenticator's response to the options from /auth/2fa/webauthn/login-options. Exactly one of totp_code, recovery_code or webauthn_assertion must be present."}}},"TwoFactorLoginOptionsPayload":{"type":"object","properties":{"challenge_ticket":{"type":"string","description":"Present for a login challenge (there is no session yet). Omitted for a step-up ceremony, where the Bearer token identifies the user."}}},"TwoFactorStepUpPayload":{"type":"object","properties":{"totp_code":{"type":"string"},"recovery_code":{"type":"string"},"webauthn_assertion":{"type":"object"}},"description":"A fresh second-factor proof for a change to the second factors themselves. Exactly one field; an empty body is answered with error_payload.code STEP_UP_REQUIRED."},"TwoFactorTotpConfirmPayload":{"type":"object","required":["totp_code"],"properties":{"totp_code":{"type":"string"}}},"TwoFactorRegisterPayload":{"type":"object","required":["attestation"],"properties":{"attestation":{"type":"object","description":"The authenticator's response to the register-options challenge."},"name":{"type":"string","description":"User-visible label for the passkey. Defaults to \"Passkey\"."},"totp_code":{"type":"string"},"recovery_code":{"type":"string"},"webauthn_assertion":{"type":"object"}}},"TwoFactorSetupResponse":{"type":"object","properties":{"otpauth_url":{"type":"string"},"qr_data_uri":{"type":"string"},"secret":{"type":"string"}}},"TwoFactorEnrollResponse":{"type":"object","properties":{"token":{"type":"string","description":"Fresh token. Enrolling revokes every existing session, so the client must adopt this one immediately or log itself out."},"recovery_codes":{"type":"array","description":"Shown exactly once. EMPTY when the account already had recovery codes (a second factor type was added) — render that as \"your existing recovery codes still apply\", never as an empty list of ten.","items":{"type":"string"}}}},"TwoFactorRegisterResponse":{"type":"object","properties":{"credential":{"$ref":"#/definitions/TwoFactorPasskey"},"recovery_codes":{"type":"array","description":"Same exactly-once rule as TwoFactorEnrollResponse.","items":{"type":"string"}},"token":{"type":"string","description":"Present only when this passkey is what enabled 2FA."}}},"TwoFactorDisableResponse":{"type":"object","properties":{"token":{"type":"string","description":"Fresh token; disabling revokes every existing session."}}},"TwoFactorRemoveCredentialResponse":{"type":"object","properties":{"disabled":{"type":"boolean","description":"True when this was the last passkey and no authenticator app was confirmed, so removing it turned two-factor authentication off entirely rather than leaving an unsatisfiable requirement."},"token":{"type":"string","description":"Present only when `disabled` is true."}}},"TwoFactorRecoveryCodesResponse":{"type":"object","properties":{"recovery_codes":{"type":"array","items":{"type":"string"}}}},"TwoFactorPasskey":{"type":"object","properties":{"credential_id":{"type":"string"},"name":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":"string","format":"date-time"}}},"TwoFactorStatusResponse":{"type":"object","properties":{"enabled":{"type":"boolean"},"totp_confirmed":{"type":"boolean"},"passkeys":{"type":"array","items":{"$ref":"#/definitions/TwoFactorPasskey"}},"recovery_codes_remaining":{"type":"integer"}}},"RegistrationPayload":{"type":"object","required":["email","password","name","code"],"properties":{"email":{"type":"string"},"name":{"type":"string"},"password":{"type":"string"},"code":{"type":"string"},"captcha_token":{"type":"string"}}},"RegistrationPaywall2Payload":{"type":"object","required":["name","email","password","payment_token"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"},"payment_token":{"type":"string"},"captcha_token":{"type":"string"}}},"RegistrationPaywall3Payload":{"type":"object","required":["name","email","password"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"},"captcha_token":{"type":"string"}}},"CheckoutSessionPayload":{"type":"object","required":["planId"],"properties":{"planId":{"type":"string"}}},"StripeCheckoutPayload":{"type":"object","required":["name","email","password"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"}}},"RegistrationPaywallTeamPayload":{"type":"object","required":["password","name","team_id","email"],"properties":{"name":{"type":"string"},"password":{"type":"string"},"team_id":{"type":"string"},"email":{"type":"string"},"captcha_token":{"type":"string"}}},"AnimationPresetsResponse":{"type":"object","properties":{"animations":{"type":"array","items":{"$ref":"#/definitions/AnimationPresetPublic"}},"perspectives":{"type":"array","items":{"$ref":"#/definitions/AnimationPerspective"}},"directions":{"type":"array","items":{"type":"string"}}}},"AnimationPresetPublic":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"type":"string"},"description":{"type":"string"},"duration":{"type":"number","format":"float"},"preview_url":{"type":"string"},"clip_url":{"type":"string","description":"Motion clip backing the preset. Present only for presets that can be retargeted onto a rigged 3D model (animate-preset endpoint); absent for sprite-only presets."}}},"AnimationPerspective":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}}},"ErrorResponse":{"type":"object","properties":{"message":{"type":"string"},"metadata":{"type":"string"},"error_payload":{"type":"string"},"intent":{"type":"object","description":"Stripe intent details sent with 3D Secure (406) errors","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["payment","setup"]},"client_secret":{"type":"string"}}}}},"UploadFilesPayload":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string"}},"mimes":{"type":"array","items":{"type":"string"}},"compress":{"type":"boolean"}}},"Coupon":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"valid":{"type":"boolean"},"amount_off":{"type":"number","format":"float"},"percent_off":{"type":"number","format":"float"},"applies_to":{"type":"array","items":{"type":"string"}}}},"Subscription":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string"},"collection_method":{"type":"string"},"cancel_at_period_end":{"type":"boolean"},"canceled_at":{"type":"integer","format":"int64"},"cancel_at":{"type":"integer","format":"int64"},"created":{"type":"integer","format":"int64"},"current_period_end":{"type":"integer","format":"int64"},"current_period_start":{"type":"integer","format":"int64"},"customer":{"type":"string"},"days_until_due":{"type":"integer","format":"int64"},"ended_at":{"type":"integer","format":"int64"},"plan":{"$ref":"#/definitions/PaymentPlan"},"quantity":{"type":"integer","format":"int64"},"start_date":{"type":"integer","format":"int64"},"status":{"type":"string"},"tax_percent":{"type":"integer","format":"int64"},"trial_end":{"type":"integer","format":"int64"},"trial_start":{"type":"integer","format":"int64"},"billing_cycle_anchor":{"type":"integer","format":"int64"},"latest_invoice":{"type":"string"},"from_team":{"type":"boolean"},"pause_collection":{"$ref":"#/definitions/SubscriptionPauseCollection"},"resume_requires_payment":{"type":"boolean","description":"Present (true) only while the subscription is paused and at least one billing cycle was skipped (voided) during the pause, meaning a resume will immediately charge the full plan price for a fresh cycle."}}},"SubscriptionPauseCollection":{"type":"object","properties":{"behavior":{"type":"string"},"resumes_at":{"type":"integer","format":"int64"}}},"PaymentPlan":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"object":{"type":"string"},"description":{"type":"string"},"amount":{"type":"integer","format":"int64"},"statement_descriptor":{"type":"string"},"trial_period_days":{"type":"integer","format":"int64"},"created":{"type":"integer","format":"int64"},"currency":{"type":"string"},"interval":{"type":"string"},"interval_count":{"type":"integer","format":"int64"},"livemode":{"type":"boolean"},"active":{"type":"boolean"},"billing_scheme":{"type":"string"},"product":{"type":"string"},"name":{"type":"string"},"usage_type":{"type":"string"},"nickname":{"type":"string"},"amount_off":{"type":"integer","format":"int64"},"percent_off":{"type":"integer","format":"int64"},"metadata":{"$ref":"#/definitions/PaymentPlanMetadata"}}},"PaymentPlanMetadata":{"type":"object","properties":{"projects":{"type":"string"},"description":{"type":"string"},"seats":{"type":"string"},"favorites":{"type":"string"},"team":{"type":"string"},"amount_text":{"type":"string"},"credits":{"type":"string"},"premium":{"type":"string"},"magic_link":{"type":"string"},"api":{"type":"string"},"rate_limits":{"type":"string"}}},"Game":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"source_id":{"type":"string"},"title":{"type":"string"},"short_description":{"type":"string"},"short_description_clean":{"type":"string"},"summary":{"type":"string"},"description":{"type":"string"},"description_clean":{"type":"string"},"description_annotations":{"type":"array","items":{"$ref":"#/definitions/Annotation"}},"user_rating_count":{"type":"integer","format":"int64"},"user_rating":{"type":"number","format":"float"},"developers":{"type":"array","items":{"type":"string"}},"urls":{"type":"array","items":{"type":"string"}},"release_date":{"type":"integer","format":"int64"},"update_date":{"type":"integer","format":"int64"},"icon":{"$ref":"#/definitions/GameImage"},"header":{"$ref":"#/definitions/GameImage"},"videos":{"type":"array","items":{"$ref":"#/definitions/MediaElement"}},"screenshots":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"url":{"type":"array","items":{"type":"string"}},"actual_genres":{"type":"array","items":{"type":"string"}},"predicted_genres":{"type":"array","items":{"type":"string"}},"augmented_genres":{"type":"array","items":{"type":"string"}},"source":{"type":"string"},"price":{"type":"string"},"platforms":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}},"game_modes":{"type":"array","items":{"type":"string"}},"genres":{"type":"array","items":{"type":"string"}},"advisory_rating":{"type":"string"}}},"Annotation":{"type":"object","properties":{"start":{"type":"integer","format":"int64"},"end":{"type":"integer","format":"int64"},"label":{"type":"string"},"text":{"type":"string"},"label_probs":{"type":"object","properties":{"feature":{"type":"number","format":"float"},"junk":{"type":"number","format":"float"},"mechanic":{"type":"number","format":"float"},"theme":{"type":"number","format":"float"}}}}},"MediaElement":{"type":"object","properties":{"type":{"type":"string"},"url":{"type":"string"}}},"GeneratedGame":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}},"id":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"text_raw":{"type":"string"},"genres":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}},"description":{"type":"string"},"description_raw":{"type":"string"},"description_annotations":{"type":"array","items":{"$ref":"#/definitions/Annotation"}},"search_results":{"$ref":"#/definitions/GenerationsSearchResult"},"images":{"type":"array","items":{"$ref":"#/definitions/PrometheusGameImage"}},"screenshots":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"image":{"$ref":"#/definitions/GameImage"},"blending":{"type":"array","items":{"type":"string"}},"blended_games":{"type":"array","items":{"$ref":"#/definitions/Game"}},"platform":{"type":"string"},"perspective":{"type":"string"},"platforms":{"type":"array","items":{"type":"string"}},"sentence":{"type":"string"},"punchline":{"type":"string"},"art_style":{"type":"string"},"quality_score":{"type":"number","format":"float"},"uniqueness_score":{"type":"number","format":"float"},"final_score":{"type":"number","format":"float"},"hints":{"type":"array","items":{"type":"string"}},"request_id":{"type":"string"},"prompt":{"type":"string"},"selected_genres":{"type":"array","items":{"type":"string"}},"mechanics":{"type":"array","items":{"$ref":"#/definitions/GeneratedText"}},"step_choices":{"type":"array","items":{"$ref":"#/definitions/StepValue"}},"score":{"$ref":"#/definitions/LudoScore"},"image_prompt":{"type":"string"}}},"Spritesheet":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}},"id":{"type":"string"},"audio_b64":{"type":"string"},"duration":{"type":"number","format":"float"},"start_timestamp":{"type":"number","format":"integer"},"end_timestamp":{"type":"number","format":"integer"},"num_frames":{"type":"number","format":"integer"},"max_num_frames":{"type":"number","format":"integer"},"margin_ratio":{"type":"number","format":"integer"},"margin_ratio_horizontal":{"type":"number","format":"float"},"margin_ratio_vertical":{"type":"number","format":"float"},"margin_ratio_mode":{"type":"string"},"target_frame_size":{"type":"number","format":"integer"},"loop":{"type":"boolean"},"bad_loop":{"type":"boolean"},"out_of_frame":{"type":"boolean"},"crop":{"type":"boolean"},"original_prompt":{"type":"string"},"motion_prompt":{"type":"string"},"prompt":{"type":"string"},"seed":{"type":"number","format":"integer"},"sprite_sheet":{"$ref":"#/definitions/PILImage"},"sprite_sheet_b64":{"type":"string"},"sprite_sheet_raw_b64":{"type":"string"},"sprite_sheet_watermarked_b64":{"type":"string","description":"URL of the watermarked variant of sprite_sheet_b64 (object key = clean key + \"_wm\"). Present only when generated by a free user; served IN PLACE of sprite_sheet_b64 to free viewers. sprite_sheet_b64 itself always stays the clean asset."},"sprite_sheet_raw_watermarked_b64":{"type":"string","description":"Watermarked variant of sprite_sheet_raw_b64 (see sprite_sheet_watermarked_b64)."},"watermarked":{"type":"boolean","readOnly":true,"description":"Response-only. True when the served sprite_sheet_b64 is the watermarked variant (free viewer). Never persisted - stripped from inbound bodies."},"video":{"$ref":"#/definitions/GeneratedVideo"},"preview_video_b64":{"type":"string"},"first_frame_b64":{"type":"string"},"last_frame_b64":{"type":"string"},"image":{"$ref":"#/definitions/GameImage"},"middle_image":{"$ref":"#/definitions/GameImage"},"final_image":{"$ref":"#/definitions/GameImage"},"has_audio":{"type":"boolean"},"is_edited":{"type":"boolean","description":"True when this spritesheet was produced by an edit (prompt, outpaint, or fix_loop) - the whole animation was re-rendered, so frames may drift from the original."},"label":{"type":"string"},"preview_scale":{"type":"number","format":"float"},"pack_alignment":{"$ref":"#/definitions/PackAlignment"}}},"PackAlignment":{"type":"object","properties":{"description":{"type":"string"},"sizeHeightPx":{"type":"number","format":"float"},"groundOffset":{"type":"number","format":"float"},"horizontalOffset":{"type":"number","format":"float"}}},"GeneratedVideo":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}},"id":{"type":"string"},"original_prompt":{"type":"string"},"prompt":{"type":"string"},"url":{"type":"string"},"image":{"$ref":"#/definitions/GameImage"},"pil_image":{"$ref":"#/definitions/PILImage"},"seed":{"type":"integer","format":"int64"},"duration":{"type":"number","format":"float"},"has_audio":{"type":"boolean"},"model":{"type":"string"},"pixel_grid":{"type":"array","x-nullable":true,"description":"Pixel-art grid dimensions [width, height] detected/used by the generation, or null. Reported by pixel-art-aware models (forge-pixel); must always be persisted with the video.","items":{"type":"integer","format":"int32"}}}},"GeneratedAudio":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}},"id":{"type":"string"},"audio_b64":{"type":"string"},"original_prompt":{"type":"string"},"original_text":{"type":"string"},"prompt":{"type":"string"},"text":{"type":"string"},"sample_url":{"type":"string"},"seed":{"type":"integer","format":"int64"},"type":{"type":"string"},"loop":{"type":"boolean"},"duration":{"type":"number","format":"float"},"voice_preset":{"$ref":"#/definitions/VoicePreset"}}},"VoicePreset":{"type":"object","properties":{"id":{"type":"string"},"language":{"type":"string"},"emotion":{"type":"string"}}},"VoicePresetList":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string"}},"emotions":{"type":"array","items":{"type":"string"}}}},"PILImage":{"type":"object","properties":{"format":{"type":"string"},"format_description":{"type":"string"}}},"PrometheusGameImage":{"type":"object","properties":{"game_id":{"type":"string"},"hash":{"type":"integer","format":"int64"},"id":{"type":"string"},"origin":{"type":"string"},"similarity":{"type":"number","format":"float"}}},"GameImage":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}},"id":{"type":"string"},"image_id":{"type":"string"},"url":{"type":"string"},"type":{"type":"string"},"final_score":{"type":"number","format":"float"},"nsfw_prob":{"type":"number","format":"float"},"similarity":{"type":"number","format":"float"},"width":{"type":"integer","format":"int64"},"height":{"type":"integer","format":"int64"},"game_id":{"type":"string"},"hosted_filename":{"type":"string"},"icon":{"type":"boolean"},"targetSize":{"type":"integer"},"seed":{"type":"integer"},"embedded":{"type":"boolean"},"analysed":{"type":"boolean"},"image":{"type":"string"},"image_type":{"type":"string"},"prompt":{"type":"string"},"motion_prompt":{"type":"string"},"request_id":{"type":"string"},"hints":{"type":"array","items":{"type":"string"}},"selected_genres":{"type":"array","items":{"type":"string"}},"selected_colors":{"type":"array","items":{"type":"string"}},"selected_style":{"type":"string"},"selected_perspective":{"type":"string"},"is_safe":{"type":"boolean"},"platform":{"type":"string"},"loading":{"type":"boolean"},"original_hints":{"type":"array","items":{"type":"string"}},"genres":{"type":"array","items":{"type":"string"}},"edit_mask":{"type":"string"},"edit_mode":{"type":"string"},"camera_rotation":{"type":"integer","minimum":-180,"maximum":180},"camera_elevation":{"type":"integer","minimum":-30,"maximum":60},"edit_mask_pil_image":{"$ref":"#/definitions/PILImage"},"pil_image":{"$ref":"#/definitions/PILImage"},"nsfw":{"type":"number","format":"float"},"pose":{"$ref":"#/definitions/SpritePose"}}},"GenerationList":{"type":"object","properties":{"items":{"type":"array","description":"This page of results, newest first.","items":{"$ref":"#/definitions/GenerationListItem"}},"page":{"type":"integer","description":"The 1-based page number these items came from."},"page_size":{"type":"integer","description":"The page size actually applied (your page_size, clamped to 1-100)."},"has_more":{"type":"boolean","description":"Whether at least one more page exists after this one. Page until this is false rather than until you get a short page."}}},"GenerationListItem":{"type":"object","properties":{"id":{"type":"string","description":"Generation-history id of this item, identifying it across these listings. API-originated results expire after 7 days - download anything you want to retain. (Assets can be kept permanently from the Ludo web app.)"},"type":{"type":"string","description":"The media type of this item, echoing the requested `type`.","enum":["image","spritesheet","video","audio","3d"]},"request_id":{"type":"string"},"created_at":{"type":"integer","description":"When the item was generated (unix seconds)."},"url":{"type":"string","description":"image / video / audio: URL to the generated file."},"duration":{"type":"number","format":"float","description":"video / audio / spritesheet: duration in seconds."},"has_audio":{"type":"boolean","description":"video only."},"audio_type":{"type":"string","description":"audio only: the kind of audio (sfx, music, speech). Named `audio_type` here, not `type`, because `type` reports the media type on every item."},"spritesheet_url":{"type":"string","description":"spritesheet only."},"video_url":{"type":"string","description":"spritesheet only: preview video."},"audio_b64":{"type":"string","description":"spritesheet only."},"num_frames":{"type":"integer","description":"spritesheet only."},"model_url":{"type":"string","description":"3d only: URL to the generated model file."},"snapshots":{"type":"array","items":{"type":"string"},"description":"3d only: preview snapshot URLs."}}},"ImageResult":{"type":"object","properties":{"url":{"type":"string"},"request_id":{"type":"string"},"created_at":{"type":"integer"}}},"SpriteResult":{"type":"object","properties":{"spritesheet_url":{"type":"string"},"video_url":{"type":"string"},"audio_b64":{"type":"string"},"gif_url":{"type":"string"},"individual_frame_urls":{"type":"array","items":{"type":"string"}},"num_frames":{"type":"integer"},"num_cols":{"type":"integer"},"num_rows":{"type":"integer"},"spritesheet_with_background_url":{"type":"string"},"individual_frame_with_background_urls":{"type":"array","items":{"type":"string"}},"duration":{"type":"number","format":"float"},"request_id":{"type":"string"},"created_at":{"type":"integer"}}},"VideoResult":{"type":"object","properties":{"url":{"type":"string","description":"URL to the generated video file"},"duration":{"type":"number","format":"float","description":"Duration of the video in seconds"},"has_audio":{"type":"boolean"},"request_id":{"type":"string"},"created_at":{"type":"integer"}}},"AudioResult":{"type":"object","properties":{"url":{"type":"string","description":"URL to the generated audio file"},"type":{"type":"string"},"duration":{"type":"number","format":"float"},"request_id":{"type":"string"},"created_at":{"type":"integer"}}},"ThreeDModelResult":{"type":"object","properties":{"model_url":{"type":"string"},"snapshots":{"type":"array","items":{"type":"string"}},"request_id":{"type":"string"},"created_at":{"type":"integer"}}},"GenerateNewPayload":{"type":"object","properties":{"request_id":{"type":"string"},"model":{"type":"string"},"descriptions":{"type":"array","items":{"type":"string"}},"game_ids":{"type":"array","items":{"type":"string"}},"search_images":{"type":"integer"},"image_type":{"type":"string"},"context":{"type":"string"},"art_style":{"type":"string"},"perspective":{"type":"string"},"components":{"type":"array","items":{"$ref":"#/definitions/GeneratedText"}},"generations":{"type":"array","items":{"$ref":"#/definitions/GeneratedGame"}},"hints":{"type":"array","items":{"type":"string"}},"gdds":{"type":"array","items":{"$ref":"#/definitions/GDD3"}},"context_gdd":{"$ref":"#/definitions/GDD3"},"project_id":{"type":"string"},"filters":{"$ref":"#/definitions/GenerateFilters"},"previous_generations":{"type":"array","items":{"$ref":"#/definitions/GeneratedGame"}},"initial_game_image":{"$ref":"#/definitions/GameImage"},"initial_generated_image":{"$ref":"#/definitions/GameImage"},"initial_image_file":{"$ref":"#/definitions/GameImage"},"middle_image_file":{"$ref":"#/definitions/GameImage"},"final_image_file":{"$ref":"#/definitions/GameImage"},"additional_input_images":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"mask_image":{"type":"string"},"strength":{"type":"number","format":"float"},"aspect_ratio":{"type":"string"},"preserve_color":{"type":"boolean"},"preserve_composition":{"type":"boolean"},"preserve_details":{"type":"boolean"},"edit_mode":{"type":"string"},"camera_rotation":{"type":"integer","minimum":-180,"maximum":180},"camera_elevation":{"type":"integer","minimum":-30,"maximum":60},"loop":{"type":"boolean"},"crop":{"type":"boolean"},"frames":{"type":"number","format":"integer"},"frame_size":{"type":"number","format":"integer"},"margin_ratio":{"type":"number","format":"float"},"margin_ratio_horizontal":{"type":"number","format":"float"},"margin_ratio_vertical":{"type":"number","format":"float"},"margin_ratio_mode":{"type":"string"},"spritesheet":{"$ref":"#/definitions/Spritesheet"},"pose":{"$ref":"#/definitions/SpritePose"},"generation_parameters":{"$ref":"#/definitions/ThreeDGenerationParameters"},"duration":{"type":"number","format":"float"}}},"AnimateSpritePayload":{"type":"object","description":"Payload for generating an animated spritesheet from a static image. Input images can either be provided in base64 or URL. If the image was generated using Ludo, ideally it should be generated using the \"sprite\", \"sprite-vfx\" or \"ui_asset\" type.","required":["motion_prompt","initial_image"],"properties":{"motion_prompt":{"type":"string","description":"Text description of the desired animation (e.g., \"walking\", \"jumping\", \"idle breathing\", \"attack slash\")."},"initial_image":{"type":"string","description":"The url OR base64 of the starting frame image to animate. This is the base sprite that will be brought to life."},"final_image":{"type":"string","description":"The url OR base64 of ending frame image. When provided, the animation will interpolate between the initial and final frames."},"loop":{"type":"boolean","default":true,"description":"Trim the animation at the beginning or end to create a seamless loop. Not guaranteed to produce a perfect loop."},"crop":{"type":"boolean","description":"Crop sprite frames to fit content. Results in smaller spritesheets but inconsistent frame sizes across different animations."},"frames":{"type":"number","format":"integer","description":"Number of frames in the output spritesheet.","default":36,"enum":[4,9,16,25,36,49,64]},"frame_size":{"type":"number","format":"integer","description":"Size of each frame in pixels (width and height). 0 is for maximum resolution. -1 is for AI 1.5x upscaling. -9 is for matching the size and position of the input frame.","default":0,"enum":[32,64,96,128,192,256,384,0,-1,-9]},"margin_ratio":{"type":"number","format":"float","description":"Deprecated - prefer margin_ratio_horizontal / margin_ratio_vertical. Amount of padding around the sprite as a ratio (0.0 to 1.0), applied to both axes (equivalent to setting both per-axis params to this value). Only used when margin_ratio_mode is \"manual\". Cannot be combined with the per-axis params: providing both fails with HTTP 400."},"margin_ratio_horizontal":{"type":"number","format":"float","description":"Horizontal padding around the sprite as a ratio (0.0 to 1.0). Only used when margin_ratio_mode is \"manual\". Useful for animations that extend sideways (e.g., sword slashes, punches). Cannot be combined with the legacy margin_ratio param."},"margin_ratio_vertical":{"type":"number","format":"float","description":"Vertical padding around the sprite as a ratio (0.0 to 1.0). Only used when margin_ratio_mode is \"manual\". Useful for animations that extend up or down (e.g., jumps). Cannot be combined with the legacy margin_ratio param."},"margin_ratio_mode":{"type":"string","description":"Controls how margins are applied around the sprite.","enum":["auto","manual","none"],"default":"auto"},"image_type":{"x-swagger-data":"spriteImageTypes","type":"string","description":"Type of sprite being animated. Affects generation parameters and styling."},"model":{"type":"string","description":"Animation model to use. forge is best for basic animations and relatively simple sprites; blitz (default) is the most reliable and predictable, but can struggle with very short animations; eagle suits complex motion or visually complex sprites; eagle-audio has the same visuals as eagle plus audio generation.","enum":["blitz","forge","forge-pixel","eagle","eagle-audio","standard"],"default":"blitz"},"duration":{"type":"number","format":"float","description":"Animation length in seconds. Available values depend on the model - the source of truth is MODEL_PRICING (credits.js), surfaced via /credits/costs; the public API docs list them per model (auto-generated)."},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"gif":{"type":"boolean","default":false,"description":"When true, generates an animated GIF from the spritesheet and returns it in gif_url. Disabled by default to reduce response time."},"individual_frames":{"type":"boolean","default":false,"description":"When true, extracts each frame from the spritesheet as an individual image and returns the URLs in individual_frame_urls."},"spritesheet_with_background":{"type":"boolean","default":false,"description":"When true, also returns the spritesheet with background intact (before background removal). Useful for manually fixing background removal issues. The with-background spritesheet URL will be in spritesheet_with_background_url."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"AnimateSpriteKeyframesPayload":{"type":"object","description":"Payload for generating an animated spritesheet that interpolates through up to three fixed keyframes (initial / middle / final). Runs on the Forge family - the only models supporting a middle keyframe. Input images can be provided in base64 or URL. At least one of initial_image or middle_image must be provided.","properties":{"motion_prompt":{"type":"string","description":"Optional text description of the desired animation (e.g., \"walking\", \"attack slash\"). When omitted, the motion is derived purely from the keyframes."},"initial_image":{"type":"string","description":"The url OR base64 of the first keyframe. Optional when a middle_image is provided."},"middle_image":{"type":"string","description":"The url OR base64 of the middle keyframe the animation passes through between the initial and final frames."},"final_image":{"type":"string","description":"The url OR base64 of the final keyframe. Requires an initial_image or middle_image to anchor the animation."},"model":{"type":"string","description":"Backing model. Only the Forge family supports a middle keyframe; forge-pixel is the pixel-art variant, for sprites whose frames are pixel art.","enum":["forge","forge-pixel"],"default":"forge"},"loop":{"type":"boolean","default":true,"description":"Trim the animation at the beginning or end to create a seamless loop. Not guaranteed to produce a perfect loop."},"crop":{"type":"boolean","description":"Crop sprite frames to fit content. Results in smaller spritesheets but inconsistent frame sizes across different animations."},"frames":{"type":"number","format":"integer","description":"Number of frames in the output spritesheet.","default":36,"enum":[4,9,16,25,36,49,64]},"frame_size":{"type":"number","format":"integer","description":"Size of each frame in pixels (width and height). 0 is for maximum resolution. -1 is for AI 1.5x upscaling. -9 is for matching the size and position of the input frame.","default":0,"enum":[32,64,96,128,192,256,384,0,-1,-9]},"margin_ratio":{"type":"number","format":"float","description":"Deprecated - prefer margin_ratio_horizontal / margin_ratio_vertical. Amount of padding around the sprite as a ratio (0.0 to 1.0), applied to both axes (equivalent to setting both per-axis params to this value). Only used when margin_ratio_mode is \"manual\". Cannot be combined with the per-axis params: providing both fails with HTTP 400."},"margin_ratio_horizontal":{"type":"number","format":"float","description":"Horizontal padding around the sprite as a ratio (0.0 to 1.0). Only used when margin_ratio_mode is \"manual\". Useful for animations that extend sideways (e.g., sword slashes, punches). Cannot be combined with the legacy margin_ratio param."},"margin_ratio_vertical":{"type":"number","format":"float","description":"Vertical padding around the sprite as a ratio (0.0 to 1.0). Only used when margin_ratio_mode is \"manual\". Useful for animations that extend up or down (e.g., jumps). Cannot be combined with the legacy margin_ratio param."},"margin_ratio_mode":{"type":"string","description":"Controls how margins are applied around the sprite.","enum":["auto","manual","none"],"default":"auto"},"image_type":{"x-swagger-data":"spriteImageTypes","type":"string","description":"Type of sprite being animated. Affects generation parameters and styling."},"duration":{"type":"number","format":"float","description":"Animation length in seconds. This endpoint always runs on the Forge model, so only Forge's durations apply - the source of truth is MODEL_PRICING (credits.js), surfaced via /credits/costs."},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"gif":{"type":"boolean","default":false,"description":"When true, generates an animated GIF from the spritesheet and returns it in gif_url. Disabled by default to reduce response time."},"individual_frames":{"type":"boolean","default":false,"description":"When true, extracts each frame from the spritesheet as an individual image and returns the URLs in individual_frame_urls."},"spritesheet_with_background":{"type":"boolean","default":false,"description":"When true, also returns the spritesheet with background intact (before background removal). Useful for manually fixing background removal issues. The with-background spritesheet URL will be in spritesheet_with_background_url."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"TransferMotionPayload":{"type":"object","description":"Payload for transferring motion from a video onto a static sprite image, producing an animated spritesheet.","required":["image"],"properties":{"image":{"type":"string","description":"Payload for generating an animated spritesheet from a static image. Input images can either be provided in base64 or URL. If the image was generated using Ludo, ideally it should be generated using the \"sprite\", \"sprite-vfx\" or \"ui_asset\" type."},"video":{"type":"string","description":"URL of the video to use as motion source. You can use videos from the animateSprite endpoint or provide your own. Videos up to 4 seconds will produce better results. Either `video` or `preset_id` + `perspective` + `direction` must be provided."},"preset_id":{"type":"string","description":"ID of an animation preset to use instead of a video URL. Use the animation-presets endpoint to list available presets. When using a preset, `perspective` and `direction` are required."},"direction":{"type":"string","description":"Direction for the animation preset. When using a preset, `direction` is required.","enum":["N","NE","E","SE","S","SW","W","NW"]},"perspective":{"type":"string","description":"Perspective ID to use with the animation preset. When using a preset, `perspective` is required."},"frames":{"type":"number","format":"integer","description":"Number of frames in the output spritesheet.","default":36,"enum":[4,9,16,25,36,49,64]},"frame_size":{"type":"number","format":"integer","description":"Size of each frame in pixels (width and height). 0 is for maximum resolution.","default":0,"enum":[32,64,96,128,192,256,384,0]},"loop":{"type":"boolean","description":"Trim the animation at the beginning or end to create a seamless loop."},"crop":{"type":"boolean","description":"Crop sprite frames to fit content. Results in smaller spritesheets but inconsistent frame sizes across different animations."},"margin_ratio":{"type":"number","format":"float","description":"Deprecated - prefer margin_ratio_horizontal / margin_ratio_vertical. Amount of padding around the sprite as a ratio (0.0 to 1.0), applied to both axes (equivalent to setting both per-axis params to this value). Only used when margin_ratio_mode is \"manual\". Cannot be combined with the per-axis params: providing both fails with HTTP 400. Defaults to 0.15 when no margin param is provided.","default":0.15},"margin_ratio_horizontal":{"type":"number","format":"float","description":"Horizontal padding around the sprite as a ratio (0.0 to 1.0). Only used when margin_ratio_mode is \"manual\". Useful for animations that extend sideways (e.g., sword slashes, punches). Cannot be combined with the legacy margin_ratio param."},"margin_ratio_vertical":{"type":"number","format":"float","description":"Vertical padding around the sprite as a ratio (0.0 to 1.0). Only used when margin_ratio_mode is \"manual\". Useful for animations that extend up or down (e.g., jumps). Cannot be combined with the legacy margin_ratio param."},"margin_ratio_mode":{"type":"string","description":"Controls how margins are applied around the sprite.","enum":["manual","none"],"default":"manual"},"gif":{"type":"boolean","default":false,"description":"When true, generates an animated GIF from the spritesheet and returns it in gif_url. Disabled by default to reduce response time."},"individual_frames":{"type":"boolean","default":false,"description":"When true, extracts each frame from the spritesheet as an individual image and returns the URLs in individual_frame_urls."},"spritesheet_with_background":{"type":"boolean","default":false,"description":"When true, also returns the spritesheet with background intact (before background removal). Useful for manually fixing background removal issues. The with-background spritesheet URL will be in spritesheet_with_background_url."},"model":{"type":"string","description":"Motion transfer model to use. Forge is cost-effective for simple motion; Tango is the most powerful for demanding use cases.","enum":["forge","forge-pixel","tango"],"default":"tango"},"duration":{"type":"number","format":"float","default":1.5,"description":"Animation length in seconds. If the reference video is longer, it will be compressed to this duration. Available values depend on the model - see /credits/costs (per-model lists are auto-generated into the public API docs)."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"PublicJobError":{"type":"object","properties":{"status":{"type":"integer","format":"int32"},"message":{"type":"string"}}},"PublicJob":{"type":"object","required":["id","task_type","status","created_at"],"properties":{"id":{"type":"string"},"task_type":{"type":"string","description":"The public operationId that created this job (e.g. createImage). Determines the shape of `result`: it is exactly the 200 response body documented for that operation."},"status":{"type":"string","enum":["queued","running","succeeded","failed","canceled"]},"request_id":{"type":"string"},"created_at":{"type":"integer","format":"int64","description":"Unix ms"},"finished_at":{"type":"integer","format":"int64"},"credits_charged":{"type":"number","format":"float","description":"Credits this job cost you. Charged at enqueue; reported as 0 once a job fails or is canceled, since the charge is refunded."},"result":{"type":"object","description":"Present only when status is succeeded. Same shape as the synchronous response of the operation named by task_type."},"error":{"$ref":"#/definitions/PublicJobError"},"poll_after_ms":{"type":"integer","format":"int32","description":"Present while the job is queued or running: wait at least this many milliseconds before polling again. Prefer GET /assets/jobs/{id}?wait=30 (long-poll) over tight loops."}}},"GenerateImagePayload":{"type":"object","description":"Payload for generating an image from text prompt","required":["image_type","prompt"],"properties":{"image_type":{"x-swagger-data":"imageTypes","type":"string","description":"What kind of image to generate."},"prompt":{"type":"string","description":"Text description of the image to generate. The more detailed the prompt, the more accurate the image will be."},"art_style":{"x-swagger-data":"artStyles","type":"string","description":"Visual art style for the sprite (e.g., \"Pixel Art\", \"Cartoonish\", \"Realistic\")."},"perspective":{"x-swagger-data":"perspectives","type":"string","description":"Camera angle/view for the sprite (e.g., \"Side view\", \"Front view\", \"Isometric\")."},"aspect_ratio":{"x-swagger-data":"aspectRatios","type":"string","description":"Aspect ratio of the output image."},"n":{"type":"number","format":"integer","default":1,"minimum":1,"maximum":8,"description":"Number of image variations to generate"},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"GenerateMapTilesPayload":{"type":"object","properties":{"request_id":{"type":"string"},"prompt":{"type":"string"},"layer":{"type":"string"},"tile_coordinates":{"type":"array","items":{"type":"array","items":{"type":"number","format":"integer"}}},"map":{"$ref":"#/definitions/GeneratedMap"}}},"GenerateNewMapPayload":{"type":"object","properties":{"art_style":{"type":"string"},"perspective":{"type":"string"},"prompt":{"type":"string"},"request_id":{"type":"string"},"tile_size":{"type":"integer"}}},"EditorState":{"type":"object","properties":{"grid_snapping":{"type":"boolean"},"viewport_x":{"type":"number","format":"float"},"viewport_y":{"type":"number","format":"float"},"viewport_zoom":{"type":"number","format":"float"},"show_grid":{"type":"boolean"},"grid_color":{"type":"string"},"grid_opacity":{"type":"number","format":"float"}}},"MapSprite":{"type":"object","properties":{"height":{"type":"number","format":"float"},"id":{"type":"string"},"image":{"$ref":"#/definitions/GameImage"},"layer":{"type":"number","format":"integer"},"meta":{"type":"object"},"opacity":{"type":"number","format":"float"},"width":{"type":"number","format":"float"},"x":{"type":"number","format":"float"},"y":{"type":"number","format":"float"}}},"MapBaseImage":{"type":"object","properties":{"height":{"type":"number","format":"float"},"opacity":{"type":"number","format":"float"},"width":{"type":"number","format":"float"},"x":{"type":"number","format":"float"},"y":{"type":"number","format":"float"},"image":{"$ref":"#/definitions/GameImage"}}},"MapLayer":{"type":"object","properties":{"base_image":{"$ref":"#/definitions/MapBaseImage"},"id":{"type":"string"},"meta":{"type":"object"},"name":{"type":"string"},"order":{"type":"number","format":"integer"},"visible":{"type":"boolean"},"sprites":{"type":"array","items":{"$ref":"#/definitions/MapSprite"}}}},"GeneratedMap":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}},"id":{"type":"string"},"art_style":{"type":"string"},"description":{"type":"string"},"editor_state":{"$ref":"#/definitions/EditorState"},"grid_size":{"type":"number","format":"integer"},"layers":{"type":"array","items":{"$ref":"#/definitions/MapLayer"}},"name":{"type":"string"},"perspective":{"type":"string"},"sprites":{"type":"array","items":{"$ref":"#/definitions/MapSprite"}}}},"GenerateSpritesheetAudioPayload":{"type":"object","properties":{"request_id":{"type":"string"},"prompt":{"type":"string"},"spritesheet":{"$ref":"#/definitions/Spritesheet"}}},"GenerateSpritesheetMotionTransferPayload":{"type":"object","properties":{"request_id":{"type":"string"},"image":{"$ref":"#/definitions/GameImage"},"spritesheet":{"$ref":"#/definitions/Spritesheet"},"video":{"type":"string"},"num_frames":{"type":"number","format":"integer"},"target_frame_size":{"type":"number","format":"integer"},"loop":{"type":"boolean"},"crop":{"type":"boolean"},"margin_ratio":{"type":"number","format":"float"},"margin_ratio_horizontal":{"type":"number","format":"float"},"margin_ratio_vertical":{"type":"number","format":"float"},"margin_ratio_mode":{"type":"string","enum":["manual","none"],"default":"manual"},"model":{"type":"string","enum":["tango","forge","forge-pixel"],"default":"tango"},"duration":{"type":"number","format":"float","default":1.5,"description":"Animation length in seconds. Available values depend on the model - see /credits/costs (per-model lists are auto-generated into the public API docs)."},"pixel_art_filter":{"type":"boolean"},"spritesheet_with_background":{"type":"boolean"}}},"EditSpritesheetPayload":{"type":"object","description":"Payload for editing a spritesheet's video. edit_mode selects the operation - prompt-driven edit (default), outpaint, or loop fixing.","required":["spritesheet"],"properties":{"request_id":{"type":"string"},"edit_mode":{"type":"string","description":"Edit operation to perform.","enum":["prompt","outpaint","fix_loop"],"default":"prompt"},"prompt":{"type":"string","description":"Edit instruction. Required for edit_mode \"prompt\", optional for \"outpaint\", not accepted for \"fix_loop\"."},"spritesheet":{"$ref":"#/definitions/Spritesheet"},"images":{"type":"array","maxItems":5,"items":{"$ref":"#/definitions/GameImage"}},"duration":{"type":"number","format":"float","default":3},"model":{"type":"string","description":"Backing model.","enum":["forge","forge-pixel"],"default":"forge"},"crop":{"type":"boolean"},"loop":{"type":"boolean","default":true},"num_frames":{"type":"number","format":"integer"},"target_frame_size":{"type":"number","format":"integer"},"spritesheet_with_background":{"type":"boolean"}}},"ReferencesToVideoPayload":{"type":"object","description":"Payload for generating a video from 1-5 reference images and a text prompt.","required":["prompt","images"],"properties":{"request_id":{"type":"string"},"prompt":{"type":"string"},"images":{"type":"array","minItems":1,"maxItems":5,"items":{"$ref":"#/definitions/GameImage"}},"duration":{"type":"number","format":"float","default":5},"model":{"type":"string","description":"Backing model.","enum":["eagle","eagle-audio","forge","forge-pixel"],"default":"eagle"},"aspect_ratio":{"type":"string","description":"Output aspect ratio. \"default\" lets the model choose.","enum":["default","ar_1_1","ar_16_9","ar_9_16","ar_4_3","ar_3_4","ar_21_9"],"default":"default"}}},"VideoEditPayload":{"type":"object","description":"Payload for editing a video from a text prompt and optional reference images.","required":["prompt","video"],"properties":{"request_id":{"type":"string"},"prompt":{"type":"string"},"video":{"$ref":"#/definitions/GeneratedVideo"},"images":{"type":"array","maxItems":5,"items":{"$ref":"#/definitions/GameImage"}},"duration":{"type":"number","format":"float","default":5},"model":{"type":"string","description":"Backing model.","enum":["eagle","forge","forge-pixel"],"default":"eagle"}}},"UpscaleVideoPayload":{"type":"object","description":"Payload for upscaling a previously generated video 2x. Only videos below 960x960 pixels can be upscaled. Billed per second of video (see /credits/costs).","required":["video"],"properties":{"request_id":{"type":"string"},"video":{"$ref":"#/definitions/GeneratedVideo"}}},"EditSpritesheetPayloadPublic":{"type":"object","description":"Payload for editing a previously generated spritesheet. edit_mode selects the operation - prompt-driven edit (default), outpaint, or loop fixing.","required":["spritesheet_url"],"properties":{"spritesheet_url":{"type":"string","description":"URL of a spritesheet you generated in the last 7 days (returned by animateSprite, transferMotion, or a previous edit as spritesheet_url). External URLs are not accepted."},"edit_mode":{"type":"string","description":"Edit operation to perform.","enum":["prompt","outpaint","fix_loop"],"default":"prompt"},"prompt":{"type":"string","description":"Edit instruction. Required for edit_mode \"prompt\", optional for \"outpaint\", not accepted for \"fix_loop\"."},"images":{"type":"array","maxItems":5,"items":{"type":"string"},"description":"Optional reference images (up to 5), each a URL or base64, to guide the edit."},"duration":{"type":"number","format":"float","description":"Output animation length in seconds. Defaults to the source spritesheet's duration."},"model":{"type":"string","description":"Backing model. forge-pixel is the pixel-art variant, for spritesheets whose frames are pixel art.","enum":["forge","forge-pixel"],"default":"forge"},"crop":{"type":"boolean","description":"Crop sprite frames to fit content. Results in smaller spritesheets but inconsistent frame sizes across different animations."},"loop":{"type":"boolean","default":true,"description":"Trim the animation at the beginning or end to create a seamless loop."},"frames":{"type":"number","format":"integer","description":"Number of frames in the output spritesheet. Defaults to the source spritesheet's frame count.","enum":[4,9,16,25,36,49,64]},"frame_size":{"type":"number","format":"integer","description":"Size of each frame in pixels (width and height). 0 is for maximum resolution. Defaults to the source spritesheet's frame size.","enum":[32,64,96,128,192,256,384,0]},"gif":{"type":"boolean","default":false,"description":"When true, generates an animated GIF from the spritesheet and returns it in gif_url. Disabled by default to reduce response time."},"individual_frames":{"type":"boolean","default":false,"description":"When true, extracts each frame from the spritesheet as an individual image and returns the URLs in individual_frame_urls."},"spritesheet_with_background":{"type":"boolean","default":false,"description":"When true, also returns the spritesheet with background intact (before background removal). The with-background spritesheet URL will be in spritesheet_with_background_url."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"ReferencesToVideoPayloadPublic":{"type":"object","description":"Payload for generating a video from 1-5 reference images and a text prompt.","required":["prompt","images"],"properties":{"prompt":{"type":"string","description":"Text description of the video to generate."},"images":{"type":"array","minItems":1,"maxItems":5,"items":{"type":"string"},"description":"Reference images (1 to 5), each a URL or base64. The generated video borrows characters, objects, and style from them."},"duration":{"type":"number","format":"float","default":5,"description":"Video length in seconds."},"model":{"type":"string","description":"Video model to use. forge-pixel renders pixel-art video.","enum":["eagle","eagle-audio","forge-pixel"],"default":"eagle"},"aspect_ratio":{"type":"string","description":"Output aspect ratio. \"default\" lets the model choose.","enum":["default","ar_1_1","ar_16_9","ar_9_16","ar_4_3","ar_3_4","ar_21_9"],"default":"default"},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"EditVideoPayloadPublic":{"type":"object","description":"Payload for editing a previously generated video with a text prompt and optional reference images.","required":["prompt","video"],"properties":{"video":{"type":"string","description":"URL of a video you generated in the last 7 days (returned by createVideo, createVideoFromReferences, or a previous edit). External URLs are not accepted."},"prompt":{"type":"string","description":"Edit instruction describing the desired change."},"images":{"type":"array","maxItems":5,"items":{"type":"string"},"description":"Optional reference images (up to 5), each a URL or base64, to guide the edit."},"duration":{"type":"number","format":"float","description":"Output video length in seconds. Defaults to the source video's duration."},"model":{"type":"string","description":"Video model to use. forge-pixel renders pixel-art video.","enum":["eagle","forge-pixel"],"default":"eagle"},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"UpscaleVideoPayloadPublic":{"type":"object","description":"Payload for upscaling a previously generated video to twice its resolution.","required":["video"],"properties":{"video":{"type":"string","description":"URL of a video you generated in the last 7 days (returned by createVideo, createVideoFromReferences, or editVideo). External URLs are not accepted. Only videos below 960x960 pixels can be upscaled."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"AlignImagesPayload":{"type":"object","required":["image1","image2"],"properties":{"image1":{"$ref":"#/definitions/GameImage"},"image2":{"$ref":"#/definitions/GameImage"}}},"AlignImagesResult":{"type":"object","properties":{"dx":{"type":"number","format":"integer"},"dy":{"type":"number","format":"integer"},"scale":{"type":"number","format":"float"},"confidence":{"type":"number","format":"float"}}},"SegmentImagePayload":{"type":"object","properties":{"image":{"type":"string"},"negative_points":{"type":"array","items":{"type":"array","items":{"type":"number","format":"integer"}}},"positive_points":{"type":"array","items":{"type":"array","items":{"type":"number","format":"integer"}}}}},"GenerateVideoPayload":{"type":"object","properties":{"request_id":{"type":"string"},"prompt":{"type":"string"},"image":{"$ref":"#/definitions/GameImage"},"final_image":{"$ref":"#/definitions/GameImage"},"duration":{"type":"number","format":"float"},"model":{"type":"string"}}},"GenerateVideoAudioPayload":{"type":"object","properties":{"request_id":{"type":"string"},"prompt":{"type":"string"},"video":{"$ref":"#/definitions/GeneratedVideo"}}},"GenerateAudioPayload":{"type":"object","properties":{"request_id":{"type":"string"},"audio_type":{"type":"string","enum":["sfx","ambiance","music","audio-transform","human-voice","non-human-voice","speech","speech-preset"]},"prompt":{"type":"string"},"text":{"type":"string"},"duration":{"type":"number","format":"float"},"modification_strength":{"type":"number","format":"float","minimum":0,"maximum":1,"description":"For audio-transform, controls how strongly the source sample is modified. 0 keeps it close to the original, 1 transforms it fully."},"loop":{"type":"boolean"},"n":{"type":"integer","format":"integer"},"voice_preset":{"$ref":"#/definitions/VoicePreset"},"sample":{"$ref":"#/definitions/GeneratedAudio"}}},"GenerationsSearchResult":{"type":"object","properties":{"similar_games":{"type":"array","items":{"$ref":"#/definitions/SimilarGame"}}}},"GenerateFilters":{"type":"object","properties":{"n":{"type":"integer","format":"int64"},"platforms":{"type":"array","items":{"type":"string"}},"platform":{"type":"string"},"genres":{"type":"array","items":{"type":"string"}},"creative_mode":{"type":"boolean"},"duration":{"type":"number","format":"float"}}},"GeneratedText":{"type":"object","properties":{"id":{"type":"string"},"request_id":{"type":"string"},"image":{"$ref":"#/definitions/GameImage"},"images":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"context_keywords":{"type":"array","items":{"type":"string"}},"text":{"type":"string"},"title":{"type":"string"},"type":{"type":"string"},"image_prompt":{"type":"string"},"final_score":{"type":"number","format":"float"}}},"UserFeedback":{"type":"object","properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"type":{"type":"string"},"text":{"type":"string"},"images":{"type":"array","items":{"type":"string"}},"date":{"type":"integer","format":"int64"}}},"PasswordRecovery":{"type":"object","properties":{"_id":{"type":"string"},"email":{"type":"string"},"code":{"type":"string"},"date":{"type":"integer","format":"int64"}}},"EmailConfirmation":{"type":"object","properties":{"_id":{"type":"string"},"email":{"type":"string"},"code":{"type":"string"},"date":{"type":"integer","format":"int64"},"name":{"type":"string"}}},"ResetPasswordPayload":{"type":"object","properties":{"code":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"}}},"DiscordLinkStartResponse":{"type":"object","properties":{"url":{"type":"string","description":"Discord OAuth authorize url the browser should be sent to."}}},"StringValue":{"type":"object","properties":{"value":{"type":"string"},"value2":{"type":"string"}}},"TopupPayload":{"type":"object","required":["credits"],"properties":{"credits":{"type":"integer","format":"int64","description":"Number of credits to purchase (server computes the price from TOPUP brackets)"}}},"AutoTopupSettings":{"type":"object","properties":{"enabled":{"type":"boolean"},"threshold":{"type":"number","format":"float","description":"When available credits (max + extra - current) drop below this, an automatic top-up is attempted"},"credits":{"type":"integer","format":"int64","description":"Fixed credit amount to buy on each automatic top-up (TOPUP bracket pricing)"},"armed":{"type":"boolean","description":"Hysteresis latch: true while the balance is above the threshold; consumed (set false) by the single attempt per below-threshold episode"}}},"AutoTopupPayload":{"type":"object","required":["enabled"],"properties":{"enabled":{"type":"boolean"},"threshold":{"type":"number","format":"float"},"credits":{"type":"integer","format":"int64"}}},"TopupResult":{"type":"object","properties":{"invoice_id":{"type":"string"},"credits":{"type":"integer","format":"int64"},"amount_cents":{"type":"integer","format":"int64"},"status":{"type":"string"}}},"TopupBracket":{"type":"object","properties":{"credits":{"type":"integer","format":"int64"},"cents":{"type":"integer","format":"int64"}}},"TopupConfig":{"type":"object","properties":{"currency":{"type":"string"},"min_credits":{"type":"integer","format":"int64"},"max_credits":{"type":"integer","format":"int64"},"step_credits":{"type":"integer","format":"int64"},"brackets":{"type":"array","items":{"$ref":"#/definitions/TopupBracket"}}}},"PauseSubscriptionRequest":{"type":"object","required":["pauseDurationMonths"],"properties":{"pauseDurationMonths":{"type":"integer","format":"int64","description":"Duration to pause subscription in months (e.g., 1, 2, or 3)"}}},"CreditCard":{"type":"object","properties":{"id":{"type":"string"},"brand":{"type":"string"},"country":{"type":"string"},"exp_month":{"type":"integer","format":"int64"},"exp_year":{"type":"integer","format":"int64"},"last4":{"type":"string"},"fingerprint":{"type":"string"},"funding":{"type":"string"},"default":{"type":"boolean"}}},"UserTracking":{"type":"object","properties":{"_id":{"type":"string"},"date":{"type":"integer","format":"int64"},"user_id":{"type":"string"},"action":{"type":"string"},"action_id":{"type":"string"},"payload":{"type":"string"},"payload_json":{"type":"object"}}},"EmailLog":{"type":"object","properties":{"_id":{"type":"string"},"date":{"type":"integer","format":"int64"},"user_id":{"type":"string"},"to":{"type":"string"},"subject":{"type":"string"},"template":{"type":"string"},"status":{"type":"string","enum":["sent","failed","skipped"]},"message_id":{"type":"string"},"error":{"type":"string"},"invoice_id":{"type":"string"},"stage":{"type":"string"}}},"RegistrationRiskLog":{"type":"object","properties":{"_id":{"type":"string"},"date":{"type":"integer","format":"int64"},"user_id":{"type":"string"},"domain":{"type":"string"},"ip":{"type":"string"},"score":{"type":"integer"},"tier":{"type":"string"},"mode":{"type":"string"},"withheld":{"type":"boolean"},"captcha":{"type":"boolean"},"signals":{"type":"object","properties":{"dv":{"type":"integer"},"iv":{"type":"integer"},"sv":{"type":"integer"},"badName":{"type":"boolean"},"knownBad":{"type":"boolean"},"site":{"type":"string"},"paidDomain":{"type":"boolean"},"farmMx":{"type":"boolean"}}}}},"RegistrationBlockLog":{"type":"object","properties":{"_id":{"type":"string"},"date":{"type":"integer","format":"int64"},"code":{"type":"string"},"reason":{"type":"string"},"domain":{"type":"string"},"ip":{"type":"string"}}},"BlockedIp":{"type":"object","properties":{"_id":{"type":"string"},"ip":{"type":"string"},"reason":{"type":"string"},"domains":{"type":"array","items":{"type":"string"}},"added_by":{"type":"string"},"date":{"type":"integer","format":"int64"}}},"BlockedDomain":{"type":"object","properties":{"_id":{"type":"string"},"domain":{"type":"string"},"reason":{"type":"string"},"added_by":{"type":"string"},"date":{"type":"integer","format":"int64"}}},"CreditChange":{"type":"object","properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"date":{"type":"integer","format":"int64"},"reason":{"type":"string","enum":["init","subscription_cycle","new_sub","plan_change","topup","topup_purchase","cron_renewal","referral_bonus","pause","unpause","unpause_with_payment","cancellation","grace_start","grace_expired","team_join","manual"]},"source":{"type":"string"},"plan_name":{"type":"string"},"before":{"$ref":"#/definitions/CreditSnapshot"},"after":{"$ref":"#/definitions/CreditSnapshot"},"delta_max":{"type":"number","format":"double"},"delta_extra":{"type":"number","format":"double"},"notes":{"type":"string"}}},"CreditSnapshot":{"type":"object","properties":{"current":{"type":"number","format":"double"},"max":{"type":"number","format":"double"},"extra":{"type":"number","format":"double"},"reset_date":{"type":"integer","format":"int64"},"paused":{"type":"boolean"}}},"History":{"type":"object","properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"description":{"type":"string"},"date":{"type":"integer","format":"int64"},"type":{"type":"string","enum":["games","generated_games"]},"game_ids":{"type":"array","items":{"type":"string"}},"generated_games":{"type":"array","items":{"$ref":"#/definitions/GeneratedGame"}},"parameters":{"$ref":"#/definitions/HistoryParameters"}}},"HistoryParameters":{"type":"object","properties":{"descriptions":{"type":"array","items":{"type":"string"}},"game_ids":{"type":"array","items":{"type":"string"}},"generations":{"type":"array","items":{"$ref":"#/definitions/GeneratedGame"}},"keywords":{"type":"array","items":{"type":"string"}},"filters":{"$ref":"#/definitions/GenerateFilters"},"strategy":{"type":"string","enum":["popular","trending","mix"]}}},"Project":{"type":"object","properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"team_id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"date":{"type":"integer","format":"int64"},"date_updated":{"type":"integer","format":"int64"},"archived":{"type":"boolean"},"genres":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}},"platforms":{"type":"array","items":{"type":"string"}},"platform":{"type":"string"},"gdd2":{"$ref":"#/definitions/GDD"},"gdd3":{"$ref":"#/definitions/GDD3"}}},"GDD":{"type":"object","properties":{"genres":{"type":"array","items":{"type":"string"}},"platform":{"type":"string"},"art_style":{"type":"string"},"perspective":{"type":"string"},"aspect_ratio":{"type":"string"},"step_choices":{"type":"array","items":{"$ref":"#/definitions/StepValue"}},"score":{"$ref":"#/definitions/LudoScore"},"header":{"$ref":"#/definitions/GameImage"},"icon":{"$ref":"#/definitions/GameImage"},"sections":{"type":"array","items":{"$ref":"#/definitions/GDDSection"}}}},"GDDSection":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"date":{"type":"integer","format":"int64"},"loading":{"type":"integer","format":"int64"},"pending":{"type":"boolean"},"value":{"$ref":"#/definitions/GDDSectionValue"}}},"GDDSectionValue":{"type":"object","properties":{"text":{"type":"string"},"country":{"type":"string"},"title":{"type":"string"},"layout":{"type":"string"},"imageType":{"type":"string"},"images":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"assets":{"type":"array","items":{"$ref":"#/definitions/ThreeDAsset"}},"games":{"type":"array","items":{"type":"string"}},"elements":{"type":"array","items":{"$ref":"#/definitions/GeneratedText"}},"comments":{"type":"array","items":{"$ref":"#/definitions/GDDComment"}},"score":{"$ref":"#/definitions/LudoScore"}}},"GDDComment":{"type":"object","properties":{"_id":{"type":"string"},"author_id":{"type":"string"},"author_email":{"type":"string"},"date":{"type":"integer","format":"int64"},"text":{"type":"string"},"section":{"type":"string"},"archived":{"type":"boolean"},"replies":{"type":"array","items":{"$ref":"#/definitions/GDDComment"}}}},"SharedItem":{"type":"object","properties":{"_id":{"type":"string"},"date":{"type":"integer","format":"int64"},"team_id":{"type":"string"},"project_id":{"type":"string"},"user_id":{"type":"string"},"type":{"type":"string","enum":["game","image","generated_game"]},"game_id":{"type":"string"},"generated_game":{"$ref":"#/definitions/GeneratedGame"},"image":{"$ref":"#/definitions/GameImage"}}},"Favorite":{"type":"object","properties":{"_id":{"type":"string"},"date":{"type":"integer","format":"int64"},"project_id":{"type":"string"},"user_id":{"type":"string"},"type":{"type":"string","enum":["game","image","generated_game","text","mechanic","feature","three_d_asset","video","spritesheet","audio"]},"payload_id":{"type":"string"},"text":{"$ref":"#/definitions/GeneratedText"},"generated_game":{"$ref":"#/definitions/GeneratedGame"},"image":{"$ref":"#/definitions/GameImage"},"video":{"$ref":"#/definitions/GeneratedVideo"},"three_d_asset":{"$ref":"#/definitions/ThreeDAsset"},"spritesheet":{"$ref":"#/definitions/Spritesheet"},"audio":{"$ref":"#/definitions/GeneratedAudio"}}},"AddFavoritePayload":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["video","spritesheet","three_d_asset","game","generated_game","image","audio"]},"video":{"$ref":"#/definitions/GeneratedVideo"},"spritesheet":{"$ref":"#/definitions/Spritesheet"},"three_d_asset":{"$ref":"#/definitions/ThreeDAsset"},"game":{"type":"string"},"generated_game":{"$ref":"#/definitions/GeneratedGame"},"image":{"$ref":"#/definitions/GameImage"},"audio":{"$ref":"#/definitions/GeneratedAudio"}}},"GameIds":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}}},"SharePayload":{"type":"object","properties":{"text":{"$ref":"#/definitions/GeneratedText"},"generated_game":{"$ref":"#/definitions/GeneratedGame"},"image":{"$ref":"#/definitions/GameImage"},"game":{"$ref":"#/definitions/Game"},"gdd_pdf":{"type":"string"},"gdd":{"$ref":"#/definitions/GDD3"}}},"SimilarGame":{"type":"object","properties":{"id":{"type":"string"},"similarity":{"type":"number","format":"float"}}},"TrendKeywords":{"type":"object","properties":{"keyword":{"type":"string"},"type":{"type":"string"},"weight":{"type":"number","format":"double"}}},"ColorFilter":{"type":"object","properties":{"name":{"type":"string"},"rgb":{"type":"array","items":{"type":"integer"}}}},"UserRefreshPayload":{"type":"object","properties":{"login_payload":{"$ref":"#/definitions/LoginPayload"},"projects":{"type":"array","items":{"$ref":"#/definitions/Project"}},"genres":{"type":"array","items":{"type":"string"}},"generationStyles":{"type":"array","items":{"type":"string"}},"generationStylesDetails":{"type":"array","items":{"$ref":"#/definitions/GeneratorOption"}},"generationTypes":{"type":"array","items":{"type":"string"}},"generationTypesStandalone":{"type":"array","items":{"$ref":"#/definitions/GeneratorOption"}},"generationTypes3d":{"type":"array","items":{"type":"string"}},"generationTypesSprite":{"type":"array","items":{"type":"string"}},"generationTypesNoBg":{"type":"array","items":{"type":"string"}},"componentGroups":{"type":"array","items":{"$ref":"#/definitions/ComponentGroup"}},"componentList":{"type":"array","items":{"$ref":"#/definitions/ComponentInfo"}},"gddPacks":{"type":"array","items":{"$ref":"#/definitions/GDDPack"}},"colors":{"type":"array","items":{"$ref":"#/definitions/ColorFilter"}},"styles":{"type":"array","items":{"type":"string"}},"platforms":{"type":"array","items":{"type":"string"}},"perspectives":{"type":"array","items":{"type":"string"}},"perspectivesDetails":{"type":"array","items":{"$ref":"#/definitions/GeneratorOption"}},"teams":{"type":"array","items":{"$ref":"#/definitions/Team"}},"health":{"type":"boolean"},"credits":{"$ref":"#/definitions/GenerationCreditsInfo"},"plans":{"type":"array","items":{"$ref":"#/definitions/PaymentPlan"}},"documentation":{"type":"array","items":{"$ref":"#/definitions/Documentation"}},"whats_new":{"type":"array","items":{"$ref":"#/definitions/WhatsNew"}},"generation_steps":{"type":"array","items":{"$ref":"#/definitions/GenerationStep"}},"credit_costs":{"type":"array","items":{"$ref":"#/definitions/CreditFeature"}},"voice_presets":{"$ref":"#/definitions/VoicePresetList"}}},"GeneratorOption":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}}},"GenerationStep":{"type":"object","properties":{"allow_input":{"type":"boolean"},"fixed_options":{"type":"boolean"},"description":{"type":"string"},"key":{"type":"string"},"name":{"type":"string"}}},"StepByStepPayload":{"type":"object","properties":{"request_id":{"type":"string"},"genre":{"type":"string"},"n":{"type":"integer","format":"int64"},"platform":{"type":"string"},"step":{"type":"string"},"step_choices":{"type":"array","items":{"$ref":"#/definitions/StepValue"}}}},"StepValue":{"type":"object","properties":{"step":{"type":"string"},"description":{"type":"string"},"value":{"type":"string"},"score":{"$ref":"#/definitions/LudoScore"}}},"LudoScore":{"type":"object","properties":{"metrics":{"type":"array","items":{"$ref":"#/definitions/LudoScoreMetric"}},"params":{"type":"string"},"score_text":{"type":"string"},"score_unit":{"type":"string"},"score":{"type":"number","format":"double"},"timestamp":{"type":"integer","format":"int64"},"country":{"type":"string"},"sources":{"type":"array","items":{"type":"string"}},"version":{"type":"string"},"badge":{"$ref":"#/definitions/LudoScoreBadge"},"insights":{"type":"array","items":{"$ref":"#/definitions/LudoScoreInsight"}}}},"LudoScoreBadge":{"type":"object","properties":{"description":{"type":"string"},"icon_url":{"type":"string"},"key":{"type":"string"},"name":{"type":"string"}}},"LudoScoreMetric":{"type":"object","properties":{"_max_similarity":{"type":"number","format":"double"},"_min_similarity":{"type":"number","format":"double"},"description":{"type":"string"},"key":{"type":"string"},"name":{"type":"string"},"score":{"type":"number","format":"double"},"supporting_games_title":{"type":"string"},"supporting_games":{"type":"array","items":{"type":"string"}},"core_metric":{"type":"boolean"},"plotly_plot":{"$ref":"#/definitions/PlotlyPlot"},"insights":{"type":"array","items":{"$ref":"#/definitions/LudoScoreInsight"}}}},"PlotlyPlot":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/definitions/PlotlyPlotData"}},"layout":{"$ref":"#/definitions/PlotlyPlotData"}}},"PlotlyPlotData":{"type":"object","properties":{"mode":{"type":"string"}}},"LudoScoreInsight":{"type":"object","properties":{"tag":{"type":"string"},"text":{"type":"string"}}},"GDDPack":{"type":"object","properties":{"components":{"type":"array","items":{"$ref":"#/definitions/GDDPackComponent"}},"description":{"type":"string"},"name":{"type":"string"},"title":{"type":"string"}}},"GDDPackComponent":{"type":"object","properties":{"n":{"type":"integer","format":"int64"},"type":{"type":"string"},"title":{"type":"string"}}},"ComponentGroup":{"type":"object","properties":{"components":{"type":"array","items":{"$ref":"#/definitions/ComponentInfo"}},"description":{"type":"string"},"name":{"type":"string"},"title":{"type":"string"}}},"ComponentInfo":{"type":"object","properties":{"description":{"type":"string"},"group":{"type":"string"},"hide":{"type":"boolean"},"images_by_default":{"type":"boolean"},"multiple_allowed":{"type":"string"},"section":{"type":"string"},"label":{"type":"string"},"followups":{"type":"boolean"},"gameElements":{"type":"boolean"},"chat":{"type":"boolean"},"related":{"type":"array","items":{"type":"string"}}}},"WhatsNew":{"type":"object","properties":{"id":{"type":"string"},"date":{"type":"integer","format":"int64"},"title":{"type":"string"},"hqTitle":{"type":"string"},"hqThumb":{"type":"string"},"hqText":{"type":"string"},"toolLabel":{"type":"string"},"toolUrl":{"type":"string"}}},"Documentation":{"type":"object","properties":{"index":{"type":"string"},"label":{"type":"string"},"section":{"type":"array","items":{"$ref":"#/definitions/DocumentationSection"}}}},"DocumentationSection":{"type":"object","properties":{"title":{"type":"string"},"content":{"type":"string"}}},"GenerationCreditsInfo":{"type":"object","properties":{"current":{"type":"number","format":"float"},"max":{"type":"number","format":"float"},"reset_date":{"type":"integer","format":"int64"},"extra":{"type":"number","format":"float"},"total":{"type":"number","format":"float"},"paused":{"type":"boolean"},"has_tier_logic":{"type":"boolean"},"uses_team_credits":{"type":"boolean"},"credits_upfront":{"type":"boolean"},"grace_until":{"type":"integer","format":"int64","description":"If set, the user's subscription ended and their remaining credits stay spendable until this unix timestamp (30-day grace period)"},"grace_reason":{"type":"string","description":"Stripe cancellation_details.reason at termination (e.g. cancellation_requested, payment_failed)"}}},"GenerationHistoryResults":{"type":"object","properties":{"number_pages":{"type":"integer","format":"int64"},"results":{"type":"array","items":{"$ref":"#/definitions/GenerationHistory"}}}},"GenerationHistory":{"type":"object","properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"type":{"type":"string","enum":["generated_image","generated_game","three_d_asset","video","spritesheet","audio","map"]},"generated_image":{"$ref":"#/definitions/GameImage"},"generated_game":{"$ref":"#/definitions/GeneratedGame"},"three_d_asset":{"$ref":"#/definitions/ThreeDAsset"},"video":{"$ref":"#/definitions/GeneratedVideo"},"spritesheet":{"$ref":"#/definitions/Spritesheet"},"audio":{"$ref":"#/definitions/GeneratedAudio"},"map":{"$ref":"#/definitions/GeneratedMap"},"date":{"type":"integer","format":"int64"},"api":{"type":"boolean"},"source":{"type":"string","enum":["web","api","mcp"],"default":"web"},"request_id":{"type":"string"},"deleted":{"type":"boolean"},"help_requested_at":{"type":"integer","format":"int64"},"help_message":{"type":"string"}}},"GenerationHistoryTag":{"type":"object","properties":{"tag":{"type":"string"},"count":{"type":"integer"}}},"GenerationHistoryTags":{"type":"object","properties":{"tags":{"type":"array","items":{"$ref":"#/definitions/GenerationHistoryTag"}}}},"GenerationJob":{"type":"object","required":["_id","user_id","task_type","status","created_at"],"properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"task_type":{"type":"string","enum":["generate_images","generate_3d_images","generate_refined_3d_images","rig_3d_model","animate_3d_model","animate_3d_preset","generate_images_inpainting","generate_video","generate_video_first_frame","generate_audio","generate_image_to_spritesheet","generate_video_to_spritesheet","generate_sprite","generate_spritesheet_motion_transfer","generate_edit_spritesheet","generate_references_to_video","generate_video_edit","generate_upscale_video","remove_image_background","test_wait","api_create_image","api_generate_with_style","api_edit_image","api_generate_pose","api_rotate_sprite","api_remove_background","api_animate_sprite","api_animate_sprite_keyframes","api_transfer_motion","api_edit_spritesheet","api_create_video","api_create_video_from_references","api_edit_video","api_upscale_video","api_create_3d_model","api_rig_model","api_animate_3d_model","api_animate_3d_model_preset","api_create_sound_effect","api_create_ambiance","api_create_music","api_create_audio_transform","api_create_voice","api_create_speech","api_create_speech_preset"]},"status":{"type":"string","enum":["queued","running","succeeded","failed","canceled"]},"origin":{"type":"string","enum":["web","api","mcp"],"default":"web"},"payload":{"type":"object"},"result":{"type":"object"},"error":{"$ref":"#/definitions/JobError"},"request_id":{"type":"string"},"history_id":{"type":"string"},"worker_id":{"type":"string"},"attempts":{"type":"integer","format":"int32"},"credits_charged":{"type":"number","format":"float"},"credit_feature":{"type":"string"},"credit_tier":{"type":"string"},"created_at":{"type":"integer","format":"int64"},"started_at":{"type":"integer","format":"int64"},"finished_at":{"type":"integer","format":"int64"},"heartbeat_at":{"type":"integer","format":"int64"},"acknowledged_at":{"type":"integer","format":"int64"},"description":{"type":"string","description":"User-facing label derived from the payload at enqueue time (e.g. 'Sound effect: laser zap'). Frontend uses this in the queue panel."},"expire_at":{"type":"string","format":"date-time"}}},"JobError":{"type":"object","properties":{"code":{"type":"string","enum":["network","prometheus_5xx","prometheus_4xx","validation","timeout","unknown"]},"subcode":{"type":"string"},"message":{"type":"string"},"retriable":{"type":"boolean"}}},"JobAcceptedResponse":{"type":"object","required":["jobId","status"],"properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["queued","running","succeeded","failed","canceled"]},"queue_position":{"type":"integer","format":"int32"}}},"JobTestWaitPayload":{"type":"object","description":"Payload for the load-test queue endpoint. The worker sleeps for a random duration in [min_ms, max_ms) instead of calling Prometheus.","properties":{"min_ms":{"type":"integer","format":"int32","description":"Minimum sleep in milliseconds. Defaults to 10000 (10s)."},"max_ms":{"type":"integer","format":"int32","description":"Maximum sleep in milliseconds (exclusive). Defaults to 120000 (2min)."},"request_id":{"type":"string","description":"Optional idempotency key, same semantics as the real queue endpoints."}}},"QueueRemoveImageBackgroundPayload":{"type":"object","required":["image"],"properties":{"image":{"$ref":"#/definitions/GameImage"},"crop":{"type":"boolean"}}},"UserRecommendations":{"type":"object","properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"date":{"type":"integer","format":"int64"},"date_string":{"type":"string"}}},"Invoice":{"type":"object","properties":{"id":{"type":"string"},"date":{"type":"integer","format":"integer"},"total":{"type":"number","format":"integer"},"currency":{"type":"string"},"customer":{"type":"string"},"description":{"type":"string"},"lines":{"type":"array","items":{"$ref":"#/definitions/InvoiceLine"}},"number":{"type":"string"},"amount_due":{"type":"number","format":"integer"},"amount_paid":{"type":"number","format":"integer"},"amount_remaining":{"type":"number","format":"integer"},"collection_method":{"type":"string"},"hosted_invoice_url":{"type":"string"},"invoice_pdf":{"type":"string"},"metadata":{"$ref":"#/definitions/CouponMetadata"},"period_start":{"type":"number","format":"integer"},"period_end":{"type":"number","format":"integer"},"subscription":{"type":"string"},"subtotal":{"type":"number","format":"integer"},"status":{"type":"string"},"created":{"type":"number","format":"integer"},"attempted":{"type":"boolean"},"payments":{"type":"array","items":{"$ref":"#/definitions/InvoicePayment"}}}},"InvoicePayment":{"type":"object","properties":{"amount_paid":{"type":"number","format":"integer"},"status":{"type":"string"},"charge":{"type":"string"},"payment_intent":{"type":"string"}}},"InvoiceLine":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string"},"amount":{"type":"number","format":"integer"},"currency":{"type":"string"},"description":{"type":"string"},"discountable":{"type":"boolean"},"type":{"type":"string"}}},"CouponMetadata":{"type":"object","properties":{"description":{"type":"string"},"summary":{"type":"string"}}},"StripeEvent":{"type":"object","properties":{"id":{"type":"string"},"created":{"type":"number","format":"float"},"livemode":{"type":"boolean"},"type":{"type":"string"},"object":{"type":"string"},"pending_webhooks":{"type":"number","format":"float"},"api_version":{"type":"string"},"data":{"type":"object"},"request":{"type":"object"}}},"AssistantLog":{"type":"object","properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"date":{"type":"number","format":"integer"},"title":{"type":"string"},"message_history":{"type":"array","items":{"$ref":"#/definitions/AssistantMessage"}}}},"ChatLog":{"type":"object","properties":{"_id":{"type":"string"},"conversation_id":{"type":"string"},"user_id":{"type":"string"},"project_id":{"type":"string"},"date":{"type":"number","format":"integer"},"message_history":{"type":"array","items":{"$ref":"#/definitions/ChatMessage"}},"selected_element":{"type":"string"}}},"ChatRequest":{"type":"object","properties":{"message":{"type":"string"},"conversation_id":{"type":"string"},"request_id":{"type":"string"},"images":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"message_history":{"type":"array","items":{"$ref":"#/definitions/ChatMessage"}},"selected_element":{"type":"string"},"is_regeneration":{"type":"boolean"},"ludo_score":{"$ref":"#/definitions/LudoScore"},"ludo_score_metric":{"type":"string"},"guide_page":{"type":"string"}}},"AssistantRequest":{"type":"object","properties":{"message":{"type":"string"},"conversation_id":{"type":"string"},"request_id":{"type":"string"},"images":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"generated_game":{"$ref":"#/definitions/GeneratedGame"},"message_history":{"type":"array","items":{"$ref":"#/definitions/AssistantMessage"}},"platform":{"type":"string"},"genre":{"type":"string"},"is_regeneration":{"type":"boolean"},"page":{"type":"string"},"image_result":{"$ref":"#/definitions/GameImage"},"sprite_result":{"$ref":"#/definitions/Spritesheet"},"video_result":{"$ref":"#/definitions/GeneratedVideo"},"audio_result":{"$ref":"#/definitions/GeneratedAudio"},"asset_result":{"$ref":"#/definitions/AssetGeneration"},"asset_and_image_result":{"$ref":"#/definitions/ThreeDAsset"}}},"SaveAssistantConversationRequest":{"type":"object","required":["conversation_id","message_history"],"properties":{"conversation_id":{"type":"string"},"message_history":{"type":"array","items":{"$ref":"#/definitions/AssistantMessage"}}}},"AssistantMessage":{"type":"object","required":["role"],"properties":{"text":{"type":"string"},"role":{"type":"string"},"timestamp":{"type":"integer","format":"int64"},"followups":{"type":"array","items":{"$ref":"#/definitions/AssistantFollowUp"}},"images":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"videos":{"type":"array","items":{"$ref":"#/definitions/GeneratedVideo"}},"sprites":{"type":"array","items":{"$ref":"#/definitions/Spritesheet"}},"references":{"type":"array","items":{"$ref":"#/definitions/AssistantReference"}},"links":{"type":"array","items":{"$ref":"#/definitions/AssistantLink"}},"function_calls":{"type":"array","items":{"$ref":"#/definitions/AssistantFunctionCall"}},"assets":{"type":"array","items":{"$ref":"#/definitions/ThreeDAsset"}},"game_ideas":{"type":"array","items":{"$ref":"#/definitions/GeneratedGame"}}}},"AssistantFollowUp":{"type":"object","properties":{"text":{"type":"string"},"type":{"type":"string"}}},"AssistantLink":{"type":"object","properties":{"page":{"type":"string"},"text":{"type":"string"}}},"AssistantFunctionCall":{"type":"object","properties":{"args":{"type":"object"},"name":{"type":"string"}}},"AssistantReference":{"type":"object","properties":{"_word_count":{"type":"integer","format":"int64"},"content":{"type":"string"},"id":{"type":"string"},"new":{"type":"boolean"},"relevance":{"type":"number","format":"float"},"retrieved_motive":{"type":"string"},"retrieved_timestamp":{"type":"integer","format":"int64"},"title":{"type":"string"},"display_title":{"type":"string"},"type":{"type":"string"},"type_title":{"type":"string"},"top_chart":{"$ref":"#/definitions/AssistantReferenceTopCharts"},"trends":{"$ref":"#/definitions/AssistantReferenceTrends"},"help":{"$ref":"#/definitions/AssistantReferenceHelp"},"game":{"$ref":"#/definitions/AssistantReferenceGame"},"function_call":{"$ref":"#/definitions/AssistantFunctionCall"}}},"AssistantReferenceGame":{"type":"object","properties":{"game_id":{"type":"string"}}},"AssistantReferenceTopCharts":{"type":"object","properties":{"country":{"type":"string"},"genre":{"type":"string"},"source":{"type":"string"}}},"AssistantReferenceTrends":{"type":"object","properties":{"category":{"type":"string"},"country":{"type":"string"},"source":{"type":"string"},"sorting":{"type":"string"},"genre":{"type":"string"}}},"AssistantReferenceHelp":{"type":"object","properties":{"flotiq_index":{"type":"string"},"flotiq_label":{"type":"string"},"doc_index":{"type":"integer","format":"int64"}}},"ChatMessage":{"type":"object","required":["role"],"properties":{"text":{"type":"string"},"role":{"type":"string"},"followups":{"type":"array","items":{"$ref":"#/definitions/AssistantFollowUp"}},"selected_element":{"type":"string"},"images":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"videos":{"type":"array","items":{"$ref":"#/definitions/GeneratedVideo"}},"sprites":{"type":"array","items":{"$ref":"#/definitions/Spritesheet"}},"function_calls":{"type":"array","items":{"$ref":"#/definitions/AssistantFunctionCall"}},"assets":{"type":"array","items":{"$ref":"#/definitions/ThreeDAsset"}},"game_ideas":{"type":"array","items":{"$ref":"#/definitions/GeneratedGame"}}}},"ProjectCode":{"type":"object","properties":{"_id":{"type":"string"},"project_id":{"type":"string"},"date":{"type":"number","format":"integer"},"name":{"type":"string"},"filename":{"type":"string"},"overview":{"type":"string"},"main_functions":{"type":"string"},"explanation":{"type":"string"},"implementation_details":{"type":"string"},"dependencies":{"type":"array","items":{"type":"string"}},"script":{"type":"string"},"loading":{"type":"boolean"},"packages":{"type":"array","items":{"type":"string"}}}},"GuideContentPayload":{"type":"object","required":["game_concept_id","action","skill_level","engine"],"properties":{"game_concept_id":{"type":"string"},"action":{"type":"string","enum":["VIEW_TOPICS","SELECT_TOPIC","VIEW_SUBTOPIC"]},"skill_level":{"type":"string","enum":["novice","expert"]},"engine":{"type":"string"},"prompt":{"type":"string"},"current_topic_id":{"type":"string"},"current_subtopic_id":{"type":"string"},"current_path":{"type":"array","items":{"type":"string"}}}},"GuideContent":{"type":"object","required":["action","content_json","skill_level"],"properties":{"_id":{"type":"string"},"project_id":{"type":"string"},"skill_level":{"type":"string"},"engine":{"type":"string"},"prompt":{"type":"string"},"action":{"type":"string","enum":["VIEW_TOPICS","VIEW_SUBTOPIC"]},"content_json":{"type":"string","description":"Stringified JSON of the content"},"date":{"type":"integer"},"date_last_view":{"type":"integer"},"topic_id":{"type":"string"},"topic_title":{"type":"string"},"subtopic_id":{"type":"string"},"subtopic_title":{"type":"string"},"total_steps":{"type":"integer"},"current_step":{"type":"integer"},"next_step":{"$ref":"#/definitions/GuideStep"},"previous_step":{"$ref":"#/definitions/GuideStep"}}},"GuideStep":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"}}},"ThreeDGenerationParameters":{"type":"object","properties":{"target_num_faces":{"type":"integer","default":50000},"dynamic_simplification":{"type":"number","format":"float","default":0.15},"remeshing":{"type":"string","default":"conservative"},"texture_size":{"type":"integer","default":2048},"texture_type":{"type":"string","default":"pbr"},"render_format":{"type":"string"},"render_video":{"type":"boolean"}}},"RetopologyPayload":{"type":"object","properties":{"request_id":{"type":"string"},"asset_generation":{"$ref":"#/definitions/AssetGeneration"},"generation_parameters":{"$ref":"#/definitions/ThreeDGenerationParameters"}}},"ImageTo3DPayload":{"type":"object","properties":{"request_id":{"type":"string"},"image":{"$ref":"#/definitions/GameImage"},"generation_parameters":{"$ref":"#/definitions/ThreeDGenerationParameters"}}},"Create3DModelPayload":{"type":"object","required":["image"],"properties":{"image":{"type":"string","description":"URL or base64-encoded image to convert to 3D"},"target_num_faces":{"type":"integer","default":50000,"minimum":1000,"maximum":200000,"description":"Maximum triangle count for the mesh (1000-200000)"},"texture_size":{"type":"integer","default":2048,"description":"Texture resolution in pixels","enum":[1024,2048]},"texture_type":{"type":"string","default":"pbr","description":"Texture type","enum":["pbr","simple","none"]},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"GenerateSoundEffectPayload":{"type":"object","description":"Payload for generating a sound effect from a text description","required":["description"],"properties":{"description":{"type":"string","description":"Text description of the sound effect to generate (e.g., \"laser gun firing\", \"footsteps on gravel\")."},"duration":{"type":"number","format":"float","minimum":0,"maximum":10,"default":0,"description":"Duration in seconds. Use 0 for automatic duration based on the description."},"loop":{"type":"boolean","default":false,"description":"Generate a sound effect that loops seamlessly."},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"GenerateAmbiancePayload":{"type":"object","description":"Payload for generating a seamless looping ambiance soundscape from a text description","required":["description"],"properties":{"description":{"type":"string","description":"Text description of the ambiance to generate (e.g., \"windy forest at dusk\", \"busy tavern interior\")."},"duration":{"type":"number","format":"float","minimum":0,"maximum":10,"default":0,"description":"Duration in seconds. Use 0 for automatic duration based on the description."},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"GenerateMusicPayload":{"type":"object","description":"Payload for generating music from a text description","required":["description"],"properties":{"description":{"type":"string","description":"Text description of the music to generate (e.g., \"epic orchestral battle theme\", \"calm piano melody\")."},"lyrics":{"type":"string","description":"Optional lyrics to include in the generated music."},"duration":{"type":"integer","default":0,"description":"Duration in seconds. Use 0 for automatic duration based on the description.","enum":[0,10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180]},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"GenerateAudioTransformPayload":{"type":"object","description":"Payload for remixing an audio sample (sound effect, ambiance or music) into a variation guided by a text prompt","required":["sample","prompt"],"properties":{"sample":{"type":"string","description":"URL or base64-encoded source audio sample to remix (15MB max).","example":"<url> OR data:audio/mp3;base64,..."},"prompt":{"type":"string","description":"Text description guiding the remix (e.g., \"make it sound like an 80s synthwave track\", \"turn this into a metal version\")."},"duration":{"type":"integer","default":0,"description":"Duration in seconds. Use 0 for automatic duration matching the source sample.","enum":[0,10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180]},"modification_strength":{"type":"number","format":"float","minimum":0,"maximum":1,"default":0.5,"description":"Controls how strongly the source sample is modified. 0 keeps it close to the original, 1 transforms it fully."},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"GenerateVoicePayload":{"type":"object","description":"Payload for generating a voice sample from a character description","required":["voice_description","text"],"properties":{"voice_description":{"type":"string","description":"Text description of the voice character (e.g., \"deep-voiced warrior\", \"cheerful young girl\")."},"text":{"type":"string","description":"Text for the voice to speak (max 20 words).","maxLength":200,"minLength":1},"type":{"type":"string","default":"human","description":"Type of voice to generate.","enum":["human","non-human"]},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"GenerateSpeechPayload":{"type":"object","description":"Payload for text-to-speech generation using voice cloning","required":["text","sample"],"properties":{"text":{"type":"string","description":"Text to convert to speech (max 100 words).","maxLength":1000,"minLength":1},"sample":{"type":"string","description":"URL or base64-encoded audio sample for voice cloning."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"GenerateSpeechPresetPayload":{"type":"object","description":"Payload for text-to-speech generation using a voice preset","required":["text","voice_preset_id"],"properties":{"text":{"type":"string","description":"Text to convert to speech (max 100 words).","maxLength":1000},"voice_preset_id":{"x-swagger-data":"voicePresetIds","type":"string","description":"Voice preset identifier."},"emotion":{"x-swagger-data":"voicePresetEmotions","type":"string","description":"Emotion to apply to the speech."},"language":{"x-swagger-data":"voicePresetLanguages","type":"string","description":"Language code for the speech."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"GenerateWithStylePayload":{"type":"object","description":"Payload for generating new content while maintaining the visual style of a reference image","required":["style_image","prompt","image_type"],"properties":{"style_image":{"type":"string","description":"URL or base64-encoded reference image whose visual style should be matched."},"prompt":{"type":"string","description":"Text description of what to generate (e.g., \"a warrior character\", \"a forest background\", \"a treasure chest\")."},"image_type":{"x-swagger-data":"imageTypes","type":"string","description":"What kind of image to generate.","default":"sprite"},"n":{"type":"number","format":"integer","description":"Number of variations to generate (1-4).","minimum":1,"maximum":4,"default":1},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"EditImagePayload":{"type":"object","description":"Payload for editing an existing image based on text instructions","required":["image","prompt"],"properties":{"image":{"type":"string","description":"URL or base64-encoded source image to edit."},"prompt":{"type":"string","description":"Text description of the changes to make (e.g., \"remove the background\", \"add clouds to the sky\", \"make it darker\", \"change the color scheme to warmer tones\")."},"reference_image":{"type":"string","description":"Optional URL or base64-encoded reference image for style or content guidance."},"n":{"type":"number","format":"integer","description":"Number of edited variations to generate (1-4).","minimum":1,"maximum":4,"default":1},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"RemoveBackgroundPayload":{"type":"object","description":"Payload for removing the background of an image","required":["image"],"properties":{"image":{"type":"string","description":"URL or base64-encoded image to remove the background from."},"crop":{"type":"boolean","default":false,"description":"Whether to crop/trim the result to fit the content."},"creative_edit":{"type":"boolean","default":true,"description":"Higher quality results but the image will not be exactly the same as the input."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"GeneratePosePayload":{"type":"object","description":"Payload for generating a new pose for an existing sprite","required":["image","pose"],"properties":{"image":{"type":"string","description":"URL or base64-encoded source sprite image to generate a new pose from."},"pose":{"type":"string","x-swagger-data":"spritePoses","description":"Target pose for the sprite. Use the value \"Other\" to generate other poses not listed in the accepted values, and fill the field description accordingly."},"description":{"type":"string","description":"Optional additional instructions or description to guide the pose generation."},"n":{"type":"number","format":"integer","description":"Number of pose variations to generate (1-4).","minimum":1,"maximum":4,"default":1},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"PoseResult":{"type":"object","description":"Result of a pose generation","properties":{"url":{"type":"string","description":"URL of the generated pose image"},"pose":{"type":"string","description":"The pose that was used to generate this image"},"description":{"type":"string","description":"The pose description that was used to generate this image"},"motion_prompt":{"type":"string","description":"A suggested motion prompt optimized for this pose. Can be used directly with 'animateSprite' for best animation results."}}},"RotateSpritePayload":{"type":"object","description":"Payload for rotating the camera view of an existing sprite","required":["image","camera_rotation"],"properties":{"image":{"type":"string","description":"URL or base64-encoded source sprite image to rotate."},"camera_rotation":{"type":"integer","enum":[0,45,90,135,180,-135,-90,-45],"description":"Camera azimuth angle in degrees. 0 = front, 45 = front-right, 90 = right side, 135 = back-right, 180 = back, -135 = back-left, -90 = left side, -45 = front-left."},"camera_elevation":{"type":"integer","enum":[0,30,60],"description":"Optional camera elevation/tilt in degrees. 0 = eye-level, 30 = elevated, 60 = high-angle. Omit to keep the sprite's current elevation."},"n":{"type":"number","format":"integer","description":"Number of variations to generate (1-4).","minimum":1,"maximum":4,"default":1},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"RotateSpriteResult":{"type":"object","description":"Result of a sprite camera rotation","properties":{"url":{"type":"string","description":"URL of the generated image"},"camera_rotation":{"type":"integer","description":"The camera azimuth angle that was applied"},"camera_elevation":{"type":"integer","description":"The camera elevation that was applied, if any"}}},"ThreeDAsset":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}},"asset":{"$ref":"#/definitions/AssetGeneration"},"image":{"$ref":"#/definitions/GameImage"},"animations":{"type":"array","description":"Accepted text-driven animation clips attached to this 3D model. Hydrated onto the history entry when listing 3D generations so the frontend can render the clip selector / management modal. Clips are stored in the Animation3DClip collection, not embedded.","items":{"$ref":"#/definitions/Animation3DClip"}}}},"ThreeDModelInput":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}},"asset":{"$ref":"#/definitions/AssetGeneration"},"image":{"$ref":"#/definitions/GameImage"}}},"AssetGeneration":{"type":"object","properties":{"id":{"type":"string"},"rigged":{"type":"boolean","description":"True once the model carries a skeleton and skin weights (set by rig-model). Gates the animation flow and the re-rig guard."},"seed":{"type":"integer"},"images_b64":{"type":"array","items":{"type":"string"}},"params_encrypted":{"type":"string"},"request_id":{"type":"string"},"video_b64":{"type":"string"},"mesh_video_b64":{"type":"string"},"snapshots_b64":{"type":"array","items":{"type":"string"}},"model_b64":{"type":"string"},"binary_data_b64":{"type":"string"},"voxels_b64":{"type":"string"},"mask_snapshots_b64":{"type":"array","items":{"type":"string"}},"depth_snapshots_b64":{"type":"array","items":{"type":"string"}},"normal_snapshots_b64":{"type":"array","items":{"type":"string"}},"generation_parameters":{"$ref":"#/definitions/ThreeDGenerationParameters"}}},"Model3DResult":{"type":"object","properties":{"model_url":{"type":"string","description":"URL to the generated 3D model file"},"snapshots":{"type":"array","items":{"type":"string"},"description":"URLs to snapshot images from different angles (useful for previews and MCP agents)"}}},"Animation3DClip":{"type":"object","required":["_id","user_id","model_asset_id"],"properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"model_asset_id":{"type":"string","description":"Parent 3D asset id (the GenerationHistory _id, which for three_d_asset equals image.id)."},"clip_name":{"type":"string"},"prompt":{"type":"string"},"preset_id":{"type":"string","description":"Set when the clip came from the preset library (retargeting) rather than a text prompt."},"mode":{"type":"string","enum":["rot_trans","rot_only"]},"seed":{"type":"integer"},"glb_url":{"type":"string","description":"Uploaded animation-only GLB (skeleton + one clip, no mesh). May be gzip-compressed like model GLBs."},"preview_url":{"type":"string","description":"Uploaded mp4 preview of the clip playing on the model."},"accepted":{"type":"boolean","description":"Whether this clip is baked into the model's downloadable full GLB. Every generated clip is persisted (so the frontend never loses them); `accepted` is the separate opt-in that controls download inclusion."},"motion":{"type":"number","format":"float"},"fit_rmse":{"type":"number","format":"float"},"date":{"type":"integer","format":"int64"},"deleted":{"type":"boolean"}}},"AnimationClip":{"type":"object","properties":{"clip_name":{"type":"string"},"prompt":{"type":"string"},"preset_id":{"type":"string","description":"Set when the clip came from the preset library (retargeting) rather than a text prompt."},"mode":{"type":"string","enum":["rot_trans","rot_only"]},"seed":{"type":"integer"},"glb_url":{"type":"string"},"preview_url":{"type":"string"},"motion":{"type":"number","format":"float"},"fit_rmse":{"type":"number","format":"float"}}},"AnimationCandidates":{"type":"object","properties":{"animations":{"type":"array","items":{"$ref":"#/definitions/AnimationClip"}}}},"RigModelPayload":{"type":"object","properties":{"request_id":{"type":"string"},"model_asset_id":{"type":"string","description":"Parent 3D asset id whose model this rig replaces in place (image.id for generated, synthetic id for uploads). Ignored when as_new_asset is true."},"generated_model":{"$ref":"#/definitions/ThreeDModelInput"},"user_model":{"type":"string","description":"URL of a user-uploaded GLB to rig."},"user_model_file_type":{"type":"string"},"rig_type":{"type":"string","enum":["general","humanoid","game","humanoid_template","humanoid_template_hands"],"default":"general","description":"Which skeleton to build. general (default) lets the model choose; humanoid/game bias toward anime-style and classic game-character rigs. The humanoid_template options pin a standard humanoid skeleton with named joints - required for the animation preset library: humanoid_template (22 joints) or humanoid_template_hands (52, five fingers per hand). Only for two-armed, two-legged characters."},"joint_naming":{"type":"string","enum":["smpl","mixamo","humanik","unreal","godot","rigify","vroid"],"default":"smpl","description":"Bone naming convention for the joints we can identify. Purely a relabel - the skeleton is identical. Joints outside the humanoid template keep their generic names."},"render_video":{"type":"boolean","default":true},"as_new_asset":{"type":"boolean","default":false,"description":"Re-rig - save the rigged result as a new 3D asset entry instead of overwriting the source, preserving the original model and its saved animations."}}},"Animate3DPayload":{"type":"object","required":["prompt"],"properties":{"request_id":{"type":"string"},"model_asset_id":{"type":"string","description":"Parent 3D asset id the resulting candidate clips belong to."},"prompt":{"type":"string"},"augment_prompt":{"type":"boolean","default":true},"generated_model":{"$ref":"#/definitions/ThreeDModelInput"},"user_model":{"type":"string","description":"URL of an already-rigged GLB to animate."},"user_model_file_type":{"type":"string"},"mode":{"type":"string","enum":["rot_trans","rot_only"],"default":"rot_trans"},"num_variants":{"type":"integer","default":4,"minimum":1,"maximum":8},"loop":{"type":"boolean","default":true,"description":"Return to the initial pose: each clip plays forward then mirrors back to the rest pose for a seamless loop. Best for one-way motions (crouch, punch, wave); reads oddly for cyclic gaits like walking."}}},"Animate3DPresetPayload":{"type":"object","required":["preset_id"],"properties":{"request_id":{"type":"string"},"model_asset_id":{"type":"string","description":"Parent 3D asset id the resulting clip belongs to."},"preset_id":{"type":"string","description":"id of an animation preset (see /utils/animation-presets). Only presets that expose a clip_url can be applied to a 3D model."},"generated_model":{"$ref":"#/definitions/ThreeDModelInput"},"user_model":{"type":"string","description":"URL of an already-rigged GLB to retarget onto."},"user_model_file_type":{"type":"string"},"crop_loop":{"type":"boolean","description":"Trim the animation to the span that loops seamlessly. Omit to follow the preset's own loop flag (cyclic motions loop, one-shots keep their full arc). Ignored when no clean loop exists in the motion."},"in_place":{"type":"boolean","description":"Remove the animation's net travel so the character moves on the spot and the engine drives locomotion. Omit to follow crop_loop."}}},"AcceptAnimationPayload":{"type":"object","required":["id","accepted"],"properties":{"id":{"type":"string","description":"The Animation3DClip id to toggle."},"accepted":{"type":"boolean","description":"Whether this clip is included when downloading the full GLB."}}},"Rig3DModelPayload":{"type":"object","required":["model"],"properties":{"model":{"type":"string","description":"URL or base64-encoded GLB to rig."},"rig_type":{"type":"string","enum":["general","humanoid","game","humanoid_template","humanoid_template_hands"],"default":"general","description":"Which skeleton to build: general (any asset), humanoid (anime-style characters), game (classic game-character rigs), or the pinned humanoid templates with named joints required for the animation preset library - humanoid_template (22 joints) / humanoid_template_hands (52, five fingers per hand). Templates only suit two-armed, two-legged characters."},"joint_naming":{"type":"string","enum":["smpl","mixamo","humanik","unreal","godot","rigify","vroid"],"default":"smpl","description":"Bone naming convention for the identified joints: smpl (default), mixamo (Unity's humanoid auto-mapper), humanik (same names unprefixed - Maya/MotionBuilder/FBX), unreal (UE mannequin), godot (SkeletonProfileHumanoid), rigify (Blender) or vroid (VRM). Purely a relabel - the skeleton is identical."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"Rig3DModelResult":{"type":"object","properties":{"model_url":{"type":"string","description":"URL to the rigged GLB (skeleton + skin weights baked in)."},"rigged":{"type":"boolean"}}},"Animate3DModelPayload":{"type":"object","required":["model","prompt"],"properties":{"model":{"type":"string","description":"URL or base64-encoded already-rigged GLB to animate. Rig it first via the rig endpoint if it has no skeleton."},"prompt":{"type":"string","description":"Desired motion, e.g. \"walking\" or \"swinging its axe\"."},"mode":{"type":"string","enum":["rot_trans","rot_only"],"default":"rot_trans","description":"Animation representation: rot_trans (per-bone rotation+translation, most faithful) or rot_only (rotation + root translation only, for retargeting)."},"num_variants":{"type":"integer","default":4,"minimum":1,"maximum":8,"description":"Number of candidate animations to generate; each returned as a standalone animation-only GLB with an mp4 preview."},"loop":{"type":"boolean","default":true,"description":"Return to the initial pose: each clip plays forward then mirrors back to the rest pose for a seamless loop. Best for one-way motions (crouch, punch, wave); reads oddly for cyclic gaits like walking."},"augment_prompt":{"type":"boolean","default":true,"description":"Rewrite the prompt into a detailed motion caption with an LLM (recommended)."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"Animate3DPresetModelPayload":{"type":"object","required":["model","preset_id"],"properties":{"model":{"type":"string","description":"URL or base64-encoded already-rigged GLB (rig it with a humanoid_template rig_type first)."},"preset_id":{"type":"string","description":"id of an animation preset from the presets list. Only presets that expose a clip_url can be applied to a 3D model."},"crop_loop":{"type":"boolean","description":"Trim the animation to the span that loops seamlessly. Omit to follow the preset's own loop flag. Ignored when no clean loop exists."},"in_place":{"type":"boolean","description":"Remove the animation's net travel so the character moves on the spot (engine-driven locomotion). Omit to follow crop_loop."},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}},"Refined3DPayload":{"type":"object","properties":{"art_style":{"type":"string"},"generated_model":{"$ref":"#/definitions/ThreeDModelInput"},"mesh_strength":{"type":"number","format":"float"},"texture_strength":{"type":"number","format":"float"},"n":{"type":"integer"},"prompt":{"type":"string"},"request_id":{"type":"string"},"user_model":{"type":"string"},"user_model_file_type":{"type":"string"},"generation_parameters":{"$ref":"#/definitions/ThreeDGenerationParameters"}}},"Download3DPayload":{"type":"object","properties":{"fill_holes":{"type":"boolean"},"simplify":{"type":"number","format":"float"},"format":{"type":"string"},"request_id":{"type":"string"},"asset_generation":{"$ref":"#/definitions/AssetGeneration"}}},"SuggestPayload":{"type":"object","properties":{"id":{"type":"string"},"image_type":{"type":"string"},"n":{"type":"integer"},"hints":{"type":"array","items":{"type":"string"}},"selected_element":{"type":"string"}}},"ShowcaseGallery":{"type":"object","properties":{"assets":{"type":"array","items":{"$ref":"#/definitions/ThreeDAsset"}},"images":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"ideas":{"type":"array","items":{"$ref":"#/definitions/GeneratedGame"}},"videos":{"type":"array","items":{"$ref":"#/definitions/GeneratedVideo"}},"spritesheets":{"type":"array","items":{"$ref":"#/definitions/Spritesheet"}},"concepts":{"type":"array","items":{"$ref":"#/definitions/Project"}},"playables":{"type":"array","items":{"$ref":"#/definitions/SharedPlayable"}}}},"PlayableRequest":{"type":"object","required":["action","prompt"],"properties":{"action":{"type":"string","enum":["GENERATE_INITIAL","ITERATE"]},"engine":{"type":"string","enum":["phaser","threejs","pixijs"]},"prompt":{"type":"string"},"images":{"type":"array","items":{"$ref":"#/definitions/GameImage"}},"disable_assets":{"type":"boolean"},"perspective":{"type":"string"},"art_style":{"type":"string"}}},"PlayableChatMessage":{"type":"object","properties":{"sender":{"type":"string"},"text":{"type":"string"},"diff":{"type":"string"},"images":{"type":"array","items":{"type":"string"}},"history_index":{"type":"integer","format":"int64"},"followups":{"type":"array","items":{"type":"string"}}}},"Playable":{"type":"object","properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"engine":{"type":"string","enum":["phaser","threejs","pixijs"]},"title":{"type":"string"},"summary":{"type":"string"},"original_prompt":{"type":"string"},"how_to_play":{"type":"string"},"code":{"type":"string"},"compiled_code":{"type":"string"},"chat_history":{"type":"array","items":{"$ref":"#/definitions/PlayableChatMessage"}},"last_modified":{"type":"integer","format":"int64"},"code_history":{"type":"string"},"assets":{"type":"array","items":{"$ref":"#/definitions/PlayableAsset"}},"image":{"$ref":"#/definitions/GameImage"},"art_style":{"type":"string"},"perspective":{"type":"string"}}},"PlayableAsset":{"type":"object","properties":{"id":{"type":"string"},"image":{"$ref":"#/definitions/GameImage"},"three_d_asset":{"$ref":"#/definitions/ThreeDAsset"},"description":{"type":"string"},"alias":{"type":"string"}}},"SharedPlayable":{"type":"object","properties":{"_id":{"type":"string"},"user_id":{"type":"string"},"engine":{"type":"string","enum":["phaser","threejs","pixijs"]},"title":{"type":"string"},"summary":{"type":"string"},"how_to_play":{"type":"string"},"code":{"type":"string"},"compiled_code":{"type":"string"},"date":{"type":"integer","format":"int64"},"remixes":{"type":"integer","format":"int64"},"views":{"type":"integer","format":"int64"},"allow_public":{"type":"boolean"},"assets":{"type":"array","items":{"$ref":"#/definitions/PlayableAsset"}},"template":{"type":"boolean"},"image":{"$ref":"#/definitions/GameImage"}}},"GDDMedia":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string"},"type":{"type":"string","enum":["image","three_d_asset","video","spritesheet","game_id","audio"]},"uploaded":{"type":"boolean"},"image":{"$ref":"#/definitions/GameImage"},"three_d_asset":{"$ref":"#/definitions/ThreeDAsset"},"video":{"$ref":"#/definitions/GeneratedVideo"},"spritesheet":{"$ref":"#/definitions/Spritesheet"},"audio":{"$ref":"#/definitions/GeneratedAudio"},"placeholder":{"$ref":"#/definitions/GDDPlaceholder"},"game_id":{"type":"string"},"loading":{"type":"boolean"}}},"GDDPlaceholder":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"}}},"GDDElement":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"},"title":{"type":"string"},"sub_title":{"type":"string"},"text":{"type":"string"},"mod_date":{"type":"integer","format":"int64"},"media":{"type":"array","items":{"$ref":"#/definitions/GDDMedia"}},"loading":{"type":"integer","format":"int64"},"pending":{"type":"boolean"},"collapsed":{"type":"boolean"},"country":{"type":"string"}}},"GDD3":{"type":"object","properties":{"project_id":{"type":"string"},"title":{"type":"string"},"elements":{"type":"array","items":{"$ref":"#/definitions/GDDElement"}},"icon":{"$ref":"#/definitions/GDDMedia"},"header":{"$ref":"#/definitions/GDDMedia"},"genres":{"type":"array","items":{"type":"string"}},"platform":{"type":"string"},"country":{"type":"string"},"art_style":{"type":"string"},"perspective":{"type":"string"},"aspect_ratio":{"type":"string"},"step_choices":{"type":"array","items":{"$ref":"#/definitions/StepValue"}},"score":{"$ref":"#/definitions/LudoScore"}}},"GDD3Version":{"type":"object","required":["project_id","version","timestamp","user_id","command"],"properties":{"_id":{"type":"string"},"project_id":{"type":"string"},"version":{"type":"integer","format":"int64"},"timestamp":{"type":"integer","format":"int64"},"user_id":{"type":"string"},"command":{"type":"string","enum":["element:create","element:update","element:delete","element:reorder","title:update","summary:update","field:update"]},"element_id":{"type":"string"},"old_value":{"type":"string"},"new_value":{"type":"string"},"after_id":{"type":"string"},"undone":{"type":"boolean"},"description":{"type":"string"}}},"SpritePose":{"type":"object","properties":{"animation_prompt":{"type":"string","description":"Animation description for the sprite pose"},"margin_ratio":{"type":"number","format":"float","description":"Margin ratio for the sprite animation"},"margin_ratio_mode":{"type":"string"},"name":{"type":"string","description":"Name of the sprite pose"},"prompt":{"type":"string","description":"Prompt description for the sprite pose"}}},"AnimationPreset":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the animation preset"},"name":{"type":"string","description":"Name of the animation preset"},"category":{"type":"string","description":"Category of the animation preset"},"description":{"type":"string","description":"Description of the animation preset"},"duration":{"type":"number","format":"float","description":"Duration of the animation in seconds"},"preview_url":{"type":"string","description":"URL to preview the animation"},"clip_url":{"type":"string","description":"Motion clip backing the preset. Only presets that expose a clip_url can be retargeted onto a rigged 3D model (animate-3d-preset)."},"perspectives":{"type":"array","items":{"$ref":"#/definitions/AnimationPresetPerspective"}}}},"AnimationPresetPerspective":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the perspective"},"name":{"type":"string","description":"Name of the perspective"},"description":{"type":"string","description":"Description of the perspective"},"directions":{"description":"Direction mappings with video URLs","$ref":"#/definitions/AnimationPresetPerspectiveDirections"}}},"AnimationPresetPerspectiveDirections":{"type":"object","properties":{"E":{"type":"string"},"N":{"type":"string"},"NE":{"type":"string"},"NW":{"type":"string"},"S":{"type":"string"},"SE":{"type":"string"},"SW":{"type":"string"},"W":{"type":"string"}}},"PlanInfo":{"type":"object","properties":{"indie":{"type":"string","description":"How credits work for Indie plan"},"pro_studio":{"type":"string","description":"How credits work for Pro/Studio plans"}}},"FeatureCategory":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}}},"CreditFeature":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"$ref":"#/definitions/FeatureCategory"},"indie_cost":{"type":"string","description":"Cost for Indie plan (number or \"Unlimited\")"},"pro_studio_cost":{"type":"string","description":"Cost for Pro/Studio plans (number or \"Unlimited\")"},"variable":{"type":"boolean"},"tooltip":{"type":"string","description":"Optional tooltip explaining cost calculation"}}},"ModelDurationCost":{"type":"object","description":"A valid duration for a model with its pre-computed credit cost.","properties":{"duration":{"type":"number","format":"float"},"cost":{"type":"number","format":"float"}}},"ModelQuality":{"type":"object","description":"Quality metrics on a 0–10 scale. UIs should render proportional bars. Null indicates the metric is not yet rated for the model.","properties":{"visual":{"type":"number","format":"float","description":"Visual quality rating (0–10)."},"motion_stability":{"type":"number","format":"float","description":"Motion stability rating (0–10)."}}},"PricingModel":{"type":"object","description":"Per-model pricing configuration with durations and pre-computed costs.","properties":{"codename":{"type":"string","description":"Canonical model identifier used by the API (e.g. \"blitz\")."},"name":{"type":"string","description":"User-facing display name (e.g. \"Blitz\")."},"description":{"type":"string","description":"Short free-form description. Optional; may be empty."},"aliases":{"type":"array","description":"Legacy identifiers that resolve to this codename (e.g. \"standard\" → \"blitz\").","items":{"type":"string"}},"legacy":{"type":"boolean","description":"True if this model is superseded and should be shown in a \"legacy models\" group in UIs."},"quality":{"$ref":"#/definitions/ModelQuality"},"features":{"type":"array","description":"Credit actions this model is available for (e.g. [\"GENERATE_VIDEO\", \"ANIMATE_SPRITE\"]).","items":{"type":"string"}},"credits_per_second":{"type":"number","format":"float"},"min_charge":{"type":"number","format":"float","description":"Minimum credit charge regardless of duration. Null when not enforced."},"durations":{"type":"array","items":{"$ref":"#/definitions/ModelDurationCost"}}}},"ChatCostExample":{"type":"object","properties":{"description":{"type":"string"},"assets":{"type":"number","format":"float"},"images":{"type":"number","format":"float"},"ideas":{"type":"number","format":"float"},"indie_cost":{"type":"number","format":"float"},"pro_studio_cost":{"type":"string"}}},"VideoCost":{"type":"object","properties":{"feature_id":{"type":"string"},"description":{"type":"string"},"models":{"type":"array","items":{"$ref":"#/definitions/PricingModel"}}}},"SpriteCost":{"type":"object","properties":{"feature_id":{"type":"string"},"description":{"type":"string"},"models":{"type":"array","items":{"$ref":"#/definitions/PricingModel"}}}},"FlatRateCost":{"type":"object","description":"Variable cost billed at a fixed per-second rate, independent of model.","properties":{"feature_id":{"type":"string"},"description":{"type":"string"},"credits_per_second":{"type":"number","format":"float"}}},"ChatCost":{"type":"object","properties":{"feature_id":{"type":"string"},"description":{"type":"string"},"indie_base_cost":{"type":"number","format":"float"},"pro_studio_base_cost":{"type":"string"},"formula":{"type":"string"},"examples":{"type":"array","items":{"$ref":"#/definitions/ChatCostExample"}}}},"VariableCosts":{"type":"object","properties":{"video":{"$ref":"#/definitions/VideoCost"},"sprite":{"$ref":"#/definitions/SpriteCost"},"edit_spritesheet":{"$ref":"#/definitions/SpriteCost","description":"Per-action pricing for spritesheet editing. models is limited to the models that support the action; credits_per_second / durations reflect the action-specific rate."},"references_to_video":{"$ref":"#/definitions/VideoCost","description":"Per-action pricing for video-from-references."},"video_edit":{"$ref":"#/definitions/VideoCost","description":"Per-action pricing for video editing."},"upscale_video":{"$ref":"#/definitions/FlatRateCost","description":"Flat per-second pricing for 2x video upscaling (model-independent)."},"chat":{"$ref":"#/definitions/ChatCost"}}},"CreditCosts":{"type":"object","description":"All credit costs for platform features","properties":{"plans":{"$ref":"#/definitions/PlanInfo"},"features":{"type":"array","items":{"$ref":"#/definitions/CreditFeature"}},"variable_costs":{"$ref":"#/definitions/VariableCosts"}}},"GenerateVideoPayloadPublic":{"type":"object","description":"Payload for generating a video from a source image and motion prompt","required":["image","prompt"],"properties":{"image":{"type":"string","description":"URL or base64-encoded source image (the starting frame for the video)","example":"<url> OR data:image/png;base64,..."},"prompt":{"type":"string","description":"Text description of the motion or action for the video (e.g., \"walking forward\", \"waving hand\", \"camera zoom in\")"},"augment_prompt":{"type":"boolean","default":true,"description":"Augment the prompt behind the scenes. Disable to have more control."},"final_image":{"type":"string","description":"URL or base64-encoded end frame image. When provided, the video will interpolate between the initial and final frames.","example":"<url> OR data:image/png;base64,..."},"duration":{"type":"number","format":"float","description":"Video duration in seconds. Available values depend on the model -\nthe source of truth is MODEL_PRICING (credits.js), surfaced via\n/credits/costs; the public API docs list them per model\n(auto-generated).\n","example":3},"model":{"type":"string","description":"Video generation model to use. forge-pixel renders pixel-art video.","enum":["blitz","eagle","eagle-audio","standard","forge-pixel"],"default":"blitz","example":"blitz"},"request_id":{"type":"string","description":"Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable with the results endpoint."},"async":{"type":"boolean","default":false,"description":"When true, return 202 immediately with a job id instead of waiting for the result. Poll GET /assets/jobs/{id}. Defaults to false (synchronous)."}}}}}