{"openapi":"3.0.0","paths":{"/v1/status/health":{"get":{"operationId":"AppController_health","parameters":[],"responses":{"200":{"description":""}},"tags":["App"]}},"/v1/status/websocket-test":{"get":{"operationId":"AppController_websocketTest","parameters":[],"responses":{"200":{"description":""}},"tags":["App"]}},"/v1/status/telegram-test":{"get":{"operationId":"AppController_telegramTest","parameters":[],"responses":{"200":{"description":""}},"tags":["App"]}},"/v1/subscription/check":{"post":{"description":"Checks subscription status across all providers (Apple, FunnelFox, Stripe) and returns the active subscription if found.","operationId":"SubscriptionController_check","parameters":[],"responses":{"200":{"description":"Subscription status retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCheckResponseDto"}}}}},"summary":"Check subscription status","tags":["Subscriptions"]}},"/v1/subscription/migrate":{"post":{"description":"Migrates the user's active subscription to a new price point through its payment provider (FunnelFox: prorated immediately; Unlimit: applied on the next payment date, no proration). Supports dryRun for previewing.","operationId":"SubscriptionController_migrate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrateSubscriptionBody"}}}},"responses":{"200":{"description":""}},"summary":"Migrate subscription to a different plan","tags":["Subscriptions"]}},"/v1/subscription/migrate/status":{"get":{"description":"Polled after the buyer passes the authentication step Upgate demanded on a prorated upgrade: the plan only switches once the postback settles the charge, so the migrate response cannot report the outcome itself.","operationId":"SubscriptionController_migrateStatus","parameters":[{"name":"ref","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Outcome of a plan change waiting on 3-D Secure","tags":["Subscriptions"]}},"/v1/subscription/disable-autorenew":{"post":{"description":"Resolves the active FunnelFox subscription for the authenticated user and turns off autorenew. Used by the post-purchase upsell flow after a separate one-click purchase via the SDK so the previous subscription does not keep renewing alongside the new one.","operationId":"SubscriptionController_disableAutorenew","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DisableAutorenewBody"}}}},"responses":{"200":{"description":""}},"summary":"Disable autorenew on the current user's active FunnelFox subscription","tags":["Subscriptions"]}},"/v1/subscription/cancel/reasons":{"get":{"operationId":"SubscriptionController_getSubscriptionCancellationReasons","parameters":[],"responses":{"200":{"description":"Subscription cancellation reasons retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSubscriptionCancellationReasonsResponseDto"}}}}},"summary":"Get available subscription cancellation reasons","tags":["Subscriptions"]}},"/v1/subscription/cancel/reason":{"post":{"operationId":"SubscriptionController_reportSubscriptionCancellation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionCancellationReportRequestDto"}}}},"responses":{"201":{"description":"Subscription cancellation reason submitted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionCancellationReportResponseDto"}}}}},"summary":"Submit a subscription cancellation reason","tags":["Subscriptions"]}},"/v1/coins/balance":{"get":{"operationId":"BillingController_getBalance","parameters":[],"responses":{"200":{"description":""}},"tags":["Coins"]}},"/v1/coins/summary":{"get":{"description":"Returns monthly subscription coins remaining vs allowance and bonus (purchased) coins. Uses quota counters when enabled; otherwise derives monthly remainder from the coin ledger since the last SUBSCRIPTION deposit.","operationId":"BillingController_getCoinSummary","parameters":[],"responses":{"200":{"description":"Coin summary retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoinSummaryDto"}}}}},"summary":"Get coin breakdown for profile (monthly quota left + bonus)","tags":["Coins"]}},"/v1/coins/products":{"get":{"description":"Returns all active web coin products with prices and coin amounts for the purchase UI.","operationId":"BillingController_getWebCoinProducts","parameters":[],"responses":{"200":{"description":""}},"summary":"Get available web coin products","tags":["Coins"]}},"/v1/coins/pack-set":{"get":{"description":"Resolves the GEN-1399 coin-pack pricing experiment and returns the ladder this user must be shown, together with the assigned variant. Users outside the experiment (registered before it started, or not on Upgate/USD) get `control` and are not enrolled.","operationId":"BillingController_getCoinPackSet","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoinPackSetDto"}}}}},"summary":"Coin packs to display for the current user","tags":["Coins"]}},"/v1/coins/confirm-purchase":{"post":{"description":"Verifies the one-off coin purchase with FunnelFox and credits the coins immediately. Idempotent — keyed by orderId, so it never double-grants with the oneoff/order webhook.","operationId":"BillingController_confirmCoinPurchase","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmCoinPurchaseDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmCoinPurchaseResponseDto"}}}}},"summary":"Confirm a coin pack purchase after FunnelFox SDK onSuccess","tags":["Coins"]}},"/v1/coins/hesoyam":{"get":{"operationId":"BillingController_hesoyam","parameters":[],"responses":{"200":{"description":""}},"tags":["Coins"]}},"/v1/coins/auto-topup":{"get":{"description":"Returns the auto-topup toggle state, the chosen coin product, the backend threshold and the last attempt outcome.","operationId":"AutoTopupController_getMyAutoTopup","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoTopupResponseDto"}}}}},"summary":"Get my auto-topup settings","tags":["Coins"]},"put":{"description":"Enabling requires a productId from the coin paywall (localized web coin product id). Re-enabling clears the auto-disable state.","operationId":"AutoTopupController_updateMyAutoTopup","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAutoTopupDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoTopupResponseDto"}}}}},"summary":"Enable/disable auto-topup and choose the coin product","tags":["Coins"]}},"/constants":{"get":{"operationId":"ConstantsController_getConstants","parameters":[],"responses":{"200":{"description":"Constants retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetConstantsResponseDto"}}}}},"summary":"Get pricing and limits constants","tags":["Constants"]}},"/v1/auth/magic-link/request":{"post":{"description":"Creates a pending email verification and sends magic link email via Postmark. When EMAIL_VERIFICATION_REQUIRED is disabled, auto-verifies and returns a Firebase custom token.","operationId":"AuthController_requestMagicLink","parameters":[{"name":"origin","required":true,"in":"header","schema":{"type":"string"}},{"name":"accept-language","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestMagicLinkDto"}}}},"responses":{"200":{"description":"Magic link sent (empty body) or email auto-verified (body with firebaseCustomToken)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestMagicLinkResponseDto"}}}}},"summary":"Request registration magic link","tags":["Auth"]}},"/v1/auth/magic-link/request-welcome":{"post":{"description":"Sends the Lovmia welcome magic link email to the specified address. Intended for manual testing and QA.","operationId":"AuthController_requestWelcomeMagicLink","parameters":[{"name":"origin","required":true,"in":"header","schema":{"type":"string"}},{"name":"accept-language","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestMagicLinkDto"}}}},"responses":{"200":{"description":"Welcome magic link sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestMagicLinkResponseDto"}}}}},"summary":"Send paid-funnel welcome magic link email","tags":["Auth"]}},"/v1/auth/magic-link/consume":{"post":{"description":"Validates one-time token and returns Firebase custom token for web sign in.","operationId":"AuthController_consumeMagicLink","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumeMagicLinkDto"}}}},"responses":{"200":{"description":"Magic link consumed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumeMagicLinkResponseDto"}}}}},"summary":"Consume registration magic link","tags":["Auth"]}},"/v1/auth/email-otp/request":{"post":{"description":"Creates a pending email verification and sends a 6-digit OTP code via email. When EMAIL_VERIFICATION_REQUIRED is disabled, or skipVerification is true (paid funnel), auto-verifies and returns a Firebase custom token.","operationId":"AuthController_requestEmailOtp","parameters":[{"name":"accept-language","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestEmailOtpDto"}}}},"responses":{"200":{"description":"OTP sent (empty body) or email auto-verified (body with firebaseCustomToken)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestEmailOtpResponseDto"}}}}},"summary":"Request email OTP verification code","tags":["Auth"]}},"/v1/auth/email-otp/verify":{"post":{"description":"Validates 6-digit OTP code and returns Firebase custom token for web sign in.","operationId":"AuthController_verifyEmailOtp","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyEmailOtpDto"}}}},"responses":{"200":{"description":"OTP verified successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyEmailOtpResponseDto"}}}}},"summary":"Verify email OTP code","tags":["Auth"]}},"/v1/auth/session-handoff":{"post":{"description":"Authenticated. Mints a short-lived token the funnel parks in its URL after checkout, so Facebook's \"Open in browser\" carries the session into the system browser. The Firebase UID comes from the verified auth token — the client never passes it.","operationId":"AuthController_issueSessionHandoff","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueSessionHandoffDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueSessionHandoffResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Issue a funnel session handoff token","tags":["Auth"]}},"/v1/auth/session-handoff/consume":{"post":{"description":"Public. Trades the token from the funnel URL for a Firebase custom token plus the funnel context needed to resume (step and answers). Valid for 15 minutes and at most 3 uses.","operationId":"AuthController_consumeSessionHandoff","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumeSessionHandoffDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumeSessionHandoffResponseDto"}}}}},"summary":"Consume a funnel session handoff token","tags":["Auth"]}},"/v1/subscription/cancel/remind":{"post":{"operationId":"SubscriptionNotificationsController_scheduleCancellationReminder","parameters":[],"responses":{"201":{"description":"Cancellation reminder scheduled successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleCancellationReminderResponseDto"}}}}},"summary":"Schedule a cancellation reminder email","tags":["Subscriptions"]}},"/v1/tracking/pixels":{"get":{"description":"Public endpoint. Returns active Facebook/TikTok pixel ids with their event schemes; the web app initializes browser pixels from this list at runtime.","operationId":"TrackingPixelsController_getPixels","parameters":[],"responses":{"200":{"description":""}},"summary":"Active tracking pixels for the web app","tags":["Tracking"]}},"/v2/subscriptions/catalog":{"get":{"description":"Returns all active tiers with their plans and provider price points. Public endpoint, no auth required.","operationId":"SubscriptionCatalogController_getCatalog","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCatalogResponseDto"}}}}},"summary":"Get subscription catalog","tags":["Subscriptions v2"]}},"/v2/subscriptions/funnels/{slug}/plans":{"get":{"description":"Returns active price points for a funnel slug. Falls back to the default standard paywall when no active funnel config exists.","operationId":"SubscriptionCatalogController_getFunnelPlans","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunnelSubscriptionPlansResponseDto"}}}}},"summary":"Get subscription plans configured for a funnel","tags":["Subscriptions v2"]}},"/v2/subscriptions/check":{"post":{"description":"Returns subscription status from the internal UserSubscription entity. Requires auth.","operationId":"SubscriptionStatusController_check","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCheckV2ResponseDto"}}}}},"summary":"Check current user subscription status (v2)","tags":["Subscriptions v2"]}},"/v2/subscriptions/me":{"get":{"description":"Same as /check but via GET. Returns subscription status from UserSubscription entity.","operationId":"SubscriptionStatusController_getMySubscription","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCheckV2ResponseDto"}}}}},"summary":"Get current user subscription details","tags":["Subscriptions v2"]}},"/v2/subscriptions/plan":{"get":{"description":"Returns tier benefits (quota base, welcome bonus, top-up multiplier), plan terms (monthly quota, billing period, price), and billing period state. Requires auth.","operationId":"SubscriptionStatusController_getMyPlan","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMySubscriptionPlanResponseDto"}}}}},"summary":"Get current user subscription plan and benefits","tags":["Subscriptions v2"]}},"/v2/subscriptions/confirm-purchase":{"post":{"description":"Verifies the purchase with FunnelFox API and creates the internal UserSubscription. Idempotent — returns existing if already created.","operationId":"SubscriptionStatusController_confirmPurchase","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmPurchaseDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCheckV2ResponseDto"}}}}},"summary":"Confirm a purchase after FunnelFox SDK onSuccess","tags":["Subscriptions v2"]}},"/v2/subscriptions/disable-autorenew":{"post":{"description":"Disables provider auto-renewal for the active subscription and marks the internal UserSubscription as canceling at period end. Requires auth.","operationId":"SubscriptionStatusController_disableAutorenew","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCheckV2ResponseDto"}}}}},"summary":"Disable auto-renewal for current user subscription (v2)","tags":["Subscriptions v2"]}},"/v2/subscriptions/enable-autorenew":{"post":{"description":"Enables provider auto-renewal for the active subscription and clears canceling at period end on the internal UserSubscription. Requires auth.","operationId":"SubscriptionStatusController_enableAutorenew","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCheckV2ResponseDto"}}}}},"summary":"Enable auto-renewal for current user subscription (v2)","tags":["Subscriptions v2"]}},"/v2/subscriptions/apply-retention-discount":{"post":{"description":"Applies a 50% discount to the next 12 subscription renewals via FunnelFox. Used by the cancellation flow v3 special offer. Requires auth.","operationId":"SubscriptionStatusController_applyRetentionDiscount","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyRetentionDiscountResponseDto"}}}}},"summary":"Apply cancellation retention discount (v2)","tags":["Subscriptions v2"]}},"/v2/subscriptions/cancellation-flow-experiment/enroll":{"post":{"description":"Assigns the authenticated active subscriber to the cancellation flow A/B/C test when they expand Manage Subscription from settings.","operationId":"SubscriptionStatusController_enrollCancellationFlowExperiment","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancellationFlowExperimentAssignmentDto"}}}}},"summary":"Enroll current subscriber into cancellation flow experiment","tags":["Subscriptions v2"]}},"/v2/subscriptions/cancel-cta-experiment/enroll":{"post":{"description":"Assigns the authenticated active subscriber to the \"cancel autorenew button vs contact support\" A/B test — from the Manage Subscription section, or when they try to remove the last saved card (`trigger`). Excluded countries (US, GB) always get the cancel variant and are never enrolled.","operationId":"SubscriptionStatusController_enrollCancelCtaExperiment","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollCancelCtaExperimentDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelCtaExperimentAssignmentDto"}}}}},"summary":"Enroll current subscriber into the cancel CTA experiment","tags":["Subscriptions v2"]}},"/v1/experiments/me":{"get":{"description":"Returns all sticky assignments for the authenticated Firebase user. Does not enroll. Suitable for hydrating client-side experiment state.","operationId":"ExperimentsController_listMyExperiments","parameters":[],"responses":{"200":{"description":""}},"summary":"List the current user's experiment assignments","tags":["Experiments"]}},"/v1/experiments/{key}/enroll":{"post":{"description":"Idempotent. Used both by the authenticated app and the unauthenticated funnel (the latter must pass `firebaseUid` in the body).","operationId":"ExperimentsController_enroll","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollExperimentDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollResponseDto"}}}}},"summary":"Explicitly enroll the subject into an experiment","tags":["Experiments"]}},"/v1/admin/experiments":{"get":{"operationId":"AdminExperimentsController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExperimentWithStatsDto"}}}}}},"summary":"List all experiments with allocation stats","tags":["Admin Experiments"]},"post":{"operationId":"AdminExperimentsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExperimentDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExperimentDto"}}}}},"summary":"Create a new experiment","tags":["Admin Experiments"]}},"/v1/admin/experiments/{key}":{"get":{"operationId":"AdminExperimentsController_get","parameters":[{"name":"key","required":true,"in":"path","description":"Experiment key","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExperimentWithStatsDto"}}}}},"summary":"Get one experiment by key with allocation stats","tags":["Admin Experiments"]}},"/v1/admin/experiments/{key}/allocation":{"patch":{"operationId":"AdminExperimentsController_updateAllocation","parameters":[{"name":"key","required":true,"in":"path","description":"Experiment key","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAllocationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExperimentDto"}}}}},"summary":"Update variant weights (does NOT rebucket existing assignments)","tags":["Admin Experiments"]}},"/v1/admin/experiments/{key}/force-variant":{"post":{"description":"Use as a kill switch (force the safe variant) or a 100% rollout (force the winning variant).","operationId":"AdminExperimentsController_setForcedVariant","parameters":[{"name":"key","required":true,"in":"path","description":"Experiment key","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetForcedVariantDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExperimentDto"}}}}},"summary":"Force a variant — all reads return this immediately","tags":["Admin Experiments"]},"delete":{"operationId":"AdminExperimentsController_clearForcedVariant","parameters":[{"name":"key","required":true,"in":"path","description":"Experiment key","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExperimentDto"}}}}},"summary":"Clear the forced variant (resume normal bucketing)","tags":["Admin Experiments"]}},"/v1/admin/experiments/{key}/ltv":{"get":{"description":"Revenue counted from the moment each subject was assigned; net of refunds and chargebacks.","operationId":"AdminExperimentsController_getLtv","parameters":[{"name":"key","required":true,"in":"path","description":"Experiment key","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExperimentLtvDto"}}}}},"summary":"Post-enroll unit economics per variant","tags":["Admin Experiments"]}},"/v1/admin/experiments/{key}/complete":{"post":{"description":"Sets status=completed, records winnerVariantId/completedAt, points defaultVariantId at the winner (what non-running reads serve) and clears any forced variant.","operationId":"AdminExperimentsController_complete","parameters":[{"name":"key","required":true,"in":"path","description":"Experiment key","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteExperimentDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExperimentDto"}}}}},"summary":"Complete the experiment with a declared winner","tags":["Admin Experiments"]}},"/v1/admin/experiments/{key}/status":{"post":{"description":"Allowed values: draft, running, paused, completed. Pause/Complete cause reads to fall through to defaultVariantId.","operationId":"AdminExperimentsController_setStatus","parameters":[{"name":"key","required":true,"in":"path","description":"Experiment key","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetStatusDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExperimentDto"}}}}},"summary":"Transition experiment status","tags":["Admin Experiments"]}},"/v1/apple/iap/coins/purchase":{"post":{"operationId":"AppleIapController_purchase","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseCoinsDto"}}}},"responses":{"200":{"description":"Purchase coins","content":{"application/json":{"schema":{"type":"object","properties":{"balance":{"type":"number"}}}}}}},"tags":["Apple IAP"]}},"/v1/apple/subscription/purchase":{"post":{"operationId":"AppleSubscriptionController_purchase","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseCoinsDto"}}}},"responses":{"201":{"description":"Subscription purchased successfully"},"400":{"description":"Bad request"}},"summary":"Purchase Apple subscription","tags":["Apple Subscriptions"]}},"/v1/apple/subscription/restore":{"post":{"operationId":"AppleSubscriptionController_restore","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestoreSubscriptionDto"}}}},"responses":{"201":{"description":"Subscription restored successfully"},"400":{"description":"Bad request"},"404":{"description":"Subscription not found"}},"summary":"Restore Apple subscription","tags":["Apple Subscriptions"]}},"/v1/apple/subscription/test-notification":{"post":{"operationId":"AppleSubscriptionController_testNotification","parameters":[],"responses":{"201":{"description":"Test notification requested successfully"}},"summary":"Request a test notification","tags":["Apple Subscriptions"]}},"/webhook/apple":{"post":{"operationId":"WebhookController_processWebhook","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"signedPayload":{"type":"string"}}}}}},"responses":{"200":{"description":""}},"tags":["Webhook"]}},"/webhook/runpod/message":{"post":{"operationId":"WebhookController_processRunpodMessageWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhook"]}},"/webhook/runpod/image":{"post":{"operationId":"WebhookController_processRunpodImageWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhook"]}},"/webhook/runpod/video-image":{"post":{"operationId":"WebhookController_processRunpodVideoImageWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhook"]}},"/webhook/runpod/prompt-rephrasing":{"post":{"operationId":"WebhookController_processPromptRephrasingWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhook"]}},"/webhook/runpod/video-prompt-rephrasing":{"post":{"operationId":"WebhookController_processVideoPromptRephrasingWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhook"]}},"/webhook/runpod/model-description":{"post":{"operationId":"WebhookController_processModelDescriptionWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhook"]}},"/webhook/runpod/model-avatar":{"post":{"operationId":"WebhookController_processModelAvatarWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhook"]}},"/webhook/wavespeed":{"post":{"operationId":"WebhookController_processWavespeedWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhook"]}},"/webhook/v2/runpod/image":{"post":{"operationId":"WebhookV2Controller_processRunpodImageWebhook","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"string"}}}},"responses":{"200":{"description":""}},"tags":["WebhookV2"]}},"/v1/chats/{id}":{"get":{"operationId":"ChatsController_getChatById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Chat retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetChatByIdResponseDto"}}}}},"summary":"Get chat by id","tags":["Chats"]},"delete":{"operationId":"ChatsController_deleteChatById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Chat deleted successfully"}},"summary":"Delete chat by id","tags":["Chats"]}},"/v1/chats":{"get":{"operationId":"ChatsController_getChats","parameters":[],"responses":{"200":{"description":"Chats retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetChatByIdResponseDto"}}}}}},"summary":"Get user chats with pagination","tags":["Chats"]},"post":{"operationId":"ChatsController_createChat","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChatRequestBodyDto"}}}},"responses":{"201":{"description":"Chat created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetChatByIdResponseDto"}}}}},"summary":"Create a new chat","tags":["Chats"]}},"/v1/chats/{id}/messages":{"get":{"operationId":"ChatsController_getMessagesByChatId","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Cursor for pagination","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Number of messages to retrieve","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}}],"responses":{"200":{"description":"Messages retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMessagesResponseDto"}}}}},"summary":"Get messages by chat id with cursor-based pagination","tags":["Chats"]}},"/v1/chats/{id}/send-message":{"post":{"operationId":"ChatsController_sendMessage","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequestBodyDto"}}}},"responses":{"200":{"description":"Message sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponseDto"}}}}},"summary":"Send a message to a chat","tags":["Chats"]}},"/v1/chats/{id}/request-image":{"post":{"operationId":"ChatsController_requestImage","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestImageRequestBodyDto"}}}},"responses":{"200":{"description":"Image requested successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponseDto"}}}}},"summary":"Request an image to a chat","tags":["Chats"]}},"/v1/chats/{id}/request-video":{"post":{"operationId":"ChatsController_requestVideo","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestImageRequestBodyDto"}}}},"responses":{"200":{"description":"Video requested successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponseDto"}}}}},"summary":"Request a video to a chat","tags":["Chats"]}},"/v1/chats/{id}/send-gift":{"post":{"operationId":"ChatsController_sendGift","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendGiftRequestBodyDto"}}}},"responses":{"200":{"description":"Gift sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponseDto"}}}}},"summary":"Send a gift to a chat","tags":["Chats"]}},"/v1/chats/{id}/animate":{"post":{"operationId":"ChatsController_animateAttachments","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnimateAttachmentsRequestDto"}}}},"responses":{"200":{"description":"Animation initiated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponseDto"}}}}},"summary":"Animate existing image attachments into video(s)","tags":["Chats"]}},"/v1/chats/messages/report":{"post":{"operationId":"ChatsController_reportMessage","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMessageReportRequestDto"}}}},"responses":{"201":{"description":"Message reported successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMessageReportResponseDto"}}}},"422":{"description":"Validation failed or already reported"},"500":{"description":"Internal server error"}},"summary":"Report a message","tags":["Chats"]}},"/v1/models/public":{"get":{"operationId":"ModelsController_getPublicModels","parameters":[{"name":"gender","required":false,"in":"query","description":"Filter by gender","schema":{"example":"female","type":"string","enum":["male","female"]}},{"name":"search","required":false,"in":"query","description":"Search models by name (case-insensitive). Authenticated users only — ignored for unauthenticated requests.","schema":{"example":"Emily","type":"string"}},{"name":"ethnicity","required":false,"in":"query","description":"Filter by ethnicity","schema":{"example":"caucasian","type":"string","enum":["caucasian","latina","asian","arab","afro","indian","furry","kemonomimi","tiefling"]}},{"name":"personality","required":false,"in":"query","description":"Filter by personality type","schema":{"example":"Dominant","type":"string","enum":["Caregiver","Sage","Innocent","Jester","Temptress","Dominant","Submissive","Lover","Nympho","Mean","Confidant","Experimenter"]}},{"name":"age","required":false,"in":"query","description":"Filter by age range (enum value: 18=18-20, 20=21-29, 30=30-39, 40=40-49, 50=50+)","schema":{"example":20,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number (starts from 1)","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of results per page (max 100)","schema":{"minimum":1,"maximum":100,"default":20,"example":20,"type":"number"}},{"name":"sort","required":false,"in":"query","description":"Sort order: recommended (default editorial order), most_popular (by views), top_rated (by likes)","schema":{"default":"recommended","type":"string","enum":["recommended","most_popular","top_rated"]}},{"name":"locale","required":false,"in":"query","description":"Preferred locale for localized model fields (unauthenticated requests only; authenticated users always get their profile locale). Unsupported values fall back to en-US.","schema":{"example":"ja-JP","type":"string"}}],"responses":{"200":{"description":"Public models retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetModelsResponseDto"}}}}},"summary":"Get public models with pagination","tags":["Models"]}},"/v1/models/private":{"get":{"operationId":"ModelsController_getPrivateModels","parameters":[{"name":"gender","required":false,"in":"query","description":"Filter by gender","schema":{"example":"female","type":"string","enum":["male","female"]}},{"name":"search","required":false,"in":"query","description":"Search models by name (case-insensitive). Authenticated users only — ignored for unauthenticated requests.","schema":{"example":"Emily","type":"string"}},{"name":"ethnicity","required":false,"in":"query","description":"Filter by ethnicity","schema":{"example":"caucasian","type":"string","enum":["caucasian","latina","asian","arab","afro","indian","furry","kemonomimi","tiefling"]}},{"name":"personality","required":false,"in":"query","description":"Filter by personality type","schema":{"example":"Dominant","type":"string","enum":["Caregiver","Sage","Innocent","Jester","Temptress","Dominant","Submissive","Lover","Nympho","Mean","Confidant","Experimenter"]}},{"name":"age","required":false,"in":"query","description":"Filter by age range (enum value: 18=18-20, 20=21-29, 30=30-39, 40=40-49, 50=50+)","schema":{"example":20,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number (starts from 1)","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Number of results per page (max 100)","schema":{"minimum":1,"maximum":100,"default":20,"example":20,"type":"number"}},{"name":"sort","required":false,"in":"query","description":"Sort order: recommended (default editorial order), most_popular (by views), top_rated (by likes)","schema":{"default":"recommended","type":"string","enum":["recommended","most_popular","top_rated"]}},{"name":"locale","required":false,"in":"query","description":"Preferred locale for localized model fields (unauthenticated requests only; authenticated users always get their profile locale). Unsupported values fall back to en-US.","schema":{"example":"ja-JP","type":"string"}}],"responses":{"200":{"description":"Private models retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetModelsResponseDto"}}}}},"summary":"Get private models with pagination","tags":["Models"]}},"/v1/models/create-form-config":{"get":{"description":"Returns the multi-step form config (steps, elements, options) for creating a model. Labels are localized to the user profile locale (or the optional locale query param for guests); option values stay English and are validated against enums on POST /v1/models.","operationId":"ModelsController_getCreateFormConfig","parameters":[{"name":"locale","required":false,"in":"query","description":"Preferred locale for localized form labels (unauthenticated requests only; authenticated users always get their profile locale). Unsupported values fall back to en-US.","schema":{"example":"ja-JP","type":"string"}}],"responses":{"200":{"description":"Form configuration retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetModelCreateFormConfigResponseDto"}}}}},"summary":"Get model creation form configuration","tags":["Models"]}},"/v1/models/pending-reveal":{"get":{"description":"Returns the current user's COMPLETED private companions whose \"ready\" reveal has not been shown yet (e.g. created while the user was away). Declared before GET :id so the param route does not swallow it.","operationId":"ModelsController_getPendingRevealModels","parameters":[],"responses":{"200":{"description":"Pending-reveal companions retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetPendingRevealModelsResponseDto"}}}}},"summary":"Get completed companions not yet revealed to the user","tags":["Models"]}},"/v1/models/popular":{"get":{"description":"Returns the public model with the most chats created in the last 30 days. If no chats were created in that period, returns the first public model by order.","operationId":"ModelsController_getMostPopularModel","parameters":[],"responses":{"200":{"description":"Most popular model retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetModelByIdResponseDto"}}}},"404":{"description":"No public models found"}},"summary":"Get the most popular public model","tags":["Models"]}},"/v1/models/tags":{"get":{"description":"Returns tag groups (ethnicity, personality) with localized labels. Pass currently selected filters to narrow the available tags — only tags that would yield results are returned.","operationId":"ModelsController_getTags","parameters":[{"name":"gender","required":false,"in":"query","description":"Filter context: gender","schema":{"type":"string","enum":["male","female"]}},{"name":"ethnicity","required":false,"in":"query","description":"Filter context: selected ethnicity","schema":{"type":"string","enum":["caucasian","latina","asian","arab","afro","indian","furry","kemonomimi","tiefling"]}},{"name":"personality","required":false,"in":"query","description":"Filter context: selected personality","schema":{"type":"string","enum":["Caregiver","Sage","Innocent","Jester","Temptress","Dominant","Submissive","Lover","Nympho","Mean","Confidant","Experimenter"]}}],"responses":{"200":{"description":"Model tags retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelTagsResponseDto"}}}}},"summary":"Get localized model search tags (narrowing)","tags":["Models"]}},"/v1/models/slug/{slug}":{"get":{"operationId":"ModelsController_getModelBySlug","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Model retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetModelByIdResponseDto"}}}},"404":{"description":"Model not found"}},"summary":"Get model by slug","tags":["Models"]}},"/v1/models/{id}":{"get":{"operationId":"ModelsController_getModelById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Model retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetModelByIdResponseDto"}}}},"404":{"description":"Model not found"}},"summary":"Get model by id","tags":["Models"]}},"/v1/models/{id}/gallery":{"get":{"description":"Returns PHOTO, VIDEO and PACK gallery cards. Packs appear in a tab when they contain that media type. Locked paid content exposes only one teaser thumbnail before purchase.","operationId":"ModelsController_getModelProfileGallery","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"type","required":false,"in":"query","description":"Gallery tab media type","schema":{"enum":["PHOTO","VIDEO"],"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"default":20,"type":"number"}}],"responses":{"200":{"description":"Model profile gallery retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelProfileGalleryResponseDto"}}}},"404":{"description":"Model not found"}},"summary":"Get paginated model profile photo/video gallery","tags":["Models"]}},"/v1/models/{id}/collection":{"get":{"description":"Returns paginated list of all media (images and videos) received by the user from this model","operationId":"ModelsController_getModelCollection","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (default: 20, max: 100)","schema":{"default":20,"type":"number"}},{"name":"type","required":false,"in":"query","description":"Filter by media type","schema":{"enum":["image","video"],"type":"string"}},{"name":"sort","required":false,"in":"query","description":"Sorting order (default: newest_first)","schema":{"$ref":"#/components/schemas/CollectionSortOrder"}}],"responses":{"200":{"description":"Model collection retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionListResponseDto"}}}}},"summary":"Get user media collection for a specific model","tags":["Models"]}},"/v1/models":{"post":{"operationId":"ModelsController_createModel","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModelRequestBodyDto"}}}},"responses":{"201":{"description":"Model created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModelResponseBodyDto"}}}},"422":{"description":"Validation failed"},"500":{"description":"Internal server error"}},"summary":"Create a new model","tags":["Models"]}},"/v1/models/from-onboarding":{"post":{"description":"Creates a private companion from the marketing-funnel quiz answers, reusing the static paywall avatar instead of generating one. Charges no coins and is not gated by the model limit. Idempotent per user: a repeat call (same or new sessionId) returns the existing onboarding companion.","operationId":"ModelsController_createModelFromOnboarding","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModelFromOnboardingRequestBodyDto"}}}},"responses":{"201":{"description":"Companion creation enqueued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModelResponseBodyDto"}}}},"422":{"description":"Validation failed"}},"summary":"Create a companion from onboarding funnel answers","tags":["Models"]}},"/v1/models/revealed":{"post":{"description":"Acknowledges the \"your companion is ready\" reveal for the given companions so they are no longer returned by GET /v1/models/pending-reveal.","operationId":"ModelsController_markModelsRevealed","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkModelsRevealedRequestDto"}}}},"responses":{"201":{"description":"Companions marked as revealed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkModelsRevealedResponseDto"}}}}},"summary":"Mark companions as revealed","tags":["Models"]}},"/v1/models/report/reasons":{"get":{"operationId":"ModelsController_getReportReasons","parameters":[],"responses":{"200":{"description":"Report reasons retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetReportReasonsResponseDto"}}}}},"summary":"Get available report reasons","tags":["Models"]}},"/v1/models/report":{"post":{"operationId":"ModelsController_reportModel","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModelReportRequestDto"}}}},"responses":{"201":{"description":"Model reported successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModelReportResponseDto"}}}},"422":{"description":"Validation failed or already reported"},"500":{"description":"Internal server error"}},"summary":"Report a model","tags":["Models"]}},"/v1/notifications/test-push":{"post":{"description":"Sends a push notification with custom title and body to the authenticated user. Requires the user to have a valid FCM token registered.","operationId":"NotificationsController_sendTestPush","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestPushNotificationDto"}}}},"responses":{"200":{"description":"Push notification sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestPushNotificationResponseDto"}}}}},"summary":"Send test push notification to yourself","tags":["Notifications"]}},"/v1/notifications":{"get":{"operationId":"UserNotificationsController_getNotifications","parameters":[{"name":"cursor","required":false,"in":"query","description":"Cursor (ISO date string) for pagination","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":50,"default":20,"type":"number"}},{"name":"type","required":false,"in":"query","schema":{"type":"string","enum":["system","promo","model","studio","chat"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedNotificationsDto"}}}}},"summary":"Get paginated notifications","tags":["Notifications"]},"delete":{"operationId":"UserNotificationsController_dismiss","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DismissDto"}}}},"responses":{"200":{"description":"Number of deleted notifications"}},"summary":"Dismiss (delete) notifications","tags":["Notifications"]}},"/v1/notifications/unread-count":{"get":{"operationId":"UserNotificationsController_getUnreadCount","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCountDto"}}}}},"summary":"Get unread notification count","tags":["Notifications"]}},"/v1/notifications/read":{"patch":{"operationId":"UserNotificationsController_markRead","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkReadDto"}}}},"responses":{"200":{"description":""}},"summary":"Batch mark notifications as read","tags":["Notifications"]}},"/v1/notifications/read-all":{"patch":{"operationId":"UserNotificationsController_markAllRead","parameters":[],"responses":{"200":{"description":""}},"summary":"Mark all notifications as read","tags":["Notifications"]}},"/v1/scenes":{"get":{"operationId":"ScenesController_getPublicScenes","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":20,"type":"number"}},{"name":"search","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Public scenes retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScenesListResponseDto"}}}}},"summary":"Get list of public scenes with pagination","tags":["Scenes"]},"post":{"operationId":"ScenesController_createScene","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSceneDto"}}}},"responses":{"201":{"description":"Scene created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneDto"}}}}},"summary":"Create a new private scene","tags":["Scenes"]}},"/v1/scenes/my":{"get":{"operationId":"ScenesController_getUserScenes","parameters":[],"responses":{"200":{"description":"User scenes retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SceneDto"}}}}}},"summary":"Get current user private scenes","tags":["Scenes"]}},"/v1/scenes/my/drafts":{"get":{"operationId":"ScenesController_getUserDrafts","parameters":[],"responses":{"200":{"description":"User drafts retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SceneDraftDto"}}}}}},"summary":"Get current user scene drafts","tags":["Scenes"]}},"/v1/scenes/{id}/characters":{"get":{"description":"Returns paginated list of characters ordered by: recent conversations, then private models, then public models","operationId":"ScenesController_getSceneCharacters","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":20,"type":"number"}}],"responses":{"200":{"description":"Characters retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneCharactersResponseDto"}}}}},"summary":"Get available characters for a scene","tags":["Scenes"]}},"/v1/scenes/{id}":{"get":{"operationId":"ScenesController_getSceneById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Scene retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneDto"}}}},"404":{"description":"Scene not found"}},"summary":"Get scene by ID","tags":["Scenes"]},"put":{"operationId":"ScenesController_updateScene","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSceneDto"}}}},"responses":{"200":{"description":"Scene updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneDto"}}}},"403":{"description":"You can only update your own scenes"},"404":{"description":"Scene not found"}},"summary":"Update a user private scene","tags":["Scenes"]},"delete":{"operationId":"ScenesController_deleteScene","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Scene deleted successfully"},"403":{"description":"You can only delete your own scenes"},"404":{"description":"Scene not found"}},"summary":"Delete a user private scene","tags":["Scenes"]}},"/v1/scenes/random":{"post":{"operationId":"ScenesController_createRandomScene","parameters":[],"responses":{"201":{"description":"Random scene creation started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitSceneResponseDto"}}}}},"summary":"Create a random scene (bills coins)","tags":["Scenes"]}},"/v1/scenes/wizard/schema":{"get":{"operationId":"ScenesWizardController_getFormSchema","parameters":[],"responses":{"200":{"description":"Form schema retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WizardFormSchema"}}}}},"summary":"Get full wizard form schema (backend-driven forms)","tags":["Scene Wizard"]}},"/v1/scenes/wizard/options":{"get":{"operationId":"ScenesWizardController_getWizardOptions","parameters":[],"responses":{"200":{"description":"Wizard options retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WizardOptionsDto"}}}}},"summary":"Get predefined wizard options (legacy)","tags":["Scene Wizard"]}},"/v1/scenes/wizard/backstory-example":{"get":{"operationId":"ScenesWizardController_getBackstoryExample","parameters":[],"responses":{"200":{"description":"Backstory example retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackstoryExampleDto"}}}}},"summary":"Get a random backstory example","tags":["Scene Wizard"]}},"/v1/scenes/wizard/suggest/genre":{"get":{"description":"Genre defines your character's style and tone.","operationId":"ScenesWizardController_suggestGenre","parameters":[],"responses":{"200":{"description":"Genre suggestion retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestFieldResponseDto"}}}}},"summary":"Get a random genre suggestion","tags":["Scene Wizard"]}},"/v1/scenes/wizard/suggest/place":{"get":{"description":"The place where your scene comes to life.","operationId":"ScenesWizardController_suggestPlace","parameters":[],"responses":{"200":{"description":"Place suggestion retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestFieldResponseDto"}}}}},"summary":"Get a random place suggestion","tags":["Scene Wizard"]}},"/v1/scenes/wizard/suggest/timePeriod":{"get":{"description":"The time period sets the context for the character's actions.","operationId":"ScenesWizardController_suggestTimePeriod","parameters":[],"responses":{"200":{"description":"Time period suggestion retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestFieldResponseDto"}}}}},"summary":"Get a random time period suggestion","tags":["Scene Wizard"]}},"/v1/scenes/wizard/suggest/name":{"get":{"description":"Give your scene a short, memorable title.","operationId":"ScenesWizardController_suggestName","parameters":[],"responses":{"200":{"description":"Name suggestion retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestFieldResponseDto"}}}}},"summary":"Get a random scene name suggestion","tags":["Scene Wizard"]}},"/v1/scenes/wizard/suggest/backstory":{"get":{"description":"Describe the scene and its backstory to guide how the story unfolds and how the character reacts.","operationId":"ScenesWizardController_suggestBackstory","parameters":[],"responses":{"200":{"description":"Backstory suggestion retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestFieldResponseDto"}}}}},"summary":"Get a random backstory suggestion","tags":["Scene Wizard"]}},"/v1/scenes/wizard/suggest/goal":{"get":{"description":"Goal — this is the character's motive that guides their actions, decisions, and emotions.","operationId":"ScenesWizardController_suggestGoal","parameters":[],"responses":{"200":{"description":"Goal suggestion retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestFieldResponseDto"}}}}},"summary":"Get a random goal suggestion","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft":{"post":{"operationId":"ScenesWizardController_createDraft","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDraftDto"}}}},"responses":{"201":{"description":"Draft created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneDraftDto"}}}}},"summary":"Create a new scene draft","tags":["Scene Wizard"]},"get":{"operationId":"ScenesWizardController_getCurrentDraft","parameters":[],"responses":{"200":{"description":"Draft retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneDraftDto"}}}}},"summary":"Get current user draft (most recent)","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}":{"get":{"operationId":"ScenesWizardController_getDraft","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Draft retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneDraftDto"}}}},"404":{"description":"Draft not found"}},"summary":"Get draft by ID","tags":["Scene Wizard"]},"patch":{"operationId":"ScenesWizardController_updateDraft","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDraftDto"}}}},"responses":{"200":{"description":"Draft updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneDraftDto"}}}},"404":{"description":"Draft not found"}},"summary":"Update draft (auto-save on step change)","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/upload-url":{"get":{"operationId":"ScenesWizardController_generateCoverUploadUrl","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Upload URL generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoverUploadUrlResponseDto"}}}},"404":{"description":"Draft not found"}},"summary":"Get S3 presigned URL for cover upload","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/generate":{"post":{"operationId":"ScenesWizardController_generateCover","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateCoverDto"}}}},"responses":{"202":{"description":"Cover generation queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneCoverDto"}}}},"400":{"description":"Cover generation limit reached"},"404":{"description":"Draft not found"}},"summary":"Generate cover image asynchronously (max 5 per scene)","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/generate/async":{"post":{"operationId":"ScenesWizardController_generateCoverAsync","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateCoverDto"}}}},"responses":{"202":{"description":"Cover generation queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneCoverDto"}}}}},"summary":"Generate cover image asynchronously (max 5 per scene)","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/generate/sync":{"post":{"operationId":"ScenesWizardController_generateCoverSync","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateCoverDto"}}}},"responses":{"201":{"description":"Cover generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneCoverDto"}}}}},"summary":"Generate cover image synchronously (returns image URL)","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/upload":{"post":{"operationId":"ScenesWizardController_addUploadedCover","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUploadedCoverDto"}}}},"responses":{"201":{"description":"Cover added successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneCoverDto"}}}},"404":{"description":"Draft not found"}},"summary":"Add uploaded cover to draft","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/{coverId}/select":{"post":{"operationId":"ScenesWizardController_selectCover","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"coverId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Cover selected successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneDraftDto"}}}},"404":{"description":"Draft or cover not found"}},"summary":"Select a cover for the scene","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/{coverId}":{"delete":{"operationId":"ScenesWizardController_deleteCover","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"coverId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Cover deleted successfully"},"404":{"description":"Draft or cover not found"}},"summary":"Delete a cover","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/history":{"get":{"operationId":"ScenesWizardController_getCoverHistory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Cover history retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoverHistoryDto"}}}},"404":{"description":"Draft not found"}},"summary":"Get generated covers history for this draft","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/prompt/suggest":{"get":{"operationId":"ScenesWizardController_suggestCoverPrompt","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Suggested prompt generated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoverPromptSuggestionDto"}}}}},"summary":"Suggest a cover generation prompt from draft data","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/{coverId}/avatar/upload-url":{"get":{"operationId":"ScenesWizardController_generateAvatarUploadUrl","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"coverId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Avatar upload URL generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvatarUploadUrlResponseDto"}}}},"404":{"description":"Draft or cover not found"}},"summary":"Get S3 presigned URL for avatar upload (cropped cover icon)","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/cover/{coverId}/avatar":{"post":{"operationId":"ScenesWizardController_addAvatar","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"coverId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAvatarDto"}}}},"responses":{"201":{"description":"Avatar added successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneCoverDto"}}}},"404":{"description":"Draft or cover not found"}},"summary":"Add cropped avatar to a cover","tags":["Scene Wizard"]}},"/v1/scenes/wizard/draft/{id}/submit":{"post":{"operationId":"ScenesWizardController_submitScene","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitSceneDto"}}}},"responses":{"202":{"description":"Scene submitted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitSceneResponseDto"}}}},"402":{"description":"Insufficient coin balance"},"404":{"description":"Draft not found"}},"summary":"Submit scene for generation (bills 10 coins)","tags":["Scene Wizard"]}},"/webhook/scenes/wavespeed":{"post":{"operationId":"ScenesWebhookController_processWavespeed","parameters":[],"responses":{"200":{"description":""}},"tags":["Scene Webhooks"]}},"/v1/collection/types":{"get":{"description":"Returns list of available media types for filtering (for segment controller UI)","operationId":"CollectionController_getTypes","parameters":[],"responses":{"200":{"description":"Types retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionTypesResponseDto"}}}}},"summary":"Get available collection types","tags":["Collection"]}},"/v1/collection/sorting-options":{"get":{"description":"Returns list of available sorting options with default value","operationId":"CollectionController_getSortingOptions","parameters":[],"responses":{"200":{"description":"Sorting options retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SortingOptionsResponseDto"}}}}},"summary":"Get available sorting options","tags":["Collection"]}},"/v1/collection/hidden":{"get":{"description":"Returns paginated list of collection items the user has hidden (pending permanent removal)","operationId":"CollectionController_getHiddenCollection","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (default: 20, max: 100)","schema":{"default":20,"type":"number"}},{"name":"type","required":false,"in":"query","description":"Filter by media type","schema":{"enum":["image","video"],"type":"string"}},{"name":"sort","required":false,"in":"query","description":"Sorting order (default: newest_first)","schema":{"$ref":"#/components/schemas/CollectionSortOrder"}}],"responses":{"200":{"description":"Hidden collection retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionListResponseDto"}}}}},"summary":"Get hidden collection items","tags":["Collection"]}},"/v1/collection/hide":{"patch":{"description":"Hide one or more items from the collection. Items can be restored within 30 days.","operationId":"CollectionController_hideItems","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionItemIdsDto"}}}},"responses":{"200":{"description":"Items hidden successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionBatchResultDto"}}}}},"summary":"Hide collection items","tags":["Collection"]}},"/v1/collection/restore":{"patch":{"description":"Restore one or more hidden items back to the collection.","operationId":"CollectionController_restoreItems","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionItemIdsDto"}}}},"responses":{"200":{"description":"Items restored successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionBatchResultDto"}}}}},"summary":"Restore hidden collection items","tags":["Collection"]}},"/v1/collection/remove":{"delete":{"description":"Permanently remove hidden items from the collection. This cannot be undone. The attachment entity is preserved for chat history.","operationId":"CollectionController_removeItems","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionItemIdsDto"}}}},"responses":{"200":{"description":"Items permanently removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionBatchResultDto"}}}}},"summary":"Permanently remove collection items","tags":["Collection"]}},"/v1/collection":{"get":{"description":"Returns paginated list of all media (images and videos) received by the user across all models","operationId":"CollectionController_getCollection","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (default: 20, max: 100)","schema":{"default":20,"type":"number"}},{"name":"type","required":false,"in":"query","description":"Filter by media type","schema":{"enum":["image","video"],"type":"string"}},{"name":"sort","required":false,"in":"query","description":"Sorting order (default: newest_first)","schema":{"$ref":"#/components/schemas/CollectionSortOrder"}}],"responses":{"200":{"description":"Collection retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionListResponseDto"}}}}},"summary":"Get user media collection","tags":["Collection"]}},"/v1/collection/{id}":{"get":{"description":"Returns a single collection item if it belongs to the user","operationId":"CollectionController_getCollectionItem","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Collection item retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionItemDto"}}}},"404":{"description":"Collection item not found"}},"summary":"Get a single collection item by ID","tags":["Collection"]},"delete":{"description":"Permanently removes a single item from the collection. This cannot be undone. The attachment entity is preserved for chat history.","operationId":"CollectionController_deleteItem","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Collection item removed"},"404":{"description":"Collection item not found"}},"summary":"Delete a collection item","tags":["Collection"]}},"/v1/collection/models/{modelId}":{"get":{"description":"Returns paginated list of all media (images and videos) received by the user from a specific model","operationId":"CollectionController_getModelCollection","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (default: 20, max: 100)","schema":{"default":20,"type":"number"}},{"name":"type","required":false,"in":"query","description":"Filter by media type","schema":{"enum":["image","video"],"type":"string"}},{"name":"sort","required":false,"in":"query","description":"Sorting order (default: newest_first)","schema":{"$ref":"#/components/schemas/CollectionSortOrder"}}],"responses":{"200":{"description":"Model collection retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionListResponseDto"}}}}},"summary":"Get user media collection for a specific model","tags":["Collection"]}},"/v1/ai-preset/available-parameters":{"get":{"operationId":"AiPresetController_getAvailableParameters","parameters":[{"name":"sfw","required":false,"in":"query","description":"Outfit, pose, emotion: if omitted, follows user explicit-content preference (SFW-only or NSFW-only); if true, SFW-only; if false, NSFW-only. Settings (surroundings) are always all available non-deleted presets.","schema":{"$ref":"#/components/schemas/Object"}}],"responses":{"200":{"description":"Available parameters for image generation","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetImageGenerationParamsResponseDto"}}}}}},"summary":"Get available parameters for image generation","tags":["Ai Preset"]}},"/v1/gifts":{"get":{"operationId":"GiftsController_listGifts","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"minimum":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"minimum":1,"maximum":100,"example":10,"type":"number"}}],"responses":{"200":{"description":"List of gifts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetGiftsResponseDto"}}}}},"summary":"List gifts (paginated)","tags":["Gifts"]}},"/v1/gifts/{giftId}":{"get":{"operationId":"GiftsController_getGift","parameters":[{"name":"giftId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Gift found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftDto"}}}}},"summary":"Get gift by id","tags":["Gifts"]}},"/image-understanding/test":{"post":{"description":"Analyzes an image from a URL using Grok vision model with a text prompt","operationId":"ImageUnderstandingController_analyzeImage","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeImageRequestDto"}}}},"responses":{"200":{"description":"Image analyzed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeImageResponseDto"}}}},"400":{"description":"Invalid request parameters"},"500":{"description":"Failed to analyze image"}},"summary":"Test endpoint for image analysis using Grok vision","tags":["Image Understanding"]}},"/v1/cfg":{"get":{"operationId":"CfgController_getCfg","parameters":[],"responses":{"200":{"description":"Current configuration values","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CfgDto"}}}}},"summary":"Get runtime app configuration","tags":["Cfg"]}},"/v1/user/me":{"get":{"operationId":"UserInfoController_getUserAccountInfo","parameters":[],"responses":{"200":{"description":""}},"tags":["User"]},"patch":{"operationId":"UserInfoController_updateUserAccountInfo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserAccountInfoDto"}}}},"responses":{"200":{"description":""}},"tags":["User"]},"delete":{"operationId":"UserInfoController_deleteUserAccountInfo","parameters":[],"responses":{"200":{"description":""}},"tags":["User"]}},"/v1/user/metadata":{"post":{"description":"Stores Cloudflare/request header metadata and optional client-side metadata for the authenticated user.","operationId":"UserInfoController_trackMetadata","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackUserMetadataDto"}}}},"responses":{"200":{"description":"Metadata tracked successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMetadataResponseDto"}}}}},"summary":"Track current user metadata","tags":["User"]}},"/v1/user/report/reasons":{"get":{"operationId":"UserInfoController_getAccountDeletionReportReasons","parameters":[],"responses":{"200":{"description":"Account deletion report reasons retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAccountDeletionReportReasonsResponseDto"}}}}},"summary":"Get available account deletion report reasons","tags":["User"]}},"/v1/user/report":{"post":{"operationId":"UserInfoController_reportAccountDeletion","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAccountDeletionReportRequestDto"}}}},"responses":{"201":{"description":"Account deletion report submitted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAccountDeletionReportResponseDto"}}}}},"summary":"Submit an account deletion report","tags":["User"]}},"/v1/age-verification/session":{"post":{"description":"Creates a session with the active provider (iDenfy or Yoti) and returns a provider-discriminated payload used to load the verification UI. Clients that send no supportedProviders are treated as iDenfy-only.","operationId":"AgeVerificationController_createSession","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgeVerificationSessionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgeVerificationSessionResponseDto"}}}}},"summary":"Start an age-verification session","tags":["Age Verification"]}},"/v1/age-verification/status":{"get":{"operationId":"AgeVerificationController_getStatus","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgeVerificationStatusResponseDto"}}}}},"summary":"Get the current age-verification status","tags":["Age Verification"]}},"/v1/billing/payment-method":{"get":{"description":"Returns the saved payment method (brand, last4, expiry, expiringSoon), the $0 product ident to purchase in order to re-bind the card, and — for providers with an own card vault (Upgate) — every saved card the user can manage. CVV is never stored.","operationId":"PaymentMethodController_getPaymentMethod","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetPaymentMethodResponseDto"}}}}},"summary":"Get the current user's payment method and card-change config","tags":["Billing"]}},"/v1/billing/payment-method/cards/{cardId}":{"delete":{"description":"Deletes the card at the gateway (token delete + card-on-file consent revoke) and retires it in the vault. When the primary card is removed and other cards remain, the freshest remaining one becomes primary and live subscriptions are repointed onto it. The only card of a subscription that still renews cannot be removed (400 `last_card_on_active_subscription`) — the client cancels the autorenew first and retries.","operationId":"PaymentMethodController_removeSavedCard","parameters":[{"name":"cardId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"400":{"description":"last_card_on_active_subscription / saved_cards_not_supported"},"404":{"description":"Not this user's card"}},"summary":"Remove one of the current user's saved cards","tags":["Billing"]}},"/v1/billing/payment-method/cards/{cardId}/primary":{"post":{"description":"Renewals and one-click purchases are charged to the primary card; live subscriptions are repointed onto it.","operationId":"PaymentMethodController_setPrimarySavedCard","parameters":[{"name":"cardId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"404":{"description":"Not this user's card"}},"summary":"Make one of the current user's saved cards the primary one","tags":["Billing"]}},"/admin/subscription-products":{"get":{"description":"Retrieve all available subscription products for filtering","operationId":"AdminController_getSubscriptionProducts","parameters":[],"responses":{"200":{"description":""}},"summary":"Get all subscription products","tags":["Admin"]}},"/admin/subscription-analytics":{"get":{"description":"Retrieve subscription statistics including total subscriptions, active subscribers, and auto-renewal status","operationId":"AdminController_getSubscriptionAnalytics","parameters":[{"name":"productId","required":false,"in":"query","description":"Optional product ID filter","schema":{}},{"name":"toDate","required":true,"in":"query","description":"End date (ISO string)","schema":{"example":"2024-12-31T23:59:59Z"}},{"name":"fromDate","required":true,"in":"query","description":"Start date (ISO string)","schema":{"example":"2024-01-01T00:00:00Z"}}],"responses":{"200":{"description":""}},"summary":"Get subscription analytics for a date range","tags":["Admin"]}},"/admin/user-analytics":{"get":{"description":"Retrieve daily user creation counts for the last N days (7, 30, 180, or 365 days)","operationId":"AdminController_getUserAnalytics","parameters":[{"name":"days","required":true,"in":"query","description":"Number of days to analyze (7, 30, 180, or 365)","schema":{"enum":[7,30,180,365],"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Get user creation analytics for a specified period","tags":["Admin"]}},"/admin/subscription-creation-analytics":{"get":{"description":"Retrieve daily subscription creation counts for the last N days (7, 30, 180, or 365 days)","operationId":"AdminController_getSubscriptionCreationAnalytics","parameters":[{"name":"days","required":true,"in":"query","description":"Number of days to analyze (7, 30, 180, or 365)","schema":{"enum":[7,30,180,365],"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Get subscription creation analytics for a specified period","tags":["Admin"]}},"/admin/gift-suggestions-gift-stats":{"get":{"description":"Returns average and median gifts sent per user for registered users in a date range, split by useGiftSuggestions=true/false and all users vs paying users.","operationId":"AdminController_getGiftSuggestionsGiftStats","parameters":[{"name":"toDate","required":true,"in":"query","description":"End of registration period (ISO string)","schema":{"example":"2025-01-31T23:59:59.999Z"}},{"name":"fromDate","required":true,"in":"query","description":"Start of registration period (ISO string)","schema":{"example":"2025-01-01T00:00:00.000Z"}}],"responses":{"200":{"description":""}},"summary":"Get gift send stats split by useGiftSuggestions flag and paying status","tags":["Admin"]}},"/admin/payment-events":{"get":{"description":"Retrieve a paginated list of payment events with optional filtering by user, notification type, subtype, and currency. Amounts are calculated as positive for income and negative for expenses.","operationId":"AdminController_getPaymentEventsList","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"minimum":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page (default: 10)","schema":{"minimum":1,"maximum":100,"example":10,"type":"number"}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"example":"user-uuid-here","type":"string"}},{"name":"firebaseUserId","required":false,"in":"query","description":"Filter by Firebase user ID","schema":{"example":"firebase-uid-here","type":"string"}},{"name":"notificationType","required":false,"in":"query","description":"Filter by notification type","schema":{"example":"SUBSCRIBED","type":"string","enum":["SUBSCRIBED","DID_CHANGE_RENEWAL_PREF","DID_CHANGE_RENEWAL_STATUS","OFFER_REDEEMED","DID_RENEW","EXPIRED","DID_FAIL_TO_RENEW","GRACE_PERIOD_EXPIRED","PRICE_INCREASE","REFUND","REFUND_DECLINED","CONSUMPTION_REQUEST","RENEWAL_EXTENDED","REVOKE","TEST","RENEWAL_EXTENSION","REFUND_REVERSED","EXTERNAL_PURCHASE_TOKEN","ONE_TIME_CHARGE"]}},{"name":"subtype","required":false,"in":"query","description":"Filter by subtype","schema":{"example":"INITIAL_BUY","type":"string","enum":["INITIAL_BUY","RESUBSCRIBE","DOWNGRADE","UPGRADE","AUTO_RENEW_ENABLED","AUTO_RENEW_DISABLED","VOLUNTARY","BILLING_RETRY","PRICE_INCREASE","GRACE_PERIOD","PENDING","ACCEPTED","BILLING_RECOVERY","PRODUCT_NOT_FOR_SALE","SUMMARY","FAILURE","UNREPORTED"]}},{"name":"currency","required":false,"in":"query","description":"Filter by currency","schema":{"example":"USD","type":"string"}},{"name":"productType","required":false,"in":"query","description":"Filter by product type","schema":{"example":"AUTO_RENEWABLE","type":"string","enum":["AUTO_RENEWABLE","NON_RENEWABLE","CONSUMABLE","NON_CONSUMABLE"]}}],"responses":{"200":{"description":"Payment events list retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentEventsListResponseDto"}}}}},"summary":"Get payment events list with pagination and filters","tags":["Admin"]}},"/admin/funnelfox-transactions":{"get":{"description":"Retrieve a paginated list of real-money FunnelFox transactions (newest first). Apple charges live in payment-events; this is the FunnelFox equivalent. Filter by userId (resolved to firebaseUid), firebaseUserId, or status.","operationId":"AdminController_getFunnelFoxTransactionsList","parameters":[{"name":"page","required":false,"in":"query","schema":{"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"example":20,"type":"number"}},{"name":"userId","required":false,"in":"query","description":"Filter by internal user ID","schema":{"type":"string"}},{"name":"firebaseUserId","required":false,"in":"query","description":"Filter by Firebase user ID","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter by transaction status (e.g. settled, declined)","schema":{"type":"string"}}],"responses":{"200":{"description":"FunnelFox transactions list retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunnelFoxTransactionsListResponseDto"}}}}},"summary":"Get FunnelFox transactions list with pagination and filters","tags":["Admin"]}},"/admin/payment-events/total-ltv":{"get":{"description":"Calculate total LTV from payment events, optionally filtered by user and currency. LTV = Total Income - Total Expenses.","operationId":"AdminController_getTotalLtv","parameters":[{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"example":"user-uuid-here","type":"string"}},{"name":"currency","required":false,"in":"query","description":"Filter by currency","schema":{"example":"USD","type":"string"}}],"responses":{"200":{"description":"Total LTV calculated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotalLtvResponseDto"}}}}},"summary":"Get total Lifetime Value (LTV)","tags":["Admin"]}},"/admin/payment-events/total-ltv-by-currency":{"get":{"description":"Calculate total LTV from payment events grouped by currency, optionally filtered by user and currency.","operationId":"AdminController_getTotalLtvByCurrency","parameters":[{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"example":"user-uuid-here","type":"string"}},{"name":"currency","required":false,"in":"query","description":"Filter by currency (to restrict to a single currency)","schema":{"example":"USD","type":"string"}}],"responses":{"200":{"description":"Total LTV by currency calculated successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TotalLtvResponseDto"}}}}}},"summary":"Get total Lifetime Value (LTV) grouped by currency","tags":["Admin"]}},"/admin/subscription-count-by-product/ios":{"get":{"description":"Retrieve subscription counts grouped by product for iOS (App Store) subscriptions","operationId":"AdminController_getIosSubscriptionCountByProduct","parameters":[{"name":"from","required":false,"in":"query","description":"Start date (ISO string)","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"End date (ISO string)","schema":{"type":"string"}}],"responses":{"200":{"description":"iOS subscription counts by product retrieved successfully"}},"summary":"Get iOS subscription count by product","tags":["Admin"]}},"/admin/subscription-count-by-product/web":{"get":{"description":"Retrieve subscription counts grouped by product for Web (Stripe) subscriptions","operationId":"AdminController_getWebSubscriptionCountByProduct","parameters":[{"name":"from","required":false,"in":"query","description":"Start date (ISO string)","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"End date (ISO string)","schema":{"type":"string"}},{"name":"statuses","required":false,"in":"query","description":"Comma-separated list of statuses to filter (e.g., active,trialing,paused). Available: incomplete, incomplete_expired, trialing, active, past_due, canceled, unpaid, paused","schema":{"type":"string"}}],"responses":{"200":{"description":"Web subscription counts by product retrieved successfully"}},"summary":"Get Web subscription count by product","tags":["Admin"]}},"/admin/models":{"get":{"description":"Retrieve a paginated list of models with optional filtering by public/private status, user ID, search terms, gender, and status","operationId":"AdminModelsController_getModelsList","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"minimum":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page (default: 10)","schema":{"minimum":1,"maximum":500,"example":10,"type":"number"}},{"name":"isPublic","required":false,"in":"query","description":"Filter by public status (true/false)","schema":{"example":true,"type":"boolean"}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID (for private models)","schema":{"example":"user-uuid-here","type":"string"}},{"name":"search","required":false,"in":"query","description":"Search in name, description, and personality","schema":{"maxLength":255,"example":"search term","type":"string"}},{"name":"gender","required":false,"in":"query","description":"Filter by gender","schema":{"enum":["male","female"],"type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter by model status","schema":{"enum":["IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED","CANCELLED","TIMED_OUT"],"type":"string"}},{"name":"chatModes","required":false,"in":"query","description":"Filter by allowed chat modes (repeatable): a model matches when it allows at least one of the listed modes","schema":{"type":"array","items":{"type":"string","enum":["ROLEPLAY","REALISTIC"]}}}],"responses":{"200":{"description":"Models list retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelsListResponseDto"}}}}},"summary":"Get models list with pagination and filters","tags":["Admin Models"]},"post":{"description":"Create a new model with the provided data","operationId":"AdminModelsController_createModel","parameters":[],"requestBody":{"required":true,"description":"Model creation data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModelDto"}}}},"responses":{"201":{"description":"Model created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelDetailResponseDto"}}}},"400":{"description":"Invalid input data"}},"summary":"Create a new model","tags":["Admin Models"]}},"/admin/models/presigned-url":{"get":{"operationId":"AdminModelsController_getPresignedUrl","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin Models"]}},"/admin/models/stats":{"get":{"description":"Retrieve statistics about models including total, public, private, active, and in-progress counts","operationId":"AdminModelsController_getModelsStats","parameters":[],"responses":{"200":{"description":"Models statistics retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelsStatsResponseDto"}}}}},"summary":"Get models statistics","tags":["Admin Models"]}},"/admin/models/{id}":{"get":{"description":"Retrieve a specific model by its ID with user and system prompt relations","operationId":"AdminModelsController_getModelById","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"example":"model-uuid-here","type":"string"}}],"responses":{"200":{"description":"Model retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelDetailResponseDto"}}}},"404":{"description":"Model not found"}},"summary":"Get model by ID","tags":["Admin Models"]},"put":{"description":"Update an existing model with the provided data","operationId":"AdminModelsController_updateModel","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"example":"model-uuid-here","type":"string"}}],"requestBody":{"required":true,"description":"Model update data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateModelDto"}}}},"responses":{"200":{"description":"Model updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelDetailResponseDto"}}}},"404":{"description":"Model not found"}},"summary":"Update a model","tags":["Admin Models"]},"delete":{"description":"Mark a model as deleted (soft delete)","operationId":"AdminModelsController_softDeleteModel","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"example":"model-uuid-here","type":"string"}}],"responses":{"200":{"description":"Model soft deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteModelResponseDto"}}}},"404":{"description":"Model not found"}},"summary":"Soft delete a model","tags":["Admin Models"]}},"/admin/models/system-prompt/{id}":{"put":{"description":"Update an existing model system prompt with the provided data","operationId":"AdminModelsController_updateModelSystemPrompt","parameters":[{"name":"id","required":true,"in":"path","description":"Model System Prompt ID","schema":{"example":"system-prompt-uuid-here","type":"string"}}],"requestBody":{"required":true,"description":"Model system prompt update data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateModelSystemPromptDtoClass"}}}},"responses":{"200":{"description":"Model system prompt updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateModelSystemPromptResponseDto"}}}},"400":{"description":"Invalid input data"},"404":{"description":"Model system prompt not found"}},"summary":"Update a model system prompt","tags":["Admin Models"]}},"/admin/models/{id}/system-prompt":{"post":{"description":"Create a new system prompt for a model that does not have one yet","operationId":"AdminModelsController_createModelSystemPrompt","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"example":"model-uuid-here","type":"string"}}],"requestBody":{"required":true,"description":"Initial text and optional status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModelSystemPromptAdminDto"}}}},"responses":{"201":{"description":"System prompt created successfully"},"400":{"description":"Model already has a system prompt or invalid input"},"404":{"description":"Model not found"}},"summary":"Create a system prompt for a model","tags":["Admin Models"]}},"/admin/models/{id}/generate-description-sync":{"post":{"description":"Generate description and system prompt for a model synchronously and wait for the response","operationId":"AdminModelsController_generateModelDescriptionSync","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"example":"model-uuid-here","type":"string"}}],"responses":{"200":{"description":"Model description generated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"Generated model description"},"systemPrompt":{"type":"string","description":"Generated system prompt"}}}}}},"404":{"description":"Model not found"},"500":{"description":"Description generation failed"}},"summary":"Generate model description synchronously","tags":["Admin Models"]}},"/admin/models/{id}/first-message-media":{"post":{"description":"Upload an image or video as the first message media for a model via FormData","operationId":"AdminModelsController_uploadFirstMessageMedia","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["image","video","audio","preview","defaultScenarioCover","firstMsgImageSfw","firstMsgImageNsfw"]},"file":{"type":"string","format":"binary"}},"required":["type","file"]}}}},"responses":{"201":{"description":""}},"summary":"Upload first message media","tags":["Admin Models"]}},"/admin/models/{id}/avatar":{"post":{"description":"Upload a model avatar image via FormData; converts to WebP (quality 80) and stores in avatars prefix","operationId":"AdminModelsController_uploadModelAvatar","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"201":{"description":""}},"summary":"Upload model avatar","tags":["Admin Models"]}},"/admin/models/{id}/static-media":{"post":{"description":"Upload one of: avatarIcon, video, tiktokVideo, onboardingImage, demoIntroVideo, demoModelPreviewVideo, onboardingSubscriptionRewardVideoPreview, onboardingSubscriptionRewardVideo. Images are converted to WebP (quality 80) where applicable. Stored under static prefix.","operationId":"AdminModelsController_uploadStaticMedia","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"property":{"type":"string","enum":["avatarIcon","video","tiktokVideo","onboardingImage","initialPhotoSFW","initialPhotoNSFW","demoIntroVideo","demoModelPreviewVideo","onboardingSubscriptionRewardVideoPreview","onboardingSubscriptionRewardVideo"]},"file":{"type":"string","format":"binary"}},"required":["property","file"]}}}},"responses":{"201":{"description":""}},"summary":"Upload static media for model","tags":["Admin Models"]}},"/admin/models/translations/overview":{"get":{"description":"Paginated list of models with per-locale translation fill status and overall progress for the UI-facing translatable fields.","operationId":"AdminModelsController_getModelTranslationsOverview","parameters":[],"responses":{"200":{"description":"Translations overview retrieved successfully"}},"summary":"Translations overview for the admin table","tags":["Admin Models"]}},"/admin/models/translations/batch-generate":{"post":{"description":"Queues (model × locale) translation tasks and processes them with a bounded pool of parallel LLM calls. Returns a batchId to poll via GET translations/batch/:batchId. mode=missing (default) only fills incomplete pairs; mode=overwrite regenerates everything.","operationId":"AdminModelsController_batchGenerateModelTranslations","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchGenerateModelTranslationsDtoClass"}}}},"responses":{"200":{"description":"Batch started; poll the returned batchId for progress"}},"summary":"Start a batch AI translation for selected models","tags":["Admin Models"]}},"/admin/models/translations/batch/{batchId}":{"get":{"description":"Progress and failures of a batch started via translations/batch-generate. Batches are tracked in memory for 24h after completion.","operationId":"AdminModelsController_getModelTranslationsBatchStatus","parameters":[{"name":"batchId","required":true,"in":"path","description":"Batch ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Batch status retrieved successfully"}},"summary":"Get progress of a translation batch","tags":["Admin Models"]}},"/admin/models/translations/batch/{batchId}/events":{"get":{"description":"Server-Sent Events stream: emits the current batch snapshot immediately, then one snapshot per finished task, and closes when the batch completes. Use GET translations/batch/:batchId as a polling fallback.","operationId":"AdminModelsController_streamModelTranslationsBatchEvents","parameters":[{"name":"batchId","required":true,"in":"path","description":"Batch ID","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"SSE stream of translation batch progress","tags":["Admin Models"]}},"/admin/models/translations/bulk-generate":{"post":{"description":"Runs the same flow as POST :id/translations/:locale/generate for each (model × locale). Use dryRun to count matches only. Respects throttleMs between Grok calls.","operationId":"AdminModelsController_bulkGenerateModelTranslations","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkGenerateModelTranslationsDtoClass"}}}},"responses":{"200":{"description":"Summary of generated translations and failures"}},"summary":"Bulk AI-generate model translations for public models in a createdAt range","tags":["Admin Models"]}},"/admin/models/{id}/translations":{"get":{"description":"Returns all locale translations for a given model","operationId":"AdminModelsController_getModelTranslations","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Translations retrieved successfully"}},"summary":"Get all translations for a model","tags":["Admin Models"]}},"/admin/models/{id}/translations/{locale}":{"get":{"description":"Returns the translation for a model in a specific locale","operationId":"AdminModelsController_getModelTranslation","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}},{"name":"locale","required":true,"in":"path","description":"Locale code (e.g. es-419, fr-FR)","schema":{"type":"string"}}],"responses":{"200":{"description":"Translation retrieved successfully"}},"summary":"Get translation for a specific locale","tags":["Admin Models"]},"put":{"description":"Upserts a translation for a model in a given locale","operationId":"AdminModelsController_upsertModelTranslation","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}},{"name":"locale","required":true,"in":"path","description":"Locale code (e.g. es-419, fr-FR)","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertModelTranslationDto"}}}},"responses":{"200":{"description":"Translation saved successfully"}},"summary":"Create or update translation for a locale","tags":["Admin Models"]},"delete":{"description":"Removes a translation for a model in a given locale","operationId":"AdminModelsController_deleteModelTranslation","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}},{"name":"locale","required":true,"in":"path","description":"Locale code (e.g. es-419, fr-FR)","schema":{"type":"string"}}],"responses":{"200":{"description":"Translation deleted successfully"}},"summary":"Delete translation for a locale","tags":["Admin Models"]}},"/admin/models/{id}/translations/{locale}/generate":{"post":{"description":"Uses AI to translate all model text fields to the specified locale. Creates or updates the translation.","operationId":"AdminModelsController_generateModelTranslation","parameters":[{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}},{"name":"locale","required":true,"in":"path","description":"Target locale code (e.g. es-419, fr-FR)","schema":{"type":"string"}}],"responses":{"200":{"description":"Translation generated and saved successfully"}},"summary":"AI-generate translation for a locale","tags":["Admin Models"]}},"/admin/models/migration/buckets":{"get":{"operationId":"AdminModelsController_getMigrationBuckets","parameters":[],"responses":{"200":{"description":""}},"summary":"Get bucket names for migration (non-sensitive)","tags":["Admin Models"]}},"/admin/models/migration/compare-model":{"post":{"operationId":"AdminModelsController_compareModelMigration","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompareModelMigrationDtoClass"}}}},"responses":{"201":{"description":""}},"summary":"Compare model (prod vs dev payload) by name (public only)","tags":["Admin Models"]}},"/admin/models/migration/update-model":{"post":{"operationId":"AdminModelsController_updateModelMigration","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateModelMigrationDtoClass"}}}},"responses":{"201":{"description":""}},"summary":"Update model from dev payload by name (public only)","tags":["Admin Models"]}},"/admin/models/migration/create-model":{"post":{"operationId":"AdminModelsController_createModelMigration","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateModelMigrationDtoClass"}}}},"responses":{"201":{"description":""}},"summary":"Create model on prod from dev payload by name","tags":["Admin Models"]}},"/admin/models/migration/compare-system-prompt":{"post":{"operationId":"AdminModelsController_compareSystemPromptMigration","parameters":[],"responses":{"201":{"description":""}},"summary":"Compare system prompt (prod vs dev payload) by model name (public only)","tags":["Admin Models"]}},"/admin/models/migration/update-system-prompt":{"post":{"operationId":"AdminModelsController_updateSystemPromptMigration","parameters":[],"responses":{"201":{"description":""}},"summary":"Update system prompt from dev payload by model name (public only)","tags":["Admin Models"]}},"/admin/models/migration/bulk-apply":{"post":{"description":"Each item: name + model snapshot (same as single migration). Optional systemPrompt, sources (S3 copy map), translations by locale. Set dryRun to compare only.","operationId":"AdminModelsController_bulkApplyModelMigration","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Object"}}}},"responses":{"201":{"description":""}},"summary":"Apply dev payloads to prod for many models (optional system prompt, assets, translations)","tags":["Admin Models"]}},"/admin/models/migration/sync-assets":{"post":{"operationId":"AdminModelsController_syncAssetsMigration","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncAssetsMigrationDtoClass"}}}},"responses":{"201":{"description":""}},"summary":"Copy model assets from dev to prod buckets","tags":["Admin Models"]}},"/admin/models/migration/compare-translation":{"post":{"operationId":"AdminModelsController_compareTranslationMigration","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompareModelTranslationMigrationDtoClass"}}}},"responses":{"201":{"description":""}},"summary":"Compare model translation (prod vs dev payload) by model name + locale","tags":["Admin Models"]}},"/admin/models/migration/upsert-translation":{"post":{"operationId":"AdminModelsController_upsertTranslationMigration","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertModelTranslationMigrationDtoClass"}}}},"responses":{"201":{"description":""}},"summary":"Create/update model translation on prod from dev payload by model name + locale","tags":["Admin Models"]}},"/admin/models/bulk-update-order":{"post":{"description":"Update the display order for multiple models in a single transaction","operationId":"AdminModelsController_bulkUpdateModelsOrder","parameters":[],"requestBody":{"required":true,"description":"Array of model ID and order pairs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateModelsOrderDtoClass"},"examples":{"example1":{"value":{"updates":[{"id":"123e4567-e89b-12d3-a456-426614174000","order":0},{"id":"223e4567-e89b-12d3-a456-426614174001","order":1},{"id":"323e4567-e89b-12d3-a456-426614174002","order":2}]}}}}}},"responses":{"200":{"description":"Models order updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateModelsOrderResponseDto"}}}},"400":{"description":"Invalid input data"},"404":{"description":"One or more models not found"}},"summary":"Bulk update model order","tags":["Admin Models"]}},"/admin/users":{"get":{"description":"Retrieve a paginated list of users with optional filtering by search terms and explicit content flag","operationId":"AdminUsersController_getUsersList","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"minimum":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page (default: 10)","schema":{"minimum":1,"maximum":100,"example":10,"type":"number"}},{"name":"search","required":false,"in":"query","description":"Search by Firebase User ID (exact match) or email (partial, case-insensitive)","schema":{"example":"user@example.com","type":"string"}},{"name":"useExplicitContent","required":false,"in":"query","description":"Filter by explicit content flag (true/false)","schema":{"example":true,"type":"boolean"}},{"name":"isSubscribed","required":false,"in":"query","description":"Filter by subscription status across all providers (Apple, Stripe, FunnelFox)","schema":{"example":true,"type":"boolean"}},{"name":"createdFrom","required":false,"in":"query","description":"Filter users created on or after this date (ISO 8601)","schema":{"example":"2024-01-01T00:00:00.000Z","type":"string"}},{"name":"createdTo","required":false,"in":"query","description":"Filter users created on or before this date (ISO 8601)","schema":{"example":"2024-12-31T23:59:59.999Z","type":"string"}},{"name":"isAnonymous","required":false,"in":"query","description":"Filter by anonymous/demo user flag (true = demo users only)","schema":{"example":true,"type":"boolean"}},{"name":"useGiftSuggestions","required":false,"in":"query","description":"Filter by gift suggestions A/B flag (true = in experiment, false = control)","schema":{"example":true,"type":"boolean"}},{"name":"subscriptionSource","required":false,"in":"query","description":"Filter by active subscription provider (apple | stripe | funnelfox)","schema":{"enum":["apple","stripe","funnelfox"],"type":"string"}},{"name":"cardFirst6","required":false,"in":"query","description":"Filter by card BIN (1-6 digits, prefix match). Combines with cardLast4; either may be used alone","schema":{"example":"424242","type":"string"}},{"name":"cardLast4","required":false,"in":"query","description":"Filter by card last four (1-4 digits, suffix match). Combines with cardFirst6; either may be used alone","schema":{"example":"4242","type":"string"}}],"responses":{"200":{"description":"Users list retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsersListResponseDto"}}}}},"summary":"Get users list with pagination and filters","tags":["Admin Users"]}},"/admin/users/stats":{"get":{"description":"Retrieve statistics about users including total, active, explicit content enabled, and deleted counts","operationId":"AdminUsersController_getUsersStats","parameters":[],"responses":{"200":{"description":"Users statistics retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsersStatsResponseDto"}}}}},"summary":"Get users statistics","tags":["Admin Users"]}},"/admin/users/by-firebase-uid/{firebaseUid}":{"get":{"description":"Retrieve a specific user by their Firebase UID. Useful for resolving web users to app users.","operationId":"AdminUsersController_getUserByFirebaseUid","parameters":[{"name":"firebaseUid","required":true,"in":"path","description":"Firebase User ID","schema":{"example":"abc123firebaseUid","type":"string"}}],"responses":{"200":{"description":"User retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetailResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Get user by Firebase UID","tags":["Admin Users"]}},"/admin/users/{id}":{"get":{"description":"Retrieve a specific user by their ID with all related data including subscriptions, payment events, coin transactions, private models, and chats","operationId":"AdminUsersController_getUserById","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":"User retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetailResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Get user by ID","tags":["Admin Users"]},"patch":{"description":"Update user information by admin","operationId":"AdminUsersController_updateUser","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"description":"User update details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDtoClass"}}}},"responses":{"200":{"description":"User updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Update user information","tags":["Admin Users"]}},"/admin/users/{id}/acquisition-lead":{"post":{"description":"Creates (or reuses) an acquisition lead tied to this user, granting them the full content catalog as if they had come through a web funnel. Idempotent — clicking twice does not create duplicates. The user identity (Firebase UID, email) is taken from the user record, not the request.","operationId":"AdminUsersController_createAcquisitionLead","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"201":{"description":"Acquisition lead created or reused successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcquisitionLeadDto"}}}},"404":{"description":"User not found"}},"summary":"Mark a user as an acquisition lead","tags":["Admin Users"]}},"/admin/users/{id}/message-analytics":{"get":{"description":"Retrieve analytics about user messages including text messages, image requests, video requests, and failure counts","operationId":"AdminUsersController_getUserMessageAnalytics","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":"User message analytics retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMessageAnalyticsResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Get user message analytics","tags":["Admin Users"]}},"/admin/users/{id}/experiments":{"get":{"description":"All experiments with the variant this user currently resolves to: forced (kill-switch, no assignment row), sticky (enrolled) or default.","operationId":"AdminUsersController_getUserExperiments","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":"User experiments retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserExperimentsResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Get user experiments","tags":["Admin Users"]}},"/admin/users/{id}/experiments/{key}":{"put":{"description":"Pins this user to the given variant. Beats weights, experiment status and the global forced variant — only this user is affected. Returns the refreshed experiments overview.","operationId":"AdminUsersController_setUserExperimentVariant","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"type":"string"}},{"name":"key","required":true,"in":"path","description":"Experiment key","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetUserExperimentVariantDto"}}}},"responses":{"200":{"description":"Variant overridden successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserExperimentsResponseDto"}}}},"400":{"description":"Unknown variant id, or user has no Firebase UID"},"404":{"description":"User or experiment not found"}},"summary":"Override the experiment variant for one user","tags":["Admin Users"]},"delete":{"description":"Deletes the assignment row: the user falls back to normal resolution and is re-bucketed on the next read of a running auto experiment.","operationId":"AdminUsersController_clearUserExperimentVariant","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"type":"string"}},{"name":"key","required":true,"in":"path","description":"Experiment key","schema":{"type":"string"}}],"responses":{"200":{"description":"Override cleared successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserExperimentsResponseDto"}}}},"404":{"description":"User not found, or no override on this experiment"}},"summary":"Clear the experiment variant override for one user","tags":["Admin Users"]}},"/admin/users/{id}/subscription":{"get":{"description":"Retrieve subscription information for a user including product details, start/expiration dates, auto-renewal status, and active status","operationId":"AdminUsersController_getUserSubscription","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":"User subscription data retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSubscriptionResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Get user subscription data","tags":["Admin Users"]}},"/admin/users/{id}/funnelfox/autorenew":{"post":{"description":"Enable or disable auto-renewal for the latest FunnelFox subscription of this user.","operationId":"AdminUsersController_toggleFunnelFoxAutorenew","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"description":"Auto-renewal toggle payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleFunnelFoxAutorenewDtoClass"}}}},"responses":{"200":{"description":"FunnelFox subscription auto-renewal updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSubscriptionResponseDto"}}}}},"summary":"Toggle FunnelFox subscription auto-renewal","tags":["Admin Users"]}},"/admin/users/{id}/funnelfox/discount":{"post":{"description":"Apply discount to the latest FunnelFox subscription of this user.","operationId":"AdminUsersController_applyFunnelFoxDiscount","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"description":"Discount payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyFunnelFoxDiscountDtoClass"}}}},"responses":{"200":{"description":"Discount applied successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSubscriptionResponseDto"}}}}},"summary":"Apply discount to FunnelFox subscription","tags":["Admin Users"]}},"/admin/users/{id}/coin-transactions":{"get":{"description":"Retrieve analytics about user coin transactions including amount deposited, amount spent, number of purchases, and current balance","operationId":"AdminUsersController_getUserCoinTransactions","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":"User coin transactions analytics retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCoinTransactionsResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Get user coin transactions analytics","tags":["Admin Users"]}},"/admin/users/{id}/coin-transactions/history":{"get":{"description":"Retrieve the individual coin ledger rows for a user (newest first): grants, purchases, spends per feature, quota resets and refunds, with the per-bucket split (quotaAmount / purchasedAmount) when v2 is enabled.","operationId":"AdminUsersController_getUserCoinTransactionsHistory","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page (default: 20, max: 100)","schema":{"example":20,"type":"number"}}],"responses":{"200":{"description":"User coin transaction history retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCoinTransactionsHistoryResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Get user coin transaction history (paginated timeline)","tags":["Admin Users"]}},"/admin/users/{id}/deposit-coins":{"post":{"description":"Deposit a specified amount of coins into a user's account with specified reason and direction","operationId":"AdminUsersController_depositCoins","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"description":"Coin deposit details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminDepositCoinsDtoClass"}}}},"responses":{"200":{"description":"Coins deposited successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminDepositCoinsResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Deposit coins for a user","tags":["Admin Users"]}},"/admin/users/{id}/subscription/refresh-cycle":{"post":{"description":"Deposit a fresh cycle of subscription coins for the user's active subscription and shift the next-grant window one cycle forward. Works in both v1 (ledger) and v2 (quota) billing modes.","operationId":"AdminUsersController_refreshSubscriptionCycle","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":"Subscription coin cycle refreshed successfully"},"404":{"description":"User or active subscription not found"}},"summary":"Refresh user subscription coin cycle","tags":["Admin Users"]}},"/admin/users/{id}/v2/subscription":{"get":{"description":"Retrieve the user's user_subscriptions row (tier/plan resolved, status, provider, billing period, auto-renewal) together with the live two-bucket coin balance (quota + purchased).","operationId":"AdminUsersController_getV2Subscription","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":"V2 subscription data"},"404":{"description":"User not found"}},"summary":"Get V2 (quota-model) subscription and coin balance","tags":["Admin Users"]}},"/admin/users/{id}/quota-usage":{"get":{"description":"Counts of recorded quota usage (user_quota_usages) grouped by message type, with totals overall and since the current quota cycle started.","operationId":"AdminUsersController_getQuotaUsage","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":"Quota usage counts"},"404":{"description":"User not found"}},"summary":"Get V2 quota usage counts","tags":["Admin Users"]}},"/admin/users/{id}/v2/subscription/grant":{"post":{"description":"Create, reactivate, or switch the V2 subscription to the given plan. Tier and billing period are derived from the plan; quota is granted. Idempotent.","operationId":"AdminUsersController_grantV2Subscription","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantV2SubscriptionDtoClass"}}}},"responses":{"200":{"description":"V2 subscription granted"}},"summary":"Grant (or reactivate/switch) a V2 subscription","tags":["Admin Users"]}},"/admin/users/{id}/v2/subscription/change-plan":{"post":{"description":"Switch an active V2 subscription to another plan. Resets the billing period to the new plan's length and re-grants quota.","operationId":"AdminUsersController_changeV2Plan","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeV2PlanDtoClass"}}}},"responses":{"200":{"description":"V2 plan changed"}},"summary":"Change the V2 subscription plan","tags":["Admin Users"]}},"/admin/users/{id}/v2/subscription/shift-period":{"post":{"description":"Set the billing period to an explicit start/end range (extend or move). Marks the subscription active and re-arms the quota cycle if needed.","operationId":"AdminUsersController_shiftV2Period","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftV2PeriodDtoClass"}}}},"responses":{"200":{"description":"V2 period shifted"}},"summary":"Shift the V2 subscription billing period","tags":["Admin Users"]}},"/admin/users/{id}/v2/subscription/expire":{"post":{"description":"Mark the V2 subscription expired. When force=true, also set the period end to now and disable renewal.","operationId":"AdminUsersController_expireV2Subscription","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpireV2SubscriptionDtoClass"}}}},"responses":{"200":{"description":"V2 subscription expired"}},"summary":"Expire the V2 subscription","tags":["Admin Users"]}},"/admin/users/{id}/v2/subscription/autorenew":{"post":{"description":"Enable or disable auto-renewal (cancelAtPeriodEnd) on the active V2 subscription.","operationId":"AdminUsersController_setV2AutoRenew","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetV2AutoRenewDtoClass"}}}},"responses":{"200":{"description":"V2 auto-renewal updated"}},"summary":"Toggle V2 subscription auto-renewal","tags":["Admin Users"]}},"/admin/users/{id}/chats":{"get":{"description":"Retrieve a paginated list of chats for a specific user with their last messages","operationId":"AdminUsersController_getUserChats","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page (default: 10)","schema":{"example":10,"type":"number"}}],"responses":{"200":{"description":"User chats retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserChatsResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Get user chats with pagination","tags":["Admin Users"]}},"/admin/users/{id}/chats/{chatId}/messages":{"get":{"description":"Retrieve messages for a specific chat with cursor-based pagination, including attachments with CDN URLs","operationId":"AdminUsersController_getUserChatMessages","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}},{"name":"chatId","required":true,"in":"path","description":"Chat ID","schema":{"example":"chat-uuid-here","type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Cursor for pagination","schema":{"example":"base64-encoded-cursor","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Number of messages to retrieve (default: 20, max: 100)","schema":{"example":20,"type":"number"}}],"responses":{"200":{"description":"User chat messages retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserChatMessagesResponseDto"}}}},"404":{"description":"User or chat not found"}},"summary":"Get user chat messages with cursor-based pagination","tags":["Admin Users"]}},"/admin/users/{id}/chats/{chatId}":{"delete":{"description":"Permanently delete a chat and all associated data (messages, attachments, logs, summaries). This operation is irreversible.","operationId":"AdminUsersController_hardDeleteChat","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"user-uuid-here","type":"string"}},{"name":"chatId","required":true,"in":"path","description":"Chat ID to delete","schema":{"example":"chat-uuid-here","type":"string"}}],"responses":{"200":{"description":"Chat deleted successfully"},"404":{"description":"User or chat not found"}},"summary":"Hard delete a chat","tags":["Admin Users"]}},"/admin/users/{userId}/memory-logs":{"get":{"operationId":"AdminUsersController_getUserMemoryLogs","parameters":[{"name":"userId","required":true,"in":"path","description":"User ID","schema":{"type":"string"}},{"name":"modelId","required":false,"in":"query","description":"Model ID to filter logs (optional)","schema":{"type":"string"}},{"name":"chatId","required":false,"in":"query","description":"Chat ID to filter logs (optional)","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of logs to return (default: 100)","schema":{"example":100,"type":"number"}}],"responses":{"200":{"description":"OK"}},"summary":"Get memory activity logs for a user","tags":["Admin Users"]}},"/admin/users/{userId}/memory-fragments":{"get":{"operationId":"AdminUsersController_getUserMemoryFragments","parameters":[{"name":"userId","required":true,"in":"path","description":"User ID","schema":{"type":"string"}},{"name":"modelId","required":false,"in":"query","description":"Model ID to filter fragments (optional)","schema":{"type":"string"}},{"name":"chatId","required":false,"in":"query","description":"Chat ID to filter fragments (optional)","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of fragments to return (default: 100)","schema":{"example":100,"type":"number"}}],"responses":{"200":{"description":"OK"}},"summary":"Get memory fragments for a user","tags":["Admin Users"]}},"/admin/users/{userId}/memory-facts":{"get":{"operationId":"AdminUsersController_getUserMemoryFacts","parameters":[{"name":"userId","required":true,"in":"path","description":"User ID","schema":{"type":"string"}},{"name":"modelId","required":false,"in":"query","description":"Model ID to filter facts to one user-model pair (optional)","schema":{"type":"string"}},{"name":"chatId","required":true,"in":"query","schema":{"type":"string"}},{"name":"includeExpired","required":false,"in":"query","description":"Include invalidated (expired) facts (default: true)","schema":{"example":true,"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of facts to return (default: 100)","schema":{"example":100,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Number of facts to skip (default: 0)","schema":{"example":0,"type":"number"}}],"responses":{"200":{"description":"OK"}},"summary":"Get memory v2 temporal facts for a user","tags":["Admin Users"]}},"/admin/users/{userId}/memory-profile":{"get":{"operationId":"AdminUsersController_getUserMemoryProfile","parameters":[{"name":"userId","required":true,"in":"path","description":"User ID","schema":{"type":"string"}},{"name":"modelId","required":true,"in":"query","description":"Model ID of the pair (required)","schema":{"type":"string"}},{"name":"chatId","required":true,"in":"query","description":"Chat the profile belongs to (required — profiles are per chat)","schema":{"type":"string"}}],"responses":{"200":{"description":"OK ({ profile: { content, version, updatedAt } | null })"}},"summary":"Get the memory v2 consolidated profile of ONE chat (memory is per conversation, GEN-1386)","tags":["Admin Users"]}},"/admin/users/sort/by-message-count":{"get":{"operationId":"AdminUsersController_getUsersSortedByMessageCount","parameters":[{"name":"order","required":false,"in":"query","description":"Order by message count (asc|desc)","schema":{"example":"desc","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max number of users to return","schema":{"example":50,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Offset for pagination","schema":{"example":0,"type":"number"}},{"name":"createdFrom","required":false,"in":"query","description":"Only include users created on or after this date (ISO 8601, UTC)","schema":{"example":"2025-01-01T00:00:00.000Z","type":"string"}},{"name":"createdTo","required":false,"in":"query","description":"Only include users created on or before this date (ISO 8601, UTC)","schema":{"example":"2025-01-31T23:59:59.999Z","type":"string"}},{"name":"useGiftSuggestions","required":false,"in":"query","description":"Filter by A/B gift suggestions flag","schema":{"example":true,"type":"boolean"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsersSortByMessageCountResponseDto"}}}}},"summary":"Get users sorted by message count","tags":["Admin Users"]}},"/admin/users/{userId}/visual-director-logs":{"get":{"operationId":"AdminUsersController_getVisualDirectorLogs","parameters":[{"name":"userId","required":true,"in":"path","description":"User ID","schema":{"type":"string"}},{"name":"chatId","required":false,"in":"query","description":"Chat ID to filter logs (optional)","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of logs to return (default: 50)","schema":{"example":50,"type":"number"}}],"responses":{"200":{"description":"OK"}},"summary":"Get Visual Director logs for a user","tags":["Admin Users"]}},"/admin/users/{userId}/visual-director-v2-logs":{"get":{"operationId":"AdminUsersController_getVisualDirectorV2Logs","parameters":[{"name":"userId","required":true,"in":"path","description":"User ID","schema":{"type":"string"}},{"name":"chatId","required":false,"in":"query","description":"Chat ID to filter logs (optional)","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of logs to return (default: 15)","schema":{"example":15,"type":"number"}}],"responses":{"200":{"description":"OK"}},"summary":"Get Visual Director v2 logs for a user","tags":["Admin Users"]}},"/admin/attachments":{"get":{"description":"Retrieve a paginated list of attachments with optional filtering by search terms, type, status, explicit content flag, and user ID","operationId":"AdminAttachmentsController_getAttachmentsList","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"minimum":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page (default: 10)","schema":{"minimum":1,"maximum":100,"example":10,"type":"number"}},{"name":"search","required":false,"in":"query","description":"Search in prompt text","schema":{"example":"beautiful sunset","type":"string"}},{"name":"type","required":false,"in":"query","description":"Filter by attachment type (use \"all\" to show all types)","schema":{"example":"IMAGE","type":"string","enum":["image","video","audio","gift","all"]}},{"name":"status","required":false,"in":"query","description":"Filter by status","schema":{"example":"COMPLETED","type":"string","enum":["IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED","CANCELLED","TIMED_OUT"]}},{"name":"explicit","required":false,"in":"query","description":"Filter by explicit content flag (true/false)","schema":{"example":false,"type":"boolean"}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"example":"user-uuid-here","type":"string"}},{"name":"from","required":false,"in":"query","description":"Filter from date (ISO string)","schema":{"example":"2024-01-01T00:00:00Z","type":"string"}},{"name":"to","required":false,"in":"query","description":"Filter to date (ISO string)","schema":{"example":"2024-12-31T23:59:59Z","type":"string"}},{"name":"isFromUser","required":false,"in":"query","description":"Filter by whether attachment was sent by user (true/false)","schema":{"example":true,"type":"boolean"}}],"responses":{"200":{"description":"Attachments list retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentsListResponseDto"}}}}},"summary":"Get attachments list with pagination and filters","tags":["Admin Attachments"]}},"/admin/attachments/stats":{"get":{"description":"Retrieve statistics about attachments including total, images, videos, explicit content, failed, and successful counts. When filters are provided, returns both general stats and filtered stats.","operationId":"AdminAttachmentsController_getAttachmentsStats","parameters":[{"name":"type","required":false,"in":"query","description":"Filter by attachment type (use \"all\" to show all types)","schema":{"example":"IMAGE","type":"string","enum":["image","video","audio","gift","all"]}},{"name":"status","required":false,"in":"query","description":"Filter by status","schema":{"example":"COMPLETED","type":"string","enum":["IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED","CANCELLED","TIMED_OUT"]}},{"name":"explicit","required":false,"in":"query","description":"Filter by explicit content flag (true/false)","schema":{"example":false,"type":"boolean"}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"example":"user-uuid-here","type":"string"}},{"name":"from","required":false,"in":"query","description":"Filter from date (ISO string)","schema":{"example":"2024-01-01T00:00:00Z","type":"string"}},{"name":"to","required":false,"in":"query","description":"Filter to date (ISO string)","schema":{"example":"2024-12-31T23:59:59Z","type":"string"}},{"name":"isFromUser","required":false,"in":"query","description":"Filter by whether attachment was sent by user (true/false)","schema":{"example":true,"type":"boolean"}}],"responses":{"200":{"description":"Attachments statistics retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentsStatsResponseDto"}}}}},"summary":"Get attachments statistics","tags":["Admin Attachments"]}},"/v1/admin/reports/models":{"get":{"operationId":"AdminReportsController_getModelReports","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"example":20,"type":"number"}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportsListResponseDto"}}}}},"summary":"Get model reports (paginated)","tags":["Admin - Reports"]}},"/v1/admin/reports/messages":{"get":{"operationId":"AdminReportsController_getMessageReports","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"example":20,"type":"number"}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportsListResponseDto"}}}}},"summary":"Get message reports (paginated)","tags":["Admin - Reports"]}},"/v1/admin/reports/subscription-cancellations":{"get":{"operationId":"AdminReportsController_getSubscriptionCancellationReports","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"example":20,"type":"number"}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportsListResponseDto"}}}}},"summary":"Get subscription cancellation reason reports (paginated)","tags":["Admin - Reports"]}},"/v1/admin/reports/iap-aggregates":{"get":{"operationId":"AdminReportsController_getIapAggregates","parameters":[{"name":"from","required":false,"in":"query","description":"From date (ISO)","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"To date (ISO)","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IapAggregatesResponseDto"}}}}},"summary":"Get global IAP aggregates by user","tags":["Admin - Reports"]}},"/v1/admin/reports/chat-length":{"get":{"operationId":"AdminReportsController_getChatLength","parameters":[{"name":"from","required":false,"in":"query","description":"From date (ISO)","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"To date (ISO)","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatLengthResponseDto"}}}}},"summary":"Get average/median messages per chat","tags":["Admin - Reports"]}},"/v1/admin/reports/explicit-attachment-ratio":{"get":{"operationId":"AdminReportsController_getExplicitAttachmentRatio","parameters":[{"name":"from","required":false,"in":"query","description":"From date (ISO)","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"To date (ISO)","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExplicitAttachmentRatioResponseDto"}}}}},"summary":"Get portion of users requesting explicit content","tags":["Admin - Reports"]}},"/v1/admin/reports/iap-by-product":{"get":{"operationId":"AdminReportsController_getIapByProduct","parameters":[{"name":"from","required":false,"in":"query","description":"From date (ISO)","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"To date (ISO)","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IapByProductResponseDto"}}}}},"summary":"Get IAP metrics by product","tags":["Admin - Reports"]}},"/v1/admin/reports/dashboard":{"get":{"operationId":"AdminReportsController_getDashboardMetrics","parameters":[{"name":"dateStart","required":true,"in":"query","description":"Start date (ISO)","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","description":"End date (ISO)","schema":{"type":"string"}},{"name":"compareMode","required":false,"in":"query","description":"Comparison mode","schema":{"default":"previous_period","type":"string","enum":["previous_period","previous_month","previous_year"]}},{"name":"section","required":false,"in":"query","description":"Section to fetch. Omit to fetch all (slower). Use per-section calls for parallel loading.","schema":{"type":"string","enum":["acquisition","engagement","monetization","funnels"]}},{"name":"skipCache","required":false,"in":"query","description":"Skip server cache and force fresh DB queries","schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardMetricsResponseDto"}}}}},"summary":"Get dashboard KPI metrics with period comparison","tags":["Admin - Reports"]}},"/v1/admin/reports/catalog-cache":{"delete":{"operationId":"AdminReportsController_invalidateCatalogCache","parameters":[{"name":"target","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Catalog cache invalidated"}},"summary":"Invalidate consumer catalog cache (public models, stories tray, live actions)","tags":["Admin - Reports"]}},"/v1/admin/gifts":{"get":{"operationId":"AdminGiftsController_list","parameters":[],"responses":{"200":{"description":""}},"summary":"List all gifts (admin)","tags":["Admin Gifts"]},"post":{"operationId":"AdminGiftsController_create","parameters":[],"responses":{"201":{"description":""}},"summary":"Create gift (admin)","tags":["Admin Gifts"]}},"/v1/admin/gifts/{id}":{"get":{"operationId":"AdminGiftsController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get gift by id (admin)","tags":["Admin Gifts"]},"put":{"operationId":"AdminGiftsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Update gift (admin)","tags":["Admin Gifts"]},"delete":{"operationId":"AdminGiftsController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Delete gift (admin)","tags":["Admin Gifts"]}},"/v1/admin/gifts/{id}/image":{"post":{"operationId":"AdminGiftsController_uploadImage","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Upload/replace gift image (admin)","tags":["Admin Gifts"]}},"/v1/admin/gifts/{id}/translations":{"get":{"description":"Returns all locale translations for a given gift","operationId":"AdminGiftsController_getGiftTranslations","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get all translations for a gift","tags":["Admin Gifts"]}},"/v1/admin/gifts/{id}/translations/{locale}":{"get":{"operationId":"AdminGiftsController_getGiftTranslation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get translation for a gift locale","tags":["Admin Gifts"]},"put":{"operationId":"AdminGiftsController_upsertGiftTranslation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertGiftTranslationDto"}}}},"responses":{"200":{"description":""}},"summary":"Create or update translation for a gift locale","tags":["Admin Gifts"]},"delete":{"operationId":"AdminGiftsController_deleteGiftTranslation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Delete translation for a gift locale","tags":["Admin Gifts"]}},"/v1/admin/gifts/{id}/translations/{locale}/generate":{"post":{"description":"Uses AI to translate gift text fields to the specified locale and saves the translation.","operationId":"AdminGiftsController_generateGiftTranslation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"AI-generate translation for a gift locale","tags":["Admin Gifts"]}},"/v1/admin/gifts/translations/generate-all":{"post":{"description":"Backfills gift translations for all non-deleted gifts across all supported non-default locales.","operationId":"AdminGiftsController_generateAllGiftTranslations","parameters":[],"responses":{"201":{"description":""}},"summary":"AI-generate translations for all gifts","tags":["Admin Gifts"]}},"/v1/admin/gifts/generate-all-translations":{"post":{"description":"Alias endpoint for bulk gift translation backfill across supported non-default locales.","operationId":"AdminGiftsController_generateAllGiftTranslationsAlias","parameters":[],"responses":{"201":{"description":""}},"summary":"AI-generate translations for all gifts (alias)","tags":["Admin Gifts"]}},"/admin/auth/login":{"post":{"operationId":"AdminAuthController_login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminLoginDto"}}}},"responses":{"200":{"description":""}},"summary":"Admin login with username and password","tags":["Admin Auth"]}},"/admin/auth/logout":{"post":{"operationId":"AdminAuthController_logout","parameters":[],"responses":{"200":{"description":""}},"summary":"Admin logout (clears cookie)","tags":["Admin Auth"]}},"/admin/auth/me":{"get":{"operationId":"AdminAuthController_me","parameters":[],"responses":{"200":{"description":""}},"summary":"Get current admin user from cookie token","tags":["Admin Auth"]}},"/admin/auth/me/password":{"patch":{"operationId":"AdminAuthController_changePassword","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordDto"}}}},"responses":{"204":{"description":""}},"summary":"Change own password","tags":["Admin Auth"]}},"/admin/constants":{"get":{"operationId":"AdminConstantsController_getConstants","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (default: 1)","schema":{"minimum":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (default: 10)","schema":{"minimum":1,"maximum":100,"example":10,"type":"number"}},{"name":"search","required":false,"in":"query","description":"Search by key (ILIKE)","schema":{"example":"PRICE","type":"string"}},{"name":"type","required":false,"in":"query","description":"Filter by type","schema":{"example":"number","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConstantsListResponseDto"}}}}},"summary":"Get constants list with pagination and filters","tags":["Admin Constants"]},"post":{"operationId":"AdminConstantsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConstantDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConstantItemDto"}}}}},"summary":"Create or upsert a constant by key","tags":["Admin Constants"]}},"/admin/constants/resolved":{"get":{"operationId":"AdminConstantsController_getResolved","parameters":[{"name":"prefix","required":false,"in":"query","schema":{"example":"VISUAL_DIRECTOR_V2","type":"string"}},{"name":"keys","required":false,"in":"query","description":"Comma-separated list of constant keys; takes precedence over prefix","schema":{"example":"UPSCALE_IMAGE_PRICE,UPSCALE_VIDEO_PRICE","type":"string"}}],"responses":{"200":{"description":""}},"summary":"Resolved (effective) values for constants under a key prefix or an explicit key list, with override flag","tags":["Admin Constants"]}},"/admin/constants/registry":{"get":{"description":"Every ConstantKey with its domain, section, label, description, control, default and effective value. Integration secrets are redacted here as everywhere outside the Integration Keys endpoints.","operationId":"AdminConstantsController_getRegistry","parameters":[],"responses":{"200":{"description":""}},"summary":"Full constant registry as domains → sections: metadata, default and current value","tags":["Admin Constants"]}},"/admin/constants/integration-keys":{"get":{"description":"Values are masked. The runtime constant wins when non-empty; otherwise the env var (AWS Secrets Manager) is used, exactly as before this screen existed.","operationId":"AdminConstantsController_listIntegrationKeys","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationApiKeysListResponseDto"}}}}},"summary":"Inbound integration API keys: which are set, from where, and a masked preview","tags":["Admin Constants"]}},"/admin/constants/integration-keys/{key}/reveal":{"post":{"description":"POST because it is an audited action. Env-backed keys are not revealed — they stay in AWS Secrets Manager.","operationId":"AdminConstantsController_revealIntegrationKey","parameters":[{"name":"key","required":true,"in":"path","schema":{"example":"MCP_MARKETING_API_KEY","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevealIntegrationApiKeyResponseDto"}}}}},"summary":"Reveal the full value of a constant-backed integration key","tags":["Admin Constants"]}},"/admin/constants/integration-keys/{key}":{"put":{"description":"The new value takes effect within the resolver TTL (60s) on every instance — no redeploy, no restart.","operationId":"AdminConstantsController_setIntegrationKey","parameters":[{"name":"key","required":true,"in":"path","schema":{"example":"MCP_MARKETING_API_KEY","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetIntegrationApiKeyDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationApiKeyMutationResponseDto"}}}}},"summary":"Set the runtime override for an integration API key","tags":["Admin Constants"]},"delete":{"operationId":"AdminConstantsController_clearIntegrationKey","parameters":[{"name":"key","required":true,"in":"path","schema":{"example":"MCP_MARKETING_API_KEY","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationApiKeyMutationResponseDto"}}}}},"summary":"Drop the runtime override so the env var takes over again","tags":["Admin Constants"]}},"/admin/constants/{key}":{"get":{"operationId":"AdminConstantsController_getByKey","parameters":[{"name":"key","required":true,"in":"path","description":"Constant key","schema":{"example":"IMAGE_REQUEST_PRICE","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConstantItemDto"}}}}},"summary":"Get a constant by key (DB only)","tags":["Admin Constants"]},"put":{"operationId":"AdminConstantsController_update","parameters":[{"name":"key","required":true,"in":"path","description":"Constant key","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConstantDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConstantItemDto"}}}}},"summary":"Update or create a constant by key (upsert)","tags":["Admin Constants"]},"delete":{"operationId":"AdminConstantsController_delete","parameters":[{"name":"key","required":true,"in":"path","description":"Constant key","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteConstantResponseDto"}}}}},"summary":"Delete a constant by key","tags":["Admin Constants"]}},"/admin/video-rephraser/pose-map":{"get":{"operationId":"AdminVideoRephraserController_getPoseMap","parameters":[],"responses":{"200":{"description":""}},"summary":"Every video generation pose with the LoRA tags the LTX rephraser resolves for it, plus mapping keys no pose can reach","tags":["Admin Video Rephraser"]}},"/admin/tracking-pixels":{"get":{"description":"Facebook/TikTok pixel repository — source of truth for browser pixels and server-side conversion APIs. Tokens are never returned.","operationId":"AdminTrackingPixelsController_list","parameters":[],"responses":{"200":{"description":""}},"summary":"List tracking pixels","tags":["Admin Tracking Pixels"]},"post":{"operationId":"AdminTrackingPixelsController_create","parameters":[],"responses":{"201":{"description":""}},"summary":"Create a tracking pixel","tags":["Admin Tracking Pixels"]}},"/admin/tracking-pixels/{id}":{"put":{"description":"accessToken: omit to keep the stored token, empty string to clear it (client-only pixel), non-empty to replace. Runtime picks changes up within ~1 minute.","operationId":"AdminTrackingPixelsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Update a tracking pixel","tags":["Admin Tracking Pixels"]},"delete":{"operationId":"AdminTrackingPixelsController_delete","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Delete a tracking pixel","tags":["Admin Tracking Pixels"]}},"/admin/image-generation/translations/overview":{"get":{"description":"Paginated cross-type list of props with per-locale translated labels and completeness progress.","operationId":"AdminImageGenerationController_getTranslationsOverview","parameters":[],"responses":{"200":{"description":""}},"summary":"Translations overview for image-generation prop labels","tags":["Admin Image Generation"]}},"/admin/image-generation/translations/batch-generate":{"post":{"description":"Chunks (item × locale) label translations into LLM tasks processed by a bounded worker pool. Returns a batchId; consume progress via the SSE events endpoint or poll the status endpoint.","operationId":"AdminImageGenerationController_batchGenerateTranslations","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchGenerateImageGenTranslationsDtoClass"}}}},"responses":{"200":{"description":""}},"summary":"Start a batch AI translation for selected props","tags":["Admin Image Generation"]}},"/admin/image-generation/translations/batch/{batchId}":{"get":{"operationId":"AdminImageGenerationController_getTranslationsBatchStatus","parameters":[{"name":"batchId","required":true,"in":"path","description":"Batch ID","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get progress of an image-gen translation batch","tags":["Admin Image Generation"]}},"/admin/image-generation/translations/batch/{batchId}/events":{"get":{"description":"Emits the current batch snapshot immediately, then one snapshot per finished task; closes when the batch completes.","operationId":"AdminImageGenerationController_streamTranslationsBatchEvents","parameters":[{"name":"batchId","required":true,"in":"path","description":"Batch ID","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"SSE stream of image-gen translation batch progress","tags":["Admin Image Generation"]}},"/admin/image-generation/migration/upsert-translations":{"post":{"description":"Matches items by (type, name), optionally syncs the English label, and upserts the provided locale translations.","operationId":"AdminImageGenerationController_migrateTranslations","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrateImageGenTranslationsDtoClass"}}}},"responses":{"200":{"description":""}},"summary":"Upsert prop label translations from a dev payload","tags":["Admin Image Generation"]}},"/admin/image-generation/{type}":{"get":{"operationId":"AdminImageGenerationController_list","parameters":[{"name":"type","required":true,"in":"path","schema":{"example":"clothes|emotions|poses|surroundings","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number","schema":{"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"example":20,"type":"number"}},{"name":"search","required":false,"in":"query","description":"Search by name","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenListResponseDto"}}}}},"summary":"List items","tags":["Admin Image Generation"]},"post":{"operationId":"AdminImageGenerationController_create","parameters":[{"name":"type","required":true,"in":"path","schema":{"example":"clothes|emotions|poses|surroundings","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateImageGenDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenItemDto"}}}}},"summary":"Create item","tags":["Admin Image Generation"]}},"/admin/image-generation/{type}/{id}":{"get":{"operationId":"AdminImageGenerationController_getById","parameters":[{"name":"type","required":true,"in":"path","schema":{"example":"clothes|emotions|poses|surroundings","type":"string"}},{"name":"id","required":true,"in":"path","schema":{"example":1,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenItemDto"}}}}},"summary":"Get item by id","tags":["Admin Image Generation"]},"patch":{"operationId":"AdminImageGenerationController_update","parameters":[{"name":"type","required":true,"in":"path","schema":{"example":"clothes|emotions|poses|surroundings","type":"string"}},{"name":"id","required":true,"in":"path","schema":{"example":1,"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateImageGenDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenItemDto"}}}}},"summary":"Update item","tags":["Admin Image Generation"]},"delete":{"operationId":"AdminImageGenerationController_delete","parameters":[{"name":"type","required":true,"in":"path","schema":{"example":"clothes|emotions|poses|surroundings","type":"string"}},{"name":"id","required":true,"in":"path","schema":{"example":1,"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Delete item","tags":["Admin Image Generation"]}},"/admin/image-generation/{type}/generate-localizations":{"post":{"operationId":"AdminImageGenerationController_generateLocalizations","parameters":[{"name":"type","required":true,"in":"path","schema":{"example":"clothes|emotions|poses|surroundings","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateImageGenLocalizationsResponseDto"}}}}},"summary":"AI-generate localizations for all parameters of a type","tags":["Admin Image Generation"]}},"/admin/image-generation/{type}/{id}/translations":{"get":{"operationId":"AdminImageGenerationController_getTranslations","parameters":[{"name":"type","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ImageGenTranslationDto"}}}}}},"summary":"List translations for an image-generation item","tags":["Admin Image Generation"]}},"/admin/image-generation/{type}/{id}/translations/{locale}":{"get":{"operationId":"AdminImageGenerationController_getTranslation","parameters":[{"name":"type","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"locale","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenTranslationDto"}}}}},"summary":"Get translation by locale for an item","tags":["Admin Image Generation"]},"put":{"operationId":"AdminImageGenerationController_upsertTranslation","parameters":[{"name":"type","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"locale","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertImageGenTranslationDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenTranslationDto"}}}}},"summary":"Create or update translation for an item locale","tags":["Admin Image Generation"]},"delete":{"operationId":"AdminImageGenerationController_deleteTranslation","parameters":[{"name":"type","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"locale","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Delete translation for an item locale","tags":["Admin Image Generation"]}},"/admin/pose-library/{model}/check-loras":{"get":{"operationId":"AdminPoseLibraryController_checkLoras","parameters":[{"name":"model","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Verify every library LoRA has a file in the loras bucket","tags":["Admin Pose Library"]}},"/admin/pose-library/models":{"get":{"operationId":"AdminPoseLibraryController_listModels","parameters":[],"responses":{"200":{"description":""}},"summary":"List image models that have a pose library","tags":["Admin Pose Library"]}},"/admin/pose-library/{model}/entries":{"get":{"operationId":"AdminPoseLibraryController_listEntries","parameters":[{"name":"model","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"List poses for an image model","tags":["Admin Pose Library"]},"post":{"operationId":"AdminPoseLibraryController_createEntry","parameters":[{"name":"model","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Create a pose","tags":["Admin Pose Library"]}},"/admin/pose-library/entries/{id}":{"patch":{"operationId":"AdminPoseLibraryController_updateEntry","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Update a pose","tags":["Admin Pose Library"]},"delete":{"operationId":"AdminPoseLibraryController_deleteEntry","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Soft-delete a pose","tags":["Admin Pose Library"]}},"/admin/pose-library/reseed":{"post":{"operationId":"AdminPoseLibraryController_reseedFromCode","parameters":[],"responses":{"201":{"description":""}},"summary":"Set default poses: replace the seed model library with the code seed and publish","tags":["Admin Pose Library"]}},"/admin/pose-library/{model}/publish":{"post":{"operationId":"AdminPoseLibraryController_publish","parameters":[{"name":"model","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Validate and publish a new library version","tags":["Admin Pose Library"]}},"/admin/pose-library/{model}/versions":{"get":{"operationId":"AdminPoseLibraryController_listVersions","parameters":[{"name":"model","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"List published versions for an image model","tags":["Admin Pose Library"]}},"/admin/pose-library/{model}/clone-from/{sourceModel}":{"post":{"operationId":"AdminPoseLibraryController_cloneFrom","parameters":[{"name":"model","required":true,"in":"path","schema":{"type":"string"}},{"name":"sourceModel","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Clone poses from another model (LoRA fields cleared, disabled)","tags":["Admin Pose Library"]}},"/admin/video-generation/generate":{"post":{"operationId":"AdminVideoGenerationController_generate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateVideoDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoGenerationTaskDto"}}}}},"summary":"Start video generation","tags":["Admin Video Generation"]}},"/admin/video-generation/history":{"get":{"operationId":"AdminVideoGenerationController_getHistory","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":20,"type":"number"}},{"name":"status","required":false,"in":"query","schema":{"enum":["created","processing","completed","failed"],"type":"string"}},{"name":"model","required":false,"in":"query","schema":{"type":"string","enum":["wavespeed-ai/wan-2.1/i2v-480p-lora","wavespeed-ai/wan-2.2/i2v-480p","wavespeed-ai/wan-2.2/i2v-480p-lora","kwaivgi/kling-v2.1-i2v-standard","kwaivgi/kling-v2.5-turbo-std/image-to-video","bytedance/seedance-v1-pro-i2v-720p"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoGenerationHistoryResponseDto"}}}}},"summary":"Get video generation history","tags":["Admin Video Generation"]}},"/admin/video-generation/{id}":{"get":{"operationId":"AdminVideoGenerationController_getById","parameters":[{"name":"id","required":true,"in":"path","schema":{"example":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoGenerationTaskDto"}}}}},"summary":"Get video generation by ID","tags":["Admin Video Generation"]}},"/admin/video-generation/{id}/poll":{"post":{"operationId":"AdminVideoGenerationController_pollStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"example":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoGenerationTaskDto"}}}}},"summary":"Poll external status and update","tags":["Admin Video Generation"]}},"/v1/admin/calls":{"get":{"operationId":"AdminCallsController_listCalls","parameters":[],"responses":{"200":{"description":"Calls retrieved"}},"summary":"List calls (paginated, newest first)","tags":["Admin - Calls"]}},"/v1/admin/calls/user/{userId}":{"get":{"operationId":"AdminCallsController_userHistory","parameters":[{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"User call history retrieved"}},"summary":"List a user's call history (paginated, newest first)","tags":["Admin - Calls"]}},"/v1/admin/calls/{id}":{"get":{"operationId":"AdminCallsController_getCall","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Call details retrieved"}},"summary":"Get a call detail with messages transcript","tags":["Admin - Calls"]}},"/v1/admin/calls/stats/overview":{"get":{"operationId":"AdminCallsController_getStats","parameters":[],"responses":{"200":{"description":"Calls statistics retrieved"}},"summary":"Get calls statistics (count, averages)","tags":["Admin - Calls"]}},"/admin/chat-summaries/{chatId}":{"post":{"operationId":"AdminChatSummariesController_create","parameters":[{"name":"chatId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["AdminChatSummaries"]},"get":{"operationId":"AdminChatSummariesController_list","parameters":[{"name":"chatId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["AdminChatSummaries"]}},"/admin/scenes":{"get":{"operationId":"AdminScenesController_getScenesList","parameters":[{"name":"page","required":false,"in":"query","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"search","required":false,"in":"query","schema":{"type":"string"}},{"name":"isPublic","required":false,"in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminScenesListResponseDto"}}}}},"summary":"Get scenes list with pagination","tags":["Admin - Scenes"]},"post":{"operationId":"AdminScenesController_createScene","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCreateSceneDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSceneResponseDto"}}}}},"summary":"Create a new scene","tags":["Admin - Scenes"]}},"/admin/scenes/{id}":{"get":{"operationId":"AdminScenesController_getSceneById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSceneResponseDto"}}}}},"summary":"Get scene by ID","tags":["Admin - Scenes"]},"put":{"operationId":"AdminScenesController_updateScene","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUpdateSceneDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSceneResponseDto"}}}}},"summary":"Update scene","tags":["Admin - Scenes"]},"delete":{"operationId":"AdminScenesController_deleteScene","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Scene deleted successfully"}},"summary":"Delete scene permanently","tags":["Admin - Scenes"]}},"/admin/scenes/{id}/cover-image":{"post":{"operationId":"AdminScenesController_uploadSceneCoverImage","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Upload scene cover image","tags":["Admin - Scenes"]}},"/admin/scenes/{id}/generate/cover":{"post":{"operationId":"AdminScenesController_generateCover","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminGenerateCoverDto"}}}},"responses":{"201":{"description":"Cover generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminGenerateCoverResponseDto"}}}},"404":{"description":"Scene not found"}},"summary":"Generate cover image for scene using AI","tags":["Admin - Scenes"]}},"/admin/scenes/{id}/generate/description":{"post":{"operationId":"AdminScenesController_generateDescription","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"Description generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminGenerateDescriptionResponseDto"}}}},"404":{"description":"Scene not found"}},"summary":"Generate description for scene using AI","tags":["Admin - Scenes"]}},"/admin/scenes/{id}/generate/context-prompt":{"post":{"operationId":"AdminScenesController_generateContextPrompt","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"Context prompt generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminGenerateContextPromptResponseDto"}}}},"404":{"description":"Scene not found"}},"summary":"Generate context prompt for scene using AI","tags":["Admin - Scenes"]}},"/admin/scenes/generate/cover":{"post":{"operationId":"AdminScenesController_generateCoverStandalone","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminGenerateCoverDto"}}}},"responses":{"201":{"description":"Cover generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminGenerateCoverResponseDto"}}}}},"summary":"Generate cover image using AI (standalone)","tags":["Admin - Scenes"]}},"/admin/scenes/generate/description":{"post":{"operationId":"AdminScenesController_generateDescriptionStandalone","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminGenerateFromDataDto"}}}},"responses":{"201":{"description":"Description generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminGenerateDescriptionResponseDto"}}}}},"summary":"Generate description from data using AI (standalone)","tags":["Admin - Scenes"]}},"/admin/scenes/generate/context-prompt":{"post":{"operationId":"AdminScenesController_generateContextPromptStandalone","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminGenerateFromDataDto"}}}},"responses":{"201":{"description":"Context prompt generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminGenerateContextPromptResponseDto"}}}}},"summary":"Generate context prompt from data using AI (standalone)","tags":["Admin - Scenes"]}},"/webhooks/admin-video/wavespeed":{"post":{"operationId":"AdminVideoWebhookController_handleWavespeedWebhook","parameters":[],"responses":{"200":{"description":""}},"summary":"Handle WaveSpeed webhook for admin video generation","tags":["Admin Video Webhooks"]}},"/admin/gallery/upload-url":{"post":{"operationId":"AdminGalleryController_generateUploadUrl","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateGalleryUploadUrlDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryUploadUrlResponseDto"}}}}},"summary":"Generate presigned upload URL for gallery image","tags":["Admin Gallery"]}},"/admin/gallery/{id}/confirm":{"post":{"operationId":"AdminGalleryController_confirmUpload","parameters":[{"name":"id","required":true,"in":"path","description":"Gallery image ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmGalleryUploadDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryImageDto"}}}}},"summary":"Confirm gallery image upload completed","tags":["Admin Gallery"]}},"/admin/gallery":{"get":{"operationId":"AdminGalleryController_list","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":10,"type":"number"}},{"name":"tags","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryListResponseDto"}}}}},"summary":"List gallery images","tags":["Admin Gallery"]}},"/admin/gallery/{id}":{"get":{"operationId":"AdminGalleryController_getById","parameters":[{"name":"id","required":true,"in":"path","description":"Gallery image ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryImageDto"}}}}},"summary":"Get gallery image by ID","tags":["Admin Gallery"]},"patch":{"operationId":"AdminGalleryController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Gallery image ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGalleryImageDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryImageDto"}}}}},"summary":"Update gallery image","tags":["Admin Gallery"]},"delete":{"operationId":"AdminGalleryController_delete","parameters":[{"name":"id","required":true,"in":"path","description":"Gallery image ID","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Delete gallery image","tags":["Admin Gallery"]}},"/admin/loras":{"get":{"operationId":"AdminLorasController_listLoras","parameters":[{"name":"prefix","required":false,"in":"query","description":"Filter by prefix/folder","schema":{"type":"string"}},{"name":"search","required":false,"in":"query","description":"Search by file name (case-insensitive partial match)","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LorasListResponseDto"}}}}},"summary":"List available LoRAs from S3 bucket","tags":["Admin LoRAs"]}},"/admin/loras/folders":{"get":{"operationId":"AdminLorasController_listFolders","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}},"summary":"List top-level folders in LoRAs bucket","tags":["Admin LoRAs"]}},"/admin/loras/presigned-url":{"get":{"operationId":"AdminLorasController_getPresignedUrl","parameters":[{"name":"key","required":true,"in":"query","description":"S3 key of the LoRA file","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoraPresignedUrlResponseDto"}}}}},"summary":"Get presigned URL for a LoRA file","tags":["Admin LoRAs"]}},"/admin/openrouter/models":{"get":{"operationId":"AdminOpenRouterController_getModels","parameters":[],"responses":{"200":{"description":""}},"summary":"List OpenRouter models (id + name) for the admin model pickers","tags":["Admin OpenRouter"]}},"/admin/openrouter/model-endpoints":{"get":{"operationId":"AdminOpenRouterController_getModelEndpoints","parameters":[{"name":"model","required":true,"in":"query","description":"OpenRouter model slug (url-encoded), e.g. deepseek%2Fdeepseek-v4-flash","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"List the providers that serve a model (base slugs for the provider-config picker)","tags":["Admin OpenRouter"]}},"/admin/chat-v3/policy-matrix":{"get":{"operationId":"AdminChatV3Controller_getPolicyMatrix","parameters":[],"responses":{"200":{"description":""}},"summary":"Effective chat-v3 message-type policy matrix (defaults + admin pass/drop overrides)","tags":["Admin Chat V3"]}},"/admin/push-notifications/test":{"post":{"description":"Sends a push notification with the avatar of the model from the user's most recent chat. Useful for testing push notification delivery with custom avatars.","operationId":"AdminPushNotificationsController_sendTestPush","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendTestPushDto"}}}},"responses":{"200":{"description":"Test push notification sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendTestPushResponseDto"}}}}},"summary":"Send test push notification to a user","tags":["Admin - Push Notifications"]}},"/admin/api-keys":{"get":{"operationId":"AdminApiKeysController_getApiKeys","parameters":[{"name":"isActive","required":false,"in":"query","schema":{"example":true}},{"name":"search","required":false,"in":"query","schema":{"example":"GPU"}},{"name":"limit","required":false,"in":"query","schema":{"example":10}},{"name":"page","required":false,"in":"query","schema":{"example":1}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeysListResponseDto"}}}}},"summary":"Get API keys list with pagination and filters","tags":["Admin API Keys"]},"post":{"operationId":"AdminApiKeysController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyResponseDto"}}}}},"summary":"Create a new API key","tags":["Admin API Keys"]}},"/admin/api-keys/{id}":{"get":{"operationId":"AdminApiKeysController_getById","parameters":[{"name":"id","required":true,"in":"path","description":"API key ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyItemDto"}}}}},"summary":"Get an API key by ID","tags":["Admin API Keys"]},"put":{"operationId":"AdminApiKeysController_update","parameters":[{"name":"id","required":true,"in":"path","description":"API key ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateApiKeyDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyItemDto"}}}}},"summary":"Update an API key","tags":["Admin API Keys"]},"delete":{"operationId":"AdminApiKeysController_delete","parameters":[{"name":"id","required":true,"in":"path","description":"API key ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteApiKeyResponseDto"}}}}},"summary":"Delete an API key permanently","tags":["Admin API Keys"]}},"/admin/api-keys/{id}/revoke":{"post":{"operationId":"AdminApiKeysController_revoke","parameters":[{"name":"id","required":true,"in":"path","description":"API key ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyItemDto"}}}}},"summary":"Revoke an API key (set isActive to false)","tags":["Admin API Keys"]}},"/admin/auth/users":{"get":{"operationId":"AdminUsersManagementController_list","parameters":[],"responses":{"200":{"description":""}},"summary":"List all admin users","tags":["Admin User Management"]},"post":{"operationId":"AdminUsersManagementController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAdminUserBody"}}}},"responses":{"201":{"description":""}},"summary":"Create a new admin user","tags":["Admin User Management"]}},"/admin/auth/users/{id}":{"patch":{"operationId":"AdminUsersManagementController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Admin user ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminUserBody"}}}},"responses":{"200":{"description":""}},"summary":"Update an admin user (username, role)","tags":["Admin User Management"]},"delete":{"operationId":"AdminUsersManagementController_remove","parameters":[{"name":"id","required":true,"in":"path","description":"Admin user ID","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Delete an admin user","tags":["Admin User Management"]}},"/admin/auth/users/{id}/password":{"patch":{"operationId":"AdminUsersManagementController_resetPassword","parameters":[{"name":"id","required":true,"in":"path","description":"Admin user ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordBody"}}}},"responses":{"204":{"description":""}},"summary":"Reset an admin user password","tags":["Admin User Management"]}},"/admin/audit-log":{"get":{"operationId":"AdminAuditLogController_list","parameters":[{"name":"page","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"string"}},{"name":"adminUserId","required":false,"in":"query","schema":{"type":"string"}},{"name":"resource","required":false,"in":"query","schema":{"type":"string"}},{"name":"action","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get audit logs with pagination and filters","tags":["Admin Audit Log"]}},"/admin/funnelfox-ltv/dashboard":{"get":{"operationId":"AdminFunnelFoxLtvController_getDashboardStats","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get dashboard stats: new subs, IAPs, MRR, active subs, auto-renewal","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/stats":{"get":{"operationId":"AdminFunnelFoxLtvController_getStats","parameters":[],"responses":{"200":{"description":""}},"summary":"Get summary statistics","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/transactions":{"get":{"operationId":"AdminFunnelFoxLtvController_getTransactions","parameters":[{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"offset","required":true,"in":"query","schema":{"type":"string"}},{"name":"search","required":true,"in":"query","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"psp","required":true,"in":"query","schema":{"type":"string"}},{"name":"pspTransactionType","required":true,"in":"query","schema":{"type":"string"}},{"name":"funnelType","required":true,"in":"query","schema":{"type":"string"}},{"name":"metaSubsId","required":true,"in":"query","schema":{"type":"string"}},{"name":"orderId","required":true,"in":"query","schema":{"type":"string"}},{"name":"webUserId","required":true,"in":"query","schema":{"type":"string"}},{"name":"pmCard","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"metaSubsIteration","required":true,"in":"query","schema":{"type":"string"}},{"name":"cohortDateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"cohortDateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":true,"in":"query","schema":{"type":"string"}},{"name":"sortDir","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get paginated transactions with filters","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/iap-transactions":{"get":{"operationId":"AdminFunnelFoxLtvController_getIapTransactions","parameters":[{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"offset","required":true,"in":"query","schema":{"type":"string"}},{"name":"search","required":true,"in":"query","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"psp","required":true,"in":"query","schema":{"type":"string"}},{"name":"pspTransactionType","required":true,"in":"query","schema":{"type":"string"}},{"name":"funnelType","required":true,"in":"query","schema":{"type":"string"}},{"name":"orderId","required":true,"in":"query","schema":{"type":"string"}},{"name":"webUserId","required":true,"in":"query","schema":{"type":"string"}},{"name":"pmCard","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":true,"in":"query","schema":{"type":"string"}},{"name":"sortDir","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get paginated IAP (one-off coin purchase) transactions","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/iap-country-breakdown":{"get":{"operationId":"AdminFunnelFoxLtvController_getIapCountryBreakdown","parameters":[{"name":"cacheDays","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get FunnelFox country distribution with server cache","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/filters":{"get":{"operationId":"AdminFunnelFoxLtvController_getFilterOptions","parameters":[],"responses":{"200":{"description":""}},"summary":"Get available filter options","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/cohorts":{"get":{"operationId":"AdminFunnelFoxLtvController_getCohorts","parameters":[{"name":"sample","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"funnelTypes","required":true,"in":"query","schema":{"type":"string"}},{"name":"productIds","required":true,"in":"query","schema":{"type":"string"}},{"name":"experiments","required":true,"in":"query","schema":{"type":"string"}},{"name":"countryBreakdown","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get cohort heatmap data","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/iap-cohorts":{"get":{"description":"Groups subscribers by subscription start date and tracks their coin purchase conversion over time periods (weeks/months after subscription).","operationId":"AdminFunnelFoxLtvController_getIapCohorts","parameters":[{"name":"sample","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"funnelTypes","required":true,"in":"query","schema":{"type":"string"}},{"name":"productIds","required":true,"in":"query","schema":{"type":"string"}},{"name":"experiments","required":true,"in":"query","schema":{"type":"string"}},{"name":"periodType","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get IAP (coin purchase) conversion cohort data","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/cumulative-ltv":{"get":{"operationId":"AdminFunnelFoxLtvController_getCumulativeLtv","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"viewBy","required":true,"in":"query","schema":{"type":"string"}},{"name":"calculateUsing","required":true,"in":"query","schema":{"type":"string"}},{"name":"funnelTypes","required":true,"in":"query","schema":{"type":"string"}},{"name":"productIds","required":true,"in":"query","schema":{"type":"string"}},{"name":"experiments","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get cumulative LTV chart data","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/retention":{"get":{"operationId":"AdminFunnelFoxLtvController_getRetention","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"funnelTypes","required":true,"in":"query","schema":{"type":"string"}},{"name":"productIds","required":true,"in":"query","schema":{"type":"string"}},{"name":"experiments","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get subscription retention data","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/sync":{"post":{"operationId":"AdminFunnelFoxLtvController_triggerSync","parameters":[{"name":"fullSync","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Trigger manual data sync from FunnelFox","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/sync-status":{"get":{"operationId":"AdminFunnelFoxLtvController_getSyncStatus","parameters":[],"responses":{"200":{"description":""}},"summary":"Get data sync status","tags":["Admin - FunnelFox LTV"]}},"/admin/funnelfox-ltv/resolve-user/{webUserId}":{"get":{"description":"Finds the app User by looking up the WebUser firebaseUid and matching it to the User firebaseUserId.","operationId":"AdminFunnelFoxLtvController_resolveWebUserToAppUser","parameters":[{"name":"webUserId","required":true,"in":"path","description":"Web User UUID from funnelfox transactions","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Resolve web user ID to app user","tags":["Admin - FunnelFox LTV"]}},"/admin/sales-ltv/stats":{"get":{"operationId":"AdminSalesLtvController_getStats","parameters":[],"responses":{"200":{"description":""}},"summary":"Revenue totals (gross + net), attempt counts and daily series","tags":["Admin - Sales LTV"]}},"/admin/sales-ltv/cohorts":{"get":{"operationId":"AdminSalesLtvController_getCohorts","parameters":[],"responses":{"200":{"description":""}},"summary":"Cohorts by first subscription purchase, with renewal iterations","tags":["Admin - Sales LTV"]}},"/admin/sales-ltv/cumulative-ltv":{"get":{"operationId":"AdminSalesLtvController_getCumulativeLtv","parameters":[],"responses":{"200":{"description":""}},"summary":"Cumulative ARPU/ARPPU curve of the cohort","tags":["Admin - Sales LTV"]}},"/admin/sales-ltv/retention":{"get":{"operationId":"AdminSalesLtvController_getRetention","parameters":[],"responses":{"200":{"description":""}},"summary":"Share of subscriptions still being charged at iteration N","tags":["Admin - Sales LTV"]}},"/admin/sales-ltv/auto-renew":{"get":{"operationId":"AdminSalesLtvController_getAutoRenew","parameters":[],"responses":{"200":{"description":""}},"summary":"Live auto-renew split (ON / OFF) from the provider state tables. Snapshot — no filters apply","tags":["Admin - Sales LTV"]}},"/admin/sales-ltv/mrr":{"get":{"operationId":"AdminSalesLtvController_getMrr","parameters":[],"responses":{"200":{"description":""}},"summary":"Current MRR split by billing cadence (weekly / monthly / quarterly / yearly). Snapshot — ignores the date range","tags":["Admin - Sales LTV"]}},"/admin/sales-ltv/transactions":{"get":{"operationId":"AdminSalesLtvController_getTransactions","parameters":[],"responses":{"200":{"description":""}},"summary":"Ledger rows with the same filters, paginated","tags":["Admin - Sales LTV"]}},"/admin/sales-ltv/filters":{"get":{"operationId":"AdminSalesLtvController_getFilterOptions","parameters":[],"responses":{"200":{"description":""}},"summary":"Filter options present in the ledger","tags":["Admin - Sales LTV"]}},"/admin/sales-ltv/costs":{"get":{"operationId":"AdminSalesLtvController_getCosts","parameters":[],"responses":{"200":{"description":""}},"summary":"Infra costs (AWS + RunPod + Wavespeed + Grok + OpenRouter) for the range; per-source cache shared with Revenue v2 and the legacy infra page","tags":["Admin - Sales LTV"]}},"/admin/sales-ltv/marketing-spend":{"get":{"operationId":"AdminSalesLtvController_getMarketingSpend","parameters":[],"responses":{"200":{"description":""}},"summary":"Marketing spend of the range from Marketing v2 analytics. Date range only — the ledger filters do not apply","tags":["Admin - Sales LTV"]}},"/admin/product-ltv/cohorts":{"get":{"operationId":"AdminProductLtvController_getCohorts","parameters":[],"responses":{"200":{"description":""}},"summary":"Paying cohorts with their product behaviour: chat, content purchases, generations, coins","tags":["Admin - Product LTV"]}},"/admin/product-ltv/users":{"get":{"operationId":"AdminProductLtvController_getUsers","parameters":[],"responses":{"200":{"description":""}},"summary":"The cohort member by member, with every counter — paginated","tags":["Admin - Product LTV"]}},"/admin/product-ltv/filters":{"get":{"operationId":"AdminProductLtvController_getFilterOptions","parameters":[],"responses":{"200":{"description":""}},"summary":"Filter options present in the ledger (shared with Sales LTV)","tags":["Admin - Product LTV"]}},"/admin/marketing-v2/analytics/cohorts":{"get":{"operationId":"AdminMarketingCohortsController_getCohorts","parameters":[],"responses":{"200":{"description":""}},"summary":"Sales LTV cohorts joined with the period ad spend (CAC / ROAS per bucket)","tags":["Admin - Marketing v2"]}},"/admin/coin-economy/summary":{"get":{"operationId":"AdminCoinEconomyController_getSummary","parameters":[],"responses":{"200":{"description":""}},"summary":"KPI totals, per-feature breakdown and valuation coverage","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/timeseries":{"get":{"operationId":"AdminCoinEconomyController_getTimeseries","parameters":[],"responses":{"200":{"description":""}},"summary":"Spend over time, optionally split by feature","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/per-user-trend":{"get":{"operationId":"AdminCoinEconomyController_getPerUserTrend","parameters":[],"responses":{"200":{"description":""}},"summary":"Rolling spend per user — separates \"more users\" from \"users spending more\"","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/heatmap":{"get":{"operationId":"AdminCoinEconomyController_getHeatmap","parameters":[],"responses":{"200":{"description":""}},"summary":"Day-of-week × hour spend grid (UTC)","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/models":{"get":{"operationId":"AdminCoinEconomyController_getModels","parameters":[],"responses":{"200":{"description":""}},"summary":"Revenue per model with a per-feature split","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/items":{"get":{"operationId":"AdminCoinEconomyController_getItems","parameters":[],"responses":{"200":{"description":""}},"summary":"Revenue per individual sellable unit","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/price-ladder":{"get":{"operationId":"AdminCoinEconomyController_getPriceLadder","parameters":[],"responses":{"200":{"description":""}},"summary":"Revenue grouped by the item list price","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/users":{"get":{"operationId":"AdminCoinEconomyController_getUsers","parameters":[],"responses":{"200":{"description":""}},"summary":"Top spenders with their effective coin rate","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/users.csv":{"get":{"operationId":"AdminCoinEconomyController_exportUsers","parameters":[],"responses":{"200":{"description":""}},"summary":"Top spenders as CSV (capped at 10k rows)","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/breakdown":{"get":{"operationId":"AdminCoinEconomyController_getBreakdown","parameters":[],"responses":{"200":{"description":""}},"summary":"Spend by country / funnel / platform / tier / bucket","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/repeat":{"get":{"operationId":"AdminCoinEconomyController_getRepeat","parameters":[],"responses":{"200":{"description":""}},"summary":"Repeat-purchase distribution for a feature","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/margin":{"get":{"operationId":"AdminCoinEconomyController_getMargin","parameters":[],"responses":{"200":{"description":""}},"summary":"Revenue vs cost per feature (actual LLM spend + unit estimates)","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/rates":{"get":{"operationId":"AdminCoinEconomyController_getRates","parameters":[],"responses":{"200":{"description":""}},"summary":"How coins were converted to dollars: rates, catalog, coverage","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/ledger-check":{"get":{"operationId":"AdminCoinEconomyController_getLedgerCheck","parameters":[],"responses":{"200":{"description":""}},"summary":"Credited − spent + refunded − expired vs stored balances","tags":["Admin - Coin Economy"]}},"/admin/coin-economy/filters":{"get":{"operationId":"AdminCoinEconomyController_getFilterOptions","parameters":[],"responses":{"200":{"description":""}},"summary":"Filter options for the page","tags":["Admin - Coin Economy"]}},"/admin/billing-products/coin-products":{"get":{"operationId":"AdminBillingProductsController_listCoinProducts","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CoinProductItemDto"}}}}}},"summary":"List coin products for admin management","tags":["Admin Billing Products"]},"post":{"operationId":"AdminBillingProductsController_createCoinProduct","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCoinProductDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoinProductItemDto"}}}}},"summary":"Create coin product","tags":["Admin Billing Products"]}},"/admin/billing-products/subscription-products":{"get":{"operationId":"AdminBillingProductsController_listSubscriptionProducts","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubscriptionProductItemDto"}}}}}},"summary":"List subscription products for admin management","tags":["Admin Billing Products"]},"post":{"operationId":"AdminBillingProductsController_createSubscriptionProduct","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionProductDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionProductItemDto"}}}}},"summary":"Create subscription product","tags":["Admin Billing Products"]}},"/admin/billing-products/web-coin-products":{"get":{"operationId":"AdminBillingProductsController_listWebCoinProducts","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebCoinProductItemDto"}}}}}},"summary":"List web coin products","tags":["Admin Billing Products"]},"post":{"operationId":"AdminBillingProductsController_createWebCoinProduct","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebCoinProductDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebCoinProductItemDto"}}}}},"summary":"Create web coin product","tags":["Admin Billing Products"]}},"/admin/billing-products/web-coin-products/{id}":{"patch":{"operationId":"AdminBillingProductsController_updateWebCoinProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebCoinProductDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebCoinProductItemDto"}}}}},"summary":"Update web coin product","tags":["Admin Billing Products"]},"delete":{"operationId":"AdminBillingProductsController_deleteWebCoinProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"summary":"Delete web coin product","tags":["Admin Billing Products"]}},"/admin/billing-products/stripe/products":{"get":{"operationId":"AdminBillingProductsController_listStripeProducts","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StripeProductDto"}}}}}},"summary":"List Stripe products","tags":["Admin Billing Products"]},"post":{"operationId":"AdminBillingProductsController_createStripeProduct","parameters":[],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StripeProductDto"}}}}},"summary":"Create Stripe product","tags":["Admin Billing Products"]}},"/admin/billing-products/stripe/products/{id}":{"patch":{"operationId":"AdminBillingProductsController_updateStripeProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StripeProductDto"}}}}},"summary":"Update Stripe product","tags":["Admin Billing Products"]}},"/admin/billing-products/stripe/prices":{"get":{"operationId":"AdminBillingProductsController_listStripePrices","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StripePriceDto"}}}}}},"summary":"List Stripe recurring prices","tags":["Admin Billing Products"]},"post":{"operationId":"AdminBillingProductsController_createStripePrice","parameters":[],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StripePriceDto"}}}}},"summary":"Create Stripe recurring price","tags":["Admin Billing Products"]}},"/admin/billing-products/stripe/prices/{id}":{"patch":{"operationId":"AdminBillingProductsController_updateStripePrice","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StripePriceDto"}}}}},"summary":"Update Stripe price mutable fields","tags":["Admin Billing Products"]}},"/admin/billing-products/funnelfox/price-points":{"get":{"operationId":"AdminBillingProductsController_listFunnelFoxPricePoints","parameters":[{"name":"ident","required":false,"in":"query","description":"Filter by price point identifier","schema":{"example":"monthly-premium","type":"string"}},{"name":"currencyCode","required":false,"in":"query","description":"Filter by currency code","schema":{"example":"USD","type":"string"}},{"name":"featureIdent","required":false,"in":"query","description":"Filter by feature identifier","schema":{"example":"premium-access","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PricePointDto"}}}}}},"summary":"List FunnelFox price points","tags":["Admin Billing Products"]},"post":{"operationId":"AdminBillingProductsController_createFunnelFoxPricePoint","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePricePointDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PricePointDto"}}}}},"summary":"Create FunnelFox price point","tags":["Admin Billing Products"]}},"/admin/billing-products/funnelfox/price-points/localize":{"post":{"operationId":"AdminBillingProductsController_localizeFunnelFoxPricePoints","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocalizePricePointsDto"}}}},"responses":{"200":{"description":""}},"summary":"Generate per-currency variants of base USD price points (dry-run by default)","tags":["Admin Billing Products"]}},"/admin/billing-products/funnelfox/price-points/{ident}":{"patch":{"operationId":"AdminBillingProductsController_updateFunnelFoxPricePoint","parameters":[{"name":"ident","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePricePointDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PricePointDto"}}}}},"summary":"Update FunnelFox price point","tags":["Admin Billing Products"]}},"/admin/billing-products/funnelfox/features":{"get":{"operationId":"AdminBillingProductsController_listFunnelFoxFeatures","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListFeaturesResponseDto"}}}}},"summary":"List FunnelFox features","tags":["Admin Billing Products"]},"post":{"operationId":"AdminBillingProductsController_createFunnelFoxFeature","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFeatureDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeatureDto"}}}}},"summary":"Create FunnelFox feature","tags":["Admin Billing Products"]}},"/admin/mcp/web-subscription-products":{"get":{"operationId":"AdminMcpWebSubscriptionProductsController_list","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebSubscriptionProductItemDto"}}}}}},"summary":"List all web subscription products (full DB shape)","tags":["Admin MCP — Web subscription products"]},"post":{"operationId":"AdminMcpWebSubscriptionProductsController_create","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebSubscriptionProductDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebSubscriptionProductItemDto"}}}}},"summary":"Create a web subscription product row","tags":["Admin MCP — Web subscription products"]}},"/admin/mcp/web-subscription-products/{id}":{"patch":{"operationId":"AdminMcpWebSubscriptionProductsController_update","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebSubscriptionProductDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebSubscriptionProductItemDto"}}}}},"summary":"Update a web subscription product row","tags":["Admin MCP — Web subscription products"]}},"/admin/mcp/support/users":{"get":{"operationId":"AdminMcpSupportController_findUsers","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"search","required":true,"in":"query","schema":{"example":"user@example.com","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"example":10,"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Find users by email (partial) or Firebase UID (exact)","tags":["Admin MCP — Support"]}},"/admin/mcp/support/search/email":{"get":{"description":"Partial, case-insensitive match on email against both the `users` and `web_users` tables. Returns each side separately, each row carrying a normalized firebaseUserId (users.firebaseUserId / web_users.firebaseUid). Intended for a debounced support search box.","operationId":"AdminMcpSupportController_searchByEmail","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"email","required":true,"in":"query","schema":{"example":"abc","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"example":20,"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Flexible email search across app users and web users","tags":["Admin MCP — Support"]}},"/admin/mcp/support/funnelfox-transactions/by-card":{"get":{"description":"Locate FunnelFox transactions for a card without the full PAN. `first6` is matched as a prefix against the stored BIN and `last4` as a suffix against the stored card tail, so any number of digits (1..6 / 1..4) narrows the search. At least one of `first6` / `last4` is required. Results are newest-first and paginated.","operationId":"AdminMcpSupportController_searchTransactionsByCard","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"first6","required":false,"in":"query","description":"Leading card digits (BIN), 1-6 digits","schema":{"example":"526220","type":"string"}},{"name":"last4","required":false,"in":"query","description":"Trailing card digits, 1-4 digits","schema":{"example":"4369","type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter by transaction status (e.g. settled, declined)","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"example":1,"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"example":20,"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Find FunnelFox transactions by a partial card number","tags":["Admin MCP — Support"]}},"/admin/mcp/support/users/by-firebase-uid/{firebaseUid}":{"get":{"operationId":"AdminMcpSupportController_getUserByFirebaseUid","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"firebaseUid","required":true,"in":"path","schema":{"example":"abc123firebaseUid","type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get user by Firebase UID","tags":["Admin MCP — Support"]}},"/admin/mcp/support/users/{id}/subscription":{"get":{"operationId":"AdminMcpSupportController_getUserSubscription","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get user subscription data","tags":["Admin MCP — Support"]}},"/admin/mcp/support/users/{id}/v2/subscription":{"get":{"description":"Retrieve the user's user_subscriptions row (tier/plan resolved, status, provider, billing period, auto-renewal) together with the live two-bucket coin balance (quota + purchased + total).","operationId":"AdminMcpSupportController_getV2Subscription","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get V2 (quota-model) subscription and coin balance","tags":["Admin MCP — Support"]}},"/admin/mcp/support/users/{id}/coins":{"get":{"description":"Aggregated coin figures (deposited, spent, purchases, refunds, per-feature spend) plus the current balance and, when v2 quota is enabled, the live two-bucket breakdown (monthly quota vs bonus).","operationId":"AdminMcpSupportController_getUserCoins","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get user coin balance and spend analytics (v2-aware)","tags":["Admin MCP — Support"]}},"/admin/mcp/support/users/{id}/autorenew":{"post":{"deprecated":true,"description":"FunnelFox-only by construction. For anything provider-agnostic use POST /admin/mcp/billing/users/:userId/autorenew, which dispatches on the user's actual provider. Kept because the support dashboard still drives FunnelFox subscriptions through this route.","operationId":"AdminMcpSupportController_toggleAutorenew","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleFunnelFoxAutorenewDtoClass"}}}},"responses":{"201":{"description":""}},"summary":"Toggle FunnelFox subscription auto-renewal","tags":["Admin MCP — Support"]}},"/admin/mcp/support/users/{id}/discount":{"post":{"operationId":"AdminMcpSupportController_applyDiscount","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyFunnelFoxDiscountDtoClass"}}}},"responses":{"201":{"description":""}},"summary":"Apply discount to FunnelFox subscription","tags":["Admin MCP — Support"]}},"/admin/mcp/billing/capabilities":{"get":{"description":"Static matrix the support UI gates its buttons on. `autorenew` can be true for a provider whose payments are handled elsewhere — it rides the older subscription-management abstraction.","operationId":"AdminMcpBillingController_capabilities","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Billing operations available per payment provider","tags":["Admin MCP — Billing"]}},"/admin/mcp/billing/users/{userId}/overview":{"get":{"description":"Returns which provider bills the user, how that was resolved (subscription row / pinned assignment / default), what this surface can do for them, and the current subscription — expired ones included, since most support tickets are about a subscription that already ended.","operationId":"AdminMcpBillingController_getOverview","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}}],"responses":{"200":{"description":""}},"summary":"A user's payment provider and subscription state","tags":["Admin MCP — Billing"]}},"/admin/mcp/billing/users/{userId}/payments":{"get":{"description":"Charges with their refund state, newest first. Each row carries a composite `<provider>:<id>` id — the only id the refund endpoint accepts. Providers served elsewhere return an empty page with `supported: false` rather than an error.","operationId":"AdminMcpBillingController_listPayments","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}},{"name":"page","required":false,"in":"query","description":"1-based page number","schema":{"example":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Rows per page (max 100)","schema":{"example":20,"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Unified payment history","tags":["Admin MCP — Billing"]}},"/admin/mcp/billing/users/{userId}/payments/{paymentId}":{"get":{"description":"404 when the id is unknown OR belongs to another user — ownership is enforced inside the query, so the two cases are intentionally indistinguishable.","operationId":"AdminMcpBillingController_getPayment","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}},{"name":"paymentId","required":true,"in":"path","schema":{"example":"upgate:9f1b2c3d-...","type":"string"}}],"responses":{"200":{"description":""}},"summary":"One payment of a user","tags":["Admin MCP — Billing"]}},"/admin/mcp/billing/users/{userId}/payments/{paymentId}/refund":{"post":{"description":"Routed by the id prefix, so it works regardless of which orchestrator ran the original charge. A `pending` status is NOT success — the gateway still has to confirm it. `accessRevocation` reports what actually happened to the subscription afterwards; a refund that does not cover the current period returns \"skipped\".","operationId":"AdminMcpBillingController_refundPayment","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}},{"name":"paymentId","required":true,"in":"path","schema":{"example":"upgate:9f1b2c3d-...","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingRefundDtoClass"}}}},"responses":{"200":{"description":""}},"summary":"Refund a payment (full or partial)","tags":["Admin MCP — Billing"]}},"/admin/mcp/billing/users/{userId}/suspend":{"post":{"description":"Stops future charges at whichever provider holds the schedule AND cuts app access immediately — no waiting for the paid period to lapse. No money moves: pair it with a refund when the customer is getting paid back. `providerError` means the provider call failed; access was still revoked.","operationId":"AdminMcpBillingController_suspendSubscription","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSuspendDtoClass"}}}},"responses":{"200":{"description":""}},"summary":"End the subscription now, whatever the provider","tags":["Admin MCP — Billing"]}},"/admin/mcp/billing/users/{userId}/autorenew":{"post":{"description":"Runs the same handlers as the user-facing endpoint, so the provider call, the local flags and the pending renewal reminders stay consistent. When the host subscription has already lapsed but self-billing still retries the renewal, disabling falls back to the stop-crane and reports the stopped subscriptions in `collectionStopped` (GEN-1571). 404 only when nothing is live on either side.","operationId":"AdminMcpBillingController_setAutorenew","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Dedicated MCP automation key (env MCP_API_KEY)","required":true,"schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"example":"user-uuid-here","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingAutorenewDtoClass"}}}},"responses":{"200":{"description":""}},"summary":"Enable or disable auto-renewal, whatever the provider","tags":["Admin MCP — Billing"]}},"/admin/mcp/marketing/dimensions":{"get":{"operationId":"AdminMcpMarketingController_getDimensions","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Allowed dimensions, metrics, presets and metric semantics + contract version","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/overview":{"get":{"operationId":"AdminMcpMarketingController_getOverview","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"preset","required":false,"in":"query","description":"Named window, ends yesterday. One of: yesterday, last_7d, last_14d, last_28d, last_30d, last_90d, this_month, last_month, lifetime","schema":{"example":"last_7d","type":"string"}},{"name":"from","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Period state in one call: totals vs previous window, accounts, best/worst concepts, health","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/summary":{"get":{"operationId":"AdminMcpMarketingController_getSummary","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"preset","required":false,"in":"query","description":"Named window, ends yesterday. One of: yesterday, last_7d, last_14d, last_28d, last_30d, last_90d, this_month, last_month, lifetime","schema":{"example":"last_7d","type":"string"}},{"name":"from","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Period totals only","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/rows":{"get":{"operationId":"AdminMcpMarketingController_getRows","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"preset","required":false,"in":"query","description":"Named window, ends yesterday. One of: yesterday, last_7d, last_14d, last_28d, last_30d, last_90d, this_month, last_month, lifetime","schema":{"example":"last_7d","type":"string"}},{"name":"from","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"groupBy","required":false,"in":"query","description":"CSV of dimensions to group by: account, campaign, adset, creative, concept, country","schema":{"example":"concept","type":"string"}},{"name":"expandBy","required":false,"in":"query","description":"Second grouping level nested under each row","schema":{"type":"string"}},{"name":"country","required":false,"in":"query","description":"ISO-3166 alpha-2 filter","schema":{"type":"string"}},{"name":"metrics","required":false,"in":"query","description":"Metric preset (scan/delivery/fb/ledger/video/all) or CSV of metric names","schema":{"example":"scan","type":"string"}},{"name":"sort","required":false,"in":"query","description":"Metric to sort by","schema":{"type":"string"}},{"name":"order","required":false,"in":"query","description":"asc | desc","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Rows returned (max 200)","schema":{"type":"number"}},{"name":"offset","required":false,"in":"query","schema":{"type":"number"}},{"name":"minSpend","required":false,"in":"query","description":"Drop rows below this spend","schema":{"type":"number"}},{"name":"search","required":false,"in":"query","description":"Case-insensitive substring match on the row label","schema":{"type":"string"}},{"name":"includeEmpty","required":false,"in":"query","description":"Include buckets carrying only web users","schema":{"type":"boolean"}}],"responses":{"200":{"description":""}},"summary":"Sorted, capped and projected cut by account/campaign/adset/creative/concept/country","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/dashboard":{"get":{"operationId":"AdminMcpMarketingController_getDashboard","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"preset","required":false,"in":"query","description":"Named window, ends yesterday. One of: yesterday, last_7d, last_14d, last_28d, last_30d, last_90d, this_month, last_month, lifetime","schema":{"example":"last_7d","type":"string"}},{"name":"from","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"depth","required":false,"in":"query","description":"concept | variant | localization","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Top-level nodes returned","schema":{"type":"number"}},{"name":"sort","required":false,"in":"query","schema":{"type":"string"}},{"name":"minSpend","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Creative repository tree (concept → variant → localization) with metrics, depth-limited","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/accounts":{"get":{"operationId":"AdminMcpMarketingController_getAccounts","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"preset","required":false,"in":"query","description":"Named window, ends yesterday. One of: yesterday, last_7d, last_14d, last_28d, last_30d, last_90d, this_month, last_month, lifetime","schema":{"example":"last_7d","type":"string"}},{"name":"from","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"sort","required":false,"in":"query","schema":{"type":"string"}},{"name":"order","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Per-ad-account breakdown of the period","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/insights":{"get":{"operationId":"AdminMcpMarketingController_getInsights","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"preset","required":false,"in":"query","description":"Named window, ends yesterday. One of: yesterday, last_7d, last_14d, last_28d, last_30d, last_90d, this_month, last_month, lifetime","schema":{"example":"last_7d","type":"string"}},{"name":"from","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"sort","required":false,"in":"query","schema":{"type":"string"}},{"name":"order","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Ad-level rows (reduced metric set), ranked within top-N by spend","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/spend-by-bucket":{"get":{"operationId":"AdminMcpMarketingController_getSpendByBucket","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"preset","required":false,"in":"query","description":"Named window, ends yesterday. One of: yesterday, last_7d, last_14d, last_28d, last_30d, last_90d, this_month, last_month, lifetime","schema":{"example":"last_7d","type":"string"}},{"name":"from","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"granularity","required":true,"in":"query","description":"daily | weekly | monthly | fixed","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Ad spend split into calendar buckets","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/video-retention":{"get":{"operationId":"AdminMcpMarketingController_getVideoRetention","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"preset","required":false,"in":"query","description":"Named window, ends yesterday. One of: yesterday, last_7d, last_14d, last_28d, last_30d, last_90d, this_month, last_month, lifetime","schema":{"example":"last_7d","type":"string"}},{"name":"from","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"dimension","required":true,"in":"query","description":"creative | concept","schema":{"type":"string"}},{"name":"id","required":true,"in":"query","description":"Creative id or concept code","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Video play-through curve, hook/hold rates and watch quartiles","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/health-signals":{"get":{"operationId":"AdminMcpMarketingController_getHealthSignals","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Live integration health — disconnected accounts, dead pixels, stale or failing syncs","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/creatives":{"get":{"operationId":"AdminMcpMarketingController_getCreatives","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"search","required":false,"in":"query","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","description":"draft | review | live | rejected | archived","schema":{"type":"string"}},{"name":"conceptCode","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"offset","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Flat, paginated creative repository (concept/variant/locale ids)","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/creatives/{localizationId}":{"get":{"operationId":"AdminMcpMarketingController_getCreativeDetail","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"localizationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"One localization with its media, drafts and linked ads","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/creatives/{localizationId}/download-url":{"get":{"operationId":"AdminMcpMarketingController_getCreativeDownload","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"localizationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Presigned S3 links (15 min) for a creative's media file and poster","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/cohorts":{"get":{"operationId":"AdminMcpMarketingController_getCohorts","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"preset","required":false,"in":"query","description":"Named window, ends yesterday. One of: yesterday, last_7d, last_14d, last_28d, last_30d, last_90d, this_month, last_month, lifetime","schema":{"example":"last_7d","type":"string"}},{"name":"from","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"sample","required":true,"in":"query","description":"daily | weekly | monthly | fixed","schema":{"type":"string"}},{"name":"breakdown","required":true,"in":"query","description":"CSV of funnel, product, country, experiment","schema":{"type":"string"}},{"name":"funnelTypes","required":false,"in":"query","schema":{"type":"string"}},{"name":"productIdents","required":false,"in":"query","schema":{"type":"string"}},{"name":"countries","required":false,"in":"query","schema":{"type":"string"}},{"name":"experiments","required":false,"in":"query","description":"CSV of experimentKey:variantId pairs","schema":{"type":"string"}},{"name":"maxIterations","required":false,"in":"query","description":"Renewal iterations rendered (1..24)","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Purchase cohorts joined with period ad spend (CAC / ROAS / renewal retention)","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/ltv":{"get":{"operationId":"AdminMcpMarketingController_getLtvReport","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"preset","required":false,"in":"query","description":"Named window, ends yesterday. One of: yesterday, last_7d, last_14d, last_28d, last_30d, last_90d, this_month, last_month, lifetime","schema":{"example":"last_7d","type":"string"}},{"name":"from","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"UTC yyyy-MM-dd","schema":{"type":"string"}},{"name":"report","required":true,"in":"query","description":"One of stats, cohorts, cumulative_ltv, retention, mrr, auto_renew, infra_costs, transactions","schema":{"type":"string"}},{"name":"revenueMode","required":false,"in":"query","description":"gross | net (default net)","schema":{"type":"string"}},{"name":"funnelTypes","required":false,"in":"query","schema":{"type":"string"}},{"name":"productIdents","required":false,"in":"query","schema":{"type":"string"}},{"name":"countries","required":false,"in":"query","schema":{"type":"string"}},{"name":"experiments","required":false,"in":"query","schema":{"type":"string"}},{"name":"sample","required":false,"in":"query","description":"cohorts only","schema":{"type":"string"}},{"name":"breakdown","required":false,"in":"query","description":"cohorts only","schema":{"type":"string"}},{"name":"viewBy","required":false,"in":"query","description":"cumulative_ltv only","schema":{"type":"string"}},{"name":"calculateUsing","required":false,"in":"query","description":"cumulative_ltv only","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"transactions only","schema":{"type":"number"}},{"name":"offset","required":false,"in":"query","description":"transactions only","schema":{"type":"number"}},{"name":"search","required":false,"in":"query","description":"transactions only","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Ledger-side reports: stats, cohorts, cumulative LTV, retention, MRR, auto-renew, infra costs, transactions","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/meta-insights":{"get":{"operationId":"AdminMcpMarketingController_getMetaInsights","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped read-only marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"level","required":true,"in":"query","description":"account | campaign | adset | ad","schema":{"type":"string"}},{"name":"accountId","required":false,"in":"query","schema":{"type":"string"}},{"name":"allAccounts","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"fields","required":true,"in":"query","description":"CSV of spend, impressions, clicks, reach, frequency, cpm, cpc, cpp, ctr, unique_clicks, inline_link_clicks, actions, action_values, website_purchase_roas","schema":{"type":"string"}},{"name":"breakdowns","required":true,"in":"query","description":"CSV of country, region, dma, age, gender, publisher_platform, platform_position, device_platform","schema":{"type":"string"}},{"name":"datePreset","required":false,"in":"query","schema":{"type":"string"}},{"name":"dateFrom","required":false,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":false,"in":"query","schema":{"type":"string"}},{"name":"timeIncrement","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Raw Facebook insights proxy — only for breakdowns we do not store (age, gender, placement, region)","tags":["Admin MCP — Marketing"]}},"/admin/mcp/marketing/creatives/{localizationId}/status":{"post":{"description":"Sets the localization status (draft | review | live | rejected | archived). Same effect as dragging the card on the admin board; audited as actor \"mcp\".","operationId":"AdminMcpMarketingActionsController_setCreativeStatus","parameters":[{"name":"x-mcp-api-key","in":"header","description":"Scoped marketing key (env MCP_MARKETING_API_KEY; the master MCP_API_KEY also works)","required":true,"schema":{"type":"string"}},{"name":"localizationId","required":true,"in":"path","schema":{"example":"localization-uuid-here","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpSetCreativeStatusDtoClass"}}}},"responses":{"200":{"description":""}},"summary":"Move a creative between Media Library board columns","tags":["Admin MCP — Marketing"]}},"/admin/notifications/broadcast":{"post":{"description":"Enqueues a broadcast notification job. Provide localized content per locale with en-US as required fallback.","operationId":"AdminNotificationsController_broadcast","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BroadcastNotificationDto"}}}},"responses":{"200":{"description":"Broadcast enqueued","content":{"application/json":{"schema":{"properties":{"jobId":{"type":"string"},"message":{"type":"string"}}}}}}},"summary":"Broadcast notification to all users","tags":["Admin - Notifications"]}},"/admin/funnel-configs":{"get":{"operationId":"AdminFunnelConfigsController_list","parameters":[{"name":"search","required":false,"in":"query","schema":{"example":"hot"}},{"name":"limit","required":false,"in":"query","schema":{"example":10}},{"name":"page","required":false,"in":"query","schema":{"example":1}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunnelConfigsListResponseDto"}}}}},"summary":"List funnel configs with pagination","tags":["Admin Funnel Configs"]},"post":{"operationId":"AdminFunnelConfigsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFunnelConfigDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunnelConfigItemDto"}}}}},"summary":"Create a new funnel config","tags":["Admin Funnel Configs"]}},"/admin/funnel-configs/{id}":{"get":{"operationId":"AdminFunnelConfigsController_getById","parameters":[{"name":"id","required":true,"in":"path","description":"Funnel config UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunnelConfigItemDto"}}}}},"summary":"Get funnel config by ID","tags":["Admin Funnel Configs"]},"put":{"operationId":"AdminFunnelConfigsController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Funnel config UUID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFunnelConfigDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunnelConfigItemDto"}}}}},"summary":"Update a funnel config by ID","tags":["Admin Funnel Configs"]},"delete":{"operationId":"AdminFunnelConfigsController_delete","parameters":[{"name":"id","required":true,"in":"path","description":"Funnel config UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteFunnelConfigResponseDto"}}}}},"summary":"Delete a funnel config by ID","tags":["Admin Funnel Configs"]}},"/admin/static-files":{"get":{"operationId":"AdminStaticFilesController_listFiles","parameters":[{"name":"maxKeys","required":false,"in":"query","schema":{"example":100,"type":"string"}},{"name":"nextToken","required":false,"in":"query","schema":{"type":"string"}},{"name":"prefix","required":false,"in":"query","schema":{"example":"static/onboarding"}}],"responses":{"200":{"description":""}},"summary":"List static files from S3","tags":["Admin Static Files"]},"delete":{"operationId":"AdminStaticFilesController_deleteFile","parameters":[],"responses":{"200":{"description":""}},"summary":"Delete a static file from S3","tags":["Admin Static Files"]}},"/admin/static-files/upload-url":{"post":{"operationId":"AdminStaticFilesController_generateUploadUrl","parameters":[],"responses":{"200":{"description":""}},"summary":"Generate presigned upload URL for static file","tags":["Admin Static Files"]}},"/admin/model-content/upload-url":{"post":{"operationId":"AdminModelContentController_generateUploadUrl","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateModelContentUploadUrlDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelContentUploadUrlResponseDto"}}}}},"summary":"Generate upload URL for model content media","tags":["Admin Model Content"]}},"/admin/model-content/confirm-upload":{"post":{"operationId":"AdminModelContentController_confirmUpload","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmModelContentUploadDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelContentUploadConfirmResponseDto"}}}}},"summary":"Confirm uploaded model content media key","tags":["Admin Model Content"]}},"/admin/model-content/locked-covers/backfill":{"post":{"description":"Media uploaded before the lockedCoverS3Key column existed has no teaser cover, so the Private Content catalog falls back to the 128px nsfw thumbnail. Use dryRun to see how many rows would be touched before spending S3 time on it.","operationId":"AdminModelContentController_backfillLockedCovers","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackfillModelContentLockedCoversDto"}}}},"responses":{"202":{"description":""}},"summary":"Generate pre-blurred locked covers for existing media","tags":["Admin Model Content"]}},"/admin/model-content/video-durations/backfill":{"post":{"description":"Video uploaded before confirmUpload started probing durations left model_content_media.duration NULL, so a pack sums to 0 seconds and the card hides the duration badge. Runs in batches — use dryRun first to see how many rows are pending, then repeat until `remaining` is 0.","operationId":"AdminModelContentController_backfillVideoDurations","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackfillModelContentVideoDurationsDto"}}}},"responses":{"202":{"description":""}},"summary":"Measure the runtime of video media that has none","tags":["Admin Model Content"]}},"/admin/model-content/packs":{"get":{"operationId":"AdminModelContentController_listPacks","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":20,"type":"number"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]}},{"name":"modelId","required":false,"in":"query","description":"Restrict to a single model.","schema":{"type":"string"}},{"name":"search","required":false,"in":"query","description":"Matches pack title or model name.","schema":{"type":"string"}},{"name":"hasPreviewVideo","required":false,"in":"query","description":"Only packs that have (or miss) a teaser video.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminPacksListResponseDto"}}}}},"summary":"List packs across every model for the Content > Packs tab","tags":["Admin Model Content"]}},"/admin/model-content/models/{modelId}/content":{"get":{"operationId":"AdminModelContentController_listContent","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":20,"type":"number"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminModelContentListResponseDto"}}}}},"summary":"List model posts and packs for admin","tags":["Admin Model Content"]}},"/admin/model-content/models/{modelId}/migration-snapshot":{"get":{"description":"Pass type=PACK to export packs alone (stories and the feed stay out of it).","operationId":"AdminModelContentController_getMigrationSnapshot","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}},{"name":"type","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminModelContentMigrationSnapshotDto"}}}}},"summary":"Get model media content migration snapshot","tags":["Admin Model Content"]}},"/admin/model-content/migration/apply":{"post":{"operationId":"AdminModelContentController_applyMigrationSnapshot","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyAdminModelContentMigrationDto"}}}},"responses":{"201":{"description":""}},"summary":"Apply model media content migration snapshot by public model name","tags":["Admin Model Content"]}},"/admin/model-content/content":{"post":{"operationId":"AdminModelContentController_createContent","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAdminModelContentDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminModelContentViewDto"}}}}},"summary":"Create model post or pack","tags":["Admin Model Content"]}},"/admin/model-content/content/{contentId}":{"get":{"operationId":"AdminModelContentController_getContent","parameters":[{"name":"contentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminModelContentViewDto"}}}}},"summary":"Get model post or pack for admin","tags":["Admin Model Content"]},"patch":{"operationId":"AdminModelContentController_updateContent","parameters":[{"name":"contentId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminModelContentDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminModelContentViewDto"}}}}},"summary":"Update model post or pack","tags":["Admin Model Content"]},"delete":{"operationId":"AdminModelContentController_deleteContent","parameters":[{"name":"contentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Delete model post or pack","tags":["Admin Model Content"]}},"/admin/model-content/content/{contentId}/publish":{"post":{"operationId":"AdminModelContentController_publishContent","parameters":[{"name":"contentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminModelContentViewDto"}}}}},"summary":"Publish model post or pack","tags":["Admin Model Content"]}},"/admin/model-content/content/{contentId}/archive":{"post":{"operationId":"AdminModelContentController_archiveContent","parameters":[{"name":"contentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminModelContentViewDto"}}}}},"summary":"Archive model post or pack","tags":["Admin Model Content"]}},"/admin/model-content/models/{modelId}/stories":{"get":{"operationId":"AdminModelContentController_listStories","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":20,"type":"number"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminModelStoriesListResponseDto"}}}}},"summary":"List model stories for admin","tags":["Admin Model Content"]}},"/admin/model-content/stories":{"post":{"operationId":"AdminModelContentController_createStory","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAdminModelStoryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelStoryDto"}}}}},"summary":"Create model story","tags":["Admin Model Content"]}},"/admin/model-content/stories/{storyId}":{"patch":{"operationId":"AdminModelContentController_updateStory","parameters":[{"name":"storyId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdminModelStoryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelStoryDto"}}}}},"summary":"Update model story","tags":["Admin Model Content"]},"delete":{"operationId":"AdminModelContentController_deleteStory","parameters":[{"name":"storyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Delete model story","tags":["Admin Model Content"]}},"/admin/model-content/stories/{storyId}/publish":{"post":{"operationId":"AdminModelContentController_publishStory","parameters":[{"name":"storyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelStoryDto"}}}}},"summary":"Publish model story","tags":["Admin Model Content"]}},"/admin/model-content/stories/{storyId}/archive":{"post":{"operationId":"AdminModelContentController_archiveStory","parameters":[{"name":"storyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelStoryDto"}}}}},"summary":"Archive model story","tags":["Admin Model Content"]}},"/admin/banners":{"get":{"operationId":"AdminBannersController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannerAdminListResponseDto"}}}}},"summary":"List home screen banners","tags":["Admin Banners"]},"post":{"operationId":"AdminBannersController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBannerDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannerAdminDto"}}}}},"summary":"Create a draft banner","tags":["Admin Banners"]}},"/admin/banners/migration/snapshot":{"get":{"operationId":"AdminBannersController_getMigrationSnapshot","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannerMigrationSnapshotResponseDto"}}}}},"summary":"Get banners migration snapshot (dev side)","tags":["Admin Banners"]}},"/admin/banners/migration/compare":{"post":{"operationId":"AdminBannersController_compareMigration","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannerMigrationItemsDto"}}}},"responses":{"201":{"description":""}},"summary":"Compare banner snapshots against prod by slug","tags":["Admin Banners"]}},"/admin/banners/migration/apply":{"post":{"operationId":"AdminBannersController_applyMigration","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannerMigrationItemsDto"}}}},"responses":{"201":{"description":""}},"summary":"Upsert banners on prod from dev snapshot by slug","tags":["Admin Banners"]}},"/admin/banners/upload-url":{"post":{"operationId":"AdminBannersController_generateUploadUrl","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateBannerUploadUrlDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannerUploadUrlResponseDto"}}}}},"summary":"Generate upload URL for a raw banner image","tags":["Admin Banners"]}},"/admin/banners/bulk-update-order":{"post":{"operationId":"AdminBannersController_bulkUpdateOrder","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUpdateBannersOrderDto"}}}},"responses":{"201":{"description":""}},"summary":"Bulk update banner order","tags":["Admin Banners"]}},"/admin/banners/{id}":{"get":{"operationId":"AdminBannersController_getById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannerAdminDto"}}}}},"summary":"Get a banner","tags":["Admin Banners"]},"put":{"operationId":"AdminBannersController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBannerDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannerAdminDto"}}}}},"summary":"Update a banner","tags":["Admin Banners"]},"delete":{"operationId":"AdminBannersController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Soft delete a banner","tags":["Admin Banners"]}},"/admin/banners/{id}/confirm-upload":{"post":{"operationId":"AdminBannersController_confirmUpload","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmBannerUploadDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannerAdminDto"}}}}},"summary":"Process an uploaded image into the banner webp variant","tags":["Admin Banners"]}},"/admin/revenue-reports/subscriptions/snapshot":{"get":{"operationId":"AdminRevenueReportsController_getSubscriptionsSnapshot","parameters":[{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Subscriptions snapshot: KPIs, tariff table, distributions","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/subscriptions/calendar":{"get":{"operationId":"AdminRevenueReportsController_getSubscriptionsCalendar","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Daily subscriptions calendar with cohort metrics","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/iap":{"get":{"operationId":"AdminRevenueReportsController_getIap","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"IAP (one-off coin purchases) block","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/parity":{"get":{"operationId":"AdminRevenueReportsController_getPaymentParity","parameters":[{"name":"metric","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"GEN-794 (temporary): old FunnelFox-table revenue vs new payment_transactions ledger, per day/week","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/upsells":{"get":{"operationId":"AdminRevenueReportsController_getUpsells","parameters":[{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"year_249_99 upsell conversions and CR","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/cohort-retention":{"get":{"operationId":"AdminRevenueReportsController_getCohortRetention","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Cohort retention by plan / iteration heatmap data","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/weekly-revenue":{"get":{"operationId":"AdminRevenueReportsController_getWeeklyRevenue","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Weekly revenue stacked by Initial vs Renewal","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/daily-new-subs":{"get":{"operationId":"AdminRevenueReportsController_getDailyNewSubs","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Daily new subscriptions count (bar series)","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/mrr-detail":{"get":{"operationId":"AdminRevenueReportsController_getMrrDetail","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"MRR detail: current MRR/ARR, daily growth timeline, money/events/users metrics for a date range","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/range-summary":{"get":{"operationId":"AdminRevenueReportsController_getRangeSummary","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Range summary: new unique subs, upsells, total sales, refunds, ARPPU, new app users","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/infra/aws":{"get":{"operationId":"AdminRevenueReportsController_getAwsCosts","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"AWS Cost Explorer daily costs and service breakdown","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/infra/runpod":{"get":{"operationId":"AdminRevenueReportsController_getRunPodCosts","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"sources","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"RunPod daily costs grouped by source. Use ?sources=endpoints,networkvolumes,pods (defaults to endpoints+networkvolumes).","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/infra/wavespeed":{"get":{"operationId":"AdminRevenueReportsController_getWavespeedCosts","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"accessKeyNames","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Wavespeed daily costs and per-model breakdown","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/infra/grok":{"get":{"operationId":"AdminRevenueReportsController_getGrokCosts","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Grok (xAI) daily spend and per-model breakdown from billing API","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/infra/openrouter":{"get":{"operationId":"AdminRevenueReportsController_getOpenRouterCosts","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"OpenRouter daily spend and per-model breakdown (from llm_request_logs actual usage.cost)","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/infra/revenue":{"get":{"operationId":"AdminRevenueReportsController_getPeriodRevenue","parameters":[{"name":"dateStart","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateEnd","required":true,"in":"query","schema":{"type":"string"}},{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Period revenue helper used by infra combo block","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports/cache":{"delete":{"operationId":"AdminRevenueReportsController_clearCache","parameters":[{"name":"prefix","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Hard refresh: clear all revenue-reports caches (subs + infra). Optional ?prefix=infra|subs.","tags":["Admin - Revenue Reports"]}},"/admin/revenue-reports-v2/snapshot":{"get":{"operationId":"AdminRevenueReportsV2Controller_getSnapshot","parameters":[{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Active subscriptions, MRR/ARR and the product table, derived from ledger money","tags":["Admin - Revenue Reports v2"]}},"/admin/revenue-reports-v2/calendar":{"get":{"operationId":"AdminRevenueReportsV2Controller_getCalendar","parameters":[],"responses":{"200":{"description":""}},"summary":"Per-UTC-day subscriptions + one-off calendar (gross and net)","tags":["Admin - Revenue Reports v2"]}},"/admin/revenue-reports-v2/iap":{"get":{"operationId":"AdminRevenueReportsV2Controller_getIap","parameters":[],"responses":{"200":{"description":""}},"summary":"One-off (coins) block with the provider filter","tags":["Admin - Revenue Reports v2"]}},"/admin/revenue-reports-v2/weekly-revenue":{"get":{"operationId":"AdminRevenueReportsV2Controller_getWeeklyRevenue","parameters":[],"responses":{"200":{"description":""}},"summary":"Weekly initial / renewal / one-off / refunds","tags":["Admin - Revenue Reports v2"]}},"/admin/revenue-reports-v2/daily-series":{"get":{"operationId":"AdminRevenueReportsV2Controller_getDailySeries","parameters":[],"responses":{"200":{"description":""}},"summary":"Daily counts: initial charges, renewals, one-off, declines","tags":["Admin - Revenue Reports v2"]}},"/admin/revenue-reports-v2/mrr-detail":{"get":{"operationId":"AdminRevenueReportsV2Controller_getMrrDetail","parameters":[],"responses":{"200":{"description":""}},"summary":"MRR timeline plus the period money breakdown","tags":["Admin - Revenue Reports v2"]}},"/admin/revenue-reports-v2/range-summary":{"get":{"operationId":"AdminRevenueReportsV2Controller_getRangeSummary","parameters":[],"responses":{"200":{"description":""}},"summary":"Range KPIs and the first-purchase cohort","tags":["Admin - Revenue Reports v2"]}},"/admin/revenue-reports-v2/costs":{"get":{"operationId":"AdminRevenueReportsV2Controller_getCosts","parameters":[],"responses":{"200":{"description":""}},"summary":"GEN-1079: infra costs (AWS + RunPod + Wavespeed + Grok + OpenRouter) normalized per day; per-source cache shared with the legacy infra endpoints","tags":["Admin - Revenue Reports v2"]}},"/admin/revenue-reports-v2/filters":{"get":{"operationId":"AdminRevenueReportsV2Controller_getFilterOptions","parameters":[{"name":"refresh","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Providers, products and the ledger date bounds","tags":["Admin - Revenue Reports v2"]}},"/admin/revenue-reports-v2/cache":{"delete":{"operationId":"AdminRevenueReportsV2Controller_clearCache","parameters":[],"responses":{"200":{"description":""}},"summary":"Drop cached v2 payloads (hard refresh)","tags":["Admin - Revenue Reports v2"]}},"/admin/facebook-purchase-event-logs":{"get":{"operationId":"AdminFacebookPurchaseEventLogsController_list","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":25,"type":"number"}},{"name":"userId","required":false,"in":"query","schema":{"type":"string"}},{"name":"webUserId","required":false,"in":"query","schema":{"type":"string"}},{"name":"firebaseUid","required":false,"in":"query","schema":{"type":"string"}},{"name":"eventId","required":false,"in":"query","schema":{"type":"string"}},{"name":"productId","required":false,"in":"query","schema":{"type":"string"}},{"name":"tier","required":false,"in":"query","schema":{"type":"string"}},{"name":"currency","required":false,"in":"query","schema":{"type":"string"}},{"name":"purchaseSource","required":false,"in":"query","schema":{"type":"string"}},{"name":"dateFrom","required":false,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacebookPurchaseEventLogsResponseDto"}}}}},"summary":"Get Facebook Purchase event logs","tags":["Admin - Facebook Purchase Event Logs"]}},"/admin/facebook-purchase-event-logs/export.csv":{"get":{"operationId":"AdminFacebookPurchaseEventLogsController_exportCsv","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":25,"type":"number"}},{"name":"userId","required":false,"in":"query","schema":{"type":"string"}},{"name":"webUserId","required":false,"in":"query","schema":{"type":"string"}},{"name":"firebaseUid","required":false,"in":"query","schema":{"type":"string"}},{"name":"eventId","required":false,"in":"query","schema":{"type":"string"}},{"name":"productId","required":false,"in":"query","schema":{"type":"string"}},{"name":"tier","required":false,"in":"query","schema":{"type":"string"}},{"name":"currency","required":false,"in":"query","schema":{"type":"string"}},{"name":"purchaseSource","required":false,"in":"query","schema":{"type":"string"}},{"name":"dateFrom","required":false,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Export Facebook Purchase event logs as CSV","tags":["Admin - Facebook Purchase Event Logs"]}},"/admin/subscription-plans/tiers":{"get":{"operationId":"AdminSubscriptionPlansController_listTiers","parameters":[],"responses":{"200":{"description":""}},"summary":"List all subscription tiers with plans and mappings","tags":["Admin Subscription Plans"]},"post":{"operationId":"AdminSubscriptionPlansController_createTier","parameters":[],"responses":{"201":{"description":""}},"summary":"Create a subscription tier","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/tiers/{id}":{"patch":{"operationId":"AdminSubscriptionPlansController_updateTier","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Update a subscription tier","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/tiers/{tierId}/provider-mappings":{"post":{"operationId":"AdminSubscriptionPlansController_upsertTierProviderMapping","parameters":[{"name":"tierId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Upsert a provider offering mapping for a tier","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/tier-provider-mappings/{id}":{"delete":{"operationId":"AdminSubscriptionPlansController_deleteTierProviderMapping","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Delete a tier provider mapping","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/plans":{"get":{"operationId":"AdminSubscriptionPlansController_listPlans","parameters":[{"name":"tierId","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"List plans, optionally filtered by tierId","tags":["Admin Subscription Plans"]},"post":{"operationId":"AdminSubscriptionPlansController_createPlan","parameters":[],"responses":{"201":{"description":""}},"summary":"Create a subscription plan","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/plans/{id}":{"patch":{"operationId":"AdminSubscriptionPlansController_updatePlan","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Update a subscription plan","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/plans/{planId}/provider-mappings":{"post":{"operationId":"AdminSubscriptionPlansController_upsertPlanProviderMapping","parameters":[{"name":"planId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Upsert a provider price-point mapping for a plan","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/plan-provider-mappings/{id}":{"delete":{"operationId":"AdminSubscriptionPlansController_deletePlanProviderMapping","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Delete a plan provider mapping","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/unlimit-plans":{"get":{"operationId":"AdminSubscriptionPlansController_listUnlimitPlans","parameters":[],"responses":{"200":{"description":""}},"summary":"List recurring plans registered at Unlimit (for the picker)","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/plans/{planId}/unlimit-pricing":{"post":{"operationId":"AdminSubscriptionPlansController_createUnlimitPricing","parameters":[{"name":"planId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Create an Unlimit recurring plan and register it as this plan's ACTIVE unlimit price-point mapping (previous ACTIVE is demoted to LEGACY)","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/plans/legacy-web-price-points":{"get":{"operationId":"AdminSubscriptionPlansController_listLegacyWebPricePoints","parameters":[],"responses":{"200":{"description":""}},"summary":"List legacy price points from web_subscription_products","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/plans/{planId}/legacy-mappings":{"post":{"operationId":"AdminSubscriptionPlansController_addLegacyPlanProviderMapping","parameters":[{"name":"planId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Attach a legacy price point to a plan (role = legacy)","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/funnel-paywalls":{"get":{"operationId":"AdminSubscriptionPlansController_listFunnelPaywalls","parameters":[],"responses":{"200":{"description":""}},"summary":"List funnel paywall pricing configurations","tags":["Admin Subscription Plans"]},"post":{"operationId":"AdminSubscriptionPlansController_createFunnelPaywall","parameters":[],"responses":{"201":{"description":""}},"summary":"Create a funnel paywall pricing configuration","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/funnel-paywalls/{id}":{"patch":{"operationId":"AdminSubscriptionPlansController_updateFunnelPaywall","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Update a funnel paywall pricing configuration","tags":["Admin Subscription Plans"]},"delete":{"operationId":"AdminSubscriptionPlansController_deleteFunnelPaywall","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Delete a funnel paywall pricing configuration","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/funnel-paywalls/{paywallId}/items":{"post":{"operationId":"AdminSubscriptionPlansController_upsertFunnelPaywallItem","parameters":[{"name":"paywallId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Upsert a funnel paywall price point","tags":["Admin Subscription Plans"]}},"/admin/subscription-plans/funnel-paywall-items/{id}":{"delete":{"operationId":"AdminSubscriptionPlansController_deleteFunnelPaywallItem","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Delete a funnel paywall price point","tags":["Admin Subscription Plans"]}},"/admin/screens-analytics":{"get":{"description":"Returns the latest uploaded screens analytics payload, or null if nothing has been uploaded yet.","operationId":"AdminScreensAnalyticsController_getScreensAnalytics","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetScreensAnalyticsResponseDto"}}}}},"summary":"Get current screens analytics JSON","tags":["Admin Screens Analytics"]},"put":{"description":"Accepts either a JSON body (object or array) or a multipart file upload with a `.json` file in the `file` field. Replaces the current analytics snapshot on subsequent uploads.","operationId":"AdminScreensAnalyticsController_uploadScreensAnalytics","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","items":{}},{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}]}},"multipart/form-data":{"schema":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","items":{}},{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}]}}}},"responses":{"200":{"description":"Screens analytics saved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScreensAnalyticsDto"}}}}},"summary":"Upload screens analytics JSON","tags":["Admin Screens Analytics"]}},"/admin/openrouter-config":{"get":{"operationId":"AdminOpenRouterConfigController_list","parameters":[{"name":"search","required":false,"in":"query","schema":{}},{"name":"limit","required":false,"in":"query","schema":{"example":10}},{"name":"page","required":false,"in":"query","schema":{"example":1}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenRouterConfigsListResponseDto"}}}}},"summary":"List OpenRouter provider configs","tags":["Admin OpenRouter Config"]},"post":{"operationId":"AdminOpenRouterConfigController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOpenRouterConfigDtoClass"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenRouterConfigItemDto"}}}}},"summary":"Create an OpenRouter provider config","tags":["Admin OpenRouter Config"]}},"/admin/openrouter-config/{id}":{"get":{"operationId":"AdminOpenRouterConfigController_getById","parameters":[{"name":"id","required":true,"in":"path","description":"Config UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenRouterConfigItemDto"}}}}},"summary":"Get OpenRouter provider config by ID","tags":["Admin OpenRouter Config"]},"put":{"operationId":"AdminOpenRouterConfigController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Config UUID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOpenRouterConfigDtoClass"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenRouterConfigItemDto"}}}}},"summary":"Update an OpenRouter provider config","tags":["Admin OpenRouter Config"]},"delete":{"operationId":"AdminOpenRouterConfigController_delete","parameters":[{"name":"id","required":true,"in":"path","description":"Config UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteOpenRouterConfigResponseDto"}}}}},"summary":"Delete an OpenRouter provider config","tags":["Admin OpenRouter Config"]}},"/admin/openrouter-config/defaults":{"post":{"operationId":"AdminOpenRouterConfigController_applyDefaults","parameters":[],"responses":{"200":{"description":""}},"summary":"Set defaults: insert the shipped default provider configs for any model that has none (skips already-configured models)","tags":["Admin OpenRouter Config"]}},"/v1/admin/llm-usage/summary":{"get":{"operationId":"AdminLlmUsageController_getSummary","parameters":[],"responses":{"200":{"description":"LLM usage summary retrieved"}},"summary":"LLM spend KPIs: totals, per-feature split, fail-open count","tags":["Admin - LLM Usage"]}},"/v1/admin/llm-usage/timeseries":{"get":{"operationId":"AdminLlmUsageController_getTimeseries","parameters":[],"responses":{"200":{"description":"LLM usage timeseries retrieved"}},"summary":"Daily (UTC) LLM cost/requests per feature","tags":["Admin - LLM Usage"]}},"/v1/admin/llm-usage/latency":{"get":{"operationId":"AdminLlmUsageController_getLatency","parameters":[],"responses":{"200":{"description":"LLM latency metrics retrieved"}},"summary":"Latency percentiles (p50/p95/avg over ok rows) + timeout/error rates, grouped by mode or provider, bucketed by day or total","tags":["Admin - LLM Usage"]}},"/v1/admin/llm-usage/users":{"get":{"operationId":"AdminLlmUsageController_getUsers","parameters":[],"responses":{"200":{"description":"Per-user LLM usage retrieved"}},"summary":"Per-user LLM spend (paginated, most expensive first)","tags":["Admin - LLM Usage"]}},"/v1/admin/llm-usage/users/export.csv":{"get":{"operationId":"AdminLlmUsageController_exportUsersCsv","parameters":[],"responses":{"200":{"description":""}},"summary":"Export per-user LLM spend as CSV (capped 50k rows)","tags":["Admin - LLM Usage"]}},"/v1/admin/product/safety/summary":{"get":{"operationId":"AdminProductSafetyController_getSummary","parameters":[],"responses":{"200":{"description":"Safety summary retrieved"}},"summary":"Safety KPIs: moderated msgs, flagged+blocked (rate), fail-open (rate), classifier p50/p95","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/verdicts-timeseries":{"get":{"operationId":"AdminProductSafetyController_getVerdictsTimeseries","parameters":[],"responses":{"200":{"description":"Safety verdicts timeseries retrieved"}},"summary":"Daily (UTC) verdict counts: allowed / flagged / blocked / failed-open","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/categories":{"get":{"operationId":"AdminProductSafetyController_getCategories","parameters":[],"responses":{"200":{"description":"Safety categories retrieved"}},"summary":"Blocks-by-category: matched-rule counts over blocked/flagged verdicts, most frequent first","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/suspicious":{"get":{"operationId":"AdminProductSafetyController_getSuspicious","parameters":[],"responses":{"200":{"description":"Suspicious conversations retrieved"}},"summary":"Paginated flagged/blocked moderation events (newest first) with user email + verdict detail. Optional filters: category (repeatable), minConfidence, search (email/userId), model","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/filters":{"get":{"operationId":"AdminProductSafetyController_getFilters","parameters":[],"responses":{"200":{"description":"Safety filter options retrieved"}},"summary":"Distinct matched-rule categories + classifier models over the range (filter-control options)","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/context":{"get":{"operationId":"AdminProductSafetyController_getContext","parameters":[{"name":"messageId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Moderated-message context retrieved"}},"summary":"~10-message chat window around a moderated message (5 up to + 5 after, createdAt ASC) plus its verdict","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/errors-by-user":{"get":{"operationId":"AdminProductSafetyController_getErrorsByUser","parameters":[],"responses":{"200":{"description":"Fail-open errors by user retrieved"}},"summary":"Top users by fail-open count + an approximate primary-retry rate for the range","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/feedback/summary":{"get":{"operationId":"AdminProductSafetyController_getFeedbackSummary","parameters":[],"responses":{"200":{"description":"Feedback summary retrieved"}},"summary":"Feedback KPIs: blocked msgs, appeals + contest rate, reviewed, confirmed/wrong blocks, wrong-block rate, pending","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/feedback/timeseries":{"get":{"operationId":"AdminProductSafetyController_getFeedbackTimeseries","parameters":[],"responses":{"200":{"description":"Feedback timeseries retrieved"}},"summary":"Daily (UTC) blocked vs appeals vs wrong-blocks","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/feedback/categories":{"get":{"operationId":"AdminProductSafetyController_getFeedbackCategories","parameters":[],"responses":{"200":{"description":"Feedback categories retrieved"}},"summary":"Per matched-rule category: blocked total, contested, contest rate, wrong-block rate","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/feedback/models":{"get":{"operationId":"AdminProductSafetyController_getFeedbackModels","parameters":[],"responses":{"200":{"description":"Feedback models retrieved"}},"summary":"Per classifier model: blocked total, contested, contest rate, wrong-block rate","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/feedback/filters":{"get":{"operationId":"AdminProductSafetyController_getFeedbackFilters","parameters":[],"responses":{"200":{"description":"Feedback filter options retrieved"}},"summary":"Distinct categories + models present in the appealed population (feedback-table filter options)","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/feedback/list":{"get":{"operationId":"AdminProductSafetyController_getFeedbackList","parameters":[],"responses":{"200":{"description":"Feedback list retrieved"}},"summary":"Paginated appeals (newest first) with message snippet + verdict detail + review status. Filters: reviewStatus, category (repeatable), model, search (email/userId)","tags":["Admin - Product Safety"]}},"/v1/admin/product/safety/feedback/{id}/review":{"patch":{"operationId":"AdminProductSafetyController_reviewFeedback","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewModerationFeedbackBody"}}}},"responses":{"200":{"description":"Feedback review updated"}},"summary":"Set the admin triage verdict (CONFIRMED_BLOCK | WRONG_BLOCK), or NEW to undo. Stamps reviewedBy/At.","tags":["Admin - Product Safety"]}},"/v1/admin/product/visual-director/summary":{"get":{"operationId":"AdminProductVisualDirectorController_getSummary","parameters":[],"responses":{"200":{"description":"VD2 summary retrieved"}},"summary":"VD2 adoption KPIs (presses only): users, median/p95 presses per user, press success rate","tags":["Admin - Product · Visual Director v2"]}},"/v1/admin/product/visual-director/runs-per-user":{"get":{"operationId":"AdminProductVisualDirectorController_getRunsPerUser","parameters":[],"responses":{"200":{"description":"VD2 runs-per-user distribution retrieved"}},"summary":"Distribution of per-user VD2 press counts (1 / 2-5 / 6-20 / 21-50 / 50+) + median/p95, segmentable free/paid","tags":["Admin - Product · Visual Director v2"]}},"/v1/admin/product/visual-director/coins-per-user":{"get":{"operationId":"AdminProductVisualDirectorController_getCoinsPerUser","parameters":[],"responses":{"200":{"description":"VD2 coins-per-user retrieved"}},"summary":"Median/p95 coins (IMAGE OUT) per VD2 press-active user — spend-per-active-user, not per-render attribution","tags":["Admin - Product · Visual Director v2"]}},"/v1/admin/product/visual-director/funnel":{"get":{"operationId":"AdminProductVisualDirectorController_getFunnel","parameters":[],"responses":{"200":{"description":"VD2 delivery funnel retrieved"}},"summary":"Delivery funnel: press → director ok → image enqueued → delivered + median press→delivered seconds (approx chatId+time correlation), segmentable free/paid","tags":["Admin - Product · Visual Director v2"]}},"/v1/admin/product/visual-director/paid-conversion":{"get":{"operationId":"AdminProductVisualDirectorController_getPaidConversion","parameters":[],"responses":{"200":{"description":"VD2 paid conversion retrieved"}},"summary":"Of users whose first VD2 use is in range, % who acquired a subscription within 7 days, vs the non-VD first-activity baseline","tags":["Admin - Product · Visual Director v2"]}},"/v1/admin/product/memory/summary":{"get":{"operationId":"AdminProductMemoryController_getSummary","parameters":[],"responses":{"200":{"description":"Memory summary retrieved"}},"summary":"Memory rollout status (write/read mode) + active-fact snapshot (facts, users, avg) + memory LLM spend in range","tags":["Admin - Product · Memory"]}},"/v1/admin/product/memory/facts-timeseries":{"get":{"operationId":"AdminProductMemoryController_getFactsTimeseries","parameters":[],"responses":{"200":{"description":"Memory facts timeseries retrieved"}},"summary":"Facts created per UTC day in range","tags":["Admin - Product · Memory"]}},"/v1/admin/product/memory/kind-breakdown":{"get":{"operationId":"AdminProductMemoryController_getKindBreakdown","parameters":[],"responses":{"200":{"description":"Memory kind breakdown retrieved"}},"summary":"Active-fact counts by kind (fact/episode/milestone/procedural)","tags":["Admin - Product · Memory"]}},"/v1/admin/product/memory/top-users":{"get":{"operationId":"AdminProductMemoryController_getTopUsers","parameters":[],"responses":{"200":{"description":"Memory top users retrieved"}},"summary":"Users ranked by active fact count","tags":["Admin - Product · Memory"]}},"/v1/admin/product/memory/facts":{"get":{"operationId":"AdminProductMemoryController_getFacts","parameters":[],"responses":{"200":{"description":"Memory facts page retrieved"}},"summary":"Global facts log: every user's v2 facts, newest first, filterable (search / kind / model / includeExpired) + paginated","tags":["Admin - Product · Memory"]}},"/admin/self-billing/customers/search":{"get":{"operationId":"AdminSbCustomersController_search","parameters":[],"responses":{"200":{"description":""}},"summary":"Resolve a customer by any secondary identifier","tags":["Admin Self-Billing"]}},"/admin/self-billing/customers/search-by-card":{"get":{"description":"GEN-1505: BIN fragment matches as a prefix, tail fragment as a suffix; either half alone is enough. Covers saved payment methods and cards seen on transactions.","operationId":"AdminSbCustomersController_searchByCard","parameters":[],"responses":{"200":{"description":""}},"summary":"List customers whose card matches a mask fragment","tags":["Admin Self-Billing"]}},"/admin/self-billing/customers/{id}":{"get":{"operationId":"AdminSbCustomersController_getById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get a customer by id","tags":["Admin Self-Billing"]}},"/admin/self-billing/customers/{id}/payment-methods":{"get":{"operationId":"AdminSbCustomersController_listPaymentMethods","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"List payment methods of a customer","tags":["Admin Self-Billing"]}},"/admin/self-billing/customers/{id}/purchases":{"get":{"operationId":"AdminSbCustomersController_listPurchases","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"List one-time purchases of a customer","tags":["Admin Self-Billing"]}},"/admin/self-billing/customers/{id}/stop-collection":{"post":{"description":"Support stop-crane for a customer who complained about a failed charge: autorenew off and the open renewal invoice voided on all his live subscriptions, so neither dunning retries nor the console retry buttons charge him again. Access is left alone (use Suspend to cut it) and Resume autorenew undoes it.","operationId":"AdminSbCustomersController_stopCollection","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Stop billing attempts: unsubscribe every live subscription","tags":["Admin Self-Billing"]}},"/admin/self-billing/subscriptions":{"get":{"operationId":"AdminSbSubscriptionsController_list","parameters":[],"responses":{"200":{"description":""}},"summary":"List subscriptions (paged, filtered)","tags":["Admin Self-Billing"]}},"/admin/self-billing/subscriptions/{id}":{"get":{"operationId":"AdminSbSubscriptionsController_getDetail","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get the full subscription aggregate","tags":["Admin Self-Billing"]}},"/admin/self-billing/subscriptions/{id}/cancel":{"post":{"operationId":"AdminSbSubscriptionsController_cancel","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Cancel a subscription (now or at period end)","tags":["Admin Self-Billing"]}},"/admin/self-billing/subscriptions/{id}/resume-autorenew":{"post":{"operationId":"AdminSbSubscriptionsController_resumeAutorenew","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Re-enable autorenew of a period-end-cancelled subscription","tags":["Admin Self-Billing"]}},"/admin/self-billing/subscriptions/{id}/retry-charge":{"post":{"operationId":"AdminSbSubscriptionsController_retryCharge","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Charge the PAST_DUE subscription right now, off the dunning schedule","tags":["Admin Self-Billing"]}},"/admin/self-billing/subscriptions/{id}/next-charge":{"post":{"description":"Sets nextChargeAt (and, on a PAST_DUE row, the dunning retry instant). An instant in the past makes the next cron tick charge the subscription — the supported way to test a renewal without editing the database.","operationId":"AdminSbSubscriptionsController_setNextCharge","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Reschedule the renewal engine for this subscription (debug/ops)","tags":["Admin Self-Billing"]}},"/admin/self-billing/subscriptions/{id}/suspend":{"post":{"operationId":"AdminSbSubscriptionsController_suspend","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Suspend now: stop the provider schedule and cut app access immediately","tags":["Admin Self-Billing"]}},"/admin/self-billing/settings/dunning":{"get":{"operationId":"AdminSbSettingsController_getDunning","parameters":[],"responses":{"200":{"description":""}},"summary":"The module-wide smart-retries (dunning) default","tags":["Admin Self-Billing"]},"put":{"operationId":"AdminSbSettingsController_updateDunning","parameters":[],"responses":{"200":{"description":""}},"summary":"Replace the smart-retries default (steps, discounts, grace)","tags":["Admin Self-Billing"]}},"/admin/self-billing/test-cards":{"get":{"operationId":"AdminSbTestCardsController_overview","parameters":[],"responses":{"200":{"description":""}},"summary":"Registered test cards with the volume each of them moved","tags":["Admin Self-Billing"]},"post":{"description":"Idempotent on the mask: re-adding a known pair updates its label and reactivates it. Classification is retroactive — every charge that card ever made counts from this moment on.","operationId":"AdminSbTestCardsController_create","parameters":[],"responses":{"201":{"description":""}},"summary":"Register a test card by BIN + last four","tags":["Admin Self-Billing"]}},"/admin/self-billing/test-cards/{id}":{"patch":{"description":"A deactivated card stays registered but stops classifying charges — its volume drops out of the report.","operationId":"AdminSbTestCardsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Edit a test card (label, note) or deactivate it","tags":["Admin Self-Billing"]},"delete":{"operationId":"AdminSbTestCardsController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Forget a test card (its charges keep their mask)","tags":["Admin Self-Billing"]}},"/admin/self-billing/test-cards/bulk-refund":{"post":{"description":"Real money: every item runs as a full console refund (gateway refund + subscription revoked + host claw-back). At most `limit` charges per call — keep calling while `remaining` is above zero.","operationId":"AdminSbTestCardsController_bulkRefund","parameters":[],"responses":{"200":{"description":""}},"summary":"Refund one batch of the charges made on test cards","tags":["Admin Self-Billing"]}},"/admin/self-billing/transactions":{"get":{"operationId":"AdminSbMoneyController_listTransactions","parameters":[],"responses":{"200":{"description":""}},"summary":"List transactions (paged, filtered; live feed)","tags":["Admin Self-Billing"]}},"/admin/self-billing/invoices":{"get":{"operationId":"AdminSbMoneyController_listInvoices","parameters":[],"responses":{"200":{"description":""}},"summary":"List invoices (paged, filtered)","tags":["Admin Self-Billing"]}},"/admin/self-billing/transactions/{id}/refund":{"post":{"operationId":"AdminSbMoneyController_refundCharge","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Refund a succeeded charge (full or partial)","tags":["Admin Self-Billing"]}},"/admin/self-billing/transactions/{id}/reconcile":{"post":{"description":"Runs the reconcile the hourly sweep would run, without its 30-minute floor: the outcome is applied through the same points a postback uses. 'still_pending' means the gateway itself has no verdict yet.","operationId":"AdminSbMoneyController_reconcileTransaction","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Ask the gateway what happened to a PENDING transaction","tags":["Admin Self-Billing"]}},"/admin/self-billing/transactions/{id}/force-retry":{"post":{"description":"Closes a stuck PENDING attempt as failed (nothing else does until the 24h reconcile TTL) and charges the invoice again, off the dunning schedule. Real money: reconcile first — if the gateway did take the payment, its late postback plus this retry mean a double charge.","operationId":"AdminSbMoneyController_forceRetryTransaction","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Force a fresh MIT attempt on a pending/failed charge","tags":["Admin Self-Billing"]}},"/admin/self-billing/products":{"get":{"operationId":"AdminSbCatalogController_listProducts","parameters":[],"responses":{"200":{"description":""}},"summary":"List all products (incl. archived)","tags":["Admin Self-Billing"]},"post":{"operationId":"AdminSbCatalogController_createProduct","parameters":[],"responses":{"201":{"description":""}},"summary":"Create a product","tags":["Admin Self-Billing"]}},"/admin/self-billing/products/{id}":{"patch":{"operationId":"AdminSbCatalogController_updateProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Update a product","tags":["Admin Self-Billing"]}},"/admin/self-billing/prices":{"get":{"operationId":"AdminSbCatalogController_listPrices","parameters":[],"responses":{"200":{"description":""}},"summary":"List prices paged, filtered by product / code search","tags":["Admin Self-Billing"]},"post":{"operationId":"AdminSbCatalogController_createPrice","parameters":[],"responses":{"201":{"description":""}},"summary":"Create a price","tags":["Admin Self-Billing"]}},"/admin/self-billing/prices/{id}":{"patch":{"operationId":"AdminSbCatalogController_updatePrice","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Update a price","tags":["Admin Self-Billing"]}},"/admin/self-billing/prices/{id}/archive":{"post":{"operationId":"AdminSbCatalogController_archivePrice","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Archive a price","tags":["Admin Self-Billing"]}},"/admin/self-billing/outbox-events":{"get":{"operationId":"AdminSbEventsController_listOutboxEvents","parameters":[],"responses":{"200":{"description":""}},"summary":"List outbox events (paged, filtered)","tags":["Admin Self-Billing"]}},"/admin/self-billing/outbox-events/{id}/retry":{"post":{"operationId":"AdminSbEventsController_retryOutboxEvent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Re-queue a FAILED outbox event (fresh attempt budget)","tags":["Admin Self-Billing"]}},"/admin/self-billing/inbox-events":{"get":{"operationId":"AdminSbEventsController_listInboxEvents","parameters":[],"responses":{"200":{"description":""}},"summary":"List webhook inbox events (paged, filtered)","tags":["Admin Self-Billing"]}},"/admin/self-billing/inbox-events/{id}/retry":{"post":{"operationId":"AdminSbEventsController_retryInboxEvent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Re-process a FAILED inbox postback","tags":["Admin Self-Billing"]}},"/admin/self-billing/providers":{"get":{"operationId":"AdminSbEventsController_listProviders","parameters":[],"responses":{"200":{"description":""}},"summary":"List payment providers with catalog link counts","tags":["Admin Self-Billing"]}},"/admin/self-billing/metrics/subscription-counts":{"get":{"operationId":"AdminSbMetricsController_getSubscriptionCounts","parameters":[],"responses":{"200":{"description":""}},"summary":"Live subscription counts by status","tags":["Admin Self-Billing"]}},"/admin/self-billing/metrics/transaction-totals":{"get":{"operationId":"AdminSbMetricsController_getTransactionTotals","parameters":[],"responses":{"200":{"description":""}},"summary":"Succeeded money movement over a date range","tags":["Admin Self-Billing"]}},"/admin/self-billing/metrics/purchase-split":{"get":{"operationId":"AdminSbMetricsController_getPurchaseSplit","parameters":[],"responses":{"200":{"description":""}},"summary":"Subscription vs one-time charges over a range","tags":["Admin Self-Billing"]}},"/admin/self-billing/metrics/revenue-by-day":{"get":{"operationId":"AdminSbMetricsController_getRevenueByDay","parameters":[],"responses":{"200":{"description":""}},"summary":"Daily amounts and counts per transaction type","tags":["Admin Self-Billing"]}},"/admin/chargebacks911/events":{"get":{"operationId":"AdminCb911Controller_listEvents","parameters":[],"responses":{"200":{"description":""}},"summary":"List received 911CB notifications (paged)","tags":["Admin Chargebacks911"]}},"/admin/chargebacks911/events/{id}":{"get":{"operationId":"AdminCb911Controller_getEvent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get a single notification with its raw payload","tags":["Admin Chargebacks911"]}},"/admin/chargebacks911/events/{id}/match-candidates":{"get":{"description":"Card-mask lookup against self-billing, ranked by amount and date. Recomputed per call — the same card keeps paying us.","operationId":"AdminCb911Controller_matchCandidates","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Charges that could be the one this alert disputes","tags":["Admin Chargebacks911"]}},"/admin/chargebacks911/events/{id}/rematch":{"post":{"description":"For alerts received before the charge landed in self-billing. A manual link is never overwritten.","operationId":"AdminCb911Controller_rematch","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Re-run matching for an event","tags":["Admin Chargebacks911"]}},"/admin/chargebacks911/events/{id}/match":{"post":{"description":"Only accepts a transaction the card lookup already proposed for this alert.","operationId":"AdminCb911Controller_match","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Link the alert to a specific self-billing charge","tags":["Admin Chargebacks911"]}},"/admin/chargebacks911/events/{id}/billing-context":{"get":{"description":"Customer, payment methods, subscriptions and recent charges — what an operator needs to decide on a refund without leaving the alert.","operationId":"AdminCb911Controller_billingContext","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Self-billing picture of the user behind this alert","tags":["Admin Chargebacks911"]}},"/admin/chargebacks911/subscriptions":{"get":{"operationId":"AdminCb911Controller_listSubscriptions","parameters":[],"responses":{"200":{"description":""}},"summary":"List webhook subscriptions (secrets are never returned)","tags":["Admin Chargebacks911"]},"post":{"description":"The response carries the signing secret — it is the only time the vendor exposes it.","operationId":"AdminCb911Controller_createSubscription","parameters":[],"responses":{"201":{"description":""}},"summary":"Register a webhook subscription at 911CB","tags":["Admin Chargebacks911"]}},"/admin/chargebacks911/subscriptions/sync":{"post":{"operationId":"AdminCb911Controller_syncSubscriptions","parameters":[],"responses":{"201":{"description":""}},"summary":"Reconcile local subscriptions with the vendor","tags":["Admin Chargebacks911"]}},"/admin/chargebacks911/subscriptions/{id}/test":{"post":{"operationId":"AdminCb911Controller_testSubscription","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Ask 911CB to fire a sample event (sandbox only)","tags":["Admin Chargebacks911"]}},"/admin/chargebacks911/subscriptions/{id}":{"delete":{"operationId":"AdminCb911Controller_deleteSubscription","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Delete a webhook subscription at 911CB and locally","tags":["Admin Chargebacks911"]}},"/admin/disputes":{"get":{"operationId":"AdminDisputesController_list","parameters":[],"responses":{"200":{"description":""}},"summary":"List disputes across all feeds (paged)","tags":["Admin Disputes"]}},"/admin/disputes/cb911-outcomes":{"get":{"description":"What an operator can report back for an alert. Read from the vendor, with the built-in list as a fallback.","operationId":"AdminDisputesController_cb911Outcomes","parameters":[],"responses":{"200":{"description":""}},"summary":"911CB's alert-outcome dictionary","tags":["Admin Disputes"]}},"/admin/disputes/{id}":{"get":{"operationId":"AdminDisputesController_getOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"One dispute with its raw feed payload","tags":["Admin Disputes"]}},"/admin/disputes/{id}/match-candidates":{"get":{"description":"Runs the same lookup intake does — ARN, gateway ids, email, card — and returns what it found, ranked. Recomputed per call.","operationId":"AdminDisputesController_matchCandidates","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Charges that could be the one this dispute is about","tags":["Admin Disputes"]}},"/admin/disputes/{id}/rematch":{"post":{"description":"For disputes that arrived before the charge landed in self-billing, or before its ARN was backfilled. A manual link is never overwritten.","operationId":"AdminDisputesController_rematch","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Re-run matching for a dispute","tags":["Admin Disputes"]}},"/admin/disputes/{id}/match":{"post":{"description":"Only accepts a transaction the lookup already proposed for this dispute.","operationId":"AdminDisputesController_match","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Link the dispute to a specific self-billing charge","tags":["Admin Disputes"]}},"/admin/disputes/{id}/cb911-outcome":{"post":{"description":"Sends the outcome to the vendor, which is what actually closes the alert in its console — a refund on our side alone leaves it open. The automation reports a refund it made itself; this is for everything else.","operationId":"AdminDisputesController_reportCb911Outcome","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Report how a 911CB alert ended","tags":["Admin Disputes"]}},"/admin/disputes/{id}/billing-context":{"get":{"description":"Customer, payment methods, subscriptions and recent charges — what an operator needs to decide on a refund without leaving the dispute.","operationId":"AdminDisputesController_billingContext","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Self-billing picture of the user behind this dispute","tags":["Admin Disputes"]}},"/admin/disputes/sync/unlimit":{"post":{"description":"Chargebacks, retrieval requests and fraud reports for the window. Idempotent — a re-run updates the rows it already stored.","operationId":"AdminDisputesController_syncUnlimit","parameters":[],"responses":{"200":{"description":""}},"summary":"Pull a period from the acquirer's dispute feed","tags":["Admin Disputes"]}},"/admin/disputes/sync/cb911":{"post":{"description":"One-off after the deploy: alerts received before the disputes view existed. Idempotent.","operationId":"AdminDisputesController_projectCb911","parameters":[],"responses":{"200":{"description":""}},"summary":"Project stored 911CB alerts into this view","tags":["Admin Disputes"]}},"/admin/disputes/backfill/arn":{"post":{"description":"Reads the acquirer's payments and recurrings for the window and stamps the ARN on the matching charge. Charges that resolve ambiguously are left alone.","operationId":"AdminDisputesController_backfillArn","parameters":[],"responses":{"200":{"description":""}},"summary":"Backfill our charges' ARN from the acquirer feed","tags":["Admin Disputes"]}},"/admin/disputes/backfill/refunded":{"post":{"description":"Marks every open matched dispute whose charge has been fully refunded. One-off for rows that predate the live hook; idempotent.","operationId":"AdminDisputesController_backfillRefunded","parameters":[],"responses":{"200":{"description":""}},"summary":"Derive 'refunded' for open disputes from our ledger","tags":["Admin Disputes"]}},"/v1/chat-modes/experiment":{"get":{"description":"Resolves the GEN-1517 experiment: whether this user may pick the chat mode (Roleplay vs Realistic) for characters that declare both. Users outside the experiment (registered before it started) get `roleplay_only` and are not enrolled.","operationId":"ChatModesController_getChatModeExperiment","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatModeExperimentDto"}}}}},"summary":"Chat-mode choice experiment branch for the current user","tags":["Chat modes"]}},"/v1/evening-push/track-open":{"post":{"operationId":"EveningPushController_trackOpen","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackPushOpenDto"}}}},"responses":{"200":{"description":"Push notification open tracked successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackPushOpenResponseDto"}}}}},"summary":"Track push notification open","tags":["Evening Push"]}},"/v1/acquisition/sessions":{"post":{"description":"Creates a new acquisition session for the given funnel. If an in-progress session for the same lead and funnel already exists, returns it instead of creating a duplicate (idempotent).","operationId":"AcquisitionSessionsController_startSession","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartAcquisitionSessionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartAcquisitionSessionResponseDto"}}}}},"summary":"Start or resume an acquisition funnel session","tags":["Acquisition Sessions"]}},"/v1/acquisition/sessions/check-email":{"post":{"description":"Used by the email step to decide whether to suggest sign-in or continue silently. Never throws on conflict.","operationId":"AcquisitionSessionsController_checkEmail","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckAcquisitionEmailDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckAcquisitionEmailResponseDto"}}}}},"summary":"Pre-check whether an email is registered (non-blocking)","tags":["Acquisition Sessions"]}},"/v1/acquisition/sessions/{id}":{"patch":{"operationId":"AcquisitionSessionsController_updateSession","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAcquisitionSessionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcquisitionSessionDto"}}}}},"summary":"Patch funnel answers / attribution / experiments for a session","tags":["Acquisition Sessions"]},"get":{"operationId":"AcquisitionSessionsController_getSession","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcquisitionSessionDto"}}}}},"summary":"Get acquisition session by id (debug / recovery)","tags":["Acquisition Sessions"]}},"/v1/acquisition/sessions/{id}/complete":{"post":{"operationId":"AcquisitionSessionsController_completeSession","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcquisitionSessionDto"}}}}},"summary":"Mark the session as completed (funnel finished)","tags":["Acquisition Sessions"]}},"/v1/acquisition/sessions/{id}/purchase":{"post":{"operationId":"AcquisitionSessionsController_purchaseSession","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcquisitionSessionDto"}}}}},"summary":"Mark the session as purchased after a successful payment","tags":["Acquisition Sessions"]}},"/v1/acquisition/sessions/{id}/paywall-reached":{"post":{"operationId":"AcquisitionSessionsController_markPaywallReached","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcquisitionSessionDto"}}}}},"summary":"Mark that the session reached the paywall step (idempotent)","tags":["Acquisition Sessions"]}},"/v1/acquisition/sessions/{id}/abandon":{"post":{"operationId":"AcquisitionSessionsController_abandonSession","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcquisitionSessionDto"}}}}},"summary":"Mark the session as abandoned","tags":["Acquisition Sessions"]}},"/v1/acquisition/sessions/{id}/capture-email":{"post":{"description":"Persists the email on the lead and returns the resolved email status (existing customer / duplicate lead / available). Never throws on duplicate email — the funnel decides UX from the status.","operationId":"AcquisitionSessionsController_captureEmail","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptureAcquisitionEmailDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptureAcquisitionEmailResponseDto"}}}}},"summary":"Capture the email entered on the email step","tags":["Acquisition Sessions"]}},"/v1/acquisition/leads/by-firebase-uid/{firebaseUid}":{"get":{"description":"Returns the lead, the latest session, and a short list of recent sessions. The funnel uses `lead.isCustomer` to decide whether to redirect the user to the app.","operationId":"AcquisitionLeadsController_getByFirebaseUid","parameters":[{"name":"firebaseUid","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcquisitionLeadSummaryDto"}}}}},"summary":"Resolve the funnel entry-guard payload for a given Firebase UID","tags":["Acquisition Leads"]}},"/v1/acquisition/leads/content-access":{"get":{"description":"Authenticated endpoint. The Firebase UID and email are taken from the verified auth token — the app never passes them. Returns `hasAccess: true` when that UID — or, as a fallback, that email — belongs to a lead that came through a web acquisition funnel. The app uses this to decide whether to show the `explicitContent` toggle. The email fallback keeps access tied to a user who deleted and recreated their account with the same email. Works for both anonymous and registered Firebase users (anonymous users simply have no email, so only the UID is matched). The result is cached server-side.","operationId":"AcquisitionLeadsController_getContentAccess","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcquisitionContentAccessDto"}}}}},"security":[{"bearer":[]}],"summary":"Check whether the current user may unlock the full content catalog","tags":["Acquisition Leads"]}},"/v1/acquisition/leads/{id}/link-user":{"post":{"description":"Called after the funnel graduates the anonymous Firebase account into a real registration. Mirrors `isCustomer` from the canonical `subscriptions` table.","operationId":"AcquisitionLeadsController_linkUser","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkAcquisitionLeadToUserDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcquisitionLeadDto"}}}}},"summary":"Link the lead to a registered user","tags":["Acquisition Leads"]}},"/v2/payments/checkout":{"post":{"description":"Returns either embedded-SDK config (FunnelFox) or a hosted payment page redirect (Unlimit), discriminated by `mode`.","operationId":"PaymentsController_createCheckout","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponseDto"}}}}},"summary":"Start a provider-agnostic checkout","tags":["Payments"]}},"/v2/payments/orders/{orderId}":{"get":{"description":"Owner-checked. While the order is pending the status is lazily reconciled against the provider, so polling works even when callbacks are delayed or misconfigured.","operationId":"PaymentsController_getOrder","parameters":[{"name":"orderId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentOrderStatusResponseDto"}}}}},"summary":"Poll a redirect payment order","tags":["Payments"]}},"/v2/payments/one-click":{"post":{"description":"Charges the saved card server-side (merchant-initiated) for Unlimit users. FunnelFox one-click stays client-driven through the FF SDK.","operationId":"PaymentsController_oneClickPurchase","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OneClickPurchaseDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OneClickPurchaseResponseDto"}}}}},"summary":"One-click coin purchase with the stored payment method","tags":["Payments"]}},"/v2/payments/provider":{"get":{"description":"Sticky: an existing subscription pins its provider; otherwise the persisted assignment from the first checkout; otherwise the default provider.","operationId":"PaymentsController_getProvider","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentProviderResponseDto"}}}}},"summary":"Get the current user's payment provider","tags":["Payments"]}},"/admin/marketing-v2/concepts":{"get":{"operationId":"AdminMarketingV2Controller_getTree","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]},"post":{"operationId":"AdminMarketingV2Controller_createConcept","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConceptDto"}}}},"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/concept-authors":{"get":{"operationId":"AdminMarketingV2Controller_getConceptAuthors","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/concepts/{id}":{"patch":{"operationId":"AdminMarketingV2Controller_updateConcept","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConceptDto"}}}},"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]},"delete":{"operationId":"AdminMarketingV2Controller_deleteConcept","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/concepts/{id}/delete-impact":{"get":{"operationId":"AdminMarketingV2Controller_getConceptDeleteImpact","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/concepts/{conceptId}/variants":{"post":{"operationId":"AdminMarketingV2Controller_createVariant","parameters":[{"name":"conceptId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVariantDto"}}}},"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/variants/{id}":{"patch":{"operationId":"AdminMarketingV2Controller_updateVariant","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVariantDto"}}}},"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/variants/{id}/name-preview":{"get":{"operationId":"AdminMarketingV2Controller_previewLocalizationName","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/variants/{variantId}/localizations":{"post":{"operationId":"AdminMarketingV2Controller_createLocalization","parameters":[{"name":"variantId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLocalizationDto"}}}},"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/repository/legacy-import/preview":{"get":{"operationId":"AdminMarketingV2Controller_previewLegacyImport","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/repository/legacy-import":{"post":{"operationId":"AdminMarketingV2Controller_applyLegacyImport","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/localizations/{id}":{"patch":{"operationId":"AdminMarketingV2Controller_updateLocalization","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLocalizationDto"}}}},"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]},"get":{"operationId":"AdminMarketingV2Controller_getLocalizationDetail","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/creative-tests":{"get":{"operationId":"AdminMarketingV2Controller_getCreativeTests","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/localizations/{id}/test-winner":{"patch":{"operationId":"AdminMarketingV2Controller_setTestWinner","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTestWinnerDto"}}}},"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/localizations/{id}/media":{"post":{"operationId":"AdminMarketingV2Controller_uploadLocalizationMedia","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/media":{"get":{"operationId":"AdminMarketingV2Controller_listMediaLibrary","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":50,"type":"number"}},{"name":"source","required":false,"in":"query","schema":{"type":"string","enum":["admin","creo_loop"]}},{"name":"status","required":false,"in":"query","description":"Creative lifecycle status of the localization","schema":{"type":"string","enum":["draft","review","live","rejected","archived"]}},{"name":"ingestStatus","required":false,"in":"query","schema":{"type":"string","enum":["pending","ready","failed"]}},{"name":"conceptId","required":false,"in":"query","schema":{"type":"string"}},{"name":"librarySync","required":false,"in":"query","description":"'none' = not pushed to any ad account yet","schema":{"type":"string","enum":["none","pending","synced","failed"]}},{"name":"search","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/media/concepts":{"get":{"operationId":"AdminMarketingV2Controller_listMediaConcepts","parameters":[{"name":"source","required":false,"in":"query","schema":{"type":"string","enum":["admin","creo_loop"]}},{"name":"search","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/media/{id}/reingest":{"post":{"operationId":"AdminMarketingV2Controller_reingestMedia","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/media/{id}/content":{"get":{"operationId":"AdminMarketingV2Controller_getMediaContent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"range","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/media/{id}/poster":{"get":{"operationId":"AdminMarketingV2Controller_getMediaPoster","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"v","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/media/{id}/push/{accountId}":{"post":{"operationId":"AdminMarketingV2Controller_pushMediaToLibrary","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/concepts/{id}/push/{accountId}":{"post":{"operationId":"AdminMarketingV2Controller_pushConceptToLibrary","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/media/{id}/library-status":{"get":{"operationId":"AdminMarketingV2Controller_getMediaLibraryStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/accounts":{"get":{"operationId":"AdminMarketingV2Controller_listAccounts","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/drafts":{"get":{"operationId":"AdminMarketingV2Controller_listDrafts","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]},"post":{"operationId":"AdminMarketingV2Controller_createDraft","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDraftDto"}}}},"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/drafts/name-preview":{"get":{"operationId":"AdminMarketingV2Controller_previewDraft","parameters":[{"name":"localizationId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/drafts/{id}/status":{"patch":{"operationId":"AdminMarketingV2Controller_updateDraftStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDraftStatusDto"}}}},"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/accounts":{"get":{"operationId":"AdminMarketingV2Controller_listFbAccounts","parameters":[{"name":"includeHidden","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]},"post":{"operationId":"AdminMarketingV2Controller_createFbAccount","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFbAccountDto"}}}},"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/accounts/v1-candidates":{"get":{"operationId":"AdminMarketingV2Controller_listV1Candidates","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/accounts/name-preview":{"get":{"operationId":"AdminMarketingV2Controller_previewAccountName","parameters":[{"name":"provider","required":true,"in":"query","schema":{"type":"string"}},{"name":"product","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/providers":{"get":{"operationId":"AdminMarketingV2Controller_listProviders","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/tokens":{"get":{"operationId":"AdminMarketingV2Controller_listReusableTokens","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/accounts/{id}":{"patch":{"operationId":"AdminMarketingV2Controller_updateFbAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFbAccountDto"}}}},"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/accounts/{id}/test-connection":{"post":{"operationId":"AdminMarketingV2Controller_testFbAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/accounts/import-v1":{"post":{"operationId":"AdminMarketingV2Controller_importV1Accounts","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportV1AccountsDto"}}}},"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/proxies":{"get":{"operationId":"AdminMarketingV2Controller_listProxies","parameters":[{"name":"page","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]},"post":{"operationId":"AdminMarketingV2Controller_createProxy","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProxyDto"}}}},"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/proxies/alive":{"get":{"operationId":"AdminMarketingV2Controller_listAliveProxies","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/proxies/{id}":{"patch":{"operationId":"AdminMarketingV2Controller_updateProxy","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProxyDto"}}}},"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]},"delete":{"operationId":"AdminMarketingV2Controller_deleteProxy","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/proxies/ping":{"post":{"operationId":"AdminMarketingV2Controller_pingProxies","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/campaigns":{"get":{"operationId":"AdminMarketingV2Controller_listCampaigns","parameters":[{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/fb/adsets":{"get":{"operationId":"AdminMarketingV2Controller_listAdsets","parameters":[{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}},{"name":"campaignId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/drafts/{id}/create-in-fb":{"post":{"operationId":"AdminMarketingV2Controller_createDraftInFb","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFbAdDto"}}}},"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/health-signals":{"get":{"operationId":"AdminMarketingV2Controller_getHealthSignals","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/analytics/dashboard":{"get":{"operationId":"AdminMarketingV2Controller_getDashboard","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/analytics/summary":{"get":{"operationId":"AdminMarketingV2Controller_getAnalyticsSummary","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/analytics/cohort-summary":{"get":{"operationId":"AdminMarketingV2Controller_getAnalyticsCohortSummary","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/analytics/accounts":{"get":{"operationId":"AdminMarketingV2Controller_getAccountBreakdown","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/analytics/rows":{"get":{"operationId":"AdminMarketingV2Controller_getAnalyticsRows","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}},{"name":"groupBy","required":true,"in":"query","schema":{"type":"string"}},{"name":"country","required":true,"in":"query","schema":{"type":"string"}},{"name":"expandBy","required":true,"in":"query","schema":{"type":"string"}},{"name":"includeEmpty","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/analytics/video-retention":{"get":{"operationId":"AdminMarketingV2Controller_getVideoRetention","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}},{"name":"dimension","required":true,"in":"query","schema":{"type":"string"}},{"name":"id","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/creative-cover/{metaAdId}":{"get":{"operationId":"AdminMarketingV2Controller_getCreativeCover","parameters":[{"name":"metaAdId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/analytics/insights":{"get":{"operationId":"AdminMarketingV2Controller_getAdInsights","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/cache/invalidate":{"post":{"operationId":"AdminMarketingV2Controller_invalidateCache","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/sync/registry":{"post":{"operationId":"AdminMarketingV2Controller_runRegistrySync","parameters":[{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/sync/insights":{"post":{"operationId":"AdminMarketingV2Controller_runInsightsSync","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunInsightsSyncDto"}}}},"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/admin/marketing-v2/sync/backfill/{accountId}":{"post":{"operationId":"AdminMarketingV2Controller_runDeepBackfill","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin - Marketing v2"]}},"/creative-metrics/registry/concept":{"post":{"operationId":"CreativeRegistryController_registerConcept","parameters":[{"name":"Authorization","in":"header","description":"Bearer <CREO_LOOP_API_KEY>","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterConceptDto"}}}},"responses":{"200":{"description":""}},"summary":"Register a creo-loop hypothesis as a concept (idempotent)","tags":["Creative registry (creo-loop)"]}},"/creative-metrics/registry/creative":{"post":{"operationId":"CreativeRegistryController_registerCreative","parameters":[{"name":"Authorization","in":"header","description":"Bearer <CREO_LOOP_API_KEY>","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterCreativeDto"}}}},"responses":{"200":{"description":""}},"summary":"Register a creo-loop creative, returning creative_uid + url_tags","tags":["Creative registry (creo-loop)"]}},"/creative-metrics/registry/media":{"post":{"operationId":"CreativeRegistryController_registerMedia","parameters":[{"name":"Authorization","in":"header","description":"Bearer <CREO_LOOP_API_KEY>","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterMediaDto"}}}},"responses":{"200":{"description":""}},"summary":"Deliver a creative file by presigned URL","tags":["Creative registry (creo-loop)"]},"get":{"operationId":"CreativeRegistryController_getMediaStatuses","parameters":[{"name":"Authorization","in":"header","description":"Bearer <CREO_LOOP_API_KEY>","required":true,"schema":{"type":"string"}},{"name":"creative_external_ids","required":true,"in":"query","description":"Comma-separated creative ids, up to 100","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Delivery status for up to 100 creatives","tags":["Creative registry (creo-loop)"]}},"/creative-metrics/registry/creative/recall":{"post":{"operationId":"CreativeRegistryController_recallCreative","parameters":[{"name":"Authorization","in":"header","description":"Bearer <CREO_LOOP_API_KEY>","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecallCreativeDto"}}}},"responses":{"200":{"description":""}},"summary":"Recall a creative that has not been launched yet","tags":["Creative registry (creo-loop)"]}},"/creative-metrics/registry/creative/winner":{"post":{"operationId":"CreativeRegistryController_markTestWinner","parameters":[{"name":"Authorization","in":"header","description":"Bearer <CREO_LOOP_API_KEY>","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkTestWinnerDto"}}}},"responses":{"200":{"description":""}},"summary":"Mark/unmark a creative as its test winner","tags":["Creative registry (creo-loop)"]}},"/creative-metrics/changes":{"get":{"operationId":"CreativeMetricsController_getChanges","parameters":[{"name":"Authorization","in":"header","description":"Bearer <CREO_LOOP_API_KEY>","required":true,"schema":{"type":"string"}},{"name":"after","required":false,"in":"query","description":"Opaque cursor from next_cursor","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"default":200,"type":"number"}},{"name":"days","required":false,"in":"query","description":"incremental adds the trailing 35-day daily slice per creative","schema":{"default":"none","type":"string","enum":["none","incremental"]}}],"responses":{"200":{"description":""}},"summary":"Creatives whose metrics or delivery status moved since the cursor","tags":["Creative metrics feed (creo-loop)"]}},"/creative-metrics/snapshot":{"post":{"operationId":"CreativeMetricsController_getSnapshot","parameters":[{"name":"Authorization","in":"header","description":"Bearer <CREO_LOOP_API_KEY>","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotBodyDto"}}}},"responses":{"200":{"description":""}},"summary":"Point read of specific creatives; does not move the feed cursor","tags":["Creative metrics feed (creo-loop)"]}},"/creative-metrics/health":{"get":{"operationId":"CreativeMetricsController_getHealth","parameters":[{"name":"Authorization","in":"header","description":"Bearer <CREO_LOOP_API_KEY>","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Ingestion liveness — tells \"nothing changed\" apart from \"collector is down\"","tags":["Creative metrics feed (creo-loop)"]}},"/admin/meta-marketing/accounts":{"get":{"operationId":"AdminMetaMarketingController_listAccounts","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]},"post":{"operationId":"AdminMetaMarketingController_createAccount","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/accounts/{id}":{"patch":{"operationId":"AdminMetaMarketingController_updateAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]},"delete":{"operationId":"AdminMetaMarketingController_deleteAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/accounts/{id}/test-connection":{"post":{"operationId":"AdminMetaMarketingController_testAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/proxies":{"get":{"operationId":"AdminMetaMarketingController_listProxies","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]},"post":{"operationId":"AdminMetaMarketingController_createProxy","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/proxies/{id}":{"patch":{"operationId":"AdminMetaMarketingController_updateProxy","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]},"delete":{"operationId":"AdminMetaMarketingController_deleteProxy","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/filters/campaigns":{"get":{"operationId":"AdminMetaMarketingController_listCampaignFilterOptions","parameters":[{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}},{"name":"provider","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/filters/creatives":{"get":{"operationId":"AdminMetaMarketingController_listCreativeFilterOptions","parameters":[{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}},{"name":"campaignId","required":true,"in":"query","schema":{"type":"string"}},{"name":"provider","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/insights/ads":{"get":{"operationId":"AdminMetaMarketingController_listAds","parameters":[{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}},{"name":"campaignId","required":true,"in":"query","schema":{"type":"string"}},{"name":"creativeId","required":true,"in":"query","schema":{"type":"string"}},{"name":"adsetId","required":true,"in":"query","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"provider","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateFrom","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":true,"in":"query","schema":{"type":"string"}},{"name":"page","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"sortBy","required":true,"in":"query","schema":{"type":"string"}},{"name":"sortOrder","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/insights/overview":{"get":{"operationId":"AdminMetaMarketingController_getOverview","parameters":[{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}},{"name":"campaignId","required":true,"in":"query","schema":{"type":"string"}},{"name":"creativeId","required":true,"in":"query","schema":{"type":"string"}},{"name":"provider","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateFrom","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/insights/query":{"get":{"operationId":"AdminMetaMarketingController_queryInsights","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/accounts/status-summary":{"get":{"operationId":"AdminMetaMarketingController_getAccountsStatusSummary","parameters":[{"name":"dateFrom","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":true,"in":"query","schema":{"type":"string"}},{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/ltv/adsets":{"get":{"operationId":"AdminMetaMarketingController_getAdsetLtvSummary","parameters":[{"name":"dateFrom","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/creatives/{creativeId}":{"get":{"operationId":"AdminMetaMarketingController_getCreativeDetail","parameters":[{"name":"creativeId","required":true,"in":"path","schema":{"type":"string"}},{"name":"dateFrom","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/creatives/{creativeId}/video":{"get":{"operationId":"AdminMetaMarketingController_streamCreativeVideo","parameters":[{"name":"creativeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/creative-cover/{adId}":{"get":{"operationId":"AdminMetaMarketingController_getCreativeCover","parameters":[{"name":"adId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/sync/status":{"get":{"operationId":"AdminMetaMarketingController_getSyncStatus","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/insights/backfill":{"post":{"operationId":"AdminMetaMarketingController_runInsightsBackfill","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/sync/run":{"post":{"operationId":"AdminMetaMarketingController_runSync","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/launch/options":{"get":{"operationId":"AdminMetaMarketingController_getLaunchOptions","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/creative-asset":{"get":{"operationId":"AdminMetaMarketingController_getCreativeAsset","parameters":[{"name":"url","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/launch/creative":{"post":{"operationId":"AdminMetaMarketingController_launchCreative","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/admin/meta-marketing/guard/run":{"post":{"operationId":"AdminMetaMarketingController_runDestinationGuard","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Meta Marketing"]}},"/v1/live-actions/models":{"get":{"operationId":"LiveActionsController_getModelsWithLiveActions","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetLiveActionModelsResponseDto"}}}}},"summary":"Get paginated list of models with live actions","tags":["Live Actions"]}},"/v1/live-actions/models/{modelId}":{"get":{"operationId":"LiveActionsController_getScenarioByModel","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScenarioResponseDtoSchema"}}}}},"summary":"Get live action scenario for model","tags":["Live Actions"]}},"/v1/live-actions/scenarios/{scenarioId}/purchase":{"post":{"operationId":"LiveActionsController_purchase","parameters":[{"name":"scenarioId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseStateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseResponseDtoSchema"}}}}},"summary":"Purchase access to a live action state","tags":["Live Actions"]}},"/v1/live-actions/models/{modelId}/unlock-all":{"post":{"operationId":"LiveActionsController_unlockAll","parameters":[],"responses":{"410":{"description":"Bulk unlock is currently disabled"}},"summary":"Disabled — live actions are sold one at a time (sequential chain)","tags":["Live Actions"]}},"/admin/live-actions/migration/snapshot/{modelId}":{"get":{"operationId":"LiveActionsAdminController_getMigrationSnapshot[0]","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]}},"/v1/admin/live-actions/migration/snapshot/{modelId}":{"get":{"operationId":"LiveActionsAdminController_getMigrationSnapshot[1]","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]}},"/admin/live-actions/migration/apply":{"post":{"operationId":"LiveActionsAdminController_applyMigration[0]","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyLiveActionsMigrationDto"}}}},"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/v1/admin/live-actions/migration/apply":{"post":{"operationId":"LiveActionsAdminController_applyMigration[1]","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyLiveActionsMigrationDto"}}}},"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/admin/live-actions/models/{modelId}":{"get":{"operationId":"LiveActionsAdminController_getScenarioByModel[0]","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]}},"/v1/admin/live-actions/models/{modelId}":{"get":{"operationId":"LiveActionsAdminController_getScenarioByModel[1]","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]}},"/admin/live-actions/scenarios":{"get":{"operationId":"LiveActionsAdminController_listScenarios[0]","parameters":[{"name":"page","required":false,"in":"query","description":"Page number for pagination (ignored when all=true)","schema":{"minimum":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page (ignored when all=true)","schema":{"minimum":1,"maximum":200,"example":20,"type":"number"}},{"name":"search","required":false,"in":"query","description":"Search by model name or scenario name","schema":{"maxLength":255,"example":"violet","type":"string"}},{"name":"all","required":false,"in":"query","description":"Return every scenario without pagination (used by the display-order manager)","schema":{"example":false,"type":"boolean"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]},"post":{"operationId":"LiveActionsAdminController_createScenario[0]","parameters":[],"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/v1/admin/live-actions/scenarios":{"get":{"operationId":"LiveActionsAdminController_listScenarios[1]","parameters":[{"name":"page","required":false,"in":"query","description":"Page number for pagination (ignored when all=true)","schema":{"minimum":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page (ignored when all=true)","schema":{"minimum":1,"maximum":200,"example":20,"type":"number"}},{"name":"search","required":false,"in":"query","description":"Search by model name or scenario name","schema":{"maxLength":255,"example":"violet","type":"string"}},{"name":"all","required":false,"in":"query","description":"Return every scenario without pagination (used by the display-order manager)","schema":{"example":false,"type":"boolean"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]},"post":{"operationId":"LiveActionsAdminController_createScenario[1]","parameters":[],"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/admin/live-actions/scenarios/bulk-update-order":{"post":{"operationId":"LiveActionsAdminController_bulkUpdateScenariosOrder[0]","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateScenariosOrderDto"}}}},"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/v1/admin/live-actions/scenarios/bulk-update-order":{"post":{"operationId":"LiveActionsAdminController_bulkUpdateScenariosOrder[1]","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateScenariosOrderDto"}}}},"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/admin/live-actions/scenarios/{id}":{"patch":{"operationId":"LiveActionsAdminController_updateScenario[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]},"delete":{"operationId":"LiveActionsAdminController_deleteScenario[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]}},"/v1/admin/live-actions/scenarios/{id}":{"patch":{"operationId":"LiveActionsAdminController_updateScenario[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]},"delete":{"operationId":"LiveActionsAdminController_deleteScenario[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]}},"/admin/live-actions/scenarios/{scenarioId}/states":{"post":{"operationId":"LiveActionsAdminController_createState[0]","parameters":[{"name":"scenarioId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/v1/admin/live-actions/scenarios/{scenarioId}/states":{"post":{"operationId":"LiveActionsAdminController_createState[1]","parameters":[{"name":"scenarioId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/admin/live-actions/scenarios/{scenarioId}/states/bulk-update-order":{"post":{"operationId":"LiveActionsAdminController_bulkUpdateStatesOrder[0]","parameters":[{"name":"scenarioId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStatesOrderDto"}}}},"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/v1/admin/live-actions/scenarios/{scenarioId}/states/bulk-update-order":{"post":{"operationId":"LiveActionsAdminController_bulkUpdateStatesOrder[1]","parameters":[{"name":"scenarioId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStatesOrderDto"}}}},"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/admin/live-actions/states/{id}":{"patch":{"operationId":"LiveActionsAdminController_updateState[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]},"delete":{"operationId":"LiveActionsAdminController_deleteState[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]}},"/v1/admin/live-actions/states/{id}":{"patch":{"operationId":"LiveActionsAdminController_updateState[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]},"delete":{"operationId":"LiveActionsAdminController_deleteState[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["LiveActionsAdmin"]}},"/admin/live-actions/assets/upload-url":{"post":{"operationId":"LiveActionsAdminController_createUploadUrl[0]","parameters":[],"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/v1/admin/live-actions/assets/upload-url":{"post":{"operationId":"LiveActionsAdminController_createUploadUrl[1]","parameters":[],"responses":{"201":{"description":""}},"tags":["LiveActionsAdmin"]}},"/admin/media-processing/submit":{"post":{"description":"Called by the admin upload hooks right after the presigned PUT succeeds, so encoding runs while the operator is still filling in the form. Idempotent.","operationId":"MediaProcessingAdminController_submit","parameters":[],"responses":{"202":{"description":""}},"summary":"Register an uploaded object for transcoding","tags":["Admin Media Processing"]}},"/admin/media-processing/assets":{"get":{"operationId":"MediaProcessingAdminController_listAssets","parameters":[{"name":"prefix","required":false,"in":"query","schema":{"enum":["avatars","chat-images","face_reference","static","live-actions","audio","scene-covers","scene-backgrounds","scene-avatars","motion","shorts"],"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"enum":["pending","processing","ready","partial","failed"],"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"string"}},{"name":"offset","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetListResponseDto"}}}}},"summary":"List registered media assets","tags":["Admin Media Processing"]}},"/admin/media-processing/assets/by-key":{"get":{"description":"What the admin upload widget polls while an encode is running.","operationId":"MediaProcessingAdminController_getByKey","parameters":[{"name":"prefix","required":true,"in":"query","schema":{"enum":["avatars","chat-images","face_reference","static","live-actions","audio","scene-covers","scene-backgrounds","scene-avatars","motion","shorts"],"type":"string"}},{"name":"key","required":true,"in":"query","schema":{"type":"string"}},{"name":"bucket","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Status of one source key","tags":["Admin Media Processing"]}},"/admin/media-processing/assets/{id}":{"get":{"operationId":"MediaProcessingAdminController_getAsset","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetResponseDto"}}}}},"summary":"Get one media asset","tags":["Admin Media Processing"]}},"/admin/media-processing/assets/{id}/retry":{"post":{"operationId":"MediaProcessingAdminController_retry","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"202":{"description":""}},"summary":"Re-queue every unfinished rendition of an asset","tags":["Admin Media Processing"]}},"/admin/media-processing/backfill":{"post":{"description":"Skips derivative keys and subtitle sidecars. Use dryRun to see what would be picked up before spending Lambda time on it.","operationId":"MediaProcessingAdminController_backfill","parameters":[],"responses":{"202":{"description":""}},"summary":"Register existing objects under a prefix","tags":["Admin Media Processing"]}},"/v1/shorts":{"get":{"operationId":"ShortsController_getShorts","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetShortsResponseDto"}}}}},"summary":"Get paginated list of published shorts","tags":["Shorts"]}},"/v1/shorts/by-model/{modelId}":{"get":{"operationId":"ShortsController_getShortsByModel","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ShortListItemDtoSchema"}}}}}},"summary":"Get all shorts (dramas) featuring a model","tags":["Shorts"]}},"/v1/shorts/slug/{slug}":{"get":{"operationId":"ShortsController_getShortBySlug","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShortResponseDtoSchema"}}}}},"summary":"Get a short by slug","tags":["Shorts"]}},"/v1/shorts/episodes/{episodeId}/subtitles":{"get":{"operationId":"ShortsController_getEpisodeSubtitles","parameters":[{"name":"episodeId","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get subtitles for an unlocked episode","tags":["Shorts"]}},"/v1/shorts/{id}":{"get":{"operationId":"ShortsController_getShortById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShortResponseDtoSchema"}}}}},"summary":"Get a short by id","tags":["Shorts"]}},"/v1/shorts/{shortId}/episodes/{episodeId}/purchase":{"post":{"operationId":"ShortsController_purchaseEpisode","parameters":[{"name":"shortId","required":true,"in":"path","schema":{"type":"string"}},{"name":"episodeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseResponseDtoSchema"}}}}},"summary":"Purchase (unlock) a short episode","tags":["Shorts"]}},"/admin/shorts":{"get":{"operationId":"ShortsAdminController_getShortsList[0]","parameters":[],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]},"post":{"operationId":"ShortsAdminController_createShort[0]","parameters":[],"responses":{"201":{"description":""}},"tags":["ShortsAdmin"]}},"/v1/admin/shorts":{"get":{"operationId":"ShortsAdminController_getShortsList[1]","parameters":[],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]},"post":{"operationId":"ShortsAdminController_createShort[1]","parameters":[],"responses":{"201":{"description":""}},"tags":["ShortsAdmin"]}},"/admin/shorts/{id}":{"get":{"operationId":"ShortsAdminController_getShort[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]},"patch":{"operationId":"ShortsAdminController_updateShort[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]},"delete":{"operationId":"ShortsAdminController_deleteShort[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]}},"/v1/admin/shorts/{id}":{"get":{"operationId":"ShortsAdminController_getShort[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]},"patch":{"operationId":"ShortsAdminController_updateShort[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]},"delete":{"operationId":"ShortsAdminController_deleteShort[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]}},"/admin/shorts/{shortId}/episodes":{"post":{"operationId":"ShortsAdminController_createEpisode[0]","parameters":[{"name":"shortId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["ShortsAdmin"]}},"/v1/admin/shorts/{shortId}/episodes":{"post":{"operationId":"ShortsAdminController_createEpisode[1]","parameters":[{"name":"shortId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["ShortsAdmin"]}},"/admin/shorts/episodes/{id}":{"patch":{"operationId":"ShortsAdminController_updateEpisode[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]},"delete":{"operationId":"ShortsAdminController_deleteEpisode[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]}},"/v1/admin/shorts/episodes/{id}":{"patch":{"operationId":"ShortsAdminController_updateEpisode[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]},"delete":{"operationId":"ShortsAdminController_deleteEpisode[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]}},"/admin/shorts/episodes/{episodeId}/subtitles":{"put":{"operationId":"ShortsAdminController_upsertSubtitle[0]","parameters":[{"name":"episodeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]}},"/v1/admin/shorts/episodes/{episodeId}/subtitles":{"put":{"operationId":"ShortsAdminController_upsertSubtitle[1]","parameters":[{"name":"episodeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]}},"/admin/shorts/episodes/{episodeId}/subtitles/{locale}":{"delete":{"operationId":"ShortsAdminController_deleteSubtitle[0]","parameters":[{"name":"episodeId","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]}},"/v1/admin/shorts/episodes/{episodeId}/subtitles/{locale}":{"delete":{"operationId":"ShortsAdminController_deleteSubtitle[1]","parameters":[{"name":"episodeId","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]}},"/admin/shorts/{id}/translations":{"get":{"operationId":"ShortsAdminController_getTranslations[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]}},"/v1/admin/shorts/{id}/translations":{"get":{"operationId":"ShortsAdminController_getTranslations[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ShortsAdmin"]}},"/admin/shorts/{id}/translations/generate":{"post":{"operationId":"ShortsAdminController_generateTranslations[0]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateShortTranslationsDto"}}}},"responses":{"201":{"description":""}},"tags":["ShortsAdmin"]}},"/v1/admin/shorts/{id}/translations/generate":{"post":{"operationId":"ShortsAdminController_generateTranslations[1]","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateShortTranslationsDto"}}}},"responses":{"201":{"description":""}},"tags":["ShortsAdmin"]}},"/admin/shorts/assets/video-upload-url":{"post":{"operationId":"ShortsAdminController_createVideoUploadUrl[0]","parameters":[],"responses":{"201":{"description":""}},"tags":["ShortsAdmin"]}},"/v1/admin/shorts/assets/video-upload-url":{"post":{"operationId":"ShortsAdminController_createVideoUploadUrl[1]","parameters":[],"responses":{"201":{"description":""}},"tags":["ShortsAdmin"]}},"/admin/shorts/assets/subtitle-upload-url":{"post":{"operationId":"ShortsAdminController_createSubtitleUploadUrl[0]","parameters":[],"responses":{"201":{"description":""}},"tags":["ShortsAdmin"]}},"/v1/admin/shorts/assets/subtitle-upload-url":{"post":{"operationId":"ShortsAdminController_createSubtitleUploadUrl[1]","parameters":[],"responses":{"201":{"description":""}},"tags":["ShortsAdmin"]}},"/v2/live-action-chats/models/{modelId}":{"post":{"operationId":"LiveActionChatController_getOrCreateChat","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveActionChatResponseDto"}}}}},"summary":"Get or create a live-action chat for a model","tags":["Live Action Chat"]}},"/v2/live-action-chats/{chatId}/messages":{"get":{"operationId":"LiveActionChatController_getMessages","parameters":[{"name":"chatId","required":true,"in":"path","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Cursor for pagination","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Number of messages to retrieve","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMessagesResponseDto"}}}}},"summary":"Get messages for a live-action chat","tags":["Live Action Chat"]},"post":{"operationId":"LiveActionChatController_sendMessage","parameters":[{"name":"chatId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendLiveActionMessageDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageDto"}}}}},"summary":"Send a text message in a live-action chat","tags":["Live Action Chat"]}},"/v2/chats":{"get":{"operationId":"ChatsV2Controller_getChats","parameters":[],"responses":{"200":{"description":"Chats retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/V2CreateChatResponseDto"}}}}}},"summary":"Get all user chats with scene information (v2)","tags":["Chats v2"]},"post":{"operationId":"ChatsV2Controller_createChat","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2CreateChatDto"}}}},"responses":{"201":{"description":"Chat created or found successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2CreateChatResponseDto"}}}},"404":{"description":"Model or scene not found"}},"summary":"Create or find a chat with optional scene support (v2)","tags":["Chats v2"]}},"/v2/chats/unread-count":{"get":{"operationId":"ChatsV2Controller_getUnreadMessagesCount","parameters":[],"responses":{"200":{"description":"Unread messages count retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadMessagesCountDto"}}}}},"summary":"Get total unread incoming messages across all user chats","tags":["Chats v2"]}},"/v2/chats/text-message-quota":{"get":{"operationId":"ChatsV2Controller_getTextMessageQuota","parameters":[],"responses":{"200":{"description":"Text message quota retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextMessageQuotaResponseDto"}}}}},"summary":"Get current usage of the non-billable text message limit for the user","tags":["Chats v2"]}},"/v2/chats/affiliate-quota":{"get":{"description":"Message/photo limits and usage for anonymous affiliate-link visitors. active=false for everyone else.","operationId":"ChatsV2Controller_getAffiliateQuota","parameters":[],"responses":{"200":{"description":"Affiliate quota retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateQuotaResponseDto"}}}}},"summary":"Affiliate link chat quota (GEN-1317)","tags":["Chats v2"]}},"/v2/chats/{id}":{"get":{"operationId":"ChatsV2Controller_getChatById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Chat retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2CreateChatResponseDto"}}}},"404":{"description":"Chat not found"}},"summary":"Get chat by id with scene information (v2)","tags":["Chats v2"]},"delete":{"operationId":"ChatsV2Controller_deleteChat","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Chat deleted successfully"},"404":{"description":"Chat not found"}},"summary":"Delete chat by id (v2)","tags":["Chats v2"]}},"/v2/chats/onboarding/scenarios":{"get":{"operationId":"ChatsV2Controller_getOnboardingScenarios","parameters":[],"responses":{"200":{"description":"Onboarding scenarios retrieved (empty if user already has chats)"}},"summary":"Get available onboarding scenarios for first-time users","tags":["Chats v2"]}},"/v2/chats/{id}/send-message":{"post":{"operationId":"ChatsV2Controller_sendMessage","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendMessageDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendMessageResponseDto"}}}}},"summary":"Enqueue send message job (v2)","tags":["Chats v2"]}},"/v2/chats/{id}/attachments/{attachmentId}/retry-generation":{"post":{"operationId":"ChatsV2Controller_retryAttachmentGeneration","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"attachmentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Retry failed chat image generation (re-bills and reuses stored prompts)","tags":["Chats v2"]}},"/v2/chats/{id}/request-image":{"post":{"operationId":"ChatsV2Controller_requestImage","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2RequestImageDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendMessageResponseDto"}}}}},"summary":"Enqueue request image job (v2)","tags":["Chats v2"]}},"/v2/chats/{id}/show-scene":{"post":{"operationId":"ChatsV2Controller_showScene","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2ShowSceneDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendMessageResponseDto"}}}},"400":{"description":"Cooldown has not elapsed yet"},"403":{"description":"Visual Director not available for this chat"}},"summary":"On-demand \"Show me the scene\" — triggers Visual Director once the cooldown has elapsed","tags":["Chats v2"]}},"/v2/chats/{id}/show-scene-video":{"post":{"operationId":"ChatsV2Controller_showSceneVideo","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2ShowSceneVideoDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendMessageResponseDto"}}}},"400":{"description":"Cooldown has not elapsed yet"},"403":{"description":"Subscription or Visual Director access is required"}},"summary":"On-demand Visual Director video — generates the scene image and animates it","tags":["Chats v2"]}},"/v2/chats/{id}/send-gift":{"post":{"operationId":"ChatsV2Controller_sendGift","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendGiftDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendMessageResponseDto"}}}}},"summary":"Send a gift (v2)","tags":["Chats v2"]}},"/v2/chats/{id}/request-video":{"post":{"operationId":"ChatsV2Controller_requestVideo","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2RequestImageDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendMessageResponseDto"}}}}},"summary":"Enqueue request video (v2)","tags":["Chats v2"]}},"/v2/chats/{id}/animate":{"post":{"operationId":"ChatsV2Controller_animateAttachments","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2AnimateAttachmentsRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendMessageResponseDto"}}}}},"summary":"Animate existing image attachments into video(s) (v2)","tags":["Chats v2"]}},"/v2/chats/{id}/messages/{messageId}/voice":{"post":{"operationId":"ChatsV2Controller_requestVoiceOver","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"messageId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Request voice-over for a message (v2)","tags":["Chats v2"]}},"/v2/chats/{id}/voice-message/upload-url":{"post":{"operationId":"ChatsV2Controller_generateVoiceMessageUploadUrl","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"uploadUrl":{"type":"string","description":"Pre-signed S3 upload URL"},"audioS3Key":{"type":"string","description":"S3 key for later reference"}}}}}}},"summary":"Generate pre-signed S3 upload URL for voice message audio","tags":["Chats v2"]}},"/v2/chats/{id}/voice-message":{"post":{"operationId":"ChatsV2Controller_sendVoiceMessage","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendVoiceMessageDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendMessageResponseDto"}}}}},"summary":"Send voice message with optional text (v2)","tags":["Chats v2"]}},"/v2/chats/{id}/suggestions":{"get":{"operationId":"ChatsV2Controller_generateSuggestions","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfully generated three suggested responses","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateSuggestionsResponseDto"}}}},"403":{"description":"User does not have an active premium subscription"},"404":{"description":"Chat not found"}},"summary":"Generate three suggested user responses based on chat history (Premium only)","tags":["Chats v2"]},"patch":{"operationId":"ChatsV2Controller_toggleSuggestions","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleSuggestionsDto"}}}},"responses":{"200":{"description":"Successfully toggled suggestions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleSuggestionsResponseDto"}}}},"404":{"description":"Chat not found"}},"summary":"Toggle suggestions on/off for a chat","tags":["Chats v2"]}},"/v2/chats/{id}/send-photo/upload-url":{"get":{"operationId":"ChatsV2Controller_generateSendPhotoUploadUrl","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"uploadUrl":{"type":"string","description":"Pre-signed S3 upload URL"},"photoS3Key":{"type":"string","description":"S3 key for later reference"},"finalUrl":{"type":"string","description":"Final CDN URL where the image will be accessible"}}}}}},"403":{"description":"User does not have an active subscription"}},"summary":"Generate pre-signed S3 upload URL for user photo (Subscribers only)","tags":["Chats v2"]}},"/v2/chats/{id}/send-photo":{"post":{"operationId":"ChatsV2Controller_sendPhoto","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendPhotoDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V2SendMessageResponseDto"}}}},"403":{"description":"User does not have an active subscription"},"422":{"description":"Photo refused by media moderation. Body carries a MediaModerationErrorCode in `errorCode` (minor / public figure / graphic content). No coins are charged and the object is deleted."}},"summary":"Send user photo with optional text (v2) (Subscribers only)","tags":["Chats v2"]}},"/v2/chats/{id}/suggestions/status":{"get":{"operationId":"ChatsV2Controller_getSuggestionsStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfully retrieved suggestions status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSuggestionsStatusResponseDto"}}}},"404":{"description":"Chat not found"}},"summary":"Get suggestions status for a chat","tags":["Chats v2"]}},"/v2/chats/{id}/messages":{"get":{"operationId":"ChatsV2Controller_getMessages","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Cursor for pagination","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Number of messages to retrieve","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}}],"responses":{"200":{"description":"Messages retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMessagesResponseDto"}}}},"404":{"description":"Chat not found"}},"summary":"Get messages by chat id with cursor-based pagination (v2)","tags":["Chats v2"]}},"/v2/chats/{id}/messages/{messageId}/moderation-feedback":{"post":{"operationId":"ChatsV2Controller_submitModerationFeedback","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"messageId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Feedback recorded; returns the updated message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageDto"}}}},"404":{"description":"Chat or message not found"}},"summary":"Record \"wrongly blocked\" feedback on a moderation-blocked message (v2)","tags":["Chats v2"]}},"/v2/chats/{id}/messages/read":{"patch":{"operationId":"ChatsV2Controller_markMessagesRead","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkMessagesReadDto"}}}},"responses":{"200":{"description":"Messages updated","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MessageDto"}}}}},"404":{"description":"Chat or messages not found"}},"summary":"Mark chat messages as read (incoming / character messages)","tags":["Chats v2"]}},"/v2/chats/messages/report":{"post":{"operationId":"ChatsV2Controller_reportMessage","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMessageReportRequestDto"}}}},"responses":{"201":{"description":"Message reported successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMessageReportResponseDto"}}}},"422":{"description":"Validation failed or already reported"}},"summary":"Report a message (v2)","tags":["Chats v2"]}},"/webhooks/image/runpod":{"post":{"operationId":"ChatV2ImageWebhooksController_runpodImage","parameters":[],"responses":{"200":{"description":""}},"tags":["ChatV2ImageWebhooks"]}},"/webhooks/image/fast-runpod":{"post":{"operationId":"ChatV2ImageWebhooksController_fastRunpodImage","parameters":[],"responses":{"200":{"description":""}},"tags":["ChatV2ImageWebhooks"]}},"/webhooks/image/flux-klein-fast":{"post":{"operationId":"ChatV2ImageWebhooksController_fluxKleinFastImage","parameters":[],"responses":{"200":{"description":""}},"tags":["ChatV2ImageWebhooks"]}},"/webhooks/video/runpod":{"post":{"operationId":"ChatV2VideoWebhooksController_ltxRunpodVideo","parameters":[{"name":"outputKey","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["ChatV2VideoWebhooks"]}},"/webhooks/video/wavespeed":{"post":{"operationId":"ChatV2VideoWebhooksController_runpodVideo","parameters":[],"responses":{"200":{"description":""}},"tags":["ChatV2VideoWebhooks"]}},"/elevenlabs/tts":{"post":{"operationId":"ElevenLabsController_textToSpeech","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ElevenLabsTtsRequestDto"}}}},"responses":{"200":{"description":""}},"summary":"Test TTS: synthesize text and save to file","tags":["ElevenLabs"]}},"/elevenlabs/stt":{"post":{"operationId":"ElevenLabsController_speechToText","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ElevenLabsSttRequestDto"}}}},"responses":{"200":{"description":""}},"summary":"Test STT: download from S3 and transcribe","tags":["ElevenLabs"]}},"/v1/calls/assistants/by-model/{modelId}":{"post":{"operationId":"AssistantsController_createByModel","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Assistants"]},"get":{"operationId":"AssistantsController_getByModel","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Assistants"]}},"/v1/calls/assistants/{assistantId}":{"put":{"operationId":"AssistantsController_update","parameters":[{"name":"assistantId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Assistants"]},"delete":{"operationId":"AssistantsController_remove","parameters":[{"name":"assistantId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Assistants"]}},"/webhook/vapi":{"post":{"operationId":"WebhookController_processVapiWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhook"]}},"/v1/calls/create":{"post":{"operationId":"CallsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCallRequestDto"}}}},"responses":{"200":{"description":"Assistant created or existing returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Assistant"}}}},"400":{"description":"User not found in context or validation failed"}},"summary":"Create assistant for model (if missing) and return it","tags":["Calls"]}},"/v1/calls/control/say":{"post":{"operationId":"CallsController_say","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SayMessageRequestDto"}}}},"responses":{"200":{"description":"Message sent"},"400":{"description":"Validation or control error"}},"summary":"Say a message into an active call (testing)","tags":["Calls"]}},"/v1/calls/control/end":{"post":{"operationId":"CallsController_end","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndCallRequestDto"}}}},"responses":{"200":{"description":"Call end requested"},"400":{"description":"Validation or control error"}},"summary":"End a call (testing)","tags":["Calls"]}},"/v1/calls/report/reasons":{"get":{"operationId":"CallsController_getCallReportReasons","parameters":[],"responses":{"200":{"description":"Call report reasons retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCallReportReasonsResponseDto"}}}}},"summary":"Get available call report reasons","tags":["Calls"]}},"/v1/calls/report":{"post":{"operationId":"CallsController_reportCall","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCallReportRequestDto"}}}},"responses":{"200":{"description":"Call reported successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCallReportResponseDto"}}}}},"summary":"Report a call","tags":["Calls"]}},"/v1/admin/cron/trigger-cleanup":{"post":{"operationId":"CronController_triggerCleanup","parameters":[],"responses":{"201":{"description":""}},"summary":"Manually trigger stalled items cleanup (Admin only)","tags":["Cron"]}},"/v1/admin/cron/jobs":{"get":{"operationId":"CronController_listJobs","parameters":[],"responses":{"200":{"description":""}},"summary":"List all registered repeatable cron jobs","tags":["Cron"]}},"/v1/web-users":{"post":{"description":"Creates a new web user or updates existing one based on firebaseUid. Returns webUserId to store in localStorage.","operationId":"WebUsersController_createWebUser","parameters":[{"name":"origin","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebUserDto"}}}},"responses":{"200":{"description":"Web user created or updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebUserResponseDto"}}}}},"summary":"Create or update web user","tags":["Web Users"]},"put":{"description":"Updates an existing web user by firebaseUid or webUserId. Used for progressive persistence.","operationId":"WebUsersController_updateWebUser","parameters":[{"name":"origin","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebUserDto"}}}},"responses":{"200":{"description":"Web user updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebUserResponseDto"}}}}},"summary":"Update existing web user","tags":["Web Users"]}},"/v1/web-users/metadata":{"post":{"description":"Stores Cloudflare/request header metadata and optional client-side metadata for a web user. Intended for anonymous web funnel tracking.","operationId":"WebUsersController_trackMetadata","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackUserMetadataDto"}}}},"responses":{"200":{"description":"Metadata tracked successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMetadataResponseDto"}}}},"404":{"description":"Web user not found"}},"summary":"Track web user metadata","tags":["Web Users"]}},"/v1/web-users/facebook-purchase-events":{"post":{"description":"Stores the client-side Purchase metadata that the web app sends or intends to send to Facebook. Requires the regular Firebase Authorization bearer token.","operationId":"WebUsersController_trackFacebookPurchaseEvent","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackFacebookPurchaseEventDto"}}}},"responses":{"201":{"description":"Facebook Purchase event log created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackFacebookPurchaseEventResponseDto"}}}}},"summary":"Track Facebook Purchase event payload","tags":["Web Users"]}},"/v1/web-users/demo/init":{"post":{"description":"Selects a demo model based on funnel answers, pre-creates an anonymous user, and returns a Firebase custom token for authentication.","operationId":"WebUsersController_initDemo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InitDemoDto"}}}},"responses":{"200":{"description":"Demo initialized successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InitDemoResponseDto"}}}},"400":{"description":"Web user missing Firebase UID"},"404":{"description":"Web user or demo model not found"}},"summary":"Initialize demo access","tags":["Web Users"]}},"/v1/web-users/demo/quick-start":{"post":{"description":"Creates an anonymous Firebase user, web user, and app user from scratch. Selects a demo model and returns a Firebase custom token. No funnel data required.","operationId":"WebUsersController_quickStartDemo","parameters":[],"responses":{"200":{"description":"Demo quick-start successful","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InitDemoResponseDto"}}}},"404":{"description":"No demo models available"}},"summary":"Quick-start demo access","tags":["Web Users"]}},"/v1/web-users/affiliate/start":{"post":{"description":"Creates an anonymous Firebase user, web user (funnel type + UTM/ad attribution) and acquisition session, resolves the model by public slug, and returns a Firebase custom token so the visitor lands straight in the chat.","operationId":"WebUsersController_affiliateStart","parameters":[{"name":"origin","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateStartDto"}}}},"responses":{"200":{"description":"Affiliate entry successful","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateStartResponseDto"}}}},"404":{"description":"Model not found by slug"}},"summary":"Affiliate link entry (GEN-1317)","tags":["Web Users"]}},"/v1/web-users/demo/preferences":{"post":{"description":"Returns a Firebase custom token and modelId for the demo. When `Authorization: Bearer <Firebase ID token>` is sent (e.g. client anonymous auth), the same Firebase UID is used: existing web_users row is updated with preferences instead of creating a second user. Without a valid token, creates a server-side anonymous Firebase user (legacy). Only ethnicity and age are used to pick among isDemo models: age 18 → young band, 20 or 30 → mid band, 40 → mature band. Body, voice, and temperament are stored on the web user but ignored for matching.","operationId":"WebUsersController_demoWithPreferences","parameters":[{"name":"authorization","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoWithPreferencesDto"}}}},"responses":{"200":{"description":"Demo initialized with preferences","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InitDemoResponseDto"}}}},"404":{"description":"No demo model for that ethnicity and age band (isDemo, not deleted)"}},"summary":"Demo access with funnel preferences","tags":["Web Users"]}},"/v1/web-users/by-firebase-uid/{firebaseUid}":{"get":{"description":"Retrieves web user data by Firebase UID. Used by the web app to resolve ad metadata for tracking postbacks.","operationId":"WebUsersController_getWebUserByFirebaseUid","parameters":[{"name":"firebaseUid","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Web user retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebUserDto"}}}},"404":{"description":"Web user not found"}},"summary":"Get web user by Firebase UID","tags":["Web Users"]}},"/v1/web-users/{id}/events":{"post":{"description":"Tracks key conversion events: email_captured, signup_completed, funnel_completed, purchase_completed","operationId":"WebUsersController_trackEvent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackEventDto"}}}},"responses":{"204":{"description":"Event tracked successfully"},"404":{"description":"Web user not found"}},"summary":"Track conversion event","tags":["Web Users"]}},"/v1/web-users/{id}":{"get":{"description":"Retrieves web user data for debugging or data recovery","operationId":"WebUsersController_getWebUser","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Web user retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebUserDto"}}}},"404":{"description":"Web user not found"}},"summary":"Get web user by ID","tags":["Web Users"]}},"/v1/admin/email-campaigns":{"get":{"operationId":"AdminEmailCampaignsController_list","parameters":[],"responses":{"200":{"description":""}},"summary":"List campaigns with headline stats","tags":["Admin Email Campaigns"]},"post":{"operationId":"AdminEmailCampaignsController_create","parameters":[],"responses":{"201":{"description":""}},"summary":"Create a campaign (draft)","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/offers":{"get":{"operationId":"AdminEmailCampaignsController_listOffers","parameters":[],"responses":{"200":{"description":""}},"summary":"List funnel paywall slugs usable as offers","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/suppressions":{"get":{"operationId":"AdminEmailCampaignsController_listSuppressions","parameters":[],"responses":{"200":{"description":""}},"summary":"List suppressed emails","tags":["Admin Email Campaigns"]},"post":{"operationId":"AdminEmailCampaignsController_addSuppression","parameters":[],"responses":{"201":{"description":""}},"summary":"Manually suppress an email","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/{id}":{"get":{"operationId":"AdminEmailCampaignsController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get one campaign with steps","tags":["Admin Email Campaigns"]},"patch":{"operationId":"AdminEmailCampaignsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Update campaign settings","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/{id}/status":{"post":{"operationId":"AdminEmailCampaignsController_setStatus","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Activate / pause / archive a campaign","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/{id}/launch":{"post":{"operationId":"AdminEmailCampaignsController_launch","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Launch a broadcast campaign (enroll audience)","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/{id}/audience/preview":{"post":{"operationId":"AdminEmailCampaignsController_previewAudience","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Count + sample the campaign audience","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/{id}/steps":{"post":{"operationId":"AdminEmailCampaignsController_createStep","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Add a step","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/{id}/steps/{stepId}":{"patch":{"operationId":"AdminEmailCampaignsController_updateStep","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"stepId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Update a step","tags":["Admin Email Campaigns"]},"delete":{"operationId":"AdminEmailCampaignsController_deleteStep","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"stepId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Delete a step","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/{id}/steps/{stepId}/preview":{"get":{"operationId":"AdminEmailCampaignsController_previewStep","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"stepId","required":true,"in":"path","schema":{"type":"string"}},{"name":"locale","required":true,"in":"query","schema":{"type":"string"}},{"name":"characterKey","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Rendered HTML preview of a step","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/{id}/steps/{stepId}/test-send":{"post":{"operationId":"AdminEmailCampaignsController_testSend","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"stepId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Send a test email of a step to a given address","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/{id}/stats":{"get":{"operationId":"AdminEmailCampaignsController_stats","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Campaign funnel stats per step","tags":["Admin Email Campaigns"]}},"/v1/admin/email-campaigns/{id}/enrollments":{"get":{"operationId":"AdminEmailCampaignsController_enrollments","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"List enrollments (debug)","tags":["Admin Email Campaigns"]}},"/v1/acquisition/sessions/resume/{resumeToken}":{"get":{"description":"Returns the acquisition session behind a campaign-email resume token so the funnel can rehydrate answers and jump to the paywall.","operationId":"AcquisitionResumeController_resume","parameters":[{"name":"resumeToken","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Resolve an email resume token into an acquisition session","tags":["Acquisition Sessions"]}},"/v1/payments/create-intent":{"post":{"description":"Creates a Stripe PaymentIntent for one-time payments. Returns clientSecret for frontend Stripe.js integration.","operationId":"StripePaymentsController_createPaymentIntent","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentIntentDto"}}}},"responses":{"200":{"description":"Payment intent created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentIntentResponseDto"}}}},"400":{"description":"Invalid request parameters or Stripe error"}},"summary":"Create a payment intent","tags":["Stripe Payments"]}},"/v1/payments/create-subscription":{"post":{"description":"Creates a Stripe Subscription for recurring payments. Returns clientSecret for frontend payment confirmation.","operationId":"StripePaymentsController_createSubscription","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionDto"}}}},"responses":{"200":{"description":"Subscription created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionResponseDto"}}}},"400":{"description":"Invalid request parameters or Stripe error"}},"summary":"Create a subscription","tags":["Stripe Payments"]}},"/v1/payments/create-customer":{"post":{"description":"Creates a Stripe Customer early in the funnel (e.g., at email capture). The customer ID can be reused when creating a subscription later. If a customer with this email already exists, returns the existing customer.","operationId":"StripePaymentsController_createCustomer","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerDto"}}}},"responses":{"200":{"description":"Customer created or found successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerResponseDto"}}}},"400":{"description":"Invalid request parameters or Stripe error"}},"summary":"Create a Stripe customer","tags":["Stripe Payments"]}},"/v1/payments/products":{"get":{"description":"Returns all available web subscription products with their Stripe price IDs and coin amounts. Use this endpoint to fetch the latest plans for the frontend paywall.","operationId":"StripePaymentsController_getWebSubscriptionProducts","parameters":[],"responses":{"200":{"description":"Web subscription products retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebSubscriptionProductsResponseDto"}}}}},"summary":"Get available web subscription products","tags":["Stripe Payments"]}},"/v1/payments/subscription-management-link":{"get":{"description":"Generates a URL for the subscription management webview. Requires authentication. The URL includes a token that can be used to authenticate in the webview.","operationId":"StripePaymentsController_getSubscriptionManagementLink","parameters":[],"responses":{"200":{"description":"Subscription management link generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionManagementLinkResponseDto"}}}},"401":{"description":"User not authenticated"},"404":{"description":"User not found or no Firebase account linked"}},"security":[{"bearer":[]}],"summary":"Generate subscription management link","tags":["Stripe Payments"]}},"/v1/payments/subscription-portal":{"get":{"description":"Returns a beautifully styled HTML page with subscription information. Add ?format=json to get JSON response instead. Used by the subscription management webview.","operationId":"StripePaymentsController_getSubscriptionPortal","parameters":[{"name":"token","required":true,"in":"query","description":"JWT token for authentication","schema":{"type":"string"}},{"name":"format","required":false,"in":"query","description":"Response format: \"json\" for JSON, otherwise returns HTML","schema":{"enum":["json"],"type":"string"}}],"responses":{"200":{"description":"Subscription portal page or info retrieved successfully"},"401":{"description":"Invalid or expired token"},"404":{"description":"User not found"}},"summary":"Get subscription portal page","tags":["Stripe Payments"]}},"/v1/studio/price":{"get":{"description":"Returns the current prices for image and video generation","operationId":"StudioController_getPrice","parameters":[],"responses":{"200":{"description":"Prices retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioPriceDto"}}}}},"summary":"Get studio generation prices","tags":["Studio"]}},"/v1/studio/characters":{"get":{"description":"Returns paginated list of characters ordered by: recent conversations, then private models, then public models. Optional query `gender` filters to male or female only.","operationId":"StudioController_getCharacters","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"default":20,"type":"number"}},{"name":"gender","required":false,"in":"query","description":"When set, only characters with this gender are returned. Omit to list all.","schema":{"type":"string","enum":["male","female"]}}],"responses":{"200":{"description":"Characters retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioCharactersResponseDto"}}}}},"summary":"Get available characters for studio generation","tags":["Studio"]}},"/v1/studio/image":{"post":{"description":"Generates image(s) for the specified character using AI presets. Returns immediately with generation status.","operationId":"StudioController_requestImage","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioRequestImageDto"}}}},"responses":{"201":{"description":"Image generation started successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioGenerationResponseDto"}}}},"402":{"description":"Insufficient coin balance"},"403":{"description":"User does not have access to this model or subscription required (studio generation is only available to subscribers)"},"404":{"description":"Model or preset parameters not found"}},"summary":"Request studio image generation","tags":["Studio"]}},"/v1/studio/video":{"post":{"description":"Generates video for the specified character. First generates an image, then converts to video. Returns immediately with generation status.","operationId":"StudioController_requestVideo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioRequestVideoDto"}}}},"responses":{"201":{"description":"Video generation started successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioGenerationResponseDto"}}}},"402":{"description":"Insufficient coin balance"},"403":{"description":"User does not have access to this model or subscription required (studio generation is only available to subscribers)"},"404":{"description":"Model or preset parameters not found"}},"summary":"Request studio video generation","tags":["Studio"]}},"/v1/studio/custom-image":{"post":{"description":"Generates image(s) for the specified character using a custom text prompt. The prompt is composed with model-specific styling before generation.","operationId":"StudioController_requestCustomImage","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioRequestCustomImageDto"}}}},"responses":{"201":{"description":"Custom image generation started successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioGenerationResponseDto"}}}},"402":{"description":"Insufficient coin balance"},"403":{"description":"User does not have access to this model or subscription required"},"404":{"description":"Model not found"}},"summary":"Request custom prompt image generation","tags":["Studio"]}},"/v1/studio/custom-video":{"post":{"description":"Generates video for the specified character using a custom text prompt. First generates an image, then converts to video.","operationId":"StudioController_requestCustomVideo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioRequestCustomVideoDto"}}}},"responses":{"201":{"description":"Custom video generation started successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioGenerationResponseDto"}}}},"402":{"description":"Insufficient coin balance"},"403":{"description":"User does not have access to this model or subscription required"},"404":{"description":"Model not found"}},"summary":"Request custom prompt video generation","tags":["Studio"]}},"/v1/studio/animate":{"post":{"description":"Takes an existing image attachment and generates a video from it. Returns immediately with generation status.","operationId":"StudioController_animateImage","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioAnimateImageDto"}}}},"responses":{"201":{"description":"Animation started successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioGenerationResponseDto"}}}},"402":{"description":"Insufficient coin balance"},"403":{"description":"User does not have access to this attachment, attachment is not a valid image, or subscription required (video generation is only available to subscribers)"},"404":{"description":"Attachment not found"}},"summary":"Animate an existing image to video","tags":["Studio"]}},"/v1/studio/upscale":{"post":{"description":"Takes an existing image or video attachment and generates a new upscaled attachment from it. Returns immediately with generation status.","operationId":"StudioController_upscaleAttachment","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioUpscaleAttachmentDto"}}}},"responses":{"201":{"description":"Upscale started successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioGenerationResponseDto"}}}},"402":{"description":"Insufficient coin balance"},"403":{"description":"User does not have access to this attachment, attachment is not a completed image/video, or subscription required (upscaling is only available to subscribers)"},"404":{"description":"Attachment not found"}},"summary":"Upscale an existing image or video attachment","tags":["Studio"]}},"/webhooks/studio/video/runpod":{"post":{"operationId":"StudioWebhooksController_handleLtxVideoWebhook","parameters":[{"name":"outputKey","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Webhooks - Studio"]}},"/webhooks/studio/image":{"post":{"operationId":"StudioWebhooksController_handleImageWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhooks - Studio"]}},"/webhooks/studio/video":{"post":{"operationId":"StudioWebhooksController_handleVideoWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhooks - Studio"]}},"/webhooks/studio/upscale":{"post":{"operationId":"StudioWebhooksController_handleUpscaleWebhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhooks - Studio"]}},"/admin/vd-benchmark/scenarios":{"get":{"operationId":"BenchmarkController_scenarios","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin Visual Director Benchmark"]}},"/admin/vd-benchmark/runs":{"get":{"operationId":"BenchmarkController_runs","parameters":[{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin Visual Director Benchmark"]}},"/admin/vd-benchmark/runs/{id}":{"get":{"operationId":"BenchmarkController_run","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin Visual Director Benchmark"]},"delete":{"operationId":"BenchmarkController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin Visual Director Benchmark"]}},"/admin/vd-benchmark/run":{"post":{"operationId":"BenchmarkController_create","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin Visual Director Benchmark"]}},"/admin/vd-benchmark/runs/{id}/cancel":{"post":{"operationId":"BenchmarkController_cancel","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin Visual Director Benchmark"]}},"/admin/vd-benchmark/runs/{id}/retry-failed":{"post":{"operationId":"BenchmarkController_retry","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin Visual Director Benchmark"]}},"/admin/vd-benchmark/runs/{id}/resume":{"post":{"operationId":"BenchmarkController_resume","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin Visual Director Benchmark"]}},"/admin/vd-benchmark/runs/{id}/rescore":{"post":{"operationId":"BenchmarkController_rescore","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin Visual Director Benchmark"]}},"/admin/vd-benchmark/runs/{id}/re-judge":{"post":{"operationId":"BenchmarkController_reJudge","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin Visual Director Benchmark"]}},"/admin/safety-benchmark/cases":{"get":{"operationId":"SafetyBenchmarkController_cases","parameters":[],"responses":{"200":{"description":""}},"summary":"Fixture summary (total + per-category counts) for the run config UI","tags":["Admin Safety Benchmark"]}},"/admin/safety-benchmark/runs":{"get":{"operationId":"SafetyBenchmarkController_runs","parameters":[{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Recent safety benchmark runs","tags":["Admin Safety Benchmark"]}},"/admin/safety-benchmark/runs/{id}":{"get":{"operationId":"SafetyBenchmarkController_run","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"A run with per-model results","tags":["Admin Safety Benchmark"]},"delete":{"operationId":"SafetyBenchmarkController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin Safety Benchmark"]}},"/admin/safety-benchmark/run":{"post":{"operationId":"SafetyBenchmarkController_create","parameters":[],"responses":{"201":{"description":""}},"summary":"Launch a safety benchmark run","tags":["Admin Safety Benchmark"]}},"/admin/safety-benchmark/runs/{id}/cancel":{"post":{"operationId":"SafetyBenchmarkController_cancel","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin Safety Benchmark"]}},"/admin/safety-benchmark/runs/{id}/resume":{"post":{"operationId":"SafetyBenchmarkController_resume","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin Safety Benchmark"]}},"/admin/memory-benchmark/cases":{"get":{"operationId":"MemoryBenchmarkController_cases","parameters":[],"responses":{"200":{"description":""}},"summary":"Fixture summary (per-category probe counts + case list) for the run config UI","tags":["Admin Memory Benchmark"]}},"/admin/memory-benchmark/runs":{"get":{"operationId":"MemoryBenchmarkController_runs","parameters":[{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Recent memory benchmark runs","tags":["Admin Memory Benchmark"]}},"/admin/memory-benchmark/runs/{id}":{"get":{"operationId":"MemoryBenchmarkController_run","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"A run with per-model results","tags":["Admin Memory Benchmark"]},"delete":{"operationId":"MemoryBenchmarkController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin Memory Benchmark"]}},"/admin/memory-benchmark/run":{"post":{"operationId":"MemoryBenchmarkController_create","parameters":[],"responses":{"201":{"description":""}},"summary":"Launch a memory benchmark run","tags":["Admin Memory Benchmark"]}},"/admin/memory-benchmark/runs/{id}/cancel":{"post":{"operationId":"MemoryBenchmarkController_cancel","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin Memory Benchmark"]}},"/admin/memory-benchmark/runs/{id}/resume":{"post":{"operationId":"MemoryBenchmarkController_resume","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin Memory Benchmark"]}},"/admin/pose-benchmark/fixture":{"get":{"operationId":"PoseBenchmarkController_fixture","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin Pose Benchmark"]}},"/admin/pose-benchmark/runs":{"get":{"operationId":"PoseBenchmarkController_runs","parameters":[{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin Pose Benchmark"]}},"/admin/pose-benchmark/runs/{id}":{"get":{"operationId":"PoseBenchmarkController_run","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin Pose Benchmark"]}},"/admin/pose-benchmark/runs/{id}/items":{"get":{"operationId":"PoseBenchmarkController_items","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"poseKey","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin Pose Benchmark"]}},"/admin/pose-benchmark/run":{"post":{"operationId":"PoseBenchmarkController_create","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin Pose Benchmark"]}},"/admin/pose-benchmark/test-pose":{"post":{"operationId":"PoseBenchmarkController_testPose","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin Pose Benchmark"]}},"/admin/pose-benchmark/runs/{id}/cancel":{"post":{"operationId":"PoseBenchmarkController_cancel","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin Pose Benchmark"]}},"/admin/pose-benchmark/runs/{id}/diff/{baselineId}":{"get":{"operationId":"PoseBenchmarkController_diff","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"baselineId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin Pose Benchmark"]}},"/v1/hyper-loras":{"post":{"description":"Creates a new hyper lora entry and returns pre-signed upload URLs for the reference photo and lora file. Files will be stored at hyperlora/[race]/[ageSpan]/[id]/source and hyperlora/[race]/[ageSpan]/[id]/lora.safetensors","operationId":"HyperLorasController_create","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateHyperLoraRequestDto"}}}},"responses":{"201":{"description":"Hyper lora created successfully with upload URLs"}},"summary":"Create a new hyper lora and get upload URLs","tags":["Hyper Loras"]},"get":{"description":"Get a list of hyper loras with optional filtering by status, race, and age span","operationId":"HyperLorasController_list","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"race","required":true,"in":"query","schema":{"type":"string"}},{"name":"ageSpan","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"number"}},{"name":"offset","required":true,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"List of hyper loras"}},"summary":"List hyper loras","tags":["Hyper Loras"]}},"/v1/hyper-loras/{id}/confirm-upload":{"post":{"description":"Notify the API that the files have been uploaded successfully to S3","operationId":"HyperLorasController_confirmUpload","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Upload confirmed successfully"}},"summary":"Confirm upload completion","tags":["Hyper Loras"]}},"/v1/hyper-loras/{id}":{"get":{"operationId":"HyperLorasController_getById","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Hyper lora retrieved successfully"}},"summary":"Get hyper lora by ID","tags":["Hyper Loras"]}},"/v1/external-models":{"post":{"description":"Creates a new public model with all required fields and returns presigned upload URLs for avatar, face reference (WebP under static/face_reference), and first message media. The caller must upload files to the returned presigned URLs within 1 hour.","operationId":"ExternalModelsController_create","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExternalModelRequestDtoClass"}}}},"responses":{"201":{"description":"Model created successfully with presigned upload URLs"},"422":{"description":"Validation failed"}},"summary":"Create a new model via external API","tags":["External Models"]}},"/v1/external-models/{id}/stories":{"post":{"description":"Creates a published model story and returns a presigned URL for uploading its media. The caller must upload the file to the returned URL within 1 hour.","operationId":"ExternalModelsController_createStory","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExternalModelStoryRequestDto"}}}},"responses":{"201":{"description":"Story created and published with presigned upload URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExternalModelStoryResponseDto"}}}}},"summary":"Create and publish a model story via external API","tags":["External Models"]}},"/v1/external-models/dry-run":{"post":{"description":"Validates the payload against all model creation rules without creating any records. Returns validation errors if any, and the list of media upload slots that would be generated.","operationId":"ExternalModelsController_dryRun","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExternalModelRequestDtoClass"}}}},"responses":{"200":{"description":"Dry run validation result"}},"summary":"Dry run model creation (validate without persisting)","tags":["External Models"]}},"/v1/external-models/{id}":{"patch":{"description":"Partially updates an existing model. Only provided fields are updated. Use \"regenerate_media\" for new presigned URLs: avatar, image, video, face_reference (combine with +, e.g. avatar+face_reference). Face reference uploads to static/face_reference (WebP), same CDN path as ethnicity-based refs.","operationId":"ExternalModelsController_update","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Model updated successfully"},"404":{"description":"Model not found"},"422":{"description":"Validation failed"}},"summary":"Update a model via external API","tags":["External Models"]},"delete":{"description":"Sets isDeleted=true on the model and all related chats, messages and attachments. Returns counts of affected rows.","operationId":"ExternalModelsController_remove","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Model and related data soft-deleted"},"404":{"description":"Model not found"}},"summary":"Soft-delete a model and cascade to chats, messages, attachments","tags":["External Models"]}},"/v1/external-models/{id}/confirm-upload":{"post":{"description":"Call after uploading files to presigned URLs. For video models, automatically extracts the first frame as a preview image.","operationId":"ExternalModelsController_confirmUpload","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","description":"Model ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Upload confirmed, video preview generated if applicable"}},"summary":"Confirm file uploads and generate video preview","tags":["External Models"]}},"/v1/model-packs/upload-url":{"post":{"description":"Returns a pre-signed S3 PUT URL for uploading a file (image/video/any) to [modelId]/[packId]/[fileName]. The request data is echoed back in the response so the client can persist it.","operationId":"ModelPacksController_createUploadUrl","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadUrlRequestDto"}}}},"responses":{"201":{"description":"Upload URL generated successfully"}},"summary":"Generate a pre-signed upload URL for a model pack file","tags":["Model Packs"]}},"/v1/model-packs/files":{"get":{"description":"Returns temporary pre-signed GET URLs for all files of a model, or only for a specific pack when packId is provided.","operationId":"ModelPacksController_listFiles","parameters":[{"name":"x-api-key","in":"header","description":"API key for authentication","required":true,"schema":{"type":"string"}},{"name":"modelId","required":true,"in":"query","description":"Model identifier to list files for","schema":{"type":"string"}},{"name":"packId","required":false,"in":"query","description":"Optional pack identifier — when provided, only files of this pack are returned","schema":{"type":"string"}}],"responses":{"200":{"description":"List of files with download URLs"}},"summary":"List model pack files with pre-signed download URLs","tags":["Model Packs"]}},"/self-billing/webhooks/{gatewayKey}":{"post":{"operationId":"SelfBillingWebhookController_handle","parameters":[{"name":"gatewayKey","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["SelfBillingWebhook"]}},"/v1/security/turnstile/verify":{"post":{"description":"Validates Turnstile token using Cloudflare Siteverify API","operationId":"TurnstileController_verify","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyTurnstileDto"}}}},"responses":{"200":{"description":"Token verified successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyTurnstileResponseDto"}}}},"400":{"description":"Token verification failed"}},"summary":"Verify Cloudflare Turnstile token","tags":["Turnstile"]}},"/v1/funnel-configs/{slug}":{"get":{"operationId":"FunnelConfigsController_getBySlug","parameters":[{"name":"slug","required":true,"in":"path","description":"Funnel slug","schema":{"example":"hot-funnel-v1","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunnelConfigItemDto"}}}}},"summary":"Get active funnel config by slug (public)","tags":["Funnel Configs"]}},"/v1/model-content/models/{modelId}/feed":{"get":{"operationId":"ModelContentController_getModelFeed","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"default":20,"type":"number"}},{"name":"type","required":false,"in":"query","schema":{"type":"string","enum":["POST","PACK"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelContentFeedResponseDto"}}}}},"summary":"Get paginated model profile posts and packs","tags":["Model Content"]}},"/v1/model-content/models/{modelId}/purchases/gallery":{"get":{"operationId":"ModelContentController_getModelPurchasedGallery","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"default":20,"type":"number"}},{"name":"type","required":false,"in":"query","schema":{"type":"string","enum":["PHOTO","VIDEO"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchasedModelContentGalleryResponseDto"}}}}},"summary":"Get paginated purchased model content gallery for a model","tags":["Model Content"]}},"/v1/model-content/models/{modelId}/stories":{"get":{"operationId":"ModelContentController_getModelStories","parameters":[{"name":"modelId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"default":20,"type":"number"}},{"name":"includeArchived","required":false,"in":"query","schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelStoriesResponseDto"}}}}},"summary":"Get paginated free model stories","tags":["Model Content"]}},"/v1/model-content/purchases":{"get":{"operationId":"ModelContentController_getPurchases","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"default":20,"type":"number"}},{"name":"type","required":false,"in":"query","schema":{"type":"string","enum":["content"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelContentPurchasesResponseDto"}}}}},"summary":"Get paginated purchased model content library","tags":["Model Content"]}},"/v1/model-content/stories/tray":{"get":{"operationId":"ModelContentController_getPublicStoriesTray","parameters":[{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"default":20,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelStoriesTrayResponseDto"}}}}},"summary":"Get paginated public model stories tray","tags":["Model Content"]}},"/v1/model-content/stories/{storyId}":{"get":{"operationId":"ModelContentController_getStory","parameters":[{"name":"storyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelStoryDto"}}}}},"summary":"Get free story details","tags":["Model Content"]}},"/v1/model-content/stories/{storyId}/view":{"post":{"operationId":"ModelContentController_markStoryViewed","parameters":[{"name":"storyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelStoryViewResponseDto"}}}}},"summary":"Mark a model story as viewed","tags":["Model Content"]}},"/v1/model-content/media/{mediaId}/purchase":{"post":{"operationId":"ModelContentController_purchaseMedia","parameters":[{"name":"mediaId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelContentPurchaseResponseDto"}}}}},"summary":"Purchase access to a paid model content media item","tags":["Model Content"]},"get":{"operationId":"ModelContentController_getMediaPurchaseState","parameters":[{"name":"mediaId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelContentPurchaseStateDto"}}}}},"summary":"Get purchase/subscription state for a paid media item","tags":["Model Content"]}},"/v1/model-content/{contentId}/purchase":{"post":{"operationId":"ModelContentController_purchaseContent","parameters":[{"name":"contentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelContentPurchaseResponseDto"}}}}},"summary":"Purchase all paid media in model content","tags":["Model Content"]},"get":{"operationId":"ModelContentController_getContentPurchaseState","parameters":[{"name":"contentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelContentPurchaseStateDto"}}}}},"summary":"Get purchase/subscription state for model content","tags":["Model Content"]}},"/v1/model-content/{contentId}":{"get":{"operationId":"ModelContentController_getContent","parameters":[{"name":"contentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelContentDto"}}}}},"summary":"Get model content details","tags":["Model Content"]}},"/v1/private-content/packs":{"get":{"description":"Tabs: all (newest), recommended (models the user chats with first), most_liked, collection (purchased, requires auth).","operationId":"PrivateContentController_listPacks","parameters":[{"name":"tab","required":false,"in":"query","schema":{"default":"all","type":"string","enum":["all","recommended","most_liked","collection"]}},{"name":"search","required":false,"in":"query","description":"Matches the pack title and the model name.","schema":{"type":"string"}},{"name":"modelId","required":false,"in":"query","description":"Only packs of this model — the chat entry point uses it.","schema":{"format":"uuid","type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"default":20,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrivateContentPacksResponseDto"}}}}},"summary":"Cross-model catalog of paid packs","tags":["Private Content"]}},"/v1/private-content/packs/{packId}":{"get":{"operationId":"PrivateContentController_getPack","parameters":[{"name":"packId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrivateContentPackDetailsDto"}}}}},"summary":"Pack details for the purchase modal","tags":["Private Content"]}},"/v1/private-content/packs/{packId}/gallery":{"get":{"description":"Returns 403 with the access state when the pack is locked.","operationId":"PrivateContentController_getPackGallery","parameters":[{"name":"packId","required":true,"in":"path","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"default":60,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrivateContentPackGalleryResponseDto"}}}}},"summary":"Contents of an unlocked pack","tags":["Private Content"]}},"/v1/private-content/packs/{packId}/reaction":{"put":{"description":"Send value: null to clear the reaction.","operationId":"PrivateContentController_setPackReaction","parameters":[{"name":"packId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPrivateContentReactionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrivateContentReactionResponseDto"}}}}},"summary":"Like or dislike a pack","tags":["Private Content"]}},"/admin/daily-report/days":{"get":{"operationId":"AdminDailyReportController_getDays","parameters":[{"name":"dateFrom","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Daily Report"]}},"/admin/daily-report/metrics/catalog":{"get":{"operationId":"AdminDailyReportController_getMetricsCatalog","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin - Daily Report"]}},"/admin/daily-report/metrics/series":{"get":{"operationId":"AdminDailyReportController_getSeries","parameters":[{"name":"keys","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateFrom","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Daily Report"]}},"/admin/daily-report/report/{date}":{"get":{"operationId":"AdminDailyReportController_getReport","parameters":[{"name":"date","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Daily Report"]}},"/admin/daily-report/report/{date}/recompute":{"post":{"operationId":"AdminDailyReportController_recompute","parameters":[{"name":"date","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin - Daily Report"]}},"/admin/daily-report/send-now":{"post":{"operationId":"AdminDailyReportController_sendNow","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Daily Report"]}},"/admin/daily-report/report/{date}/send-slack":{"post":{"operationId":"AdminDailyReportController_sendSlack","parameters":[{"name":"date","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin - Daily Report"]}},"/admin/daily-report/backfill":{"post":{"operationId":"AdminDailyReportController_backfill","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Daily Report"]}},"/admin/release-markers":{"get":{"operationId":"AdminReleaseMarkersController_list","parameters":[{"name":"dateFrom","required":true,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Daily Report"]},"post":{"operationId":"AdminReleaseMarkersController_create","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin - Daily Report"]}},"/admin/release-markers/{id}":{"put":{"operationId":"AdminReleaseMarkersController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Daily Report"]},"delete":{"operationId":"AdminReleaseMarkersController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin - Daily Report"]}},"/v1/rewind":{"get":{"description":"Returns aggregated profile statistics (messages, companions, photos, videos, affinity, created girlfriends).","operationId":"RewindController_getMyRewind","parameters":[],"responses":{"200":{"description":"Rewind retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewindDto"}}}}},"summary":"Get the current user Rewind (profile stats summary)","tags":["Rewind"]}},"/v1/plan-features":{"get":{"description":"Returns boolean flags indicating whether the user has ever used each plan feature, plus used/total counts.","operationId":"PlanFeaturesController_getMyPlanFeatures","parameters":[],"responses":{"200":{"description":"Feature usage retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanFeaturesDto"}}}}},"summary":"Get the current user feature usage (\"Your plan includes\")","tags":["Plan Features"]}},"/v1/banners":{"get":{"operationId":"BannersController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannersResponseDto"}}}}},"summary":"List published home screen banners","tags":["Banners"]}}},"info":{"title":"Backend Template API","description":"Backend Template API description\n\n📥 **Export for Postman:** [Download OpenAPI YAML](/api-postman.yaml) · [Download OpenAPI JSON](/api-json).\n\nIn Postman: **Import → File** (or **Link**) and select the downloaded `api-postman.yaml` — Postman will generate a collection from the spec.","version":"1.0","contact":{}},"tags":[{"name":"backend-template","description":""}],"servers":[],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http","in":"header","name":"Authorization","description":"Enter your Bearer token"}},"schemas":{"SubscriptionCheckResponseDto":{"type":"object","properties":{"isSubscribed":{"type":"boolean","description":"Whether user has an active subscription","example":true},"source":{"type":"string","description":"Subscription source/provider","enum":["apple","stripe","funnelfox","unlimit","upgate"],"example":"apple"},"expirationDate":{"format":"date-time","type":"string","description":"Subscription expiration date","example":"2025-02-15T00:00:00.000Z"},"status":{"type":"string","description":"Subscription status","example":"active"},"cancelAtPeriodEnd":{"type":"boolean","description":"Whether subscription will be cancelled at period end","example":false},"planName":{"type":"string","description":"Name of the subscription plan","example":"premium_monthly"}},"required":["isSubscribed"]},"MigrateSubscriptionBody":{"type":"object","properties":{}},"DisableAutorenewBody":{"type":"object","properties":{}},"SubscriptionCancellationReasonDto":{"type":"object","properties":{"id":{"type":"string","enum":["too_expensive","didnt_meet_expectations","technical_payment_issues","confusing_tokens_limits","other"]},"label":{"type":"string"},"description":{"type":"string"}},"required":["id","label","description"]},"GetSubscriptionCancellationReasonsResponseDto":{"type":"object","properties":{"reasons":{"type":"array","items":{"$ref":"#/components/schemas/SubscriptionCancellationReasonDto"}}},"required":["reasons"]},"CreateSubscriptionCancellationReportRequestDto":{"type":"object","properties":{"reasonId":{"type":"string","enum":["too_expensive","didnt_meet_expectations","technical_payment_issues","confusing_tokens_limits","other"]},"additionalDetails":{"type":"string","description":"Optional feedback about what could be better"}},"required":["reasonId"]},"CreateSubscriptionCancellationReportResponseDto":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"reportId":{"type":"number"}},"required":["success","message","reportId"]},"CoinSummaryBucketDto":{"type":"object","properties":{"left":{"type":"number","description":"Coins remaining in this bucket"},"limit":{"type":"number","description":"Monthly allowance for the current billing cycle (monthly bucket only)"}},"required":["left"]},"CoinSummaryDto":{"type":"object","properties":{"total":{"type":"number","description":"Total spendable coins (monthly + bonus)"},"monthlyCoins":{"description":"Subscription quota coins for the current billing cycle","allOf":[{"$ref":"#/components/schemas/CoinSummaryBucketDto"}]},"bonusCoins":{"description":"Non-expiring coins (purchases, welcome bonus, promos, legacy balance)","allOf":[{"$ref":"#/components/schemas/CoinSummaryBucketDto"}]},"quotaResetAt":{"type":"string","nullable":true,"description":"When the monthly quota resets (ISO 8601), if applicable"}},"required":["total","monthlyCoins","bonusCoins"]},"CoinPackDto":{"type":"object","properties":{"productId":{"type":"string","description":"Price-point ident of the pack (web_coin_products.productId)","example":"550_coins_49_99"},"coinsAmount":{"type":"number","description":"Coins granted on purchase","example":550},"priceAmount":{"type":"number","description":"Price in minor units","example":4999},"currency":{"type":"string","description":"ISO 4217 currency code","example":"USD"}},"required":["productId","coinsAmount","priceAmount","currency"]},"CoinPackSetDto":{"type":"object","properties":{"variantId":{"type":"string","description":"Assigned variant of the coin-pack pricing experiment. `control` both for the control branch and for users outside the experiment.","example":"control"},"packs":{"description":"Packs to display, in ladder order","type":"array","items":{"$ref":"#/components/schemas/CoinPackDto"}}},"required":["variantId","packs"]},"ConfirmCoinPurchaseDto":{"type":"object","properties":{"pricePointIdent":{"type":"string","description":"FunnelFox price point ident of the purchased coin pack","example":"mia_coins_1000"},"orderId":{"type":"string","description":"FunnelFox order id returned by the checkout SDK onSuccess. Used as the idempotency key so this grant can never double up with the webhook.","example":"ord_abc123"}},"required":["pricePointIdent","orderId"]},"ConfirmCoinPurchaseResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the purchase was confirmed and credited"},"balance":{"type":"number","description":"Current total coin balance after crediting"},"coinsAmount":{"type":"number","description":"Coins credited by this purchase (before any top-up multiplier)"}},"required":["success","balance"]},"AutoTopupProductDto":{"type":"object","properties":{"productId":{"type":"string","example":"100_coins_9_99_eur"},"coinsAmount":{"type":"number"},"priceAmount":{"type":"number","description":"Price in the currency minor units"},"currency":{"type":"string","example":"EUR"}},"required":["productId","coinsAmount","priceAmount","currency"]},"AutoTopupResponseDto":{"type":"object","properties":{"enabled":{"type":"boolean"},"product":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/AutoTopupProductDto"}]},"thresholdCoins":{"type":"number","description":"Balance threshold below which auto-topup fires (backend constant, read-only)"},"lastStatus":{"type":"string","nullable":true,"enum":["success","failed","pending"]},"lastAttemptAt":{"format":"date-time","type":"string","nullable":true},"disabledReason":{"type":"string","nullable":true,"enum":["payment_failed","product_unavailable","no_payment_method"],"description":"Why auto-topup was auto-disabled (null while enabled)"}},"required":["enabled","product","thresholdCoins","lastStatus","lastAttemptAt","disabledReason"]},"UpdateAutoTopupDto":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Turn auto-topup on or off"},"productId":{"type":"string","description":"Localized web coin product id (equals its FunnelFox price point ident) to auto-purchase. Required when enabling.","example":"100_coins_9_99_eur"}},"required":["enabled"]},"GetConstantsResponseDto":{"type":"object","properties":{"nonSubscriberMessageLimit":{"type":"number","description":"Maximum number of messages for non-subscribers","example":40},"nonSubscriberModelsLimit":{"type":"number","description":"Maximum number of models for non-subscribers","example":1},"imageRequestPrice":{"type":"number","description":"Price for image requests in coins","example":4},"videoRequestPrice":{"type":"number","description":"Price for video requests in coins","example":10},"textMessagePrice":{"type":"number","description":"Price for text messages in coins","example":0},"modelCreatePrice":{"type":"number","description":"Price for creating a new model in coins","example":15},"chatTextMessageDailyLimit":{"type":"number","description":"Maximum number of non-billable text chat messages per user per day (UTC)","example":2000},"chatTextMessageLimitWarningThreshold":{"type":"number","description":"How many messages before the text limit the warning notification is sent","example":100}},"required":["nonSubscriberMessageLimit","nonSubscriberModelsLimit","imageRequestPrice","videoRequestPrice","textMessagePrice","modelCreatePrice","chatTextMessageDailyLimit","chatTextMessageLimitWarningThreshold"]},"RequestMagicLinkDto":{"type":"object","properties":{"email":{"type":"string","example":"user@example.com"},"firebaseUid":{"type":"string","example":"firebase-uid-123"},"redirectPath":{"type":"string","example":"/chat/abc-123","description":"Relative path to redirect the user to after email confirmation"},"locale":{"type":"string","example":"de","description":"UI locale for the email copy (bare code or BCP-47); falls back to Accept-Language, then English"}},"required":["email"]},"RequestMagicLinkResponseDto":{"type":"object","properties":{"firebaseCustomToken":{"type":"string","description":"Present when email verification is disabled — use to sign in directly"},"firebaseUid":{"type":"string"}}},"ConsumeMagicLinkDto":{"type":"object","properties":{"token":{"type":"string","description":"One-time token from registration confirmation email"}},"required":["token"]},"ConsumeMagicLinkResponseDto":{"type":"object","properties":{"firebaseCustomToken":{"type":"string"},"firebaseUid":{"type":"string"},"email":{"type":"string"}},"required":["firebaseCustomToken","firebaseUid","email"]},"RequestEmailOtpDto":{"type":"object","properties":{"email":{"type":"string","example":"user@example.com"},"firebaseUid":{"type":"string","example":"firebase-uid-123"},"skipVerification":{"type":"boolean","description":"When true, marks the user as email-verified without sending OTP. Used by paid funnel create-account."},"locale":{"type":"string","example":"de","description":"UI locale for the email copy (bare code or BCP-47); falls back to Accept-Language, then English"}},"required":["email"]},"RequestEmailOtpResponseDto":{"type":"object","properties":{"firebaseCustomToken":{"type":"string","description":"Present when email verification is disabled — use to sign in directly"},"firebaseUid":{"type":"string"}}},"VerifyEmailOtpDto":{"type":"object","properties":{"email":{"type":"string","example":"user@example.com"},"code":{"type":"string","example":"123456","description":"6-digit OTP code"}},"required":["email","code"]},"VerifyEmailOtpResponseDto":{"type":"object","properties":{"firebaseCustomToken":{"type":"string"},"firebaseUid":{"type":"string"},"email":{"type":"string"}},"required":["firebaseCustomToken","firebaseUid","email"]},"IssueSessionHandoffDto":{"type":"object","properties":{"funnelId":{"type":"string","example":"mia_1"},"stepId":{"type":"string","example":"create_account","description":"Funnel step the new browser should resume on"},"acquisitionSessionId":{"type":"string","format":"uuid"}},"required":["funnelId","stepId"]},"IssueSessionHandoffResponseDto":{"type":"object","properties":{"token":{"type":"string","description":"Absent when the per-uid issuance cap is exhausted — the funnel carries on without a handoff link"},"expiresAt":{"type":"string"},"ttlSeconds":{"type":"number","example":900}},"required":["ttlSeconds"]},"ConsumeSessionHandoffDto":{"type":"object","properties":{"token":{"type":"string","description":"Handoff token lifted from the funnel URL"}},"required":["token"]},"ConsumeSessionHandoffResponseDto":{"type":"object","properties":{"firebaseCustomToken":{"type":"string"},"firebaseUid":{"type":"string"},"funnelId":{"type":"string"},"stepId":{"type":"string"},"sessionId":{"type":"string","nullable":true},"leadId":{"type":"string","nullable":true},"answers":{"type":"object","description":"Funnel answers snapshot"},"isAccountLinked":{"type":"boolean"},"email":{"type":"string","nullable":true}},"required":["firebaseCustomToken","firebaseUid","funnelId","stepId","sessionId","leadId","answers","isAccountLinked","email"]},"ScheduleCancellationReminderResponseDto":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Cancellation reminder scheduled successfully"},"scheduledAt":{"type":"string","example":"2026-06-17T08:00:00.000Z"},"reminderOffsetDays":{"type":"number","example":3},"periodEnd":{"type":"string","example":"2026-06-20T08:00:00.000Z"}},"required":["success","message","scheduledAt","reminderOffsetDays","periodEnd"]},"SubscriptionCatalogResponseDto":{"type":"object","properties":{}},"FunnelSubscriptionPlansResponseDto":{"type":"object","properties":{}},"SubscriptionCheckV2ResponseDto":{"type":"object","properties":{}},"GetMySubscriptionPlanResponseDto":{"type":"object","properties":{}},"ConfirmPurchaseDto":{"type":"object","properties":{}},"ApplyRetentionDiscountResponseDto":{"type":"object","properties":{}},"CancellationFlowExperimentAssignmentDto":{"type":"object","properties":{"experimentKey":{"type":"string","example":"subscription_cancellation_flow_v1"},"variantId":{"type":"string","nullable":true,"example":"control_v2"},"source":{"type":"string","enum":["forced","sticky","fresh","default"]},"assignedAt":{"format":"date-time","type":"string","nullable":true}},"required":["experimentKey","variantId","source"]},"EnrollCancelCtaExperimentDto":{"type":"object","properties":{"trigger":{"type":"string","enum":["settings_subscription_manage_show","settings_payment_methods_remove_last_card"],"default":"settings_subscription_manage_show","description":"Which entry point asks for the assignment — the Manage Subscription section, or removing the last saved card. Recorded on the assignment context."}}},"CancelCtaExperimentAssignmentDto":{"type":"object","properties":{"experimentKey":{"type":"string","example":"subscription_cancel_cta_v1"},"variantId":{"type":"string","nullable":true,"example":"contact_support"},"source":{"type":"string","enum":["forced","sticky","fresh","default"]},"assignedAt":{"format":"date-time","type":"string","nullable":true}},"required":["experimentKey","variantId","source"]},"EnrollExperimentDto":{"type":"object","properties":{"firebaseUid":{"type":"string","nullable":true,"description":"Required when the request is unauthenticated (funnel). Ignored when the caller is authenticated — the UID is taken from the auth context."},"context":{"type":"object","nullable":true,"description":"Free-form metadata about the trigger event."}}},"EnrollResponseDto":{"type":"object","properties":{"variantId":{"type":"string","nullable":true},"assignedAt":{"format":"date-time","type":"string","nullable":true}},"required":["variantId"]},"ExperimentVariantDto":{"type":"object","properties":{"id":{"type":"string"},"weight":{"type":"number"}},"required":["id","weight"]},"ExperimentVariantStatsDto":{"type":"object","properties":{"variantId":{"type":"string"},"weight":{"type":"number"},"assignmentCount":{"type":"number","description":"Number of subjects pinned to this variant."}},"required":["variantId","weight","assignmentCount"]},"ExperimentWithStatsDto":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["draft","running","paused","completed"]},"enrollmentMode":{"type":"string","enum":["auto","manual"]},"variants":{"type":"array","items":{"$ref":"#/components/schemas/ExperimentVariantDto"}},"defaultVariantId":{"type":"string","nullable":true},"forcedVariantId":{"type":"string","nullable":true},"winnerVariantId":{"type":"string","nullable":true},"completedAt":{"format":"date-time","type":"string","nullable":true},"seed":{"type":"string"},"subjectType":{"type":"string","enum":["firebase_uid"]},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"allocation":{"type":"array","items":{"$ref":"#/components/schemas/ExperimentVariantStatsDto"}},"totalAssignments":{"type":"number","description":"Total number of subjects pinned to any variant."}},"required":["id","key","name","status","enrollmentMode","variants","seed","subjectType","createdAt","updatedAt","allocation","totalAssignments"]},"CreateExperimentDto":{"type":"object","properties":{"key":{"type":"string","description":"Stable, lower_snake_case key."},"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["draft","running","paused","completed"],"default":"draft"},"enrollmentMode":{"type":"string","enum":["auto","manual"],"default":"auto"},"variants":{"type":"array","items":{"$ref":"#/components/schemas/ExperimentVariantDto"}},"defaultVariantId":{"type":"string","nullable":true},"seed":{"type":"string","description":"Optional explicit seed. Defaults to a random uuid."}},"required":["key","name","variants"]},"ExperimentDto":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["draft","running","paused","completed"]},"enrollmentMode":{"type":"string","enum":["auto","manual"]},"variants":{"type":"array","items":{"$ref":"#/components/schemas/ExperimentVariantDto"}},"defaultVariantId":{"type":"string","nullable":true},"forcedVariantId":{"type":"string","nullable":true},"winnerVariantId":{"type":"string","nullable":true},"completedAt":{"format":"date-time","type":"string","nullable":true},"seed":{"type":"string"},"subjectType":{"type":"string","enum":["firebase_uid"]},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","key","name","status","enrollmentMode","variants","seed","subjectType","createdAt","updatedAt"]},"UpdateAllocationDto":{"type":"object","properties":{"variants":{"description":"Full replacement list. Each id must already exist on the experiment, and the weight sum must be > 0. Existing assignments are NOT rebucketed.","type":"array","items":{"$ref":"#/components/schemas/ExperimentVariantDto"}}},"required":["variants"]},"SetForcedVariantDto":{"type":"object","properties":{"variantId":{"type":"string","description":"Must be one of the variant ids defined on the experiment. All reads will return this variant immediately."}},"required":["variantId"]},"ExperimentVariantLtvDto":{"type":"object","properties":{"variantId":{"type":"string"},"enrolled":{"type":"number","description":"Distinct subjects assigned to this variant"},"payers":{"type":"number","description":"Enrolled subjects with ≥1 post-enroll charge"},"payerConversion":{"type":"number","description":"payers / enrolled, 0..1"},"revenueUsd":{"type":"number","description":"Gross post-enroll revenue, USD"},"refundsUsd":{"type":"number","description":"Post-enroll refunds + chargebacks, USD (≥0)"},"netRevenueUsd":{"type":"number","description":"revenueUsd - refundsUsd"},"ltv":{"type":"number","description":"netRevenueUsd / enrolled"},"arppu":{"type":"number","description":"netRevenueUsd / payers"},"salesCount":{"type":"number","description":"Post-enroll successful charges (count)"},"chargebackCount":{"type":"number","description":"Post-enroll succeeded chargebacks (count)"},"chargebackRatio":{"type":"number","description":"chargebackCount / salesCount, 0..1"}},"required":["variantId","enrolled","payers","payerConversion","revenueUsd","refundsUsd","netRevenueUsd","ltv","arppu","salesCount","chargebackCount","chargebackRatio"]},"ExperimentLtvDto":{"type":"object","properties":{"experimentKey":{"type":"string"},"variants":{"type":"array","items":{"$ref":"#/components/schemas/ExperimentVariantLtvDto"}},"generatedAt":{"format":"date-time","type":"string"}},"required":["experimentKey","variants","generatedAt"]},"CompleteExperimentDto":{"type":"object","properties":{"winnerVariantId":{"type":"string","description":"Variant declared as the experiment winner"}},"required":["winnerVariantId"]},"SetStatusDto":{"type":"object","properties":{"status":{"type":"string","enum":["draft","running","paused","completed"]}},"required":["status"]},"PurchaseCoinsDto":{"type":"object","properties":{"transactionId":{"type":"string","description":"The transaction ID from Apple","example":"1234567890"}},"required":["transactionId"]},"RestoreSubscriptionDto":{"type":"object","properties":{"originalTransactionId":{"type":"string","description":"The original transaction ID for subscription restoration","example":"123280174358914"}},"required":["originalTransactionId"]},"ModelContentMediaDto":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["PHOTO","VIDEO"]},"url":{"type":"string"},"videoUrl":{"type":"string"},"previewUrl":{"type":"string"},"nsfwThumbnailUrl":{"type":"string"},"lockedCoverUrl":{"type":"string"},"duration":{"type":"number"},"order":{"type":"number"},"price":{"type":"number"},"isPurchased":{"type":"boolean"},"isLocked":{"type":"boolean"}},"required":["id","type","order","price","isPurchased","isLocked"]},"ModelProfileMediaCountsDto":{"type":"object","properties":{"photos":{"type":"number"},"videos":{"type":"number"}},"required":["photos","videos"]},"SocialRatingDto":{"type":"object","properties":{"views":{"type":"number","description":"Number of chats created with the model","example":1234},"likes":{"type":"number","description":"Number of chats with >= 2 completed attachment messages","example":87}},"required":["views","likes"]},"GetModelByIdResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"The model ID","example":"123e4567-e89b-12d3-a456-426614174000"},"slug":{"type":"string","description":"The model URL slug","example":"anna-smith-x7f2k"},"name":{"type":"string","description":"The model name","example":"Emily Davis"},"status":{"type":"string","description":"The model status","example":"IN_PROGRESS","enum":["IN_PROGRESS","COMPLETED","FAILED"]},"description":{"type":"string","description":"The model description","example":"Emily, a 5'8\" blonde model..."},"photo":{"type":"string","description":"The model photo URL","example":"https://example.com/emily.jpg"},"avatarIcon":{"type":"string","description":"The model avatar icon URL","example":"https://cdn.example.com/avatars/annika-avatar.webp"},"video":{"type":"string","description":"The model video URL","example":"https://example.com/emily.mp4"},"useVideo":{"type":"boolean","description":"Whether the model uses video","example":true},"isPublic":{"type":"boolean","description":"Whether the model is public","example":true},"gender":{"type":"string","description":"The model gender","example":"female","enum":["male","female"]},"age":{"type":"number","description":"The model age","example":18},"relationship":{"type":"string","description":"The model relationship","example":"Colleague"},"hairColor":{"type":"string","description":"The model hair color","example":"Black","enum":["Blonde","Brunette","Black","Red","Pink","Blue","Violet","Airtouch","Black with blonde highlights","Ombre"]},"hairStyle":{"type":"string","description":"The model hair style","example":"Long","enum":["Straight","Braids","Bangs","Bun","Curly","Short","Long","Pigtails","Ponytail","Emo","Shoulder-length","Buzz","Pixie","Hijab","Veil"]},"eyesColor":{"type":"string","description":"The model eyes color","example":"Blue","enum":["Blue","Brown","Green"]},"ethnicity":{"type":"string","description":"The model ethnicity","example":"caucasian","enum":["caucasian","latina","asian","arab","afro","indian","furry","kemonomimi","tiefling"]},"buttSize":{"type":"string","description":"The model butt size","example":"small","enum":["small","medium","large"]},"bodyType":{"type":"string","description":"The model body type","example":"slim","enum":["slim","average","athletic","chubby"]},"breastType":{"type":"string","description":"The model breast size","example":"large","enum":["small","medium","large","xl"]},"personality":{"type":"string","description":"The model personality","example":"Playful, Adventurous"},"occupation":{"type":"string","description":"The model occupation","example":"Model","enum":["Massage Therapist","Dentist","Nutritionist","Fitness Coach","Artist","Pharmacist","Hairdresser","Makeup Artist","Gynecologist","Writer","Secretary","Librarian","Social Worker","Fashion Designer","Model","Police Officer","Nurse","Yoga Instructor","Waitress","Cook","Unemployed","Dancer","Teacher","Student","Life Coach","Astronaut","Kindergarten Teacher","Soldier/Military Personnel","Architect","DJ","Nun"]},"hobbies":{"type":"string","description":"The model hobbies","example":"Yoga, Beach Volleyball"},"isPro":{"type":"boolean","description":"Whether the model is pro","example":true},"isNew":{"type":"boolean","description":"Whether the model is new","example":true},"tiktokVideo":{"type":"string","description":"The model TikTok video URL","example":"https://example.com/tiktok/annika_tiktok.mp4"},"onboardingImage":{"type":"string","description":"The model onboarding image URL","example":"https://example.com/onboarding/annika_onboarding.webp"},"demoModelPreviewVideo":{"type":"string","description":"Short demo clip URL (static CDN) for model card / preview in demo flows"},"onboardingSubscriptionRewardVideoPreview":{"type":"string","description":"Still preview for onboarding subscription reward (FLUX Klein edit pipeline; static CDN image/WebP)"},"onboardingSubscriptionRewardVideo":{"type":"string","description":"Video for onboarding subscription reward (i2v from FLUX edit; static CDN)"},"hasDefaultScenario":{"type":"boolean","description":"Whether the model has its own default scenario (cover/initial message)","example":false},"allowedChatModes":{"type":"array","description":"Chat modes this model may be started in (GEN-1386). Never empty. More than one → the web asks the user to pick before creating the chat.","example":["ROLEPLAY"],"items":{"type":"string","enum":["ROLEPLAY","REALISTIC"]}},"defaultScenarioCover":{"type":"string","description":"CDN URL for the model default scenario cover image (chat-images)","example":"https://cdn.example.com/chat-images/uuid.jpg"},"headerGallery":{"description":"Up to five ordered public profile photos for the model header. Locked paid content exposes only one teaser thumbnail.","type":"array","items":{"$ref":"#/components/schemas/ModelContentMediaDto"}},"mediaCounts":{"description":"Gallery item counts by tab, including packs in matching tabs.","allOf":[{"$ref":"#/components/schemas/ModelProfileMediaCountsDto"}]},"socialRating":{"description":"Social-proof counters (views/likes). Populated for the public discovery listing; defaults to zeros elsewhere.","allOf":[{"$ref":"#/components/schemas/SocialRatingDto"}]}},"required":["id","slug","name","status","description","photo","isPublic","gender","age","relationship","hairColor","hairStyle","eyesColor","ethnicity","buttSize","bodyType","breastType","personality","occupation","hobbies","isPro","isNew","hasDefaultScenario","allowedChatModes","headerGallery","mediaCounts","socialRating"]},"AttachmentDto":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the attachment"},"messageId":{"type":"string","description":"The ID of the message"},"status":{"type":"string","description":"The status of the attachment. IN_QUEUE = scene generation is still in the Visual Director (prompt-building) phase; IN_PROGRESS = media is rendering.","enum":["IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED"],"default":"IN_PROGRESS"},"type":{"type":"string","description":"The type of the attachment"},"url":{"type":"string","description":"The URL of the attachment"},"videoUrl":{"type":"string","description":"The video URL of the attachment","nullable":true},"blurredUrl":{"type":"string","description":"Pre-blurred stand-in to render while the media is gated. Empty when the media has no teaser.","nullable":true},"explicit":{"type":"boolean","description":"Whether the attachment is explicit"},"mimeType":{"type":"string","description":"The MIME type of the attachment"},"prompt":{"type":"string","description":"The prompt used for generation or gift description","nullable":true},"rephrasedPrompt":{"type":"string","description":"The raw rephrased prompt before composition","nullable":true},"source":{"type":"string","description":"The source of the attachment (e.g., chat_ask, ai_initiated)","enum":["chat_ask","scene_maker","chat_animate_image","chat_gift","user_photo","voice_over","voice_message","ai_initiated","studio","demo_subscription_reward","upscale"],"nullable":true},"durationSeconds":{"type":"number","description":"Duration in seconds for audio/video attachments","nullable":true},"canRetry":{"type":"boolean","description":"Whether \"retry generation\" is still available for this attachment (failed image or video with retries left). False once the retry limit is spent — the client should stop offering the button."},"retryCount":{"type":"number","description":"Manual retries already spent on this attachment"}},"required":["id","messageId","status","type","url","videoUrl","blurredUrl","explicit","mimeType","prompt","rephrasedPrompt","source","durationSeconds","canRetry","retryCount"]},"MessageDto":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the message"},"chatId":{"type":"string","description":"The ID of the chat"},"text":{"type":"string","description":"The text of the message"},"status":{"type":"string","description":"The status of the message","enum":["IN_PROGRESS","COMPLETED","FAILED"],"default":"IN_PROGRESS"},"isFromUser":{"type":"boolean","description":"Whether the message is from the user"},"isFromSystem":{"type":"boolean","description":"Whether the message is a system message"},"isRead":{"type":"boolean","description":"Whether the message is read"},"createdAt":{"format":"date-time","type":"string","description":"The date and time the message was created"},"attachments":{"description":"The attachments of the message","type":"array","items":{"$ref":"#/components/schemas/AttachmentDto"}},"messageType":{"type":"string","description":"The type of the message","enum":["TEXT","IMAGE_REQUEST","VIDEO_REQUEST","ATTACHMENT","ANIMATE_REQUEST"]},"moderationStatus":{"type":"string","description":"Moderation verdict. BLOCKED = hidden from the character (still shown to the user with a notice); FAILED_OPEN = classifier failed and the message was let through.","enum":["NONE","ALLOWED","BLOCKED","FAILED_OPEN","FLAGGED"],"default":"NONE"},"moderationFeedbackSent":{"type":"boolean","description":"Whether the user submitted feedback on a blocked message.","default":false},"isDeleted":{"type":"boolean","description":"Soft-deleted flag. Emitted true so the client removes a message (e.g. a torn-down typing placeholder)."},"idempotencyKey":{"type":"string","description":"The idempotency key of the message"},"suggestions":{"description":"Suggested follow-up responses for the user","type":"array","items":{"type":"string"}},"giftSuggestion":{"type":"object","description":"Gift suggestion from the AI companion"},"actions":{"description":"Structured UI actions for the client to render","type":"array","items":{"type":"string"}}},"required":["id","chatId","text","status","isFromUser","isFromSystem","isRead","createdAt","attachments","messageType","moderationStatus","moderationFeedbackSent","idempotencyKey"]},"GetChatByIdResponseDto":{"type":"object","properties":{"id":{"type":"string"},"model":{"$ref":"#/components/schemas/GetModelByIdResponseDto"},"lastMessage":{"$ref":"#/components/schemas/MessageDto"},"suggestionsEnabled":{"type":"boolean","description":"Whether suggestions are enabled for this chat","default":true}},"required":["id","model","lastMessage","suggestionsEnabled"]},"CreateChatRequestBodyDto":{"type":"object","properties":{"modelId":{"type":"string","description":"The model ID","example":"123e4567-e89b-12d3-a456-426614174000"}},"required":["modelId"]},"GetMessagesResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/MessageDto"}},"nextCursor":{"type":"string"},"prevCursor":{"type":"string"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}},"required":["items","hasNext","hasPrev"]},"SendMessageRequestBodyDto":{"type":"object","properties":{"text":{"type":"string","description":"The text of the message","example":"Hello, how are you?"},"imageRequestCount":{"type":"number","description":"The number of images to request","example":1}},"required":["text","imageRequestCount"]},"SendMessageResponseDto":{"type":"object","properties":{"message":{"description":"The message","allOf":[{"$ref":"#/components/schemas/MessageDto"}]},"userMessage":{"description":"The user message","allOf":[{"$ref":"#/components/schemas/MessageDto"}]},"balance":{"type":"number","description":"The balance","example":100}},"required":["message","userMessage","balance"]},"RequestImageRequestBodyDto":{"type":"object","properties":{"surroundingsId":{"type":"number","description":"The id of the surrounding","example":1},"clothesId":{"type":"number","description":"The id of the clothes","example":1},"emotionsId":{"type":"number","description":"The id of the emotions","example":1},"posesId":{"type":"number","description":"The id of the poses","example":1},"imageRequestCount":{"type":"number","description":"The number of images to request","example":1}},"required":["surroundingsId","clothesId","emotionsId","posesId","imageRequestCount"]},"SendGiftRequestBodyDto":{"type":"object","properties":{"giftId":{"type":"string","description":"Gift ID","example":"0f7e98a1-9f2a-4b6b-9a94-1f2c3d4e5f6a"},"text":{"type":"string","description":"Optional message text"},"imageRequestCount":{"type":"number","description":"Number of images to generate (default: 1)","example":1}},"required":["giftId"]},"AnimateAttachmentsRequestDto":{"type":"object","properties":{"attachmentIds":{"description":"IDs of image attachments to animate","type":"array","items":{"type":"string"}},"promptOverride":{"type":"string","description":"Optional prompt override for animation"}},"required":["attachmentIds"]},"CreateMessageReportRequestDto":{"type":"object","properties":{"messageId":{"type":"string","description":"ID of the message being reported","example":"3ae9f1c1-1658-4689-b8a2-a2afb998d2ae"},"reasonId":{"type":"string","description":"Reason for reporting the message","enum":["inappropriate_content","spam","harassment","other"],"example":"inappropriate_content"},"additionalDetails":{"type":"string","description":"Additional details about the report (optional)","example":"This message contains inappropriate content that violates community guidelines"}},"required":["messageId","reasonId"]},"CreateMessageReportResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the report was created successfully","example":true},"message":{"type":"string","description":"Response message","example":"Message reported successfully"},"reportId":{"type":"number","description":"ID of the created report","example":1}},"required":["success","message"]},"GetModelsResponseDto":{"type":"object","properties":{"total":{"type":"number","description":"Total number of models","example":100},"pageSize":{"type":"number","description":"Number of models per page","example":20},"page":{"type":"number","description":"Current page number","example":1},"items":{"description":"Array of models","type":"array","items":{"$ref":"#/components/schemas/GetModelByIdResponseDto"}}},"required":["total","pageSize","page","items"]},"FormOptionDto":{"type":"object","properties":{"value":{"type":"object","description":"Value to submit with the form"},"label":{"type":"string","description":"Label displayed in the UI"},"imageUrl":{"type":"string","description":"Optional image URL"},"coverImage":{"type":"string","description":"Optional cover image URL"},"audioUrl":{"type":"string","description":"Optional audio URL (voice step)"},"type":{"type":"string","description":"Optional type e.g. \"audio\""},"emoji":{"type":"string","description":"Optional emoji/icon for display"},"description":{"type":"string","description":"Optional description (personality)"},"icon":{"type":"string","description":"Optional icon for display"}},"required":["value","label"]},"FormElementDto":{"type":"object","properties":{"elementIndex":{"type":"number","description":"Index of the element within the step"},"type":{"type":"string","description":"Element type: single-select, multi-select, audio-select, option-grid, option-list, option-pills, option-squares"},"fieldKey":{"type":"string","description":"Field key used in form state and in CreateModelRequestBodyDto"},"title":{"type":"string","description":"Title above the element (e.g. \"Choose Hair Style\")"},"label":{"type":"string","description":"Helper or placeholder text"},"options":{"description":"Options for this element","type":"array","items":{"$ref":"#/components/schemas/FormOptionDto"}},"maxSelections":{"type":"number","description":"Max selections (e.g. 3 for hobbies)"}},"required":["elementIndex","type","fieldKey","options"]},"FormStepDto":{"type":"object","properties":{"stepIndex":{"type":"number","description":"Step index (0-based)"},"title":{"type":"string","description":"Optional step title"},"sectionType":{"type":"string","description":"UI hint: grid, list, pills, cards, squares, single-select"},"elements":{"description":"Elements in this step","type":"array","items":{"$ref":"#/components/schemas/FormElementDto"}}},"required":["stepIndex","elements"]},"GetModelCreateFormConfigResponseDto":{"type":"object","properties":{"steps":{"description":"Ordered steps for the model creation form","type":"array","items":{"$ref":"#/components/schemas/FormStepDto"}}},"required":["steps"]},"GetPendingRevealModelsResponseDto":{"type":"object","properties":{"items":{"description":"Completed private companions not yet revealed to the user (newest first).","type":"array","items":{"$ref":"#/components/schemas/GetModelByIdResponseDto"}}},"required":["items"]},"ModelTagDto":{"type":"object","properties":{"id":{"type":"string","description":"Enum value used for filtering (query parameter)","example":"Caucasian"},"label":{"type":"string","description":"Localized display label","example":"Caucasian"}},"required":["id","label"]},"ModelTagGroupDto":{"type":"object","properties":{"group":{"type":"string","description":"Tag group identifier","example":"ethnicity"},"tags":{"description":"List of tags in this group","type":"array","items":{"$ref":"#/components/schemas/ModelTagDto"}}},"required":["group","tags"]},"ModelTagsResponseDto":{"type":"object","properties":{"groups":{"description":"Tag groups for model filtering","type":"array","items":{"$ref":"#/components/schemas/ModelTagGroupDto"}}},"required":["groups"]},"ModelContentPurchaseStateDto":{"type":"object","properties":{"state":{"type":"string","enum":["AUTH_REQUIRED","SUBSCRIPTION_REQUIRED","PURCHASE_REQUIRED","UNLOCKED"]},"canPurchase":{"type":"boolean"},"isSubscribed":{"type":"boolean"},"isPurchased":{"type":"boolean"},"isLocked":{"type":"boolean"},"warning":{"type":"string"}},"required":["state","canPurchase","isSubscribed","isPurchased","isLocked"]},"ModelProfileGalleryItemDto":{"type":"object","properties":{"kind":{"type":"string","enum":["PHOTO","VIDEO","PACK"]},"contentId":{"type":"string"},"mediaId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"price":{"type":"number"},"mediaCount":{"type":"number"},"isPurchased":{"type":"boolean"},"isLocked":{"type":"boolean"},"access":{"$ref":"#/components/schemas/ModelContentPurchaseStateDto"},"publishedAt":{"type":"string"},"previewVideoUrl":{"type":"string","nullable":true,"description":"PACK only: looping teaser clip shown over the blurred cover while the pack is locked."},"previewVideoPosterUrl":{"type":"string","nullable":true,"description":"First frame of `previewVideoUrl`, used as its poster."},"media":{"type":"array","items":{"$ref":"#/components/schemas/ModelContentMediaDto"}}},"required":["kind","contentId","price","isPurchased","isLocked","access","media"]},"ModelProfileGalleryResponseDto":{"type":"object","properties":{"total":{"type":"number"},"pageSize":{"type":"number"},"page":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ModelProfileGalleryItemDto"}}},"required":["total","pageSize","page","items"]},"CollectionSortOrder":{"type":"string","enum":["newest_first","oldest_first"]},"AttachmentType":{"type":"string","enum":["image","video","audio","gift"],"description":"Type of media"},"RunpodJobState":{"type":"string","enum":["IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED","CANCELLED","TIMED_OUT"],"description":"Status of the media generation"},"CollectionModelDto":{"type":"object","properties":{"id":{"type":"string","description":"Model ID"},"name":{"type":"string","description":"Model name"},"photo":{"type":"string","description":"Model photo CDN URL"}},"required":["id","name","photo"]},"CollectionItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the collection item"},"type":{"description":"Type of media","allOf":[{"$ref":"#/components/schemas/AttachmentType"}]},"url":{"type":"string","description":"CDN URL of the media"},"videoUrl":{"type":"string","description":"CDN URL of the video (for video type)"},"explicit":{"type":"boolean","description":"Whether the content is explicit"},"status":{"description":"Status of the media generation","allOf":[{"$ref":"#/components/schemas/RunpodJobState"}]},"model":{"description":"Model associated with this item","allOf":[{"$ref":"#/components/schemas/CollectionModelDto"}]}},"required":["id","type","url","explicit","status","model"]},"CollectionListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/CollectionItemDto"}},"total":{"type":"number","description":"Total number of items"},"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Number of items per page"},"totalPages":{"type":"number","description":"Total number of pages"}},"required":["items","total","page","limit","totalPages"]},"CreateModelRequestBodyDto":{"type":"object","properties":{"requestId":{"type":"string","description":"Optional request id for analytics correlation","example":"b1b3d2f0-5a4c-4e2f-9a1b-1234567890ab"},"ethnicity":{"type":"string","description":"The ethnicity of the model","example":"asian","enum":["caucasian","latina","asian","arab","afro","indian","furry","kemonomimi","tiefling"]},"age":{"type":"number","description":"The age of the model","example":20,"enum":[18,20,30,40,50]},"eyesColor":{"type":"string","description":"The eyes color of the model","example":"Brown","enum":["Blue","Brown","Green"]},"hairStyle":{"type":"string","description":"The hair style of the model","example":"Long","enum":["Straight","Braids","Bangs","Bun","Curly","Short","Long","Pigtails","Ponytail","Emo","Shoulder-length","Buzz","Pixie","Hijab","Veil"]},"hairColor":{"type":"string","description":"The hair color of the model","example":"Black","enum":["Blonde","Brunette","Black","Red","Pink","Blue","Violet","Airtouch","Black with blonde highlights","Ombre"]},"bodyType":{"type":"string","description":"The body type of the model","example":"slim","enum":["slim","average","athletic","chubby"]},"breastSize":{"type":"string","description":"The breast size of the model","example":"small","enum":["small","medium","large","xl"]},"buttSize":{"type":"string","description":"The butt size of the model","example":"small","enum":["small","medium","large"]},"personality":{"type":"string","description":"The personality of the model","example":"Caregiver","enum":["Caregiver","Sage","Innocent","Jester","Temptress","Dominant","Submissive","Lover","Nympho","Mean","Confidant","Experimenter"]},"occupation":{"type":"string","description":"The occupation of the model","example":"Model","enum":["Massage Therapist","Dentist","Nutritionist","Fitness Coach","Artist","Pharmacist","Hairdresser","Makeup Artist","Gynecologist","Writer","Secretary","Librarian","Social Worker","Fashion Designer","Model","Police Officer","Nurse","Yoga Instructor","Waitress","Cook","Unemployed","Dancer","Teacher","Student","Life Coach","Astronaut","Kindergarten Teacher","Soldier/Military Personnel","Architect","DJ","Nun"]},"hobbies":{"type":"string","description":"The hobbies of the model","example":["Art","Cosplay"],"enum":["Yoga","Vlogging","Fitness","Hiking","Traveling","Dancing","Gaming","Parties","Rock","Series","Anime","Cosplay","Art","Personal Growth","Self-Development","Cars","Diy Crafting","Veganism","Photography","Volunteering","Watching Netflix","Martial Arts"]},"relationship":{"type":"string","description":"The relationship of the model to the user","example":"Friend","enum":["Stranger","School Mate","Colleague","Mentor","Girlfriend","Sex Friend","Wife","Mistress","Friend","Best Friend","Step Sister","Step Mom"]},"voice":{"type":"string","description":"Optional stable voice slug from create-form-config (e.g. \"sweet-and-seductive\"). Resolved to ElevenLabs id server-side; slugs stay stable if ids change. If omitted, a voice is chosen at random from the same set.","example":"sweet-and-seductive"}},"required":["ethnicity","age","eyesColor","hairStyle","hairColor","bodyType","breastSize","buttSize","personality","occupation","hobbies","relationship"]},"CreateModelResponseBodyDto":{"type":"object","properties":{"modelId":{"type":"string","description":"The model ID","example":"123e4567-e89b-12d3-a456-426614174000"},"balance":{"type":"number","description":"The balance after the model creation","example":100}},"required":["modelId","balance"]},"CreateModelFromOnboardingRequestBodyDto":{"type":"object","properties":{"sessionId":{"type":"string","description":"Acquisition session id of the funnel run, stored on the created model for tracing. Idempotency itself is per user: any repeat call returns the existing onboarding companion.","example":"b1b3d2f0-5a4c-4e2f-9a1b-1234567890ab"},"gender":{"type":"string","description":"Companion gender, from the funnel `welcome` answer.","enum":["male","female"],"example":"female"},"age":{"type":"string","description":"Raw funnel `age` bucket token (e.g. \"18-24\").","example":"18-24"},"ethnicity":{"type":"string","description":"Raw funnel `ethnicity` token (e.g. \"european\").","example":"european"},"bodyShape":{"type":"string","description":"Raw funnel `body_shape` token.","example":"athletic"},"hairColor":{"type":"string","description":"Raw funnel `hair_color` token (female grid).","example":"brunette"},"requestId":{"type":"string","description":"Optional request id for analytics correlation."}},"required":["sessionId","gender"]},"MarkModelsRevealedRequestDto":{"type":"object","properties":{"modelIds":{"description":"Ids of the companions to mark as revealed.","example":["b1b3d2f0-5a4c-4e2f-9a1b-1234567890ab"],"type":"array","items":{"type":"string"}}},"required":["modelIds"]},"MarkModelsRevealedResponseDto":{"type":"object","properties":{"updated":{"type":"number","description":"Number of companions newly marked as revealed."}},"required":["updated"]},"ReportReasonDto":{"type":"object","properties":{"id":{"type":"string","description":"Report reason identifier","enum":["inappropriate_content","spam","harassment","other"],"example":"inappropriate_content"},"label":{"type":"string","description":"Human-readable label for the report reason","example":"Inappropriate Content"},"description":{"type":"string","description":"Detailed description of the report reason","example":"Content that is inappropriate, offensive, or violates community guidelines"}},"required":["id","label","description"]},"GetReportReasonsResponseDto":{"type":"object","properties":{"reasons":{"description":"List of available report reasons","type":"array","items":{"$ref":"#/components/schemas/ReportReasonDto"}}},"required":["reasons"]},"CreateModelReportRequestDto":{"type":"object","properties":{"modelId":{"type":"string","description":"ID of the model being reported","example":"3ae9f1c1-1658-4689-b8a2-a2afb998d2ae"},"reasonId":{"type":"string","description":"Reason for reporting the model","enum":["inappropriate_content","spam","harassment","other"],"example":"inappropriate_content"},"additionalDetails":{"type":"string","description":"Additional details about the report (optional)","example":"This model contains inappropriate content that violates community guidelines"}},"required":["modelId","reasonId"]},"CreateModelReportResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the report was created successfully","example":true},"message":{"type":"string","description":"Response message","example":"Model reported successfully"},"reportId":{"type":"number","description":"ID of the created report","example":1}},"required":["success","message"]},"TestPushNotificationDto":{"type":"object","properties":{"title":{"type":"string","description":"Notification title"},"body":{"type":"string","description":"Notification body text"},"data":{"type":"object","description":"Optional custom data payload","additionalProperties":{"type":"string"}}},"required":["title","body"]},"TestPushNotificationResponseDto":{"type":"object","properties":{"message":{"type":"string","description":"Success message"},"userId":{"type":"string","description":"User ID the notification was sent to"},"successCount":{"type":"number","description":"Number of successful sends"},"failureCount":{"type":"number","description":"Number of failed sends"}},"required":["message","userId","successCount","failureCount"]},"NotificationDto":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["system","promo","model","studio","chat"]},"title":{"type":"string"},"body":{"type":"string"},"imageUrl":{"type":"string"},"actionUrl":{"type":"string"},"metadata":{"type":"object"},"isRead":{"type":"boolean"},"createdAt":{"type":"string"}},"required":["id","type","title","body","isRead","createdAt"]},"PaginatedNotificationsDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/NotificationDto"}},"nextCursor":{"type":"string","description":"Cursor for next page, null if no more"}},"required":["items"]},"UnreadCountDto":{"type":"object","properties":{"count":{"type":"number"}},"required":["count"]},"MarkReadDto":{"type":"object","properties":{"ids":{"description":"Notification IDs to mark as read","type":"array","items":{"type":"string"}}},"required":["ids"]},"DismissDto":{"type":"object","properties":{"ids":{"description":"Notification IDs to dismiss","type":"array","items":{"type":"string"}}},"required":["ids"]},"SceneStatus":{"type":"string","enum":["DRAFT","IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED","CANCELLED","TIMED_OUT"],"description":"Current status of the scene"},"SceneDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"coverImage":{"type":"string"},"avatarUrl":{"type":"string","description":"CDN URL of the scene avatar icon"},"genre":{"type":"string"},"isPublic":{"type":"boolean"},"status":{"description":"Current status of the scene","example":"COMPLETED","allOf":[{"$ref":"#/components/schemas/SceneStatus"}]},"isNew":{"type":"boolean","description":"Whether the scene was created within the last 30 days","example":true},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","name","description","isPublic","status","isNew","createdAt","updatedAt"]},"ScenesListResponseDto":{"type":"object","properties":{"scenes":{"type":"array","items":{"$ref":"#/components/schemas/SceneDto"}},"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"totalPages":{"type":"number"}},"required":["scenes","total","page","limit","totalPages"]},"SceneCoverDto":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","description":"CDN URL of the cover image"},"s3Key":{"type":"string"},"prompt":{"type":"string"},"source":{"type":"string","enum":["UPLOAD","GENERATED"]},"status":{"type":"string","enum":["PENDING","COMPLETED","FAILED"]},"requestId":{"type":"string"},"isSelected":{"type":"boolean"},"avatarUrl":{"type":"string","description":"CDN URL of the cropped avatar image"},"avatarS3Key":{"type":"string","description":"S3 key for the avatar image"},"createdAt":{"format":"date-time","type":"string"}},"required":["id","source","status","isSelected","createdAt"]},"SceneDraftDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"coverImage":{"type":"string"},"genre":{"type":"string"},"place":{"type":"string"},"timePeriod":{"type":"string"},"backstory":{"type":"string"},"goal":{"type":"string"},"isDraft":{"type":"boolean"},"status":{"type":"string","enum":["DRAFT","IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED","CANCELLED","TIMED_OUT"]},"selectedCoverId":{"type":"string"},"covers":{"description":"All covers for this scene","type":"array","items":{"$ref":"#/components/schemas/SceneCoverDto"}},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","isDraft","status","covers","createdAt","updatedAt"]},"SceneCharactersResponseDto":{"type":"object","properties":{"total":{"type":"number","description":"Total number of characters available","example":100},"pageSize":{"type":"number","description":"Number of characters per page","example":20},"page":{"type":"number","description":"Current page number","example":1},"items":{"description":"Array of characters (models)","type":"array","items":{"$ref":"#/components/schemas/GetModelByIdResponseDto"}}},"required":["total","pageSize","page","items"]},"CreateSceneDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"contextPrompt":{"type":"string"},"coverImage":{"type":"string"},"genre":{"type":"string"}},"required":["name","description","contextPrompt"]},"SubmitSceneResponseDto":{"type":"object","properties":{"sceneId":{"type":"string"},"status":{"type":"string","enum":["DRAFT","IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED","CANCELLED","TIMED_OUT"]},"message":{"type":"string"},"transactionId":{"type":"string"}},"required":["sceneId","status","message"]},"UpdateSceneDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"contextPrompt":{"type":"string"},"coverImage":{"type":"string"},"genre":{"type":"string"}}},"FormFieldSchema":{"type":"object","properties":{"name":{"type":"string","description":"Field identifier"},"type":{"type":"string","enum":["text","textarea","select","image_upload","image_generate","image_crop"],"description":"Input type"},"label":{"type":"string","description":"Display label"},"placeholder":{"type":"string","description":"Placeholder text"},"required":{"type":"boolean","description":"Whether field is required"},"examples":{"description":"Example values","type":"array","items":{"type":"string"}},"maxLength":{"type":"number","description":"Maximum character length"},"minLength":{"type":"number","description":"Minimum character length"},"options":{"description":"Predefined options (for select type)","type":"array","items":{"type":"string"}},"allowCustom":{"type":"boolean","description":"Allow custom value input (for select type)"},"helperText":{"type":"string","description":"Helper text / description"}},"required":["name","type","label","required"]},"WizardStepSchema":{"type":"object","properties":{"step":{"type":"number","description":"Step number (1-based)"},"title":{"type":"string","description":"Step title"},"description":{"type":"string","description":"Step description"},"fields":{"description":"Fields in this step","type":"array","items":{"$ref":"#/components/schemas/FormFieldSchema"}}},"required":["step","title","fields"]},"WizardFormSchema":{"type":"object","properties":{"steps":{"description":"All wizard steps","type":"array","items":{"$ref":"#/components/schemas/WizardStepSchema"}},"totalSteps":{"type":"number","description":"Total number of steps"},"price":{"type":"number","description":"Price in coins to create scene"},"maxCoverGenerations":{"type":"number","description":"Maximum cover generations per scene"},"coverGenerationCost":{"type":"number","description":"Cost in coins per cover generation"}},"required":["steps","totalSteps","price","maxCoverGenerations","coverGenerationCost"]},"WizardOptionsDto":{"type":"object","properties":{"genres":{"description":"Genre defines your character's style and tone. Choose from predefined genres or create your own.","example":["Fantasy","Romance","Horror"],"type":"array","items":{"type":"string"}},"places":{"description":"The place where your scene comes to life. Select a location or enter a custom one.","example":["Mansion","Cafe","Beach"],"type":"array","items":{"type":"string"}},"timePeriods":{"description":"The time period sets the context for the character's actions. Choose when your scene takes place.","example":["Present Day","Victorian Era","Near Future"],"type":"array","items":{"type":"string"}}},"required":["genres","places","timePeriods"]},"BackstoryExampleDto":{"type":"object","properties":{"example":{"type":"string","description":"A random backstory example. Backstory describes the scene and its background to guide how the story unfolds and how the character reacts.","example":"You meet at a mysterious masquerade ball where identities are hidden."}},"required":["example"]},"SuggestFieldResponseDto":{"type":"object","properties":{"value":{"type":"string","description":"The suggested value for the field","example":"Fantasy"},"field":{"type":"string","description":"The field name that this suggestion is for","example":"genre"}},"required":["value","field"]},"CreateDraftDto":{"type":"object","properties":{"localizationId":{"type":"string"},"metaAdAccountId":{"type":"string"},"campaignName":{"type":"string"},"funnel":{"type":"string","example":"ob/mia_1"}},"required":["localizationId","metaAdAccountId","funnel"]},"UpdateDraftDto":{"type":"object","properties":{"name":{"type":"string","description":"Scene name","minLength":3,"maxLength":100},"genre":{"type":"string","description":"Genre","maxLength":100},"place":{"type":"string","description":"Place/Location","maxLength":100},"timePeriod":{"type":"string","description":"Time period","maxLength":100},"backstory":{"type":"string","description":"Backstory","minLength":10,"maxLength":2000},"goal":{"type":"string","description":"Goal for the scene","minLength":5,"maxLength":1000}}},"CoverUploadUrlResponseDto":{"type":"object","properties":{"uploadUrl":{"type":"string","description":"Pre-signed S3 upload URL"},"coverS3Key":{"type":"string","description":"S3 key for later reference"},"finalUrl":{"type":"string","description":"Final CDN URL where the image will be accessible"}},"required":["uploadUrl","coverS3Key","finalUrl"]},"GenerateCoverDto":{"type":"object","properties":{"prompt":{"type":"string","description":"Prompt for cover generation"}},"required":["prompt"]},"AddUploadedCoverDto":{"type":"object","properties":{"s3Key":{"type":"string","description":"S3 key of the uploaded cover"}},"required":["s3Key"]},"CoverHistoryDto":{"type":"object","properties":{"covers":{"description":"List of covers","type":"array","items":{"$ref":"#/components/schemas/SceneCoverDto"}},"generatedCount":{"type":"number","description":"Number of generated covers"},"uploadedCount":{"type":"number","description":"Number of uploaded covers"},"totalCount":{"type":"number","description":"Total number of covers (including uploads)"},"maxGenerations":{"type":"number","description":"Maximum number of covers that can be generated"},"maxTotal":{"type":"number","description":"Maximum total number of covers (generated + uploaded)"}},"required":["covers","generatedCount","uploadedCount","totalCount","maxGenerations","maxTotal"]},"CoverPromptSuggestionDto":{"type":"object","properties":{"prompt":{"type":"string","description":"Suggested prompt for cover generation"}},"required":["prompt"]},"AvatarUploadUrlResponseDto":{"type":"object","properties":{"uploadUrl":{"type":"string","description":"Pre-signed S3 upload URL for avatar"},"avatarS3Key":{"type":"string","description":"S3 key for the avatar"},"finalUrl":{"type":"string","description":"Final CDN URL where the avatar will be accessible"}},"required":["uploadUrl","avatarS3Key","finalUrl"]},"AddAvatarDto":{"type":"object","properties":{"s3Key":{"type":"string","description":"S3 key of the uploaded cropped avatar"}},"required":["s3Key"]},"SubmitSceneDto":{"type":"object","properties":{"name":{"type":"string","description":"Scene name","minLength":3,"maxLength":100},"genre":{"type":"string","description":"Genre"},"place":{"type":"string","description":"Place/Location"},"timePeriod":{"type":"string","description":"Time period"},"backstory":{"type":"string","description":"Backstory","minLength":10,"maxLength":2000},"goal":{"type":"string","description":"Goal for the scene","minLength":5,"maxLength":1000},"description":{"type":"string","description":"Scene description/summary","minLength":10,"maxLength":1000}}},"CollectionTypeDto":{"type":"object","properties":{"id":{"type":"string","description":"Type identifier (\"all\" for no filter, or specific type)","example":"all"},"label":{"type":"string","description":"Display label for the type","example":"All"}},"required":["id","label"]},"CollectionTypesResponseDto":{"type":"object","properties":{"types":{"description":"List of available media types for filtering","type":"array","items":{"$ref":"#/components/schemas/CollectionTypeDto"}}},"required":["types"]},"SortingOptionDto":{"type":"object","properties":{"id":{"description":"Sort order identifier","example":"newest_first","allOf":[{"$ref":"#/components/schemas/CollectionSortOrder"}]},"label":{"type":"string","description":"Display label for the sorting option","example":"Newest first"},"queryValue":{"type":"string","description":"Query parameter value to use","example":"newest_first"}},"required":["id","label","queryValue"]},"SortingOptionsResponseDto":{"type":"object","properties":{"options":{"description":"List of available sorting options","type":"array","items":{"$ref":"#/components/schemas/SortingOptionDto"}},"default":{"description":"Default sorting order","example":"newest_first","allOf":[{"$ref":"#/components/schemas/CollectionSortOrder"}]}},"required":["options","default"]},"CollectionItemIdsDto":{"type":"object","properties":{"ids":{"description":"Array of collection item (attachment) IDs","example":["uuid-1","uuid-2"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"CollectionBatchResultDto":{"type":"object","properties":{"affected":{"type":"number","description":"Number of items affected by the operation"}},"required":["affected"]},"Object":{"type":"object","properties":{}},"AvailableParameterDto":{"type":"object","properties":{"id":{"type":"number","description":"The id of the parameter","example":1},"name":{"type":"string","description":"The name of the parameter","example":"Style"},"label":{"type":"string","description":"The user-facing English label of the parameter (source for locale translations)","example":"Style"},"displayName":{"type":"string","description":"Deprecated alias of `label`","example":"Style","deprecated":true},"image":{"type":"string","description":"The image of the parameter","example":"https://example.com/image.png"},"emojiLabel":{"type":"string","description":"The emoji label of the parameter","example":"🌅"},"sfw":{"type":"boolean","description":"Whether the parameter is SFW (Safe For Work)","example":false}},"required":["id","name","image","emojiLabel","sfw"]},"GetImageGenerationParamsResponseDto":{"type":"object","properties":{"sfw":{"type":"boolean","description":"Default SFW toggle value for the device UI","example":false},"surroundings":{"description":"The available parameters","type":"array","items":{"$ref":"#/components/schemas/AvailableParameterDto"}},"clothes":{"description":"The available parameters","type":"array","items":{"$ref":"#/components/schemas/AvailableParameterDto"}},"emotions":{"description":"The available parameters","type":"array","items":{"$ref":"#/components/schemas/AvailableParameterDto"}},"poses":{"description":"The available parameters","type":"array","items":{"$ref":"#/components/schemas/AvailableParameterDto"}}},"required":["sfw","surroundings","clothes","emotions","poses"]},"GiftDto":{"type":"object","properties":{"id":{"type":"string","description":"Gift ID"},"name":{"type":"string","description":"Gift name"},"prompt":{"type":"string","description":"Optional prompt describing the gift"},"imageS3key":{"type":"string","description":"S3 key under STATIC prefix"},"isExplicit":{"type":"boolean","description":"Whether the gift is explicit/NSFW"},"price":{"type":"number","description":"Gift price in coins"},"createdAt":{"format":"date-time","type":"string","description":"Creation timestamp"},"updatedAt":{"format":"date-time","type":"string","description":"Update timestamp"}},"required":["id","name","imageS3key","isExplicit","price","createdAt","updatedAt"]},"GetGiftsResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/GiftDto"}},"total":{"type":"number","description":"Total number of gifts"},"page":{"type":"number","description":"Current page"},"limit":{"type":"number","description":"Items per page"},"locale":{"type":"string","description":"Resolved locale used for localization"}},"required":["items","total","page","limit","locale"]},"AnalyzeImageRequestDto":{"type":"object","properties":{"imageUrl":{"type":"string","description":"URL of the image to analyze","example":"https://example.com/image.jpg"},"prompt":{"type":"string","description":"Text prompt/instruction for image analysis","example":"Describe what you see in this image in detail"},"systemPrompt":{"type":"string","description":"Optional system prompt to guide the analysis","example":"You are an expert image analyst"}},"required":["imageUrl","prompt"]},"AnalyzeImageResponseDto":{"type":"object","properties":{"result":{"type":"string","description":"The analysis result from Grok vision model","example":"This image shows a beautiful sunset over the ocean..."},"model":{"type":"string","description":"The model used for analysis","example":"grok-4-fast-non-reasoning"},"usage":{"type":"object","description":"Token usage information"}},"required":["result","model","usage"]},"VisualDirectorMode":{"type":"string","enum":["manual","auto"],"description":"Visual Director operating mode. \"manual\" → UI shows the on-demand \"Show me the scene\" button. \"auto\" → legacy behaviour where VD is evaluated after every assistant reply."},"CfgDto":{"type":"object","properties":{"visualDirectorMode":{"description":"Visual Director operating mode. \"manual\" → UI shows the on-demand \"Show me the scene\" button. \"auto\" → legacy behaviour where VD is evaluated after every assistant reply.","example":"manual","allOf":[{"$ref":"#/components/schemas/VisualDirectorMode"}]},"visualDirectorOnDemandCooldownMessages":{"type":"number","description":"Number of chat messages that must elapse between two on-demand Visual Director requests (only meaningful when `visualDirectorMode` is \"manual\").","example":5},"visualDirectorEnabled":{"type":"boolean","description":"Whether the Visual Director feature is globally enabled on the backend. When false, no mode will allow image generation via VD.","example":true},"visualDirectorCustomModelsEnabled":{"type":"boolean","description":"Whether Visual Director is available for user-created (custom) characters. When false, VD is only available for public characters.","example":false},"lovmiaSeriesEnabled":{"type":"boolean","description":"Whether the Lovmia Series (shorts) section is visible in the web app. Driven by the WEB_FEATURES_ENABLED admin constant; when false the client must hide the section entirely (sidebar, tab bar, direct links).","example":true},"privateContentEnabled":{"type":"boolean","description":"Whether the Private Content section is available to THIS caller: the WEB_FEATURES_ENABLED admin constant must include it AND the user must have explicit content enabled. Always false for anonymous callers. When false the client must hide the section entirely.","example":false},"autoTopUpEnabled":{"type":"boolean","description":"Whether the auto top-up settings are operational (AUTO_TOPUP_SETTINGS_ENABLED admin constant). When false the client must render the settings section as \"under construction\" — the API also refuses to enable the toggle.","example":true}},"required":["visualDirectorMode","visualDirectorOnDemandCooldownMessages","visualDirectorEnabled","visualDirectorCustomModelsEnabled","lovmiaSeriesEnabled","privateContentEnabled","autoTopUpEnabled"]},"UpdateUserAccountInfoDto":{"type":"object","properties":{"fcmToken":{"type":"string","description":"The user FCM token","example":"123"},"useExplicitContent":{"type":"boolean","description":"Whether the user allows explicit content","example":true},"timezone":{"type":"string","description":"The user timezone","example":"America/New_York"},"language":{"type":"string","description":"The user language","example":"en-US"},"locale":{"type":"string","description":"The user locale for UI localization. Supported: en-US, es-419, pt-BR, fr-FR, de-DE, ja-JP, ko-KR, zh-Hans, it-IT, nl-NL, sv-SE, tr-TR, ar, hi, id, th, vi","example":"en-US"},"name":{"type":"string","description":"The user name","example":"John Doe"},"notificationsEmail":{"type":"string","description":"Email address used for subscription and account notifications. Pass null to clear.","example":"notifications@example.com","nullable":true},"useGiftSuggestions":{"type":"boolean","description":"Whether the user has gift suggestions enabled","example":true},"onboardingCompleted":{"type":"boolean","description":"Set to true when the user finishes the first-run onboarding flow. One-way: once recorded it cannot be unset.","example":true}},"required":["fcmToken","useExplicitContent","timezone","language","name"]},"TrackUserMetadataDto":{"type":"object","properties":{"webUserId":{"type":"string","description":"Web user ID stored by the frontend in localStorage"},"firebaseUid":{"type":"string","description":"Firebase UID, used to link metadata to web_users and users"},"clientMetadata":{"type":"object","description":"Client-side metadata unavailable in request headers: screen, timezone, navigator fields, app session IDs, etc."}}},"UserMetadataResponseDto":{"type":"object","properties":{"webUserId":{"type":"string"},"userId":{"type":"string"},"metadata":{"type":"object"}},"required":["metadata"]},"AccountDeletionReportReasonDto":{"type":"object","properties":{"id":{"type":"string","enum":["ai_conversations_not_satisfying","too_expensive","lost_interest","content_quality","bugs_technical_issues","missing_feature","privacy_concerns","other"]},"label":{"type":"string"},"description":{"type":"string"}},"required":["id","label","description"]},"GetAccountDeletionReportReasonsResponseDto":{"type":"object","properties":{"reasons":{"type":"array","items":{"$ref":"#/components/schemas/AccountDeletionReportReasonDto"}}},"required":["reasons"]},"CreateAccountDeletionReportRequestDto":{"type":"object","properties":{"reasonId":{"type":"string","enum":["ai_conversations_not_satisfying","too_expensive","lost_interest","content_quality","bugs_technical_issues","missing_feature","privacy_concerns","other"]},"additionalDetails":{"type":"string","description":"Additional details when reason is OTHER"}},"required":["reasonId"]},"CreateAccountDeletionReportResponseDto":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"reportId":{"type":"number"}},"required":["success","message","reportId"]},"CreateAgeVerificationSessionDto":{"type":"object","properties":{"supportedProviders":{"type":"array","description":"Verification providers this client build can render. Absent = legacy client that only understands the iDenfy authToken response.","example":["idenfy","yoti"],"items":{"type":"string","enum":["idenfy","yoti"]}}}},"CreateAgeVerificationSessionResponseDto":{"type":"object","properties":{"provider":{"type":"string","description":"Provider the session was created with","enum":["idenfy","yoti"],"example":"idenfy"},"authToken":{"type":"string","description":"iDenfy authToken to load the verification iframe (provider=idenfy)","example":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."},"sessionId":{"type":"string","description":"Yoti AVS session id (provider=yoti)","example":"28f83246-8977-4f74-87a4-6f23db2bd0c8"},"url":{"type":"string","description":"Full Yoti journey URL for the verification iframe (provider=yoti)","example":"https://age.yoti.com?sessionId=...&sdkId=..."}},"required":["provider"]},"AgeVerificationStatusResponseDto":{"type":"object","properties":{"status":{"type":"string","description":"Current age-verification status for the user","enum":["unverified","pending","approved","denied","suspected","expired"],"example":"unverified"},"method":{"type":"string","description":"Method of the latest verification attempt","enum":["idenfy","yoti","credit_card"],"nullable":true,"example":"idenfy"},"denyReason":{"type":"string","description":"Reason the latest verification did not pass, if any","nullable":true,"example":"UNDER_AGE"}},"required":["status","method","denyReason"]},"PaymentMethodDto":{"type":"object","properties":{"source":{"type":"string","enum":["apple","stripe","funnelfox","unlimit","upgate"],"description":"Provider that owns this payment method","example":"funnelfox"},"type":{"type":"string","description":"Payment method type, e.g. 'card', 'apple_pay'","example":"card","nullable":true},"brand":{"type":"string","description":"Card network / brand","example":"Visa","nullable":true},"last4":{"type":"string","description":"Last four digits of the card","example":"4242","nullable":true},"expMonth":{"type":"number","description":"Expiry month (1-12)","example":11,"nullable":true},"expYear":{"type":"number","description":"Expiry year (4-digit)","example":2027,"nullable":true},"expiringSoon":{"type":"boolean","description":"True when the card expires within 6 months (or has expired)","example":false}},"required":["source","expiringSoon"]},"SavedCardDto":{"type":"object","properties":{"id":{"type":"string","description":"Saved card id — the handle for DELETE / make-primary calls","format":"uuid"},"brand":{"type":"string","description":"Card network / brand","nullable":true},"last4":{"type":"string","description":"Last four digits of the card","nullable":true},"expMonth":{"type":"number","description":"Expiry month (1-12)","nullable":true},"expYear":{"type":"number","description":"Expiry year (4-digit)","nullable":true},"expiringSoon":{"type":"boolean","description":"True when the card expires within 6 months (or has expired)"},"isPrimary":{"type":"boolean","description":"The card renewals and one-click purchases are charged to (exactly one per user)"},"removalRequiresCancellation":{"type":"boolean","description":"True when this is the last card of a subscription that still renews: the client has to take the user through the cancellation flow before DELETE succeeds (GEN-1282)"}},"required":["id","expiringSoon","isPrimary","removalRequiresCancellation"]},"GetPaymentMethodResponseDto":{"type":"object","properties":{"paymentMethod":{"description":"Current saved payment method, or null if none is known","nullable":true,"allOf":[{"$ref":"#/components/schemas/PaymentMethodDto"}]},"rebindProductIdent":{"type":"string","description":"FunnelFox $0 product ident to purchase via the normal checkout flow in order to change the payment method. Empty for redirect-checkout providers.","example":"card_update_0"},"rebindMode":{"type":"string","enum":["funnelfox_product","redirect_checkout"],"description":"How to change the card: buy the FF $0 product via the embedded SDK, or start a card_update redirect checkout (POST /v2/payments/checkout)."},"savedCards":{"description":"Every saved card the user can manage (remove / make primary). Empty for providers without an own card vault (FunnelFox, Unlimit).","type":"array","items":{"$ref":"#/components/schemas/SavedCardDto"}}},"required":["rebindProductIdent","rebindMode","savedCards"]},"PaymentEventItemDto":{"type":"object","properties":{"type":{"type":"string","description":"Product Type","enum":["AUTO_RENEWABLE","NON_RENEWABLE","CONSUMABLE","NON_CONSUMABLE"],"nullable":true},"subtype":{"type":"string","description":"Subtype","enum":["INITIAL_BUY","RESUBSCRIBE","DOWNGRADE","UPGRADE","AUTO_RENEW_ENABLED","AUTO_RENEW_DISABLED","VOLUNTARY","BILLING_RETRY","PRICE_INCREASE","GRACE_PERIOD","PENDING","ACCEPTED","BILLING_RECOVERY","PRODUCT_NOT_FOR_SALE","SUMMARY","FAILURE","UNREPORTED"],"example":"INITIAL_BUY","nullable":true},"currency":{"type":"string","description":"Currency","example":"USD","nullable":true},"calculatedAmount":{"type":"number","description":"Calculated Amount (positive for income, negative for expense)","example":9.99},"isIncome":{"type":"boolean","description":"Is Income","example":true},"isExpense":{"type":"boolean","description":"Is Expense","example":false},"eventName":{"type":"string","description":"Human-readable event name","example":"Subscription Started"},"productId":{"type":"string","description":"Product ID","example":"com.example.premium","nullable":true},"purchaseDate":{"format":"date-time","type":"string","description":"Purchase Date","example":"2024-01-01T00:00:00Z","nullable":true}},"required":["type","subtype","currency","calculatedAmount","isIncome","isExpense","eventName","productId","purchaseDate"]},"PaymentEventsListResponseDto":{"type":"object","properties":{"paymentEvents":{"description":"List of payment events","type":"array","items":{"$ref":"#/components/schemas/PaymentEventItemDto"}},"total":{"type":"number","description":"Total number of payment events","example":150},"page":{"type":"number","description":"Current page number","example":1},"limit":{"type":"number","description":"Number of items per page","example":10},"totalPages":{"type":"number","description":"Total number of pages","example":15}},"required":["paymentEvents","total","page","limit","totalPages"]},"FunnelFoxTransactionItemDto":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"trxId":{"type":"string","nullable":true},"amount":{"type":"number","nullable":true,"description":"Charged amount (original currency)"},"amountUsd":{"type":"number","nullable":true,"description":"Charged amount in USD"},"currencyCode":{"type":"string","nullable":true},"status":{"type":"string","description":"Transaction status, e.g. settled / declined"},"integrationType":{"type":"string","description":"Integration type (e.g. subscription, oneoff)"},"psp":{"type":"string","nullable":true,"description":"Payment service provider"},"pspStatus":{"type":"string","nullable":true},"pmType":{"type":"string","nullable":true,"description":"Payment method type"},"network":{"type":"string","nullable":true},"cardFirst6":{"type":"string","nullable":true,"description":"Card first 6 digits (BIN)"},"cardLast4":{"type":"string","nullable":true,"description":"Card last 4 digits"},"metaSubsId":{"type":"string","nullable":true},"metaSubsIteration":{"type":"string","nullable":true},"metaOneoffId":{"type":"string","nullable":true},"firebaseUid":{"type":"string","nullable":true},"webUserId":{"type":"string","nullable":true},"trxCreatedAt":{"format":"date-time","type":"string","nullable":true,"description":"When the charge happened at FunnelFox"},"createdAt":{"format":"date-time","type":"string","description":"When the row was synced into our DB"}},"required":["id","orderId","trxId","amount","amountUsd","currencyCode","status","integrationType","psp","pspStatus","pmType","network","cardFirst6","cardLast4","metaSubsId","metaSubsIteration","metaOneoffId","firebaseUid","webUserId","trxCreatedAt","createdAt"]},"FunnelFoxTransactionsListResponseDto":{"type":"object","properties":{"transactions":{"type":"array","items":{"$ref":"#/components/schemas/FunnelFoxTransactionItemDto"}},"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"totalPages":{"type":"number"}},"required":["transactions","total","page","limit","totalPages"]},"TotalLtvResponseDto":{"type":"object","properties":{"totalLtv":{"type":"number","description":"Total Lifetime Value","example":299.97},"currency":{"type":"string","description":"Currency","example":"USD"},"userId":{"type":"string","description":"User ID (if filtered by user)","example":"user-uuid-here","nullable":true},"totalIncome":{"type":"number","description":"Total Income","example":399.96},"totalExpenses":{"type":"number","description":"Total Expenses (refunds, etc.)","example":99.99},"transactionCount":{"type":"number","description":"Total number of transactions","example":15}},"required":["totalLtv","currency","userId","totalIncome","totalExpenses","transactionCount"]},"Model":{"type":"object","properties":{}},"ModelsListResponseDto":{"type":"object","properties":{"models":{"description":"Array of models","type":"array","items":{"$ref":"#/components/schemas/Model"}},"total":{"type":"number","description":"Total number of models matching the filters","example":150},"page":{"type":"number","description":"Current page number","example":1},"limit":{"type":"number","description":"Number of items per page","example":10},"totalPages":{"type":"number","description":"Total number of pages","example":15}},"required":["models","total","page","limit","totalPages"]},"ModelsStatsResponseDto":{"type":"object","properties":{"total":{"type":"number","description":"Total number of models","example":150},"public":{"type":"number","description":"Number of public models","example":120},"private":{"type":"number","description":"Number of private models","example":30},"active":{"type":"number","description":"Number of active (completed) models","example":100},"inProgress":{"type":"number","description":"Number of models in progress","example":50}},"required":["total","public","private","active","inProgress"]},"ModelDetailResponseDto":{"type":"object","properties":{"model":{"description":"The model details","allOf":[{"$ref":"#/components/schemas/Model"}]}},"required":["model"]},"CreateModelDto":{"type":"object","properties":{"name":{"type":"string","description":"The name of the model","example":"Sarah","minLength":1,"maxLength":255},"description":{"type":"string","description":"The description of the model","example":"A friendly and caring personality","maxLength":1000},"photo":{"type":"string","description":"The photo URL of the model","example":"https://example.com/photo.jpg"},"video":{"type":"string","description":"The video URL of the model","example":"https://example.com/video.mp4"},"avatarIcon":{"type":"string","description":"Avatar icon key under static prefix (e.g., avatarIcon.webp)","example":"avatarIcon.webp"},"tiktokVideo":{"type":"string","description":"TikTok video key under static/tiktok","example":"tiktok/model_tiktok.mp4"},"onboardingImage":{"type":"string","description":"Onboarding image key under static/onboarding","example":"onboarding/model_onboarding.webp"},"useVideo":{"type":"boolean","description":"Whether the model uses video","example":false},"isPublic":{"type":"boolean","description":"Whether the model is public","example":true},"isExplicit":{"type":"boolean","description":"Whether the model contains explicit content","example":false},"gender":{"type":"string","description":"The gender of the model","enum":["male","female"],"example":"female"},"breastSize":{"type":"string","description":"The breast size of the model","enum":["small","medium","large","xl"],"example":"medium"},"bodyType":{"type":"string","description":"The body type of the model","enum":["slim","average","athletic","chubby"],"example":"slim"},"buttSize":{"type":"string","description":"The butt size of the model","enum":["small","medium","large"],"example":"small"},"ethnicity":{"type":"string","description":"The ethnicity of the model","enum":["caucasian","latina","asian","arab","afro","indian","furry","kemonomimi","tiefling"],"example":"asian"},"age":{"type":"number","description":"The age of the model (any number between 18 and 99)","example":25,"minimum":18,"maximum":99},"eyesColor":{"type":"string","description":"The eye color of the model","enum":["Blue","Brown","Green"],"example":"Brown"},"hairStyle":{"type":"string","description":"The hair style of the model","enum":["Straight","Braids","Bangs","Bun","Curly","Short","Long","Pigtails","Ponytail","Emo","Shoulder-length","Buzz","Pixie","Hijab","Veil"],"example":"Long"},"hairColor":{"type":"string","description":"The hair color of the model","enum":["Blonde","Brunette","Black","Red","Pink","Blue","Violet","Airtouch","Black with blonde highlights","Ombre"],"example":"Black"},"personality":{"type":"string","description":"The personality of the model","enum":["Caregiver","Sage","Innocent","Jester","Temptress","Dominant","Submissive","Lover","Nympho","Mean","Confidant","Experimenter"],"example":"Caregiver"},"occupation":{"type":"string","description":"The occupation of the model","enum":["Massage Therapist","Dentist","Nutritionist","Fitness Coach","Artist","Pharmacist","Hairdresser","Makeup Artist","Gynecologist","Writer","Secretary","Librarian","Social Worker","Fashion Designer","Model","Police Officer","Nurse","Yoga Instructor","Waitress","Cook","Unemployed","Dancer","Teacher","Student","Life Coach","Astronaut","Kindergarten Teacher","Soldier/Military Personnel","Architect","DJ","Nun"],"example":"Nurse"},"hobbies":{"type":"array","description":"The hobbies of the model","example":["Yoga","Fitness"],"items":{"type":"string","enum":["Yoga","Vlogging","Fitness","Hiking","Traveling","Dancing","Gaming","Parties","Rock","Series","Anime","Cosplay","Art","Personal Growth","Self-Development","Cars","Diy Crafting","Veganism","Photography","Volunteering","Watching Netflix","Martial Arts"]}},"relationship":{"type":"string","description":"The relationship type of the model","enum":["Stranger","School Mate","Colleague","Mentor","Girlfriend","Sex Friend","Wife","Mistress","Friend","Best Friend","Step Sister","Step Mom"],"example":"Friend"},"voice":{"type":"string","description":"The voice type of the model","example":"Soft and gentle","maxLength":100},"firstMsg":{"type":"string","description":"The first message from the model","example":"Hi! I'm so glad to meet you!","maxLength":1000},"firstMsgImage":{"type":"string","description":"The first message image URL","example":"https://example.com/first-msg.jpg"},"firstMsgImageSfw":{"type":"string","description":"Open first-message photo, shown to everyone","example":"https://example.com/first-msg-sfw.webp"},"firstMsgImageNsfw":{"type":"string","description":"Explicit first-message photo, blurred until subscribed","example":"https://example.com/first-msg-nsfw.webp"},"firstMsgImageNsfwBlurred":{"type":"string","description":"Pre-blurred copy of the explicit photo, generated on upload"},"firstMsgVideo":{"type":"string","description":"The first message video URL","example":"https://example.com/first-msg.mp4"},"firstMsgVideoPreview":{"type":"string","description":"The first message video preview URL","example":"https://example.com/first-msg-preview.jpg"},"firstMsgVideoDuration":{"type":"number","description":"The duration of the first message video in seconds","example":30,"minimum":0},"userFirstMsgHelp":{"type":"string","description":"Help text for the user's first message","example":"Try saying hello or asking about my day!","maxLength":1000},"userId":{"type":"string","description":"The user ID who owns this model (for private models)","example":"user-uuid-here"},"order":{"type":"number","description":"The display order of the model","example":1,"minimum":0},"isPro":{"type":"boolean","description":"Whether this is a premium model","example":false},"baseViews":{"type":"number","description":"Synthetic baseline for the social-rating views shown on the card","example":280,"minimum":0},"baseLikes":{"type":"number","description":"Synthetic baseline for the social-rating likes shown on the card","example":120,"minimum":0},"ohwxPersonalityPlaceholder":{"type":"string","description":"OHWX personality LoRA placeholder (e.g., <lora:NAME:W>)"},"ohwxAppearancePlaceholder":{"type":"string","description":"OHWX appearance LoRA placeholder (e.g., <lora:NAME:W1> or <lora:NAME:W1:W2>)"},"ohwxFemaleReplace":{"type":"string","description":"Replacement text for occurrences of \"ohwx female\" in prompts"},"systemPromptText":{"type":"string","description":"Initial system prompt text (optional; if provided and generation is off, it will be saved as-is)","maxLength":10000},"generateSystemPrompt":{"type":"boolean","description":"Whether to generate system prompt and description automatically after creation","example":true},"initialImagePrompt":{"type":"string","description":"Initial image generation prompt (for direct chat)","maxLength":2000},"defaultScenarioCover":{"type":"string","description":"S3 key for the default scenario cover image (direct chat)"},"defaultScenarioInitialMessage":{"type":"string","description":"Initial message shown after the cover image (direct chat)","maxLength":1000},"defaultScenarioPrompt":{"type":"string","description":"Prompt stored in the cover image attachment (direct chat)","maxLength":2000},"allowedChatModes":{"type":"array","description":"Chat modes this model may be started in (GEN-1386). At least one; ROLEPLAY runs the scripted opening + Visual Director, REALISTIC opens with the plain firstMsg and no Director.","items":{"type":"string","enum":["ROLEPLAY","REALISTIC"]}},"isDemo":{"type":"boolean","description":"Whether this model is a demo character","example":false},"initialPhotoSFW":{"type":"string","description":"S3 key for the SFW intro photo (demo models)"},"initialPhotoNSFW":{"type":"string","description":"S3 key for the NSFW blurred intro photo (demo models)"},"demoIntroVideo":{"type":"string","description":"S3 key for demo intro video (static / demo_intro)"},"demoModelPreviewVideo":{"type":"string","description":"S3 key for ethnicity source demo preview clip (static / demo_model_preview)"},"onboardingSubscriptionRewardVideoPreview":{"type":"string","description":"S3 key for subscription reward still (FLUX Klein edit) under static / onboarding_sub_reward_preview"},"onboardingSubscriptionRewardVideo":{"type":"string","description":"S3 key for subscription reward short video (i2v from edit) under static / onboarding_sub_reward_video"}},"required":["name","age"]},"UpdateModelDto":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the model to update","example":"model-uuid-here"},"name":{"type":"string","description":"The name of the model","example":"Sarah","minLength":1,"maxLength":255},"description":{"type":"string","description":"The description of the model","example":"A friendly and caring personality","maxLength":1000},"photo":{"type":"string","description":"The photo URL of the model","example":"https://example.com/photo.jpg"},"video":{"type":"string","description":"The video URL of the model","example":"https://example.com/video.mp4"},"avatarIcon":{"type":"string","description":"Avatar icon key under static prefix (e.g., avatarIcon.webp)","example":"avatarIcon.webp"},"tiktokVideo":{"type":"string","description":"TikTok video key under static/tiktok","example":"tiktok/model_tiktok.mp4"},"onboardingImage":{"type":"string","description":"Onboarding image key under static/onboarding","example":"onboarding/model_onboarding.webp"},"useVideo":{"type":"boolean","description":"Whether the model uses video","example":false},"isPublic":{"type":"boolean","description":"Whether the model is public","example":true},"isExplicit":{"type":"boolean","description":"Whether the model contains explicit content","example":false},"gender":{"type":"string","description":"The gender of the model","enum":["male","female"],"example":"female"},"breastSize":{"type":"string","description":"The breast size of the model","enum":["small","medium","large","xl"],"example":"medium"},"bodyType":{"type":"string","description":"The body type of the model","enum":["slim","average","athletic","chubby"],"example":"slim"},"buttSize":{"type":"string","description":"The butt size of the model","enum":["small","medium","large"],"example":"small"},"ethnicity":{"type":"string","description":"The ethnicity of the model","enum":["caucasian","latina","asian","arab","afro","indian","furry","kemonomimi","tiefling"],"example":"asian"},"age":{"type":"number","description":"The age of the model (any number between 18 and 99)","example":25,"minimum":18,"maximum":99},"eyesColor":{"type":"string","description":"The eye color of the model","enum":["Blue","Brown","Green"],"example":"Brown"},"hairStyle":{"type":"string","description":"The hair style of the model","enum":["Straight","Braids","Bangs","Bun","Curly","Short","Long","Pigtails","Ponytail","Emo","Shoulder-length","Buzz","Pixie","Hijab","Veil"],"example":"Long"},"hairColor":{"type":"string","description":"The hair color of the model","enum":["Blonde","Brunette","Black","Red","Pink","Blue","Violet","Airtouch","Black with blonde highlights","Ombre"],"example":"Black"},"personality":{"type":"string","description":"The personality of the model","enum":["Caregiver","Sage","Innocent","Jester","Temptress","Dominant","Submissive","Lover","Nympho","Mean","Confidant","Experimenter"],"example":"Caregiver"},"occupation":{"type":"string","description":"The occupation of the model","enum":["Massage Therapist","Dentist","Nutritionist","Fitness Coach","Artist","Pharmacist","Hairdresser","Makeup Artist","Gynecologist","Writer","Secretary","Librarian","Social Worker","Fashion Designer","Model","Police Officer","Nurse","Yoga Instructor","Waitress","Cook","Unemployed","Dancer","Teacher","Student","Life Coach","Astronaut","Kindergarten Teacher","Soldier/Military Personnel","Architect","DJ","Nun"],"example":"Nurse"},"hobbies":{"type":"array","description":"The hobbies of the model","example":["Yoga","Fitness"],"items":{"type":"string","enum":["Yoga","Vlogging","Fitness","Hiking","Traveling","Dancing","Gaming","Parties","Rock","Series","Anime","Cosplay","Art","Personal Growth","Self-Development","Cars","Diy Crafting","Veganism","Photography","Volunteering","Watching Netflix","Martial Arts"]}},"relationship":{"type":"string","description":"The relationship type of the model","enum":["Stranger","School Mate","Colleague","Mentor","Girlfriend","Sex Friend","Wife","Mistress","Friend","Best Friend","Step Sister","Step Mom"],"example":"Friend"},"voice":{"type":"string","description":"The voice type of the model","example":"Soft and gentle","maxLength":100},"firstMsg":{"type":"string","description":"The first message from the model","example":"Hi! I'm so glad to meet you!","maxLength":1000},"firstMsgImage":{"type":"string","description":"The first message image URL","example":"https://example.com/first-msg.jpg"},"firstMsgImageSfw":{"type":"string","description":"Open first-message photo, shown to everyone","example":"https://example.com/first-msg-sfw.webp"},"firstMsgImageNsfw":{"type":"string","description":"Explicit first-message photo, blurred until subscribed","example":"https://example.com/first-msg-nsfw.webp"},"firstMsgImageNsfwBlurred":{"type":"string","description":"Pre-blurred copy of the explicit photo, generated on upload"},"firstMsgVideo":{"type":"string","description":"The first message video URL","example":"https://example.com/first-msg.mp4"},"firstMsgVideoPreview":{"type":"string","description":"The first message video preview URL","example":"https://example.com/first-msg-preview.jpg"},"firstMsgVideoDuration":{"type":"number","description":"The duration of the first message video in seconds","example":30,"minimum":0},"userFirstMsgHelp":{"type":"string","description":"Help text for the user's first message","example":"Try saying hello or asking about my day!","maxLength":1000},"userId":{"type":"string","description":"The user ID who owns this model (for private models)","example":"user-uuid-here"},"order":{"type":"number","description":"The display order of the model","example":1,"minimum":0},"isPro":{"type":"boolean","description":"Whether this is a premium model","example":false},"baseViews":{"type":"number","description":"Synthetic baseline for the social-rating views shown on the card","example":280,"minimum":0},"baseLikes":{"type":"number","description":"Synthetic baseline for the social-rating likes shown on the card","example":120,"minimum":0},"ohwxPersonalityPlaceholder":{"type":"string","description":"OHWX personality LoRA placeholder (e.g., <lora:NAME:W>)"},"ohwxAppearancePlaceholder":{"type":"string","description":"OHWX appearance LoRA placeholder (e.g., <lora:NAME:W1> or <lora:NAME:W1:W2>)"},"ohwxFemaleReplace":{"type":"string","description":"Replacement text for occurrences of \"ohwx female\" in prompts"},"defaultScenarioCover":{"type":"string","description":"S3 key for the default scenario cover image (direct chat)"},"defaultScenarioInitialMessage":{"type":"string","description":"Initial message shown after the cover image (direct chat)","maxLength":1000},"defaultScenarioPrompt":{"type":"string","description":"Prompt stored in the cover image attachment (direct chat)","maxLength":2000},"allowedChatModes":{"type":"array","description":"Chat modes this model may be started in (GEN-1386). At least one; ROLEPLAY runs the scripted opening + Visual Director, REALISTIC opens with the plain firstMsg and no Director.","items":{"type":"string","enum":["ROLEPLAY","REALISTIC"]}},"initialImagePrompt":{"type":"string","description":"Initial image prompt for first message attachment generation","maxLength":2000},"isDemo":{"type":"boolean","description":"Whether this model is a demo character","example":false},"initialPhotoSFW":{"type":"string","description":"S3 key for the SFW intro photo (demo models)"},"initialPhotoNSFW":{"type":"string","description":"S3 key for the NSFW blurred intro photo (demo models)"},"demoIntroVideo":{"type":"string","description":"S3 key for demo intro video (static / demo_intro)"},"demoModelPreviewVideo":{"type":"string","description":"S3 key for ethnicity source demo preview clip (static / demo_model_preview)"},"onboardingSubscriptionRewardVideoPreview":{"type":"string","description":"S3 key for subscription reward still (FLUX Klein edit) under static / onboarding_sub_reward_preview"},"onboardingSubscriptionRewardVideo":{"type":"string","description":"S3 key for subscription reward short video (i2v from edit) under static / onboarding_sub_reward_video"}},"required":["id"]},"DeleteModelResponseDto":{"type":"object","properties":{"message":{"type":"string","description":"Success message","example":"Model soft deleted successfully"}},"required":["message"]},"UpdateModelSystemPromptDtoClass":{"type":"object","properties":{"text":{"type":"string","description":"The system prompt text content","example":"You are a friendly AI assistant...","maxLength":10000},"status":{"type":"string","description":"The status of the system prompt generation","enum":["IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED","CANCELLED","TIMED_OUT"],"example":"COMPLETED"}}},"UpdateModelSystemPromptResponseDto":{"type":"object","properties":{"message":{"type":"string","description":"Success message","example":"Model system prompt updated successfully"}},"required":["message"]},"CreateModelSystemPromptAdminDto":{"type":"object","properties":{"text":{"type":"string","description":"Initial system prompt text","example":"You are a friendly AI assistant...","maxLength":10000},"status":{"type":"string","description":"Status of the system prompt","enum":["IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED","CANCELLED","TIMED_OUT"],"example":"COMPLETED"}}},"BatchGenerateModelTranslationsDtoClass":{"type":"object","properties":{}},"BulkGenerateModelTranslationsDtoClass":{"type":"object","properties":{}},"UpsertModelTranslationDto":{"type":"object","properties":{"name":{"type":"string","description":"Translated model name"},"description":{"type":"string","description":"Translated model description"},"firstMsg":{"type":"string","description":"Translated first message"},"userFirstMsgHelp":{"type":"string","description":"Translated user first message help"},"defaultScenarioInitialMessage":{"type":"string","description":"Translated default scenario initial message"},"hobbies":{"type":"string","description":"Translated hobbies (comma-separated)"}}},"CompareModelMigrationDtoClass":{"type":"object","properties":{}},"UpdateModelMigrationDtoClass":{"type":"object","properties":{}},"SyncAssetsMigrationDtoClass":{"type":"object","properties":{}},"CompareModelTranslationMigrationDtoClass":{"type":"object","properties":{}},"UpsertModelTranslationMigrationDtoClass":{"type":"object","properties":{}},"UpdateModelsOrderDtoClass":{"type":"object","properties":{}},"UpdateModelsOrderResponseDto":{"type":"object","properties":{"updated":{"type":"number","description":"Number of models updated","example":5},"message":{"type":"string","description":"Success message","example":"Models order updated successfully"}},"required":["updated","message"]},"UserListItemDto":{"type":"object","properties":{"id":{"type":"string","description":"User ID","example":"user-uuid-here"},"firebaseUserId":{"type":"string","description":"Firebase User ID","example":"firebase-user-id"},"email":{"type":"string","description":"Email","example":"user@example.com","nullable":true},"phoneNumber":{"type":"string","description":"Phone number","example":"+1234567890","nullable":true},"timezone":{"type":"string","description":"Timezone identifier (IANA)","example":"America/Los_Angeles","nullable":true},"language":{"type":"string","description":"Preferred language (BCP-47 code)","example":"en-US","nullable":true},"name":{"type":"string","description":"Display name","example":"Jane Doe","nullable":true},"fcmToken":{"type":"string","description":"FCM Token","example":"fcm-token-here","nullable":true},"appAccountToken":{"type":"string","description":"App Account Token","example":"app-account-token","nullable":true},"useExplicitContent":{"type":"boolean","description":"Use Explicit Content Flag","example":false},"isDeleted":{"type":"boolean","description":"Is Deleted Flag","example":false},"isAnonymous":{"type":"boolean","description":"Is Anonymous/Demo User Flag","example":false},"useGiftSuggestions":{"type":"boolean","description":"Gift suggestions A/B flag","example":false},"createdAt":{"format":"date-time","type":"string","description":"Created At","example":"2024-01-01T00:00:00Z"},"updatedAt":{"format":"date-time","type":"string","description":"Updated At","example":"2024-01-01T00:00:00Z"},"isSubscribed":{"type":"boolean","description":"Whether the user currently has an active subscription","example":true},"autoRenewal":{"type":"boolean","description":"Auto-renewal status of the latest subscription","example":true,"nullable":true},"subscriptionSource":{"type":"string","description":"Source of the subscription (apple, stripe, funnelfox)","example":"stripe","nullable":true,"enum":["apple","stripe","funnelfox"]}},"required":["id","firebaseUserId","email","phoneNumber","timezone","language","name","fcmToken","appAccountToken","useExplicitContent","isDeleted","isAnonymous","useGiftSuggestions","createdAt","updatedAt","isSubscribed","autoRenewal","subscriptionSource"]},"UsersListResponseDto":{"type":"object","properties":{"users":{"description":"List of users","type":"array","items":{"$ref":"#/components/schemas/UserListItemDto"}},"total":{"type":"number","description":"Total number of users","example":150},"page":{"type":"number","description":"Current page number","example":1},"limit":{"type":"number","description":"Number of items per page","example":10},"totalPages":{"type":"number","description":"Total number of pages","example":15}},"required":["users","total","page","limit","totalPages"]},"UsersStatsResponseDto":{"type":"object","properties":{"total":{"type":"number","description":"Total number of users","example":150},"active":{"type":"number","description":"Number of active users","example":120},"explicitContentEnabled":{"type":"number","description":"Number of users with explicit content enabled","example":30},"deleted":{"type":"number","description":"Number of deleted users","example":5}},"required":["total","active","explicitContentEnabled","deleted"]},"UserDetailResponseDto":{"type":"object","properties":{"user":{"description":"User details","allOf":[{"$ref":"#/components/schemas/UserListItemDto"}]}},"required":["user"]},"UpdateUserDtoClass":{"type":"object","properties":{"name":{"type":"string","description":"The user name","example":"John Doe"},"language":{"type":"string","description":"The user language","example":"en-US"},"timezone":{"type":"string","description":"The user timezone","example":"America/New_York"},"useExplicitContent":{"type":"boolean","description":"Whether the user allows explicit content","example":true},"fcmToken":{"type":"string","description":"The user FCM token","example":"123"},"appAccountToken":{"type":"string","description":"The user app account token","example":"123"},"latestAppVersion":{"type":"string","description":"The latest app version used by the user","example":"1.2.3"}}},"UpdateUserResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"User ID","example":"user-uuid-here"},"firebaseUserId":{"type":"string","description":"Firebase user ID","example":"firebase-user-id"},"name":{"type":"string","description":"User name","example":"John Doe"},"language":{"type":"string","description":"User language","example":"en-US"},"timezone":{"type":"string","description":"User timezone","example":"America/New_York"},"useExplicitContent":{"type":"boolean","description":"Whether the user allows explicit content","example":true},"fcmToken":{"type":"string","description":"User FCM token","example":"123"},"appAccountToken":{"type":"string","description":"User app account token","example":"123"},"isDeleted":{"type":"boolean","description":"Whether the user is deleted","example":false},"createdAt":{"format":"date-time","type":"string","description":"User creation date","example":"2023-01-01T00:00:00.000Z"},"email":{"type":"string","description":"User email","example":"user@example.com"},"phoneNumber":{"type":"string","description":"User phone number","example":"+1234567890"}},"required":["id","firebaseUserId","useExplicitContent","isDeleted","createdAt"]},"AcquisitionLeadDto":{"type":"object","properties":{"id":{"type":"string"},"firebaseUid":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"linkedAppUserId":{"type":"string","nullable":true},"isCustomer":{"type":"boolean"},"firstSeenAt":{"format":"date-time","type":"string"},"lastSeenAt":{"format":"date-time","type":"string"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","isCustomer","firstSeenAt","lastSeenAt","createdAt","updatedAt"]},"UserMessageAnalyticsResponseDto":{"type":"object","properties":{"userId":{"type":"string","description":"User ID","example":"user-uuid-here"},"textMessagesSent":{"type":"number","description":"Number of text messages sent by the user","example":150},"imageRequestsSent":{"type":"number","description":"Number of image requests sent by the user","example":25},"videoRequestsSent":{"type":"number","description":"Number of video requests sent by the user","example":10},"attachmentFailures":{"type":"number","description":"Number of attachment failures (failed status)","example":3},"messageFailures":{"type":"number","description":"Number of message failures","example":2},"callsCount":{"type":"number","description":"Number of calls made by the user","example":7}},"required":["userId","textMessagesSent","imageRequestsSent","videoRequestsSent","attachmentFailures","messageFailures"]},"UserExperimentItemDto":{"type":"object","properties":{"key":{"type":"string","example":"video_model_ltx_v1"},"name":{"type":"string","example":"Video generation model: Wan vs LTX"},"status":{"type":"string","enum":["draft","running","paused","completed"]},"enrollmentMode":{"type":"string","enum":["auto","manual"]},"variants":{"example":["wan","ltx"],"description":"Variant ids declared on the experiment","type":"array","items":{"type":"string"}},"defaultVariantId":{"type":"string","nullable":true,"example":"wan"},"forcedVariantId":{"type":"string","nullable":true,"example":"wan"},"winnerVariantId":{"type":"string","nullable":true,"example":null},"effectiveVariantId":{"type":"string","nullable":true,"example":"wan","description":"Variant the experiments service resolves for this user now"},"effectiveSource":{"type":"string","enum":["override","forced","sticky","default"]},"enrolledVariantId":{"type":"string","nullable":true,"example":"ltx"},"enrollSource":{"type":"string","nullable":true,"enum":["auto_read","manual_event","forced","admin_override"]},"assignedAt":{"type":"string","nullable":true,"format":"date-time"},"overrideVariantId":{"type":"string","nullable":true,"example":"ltx","description":"Variant pinned to this user by an admin, if any"}},"required":["key","name","status","enrollmentMode","variants","defaultVariantId","forcedVariantId","winnerVariantId","effectiveVariantId","effectiveSource","enrolledVariantId","enrollSource","assignedAt","overrideVariantId"]},"UserExperimentsResponseDto":{"type":"object","properties":{"firebaseUserId":{"type":"string","nullable":true,"description":"Experiment subject id; null when the user has no Firebase UID"},"experiments":{"type":"array","items":{"$ref":"#/components/schemas/UserExperimentItemDto"}}},"required":["firebaseUserId","experiments"]},"SetUserExperimentVariantDto":{"type":"object","properties":{"variantId":{"type":"string","example":"ltx","description":"Must be one of the variant ids declared on the experiment. Wins over weights, status and the global forced variant for this user only."}},"required":["variantId"]},"UserSubscriptionResponseDto":{"type":"object","properties":{"userId":{"type":"string","description":"User ID","example":"user-uuid-here"},"subscriptionId":{"type":"string","description":"Subscription ID","example":"subscription-uuid-here"},"providerSubscriptionId":{"type":"string","description":"Provider-level subscription ID","example":"sub_123"},"productId":{"type":"string","description":"Product ID","example":"com.app.product.premium"},"startedAt":{"format":"date-time","type":"string","description":"Subscription start date","example":"2024-01-01T00:00:00.000Z"},"expiresAt":{"format":"date-time","type":"string","description":"Subscription expiration date","example":"2024-02-01T00:00:00.000Z"},"autoRenewal":{"type":"boolean","description":"Auto renewal status","example":true},"isActive":{"type":"boolean","description":"Whether subscription is currently active","example":true},"subscriptionSource":{"type":"string","description":"Subscription source/provider","example":"stripe","enum":["apple","stripe","funnelfox"]},"subscriptionStatus":{"type":"string","description":"Subscription status in source provider","example":"active"},"funnelFoxUserId":{"type":"string","description":"FunnelFox external user identifier","example":"firebase-uid"},"funnelFoxSubscriptionId":{"type":"string","description":"FunnelFox subscription ID (subs_id)","example":"3c90c3cc-0d44-4b50-8888-8dd25736052a"},"cancelAtPeriodEnd":{"type":"boolean","description":"FunnelFox cancel-at-period-end flag","example":false}},"required":["userId"]},"ToggleFunnelFoxAutorenewDtoClass":{"type":"object","properties":{"enable":{"type":"boolean","description":"Enable or disable auto-renewal (true = enable, false = disable)","example":false}},"required":["enable"]},"ApplyFunnelFoxDiscountDtoClass":{"type":"object","properties":{"percent":{"type":"number","description":"Discount percentage (0-100)","example":50},"countOfIterations":{"type":"number","description":"Number of billing iterations to apply discount","example":1},"reason":{"type":"string","description":"Reason of discount application","example":"retention_offer"},"comment":{"type":"string","description":"Internal comment","example":"LTV experiment retention"}},"required":["percent","countOfIterations"]},"V2BalanceBreakdownDto":{"type":"object","properties":{"monthlyLeft":{"type":"number","description":"Monthly subscription coins remaining (quota bucket)","example":800},"monthlyLimit":{"type":"number","description":"Monthly subscription coin allowance for the current plan","example":1000},"bonusLeft":{"type":"number","description":"Bonus / purchased coins remaining (never expire)","example":250},"total":{"type":"number","description":"monthlyLeft + bonusLeft (matches currentBalance in v2)","example":1050},"quotaResetAt":{"type":"string","description":"When the monthly quota next resets (ISO), or null","example":"2026-07-01T00:00:00.000Z","nullable":true}},"required":["monthlyLeft","monthlyLimit","bonusLeft","total","quotaResetAt"]},"UserCoinTransactionsResponseDto":{"type":"object","properties":{"userId":{"type":"string","description":"User ID","example":"user-uuid-here"},"amountDeposited":{"type":"number","description":"Total amount deposited","example":1000},"amountSpent":{"type":"number","description":"Total amount spent","example":500},"numberOfPurchases":{"type":"number","description":"Number of purchases (IN direction with PURCHASE reason)","example":5},"purchasedCoinsTotal":{"type":"number","description":"Coins bought via in-app/coin-pack purchases (reason PURCHASE, IN), isolated from subscription grants / welcome bonus / admin deposits","example":1200},"refundAmount":{"type":"number","description":"Total amount refunded (any direction with REFUND reason)","example":200},"currentBalance":{"type":"number","description":"Current user balance","example":500},"spentByFeature":{"type":"object","description":"Amount spent by feature (TEXT_MESSAGE, IMAGE_REQUEST, VIDEO_REQUEST, MODEL_CREATE, LIVE_ACTION, GIFT, CALL)","example":{"TEXT_MESSAGE":120,"IMAGE_REQUEST":300,"VIDEO_REQUEST":0,"MODEL_CREATE":200,"LIVE_ACTION":0,"GIFT":50,"CALL":400}},"spentFromMonthly":{"type":"number","description":"Spend (reason SPENDING) drawn from the monthly quota bucket (v2)","example":400},"spentFromBonus":{"type":"number","description":"Spend (reason SPENDING) drawn from the purchased/bonus bucket (v2)","example":100},"quotaExpired":{"type":"number","description":"Quota coins that expired at monthly resets (reason QUOTA_RESET, v2). Not counted in amountSpent.","example":200},"v2Balance":{"description":"Live two-bucket balance breakdown (monthly vs bonus). Null in v1/ledger mode.","nullable":true,"allOf":[{"$ref":"#/components/schemas/V2BalanceBreakdownDto"}]}},"required":["userId","amountDeposited","amountSpent","numberOfPurchases","purchasedCoinsTotal","refundAmount","currentBalance","spentByFeature","spentFromMonthly","spentFromBonus","quotaExpired"]},"CoinTransactionHistoryItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Transaction id"},"createdAt":{"format":"date-time","type":"string","description":"When the transaction was created"},"direction":{"type":"string","enum":["IN","OUT"],"description":"IN or OUT"},"reason":{"type":"string","enum":["PURCHASE","PROMO","REFUND","SPENDING","SUBSCRIPTION","RESTORE","QUOTA_RESET","WELCOME_BONUS"],"description":"PURCHASE, SUBSCRIPTION, WELCOME_BONUS, SPENDING, QUOTA_RESET, REFUND, …"},"amount":{"type":"number","description":"Absolute coin amount (sign implied by direction)"},"feature":{"type":"string","enum":["TEXT_MESSAGE","IMAGE_REQUEST","VIDEO_REQUEST","MODEL_CREATE","LIVE_ACTION","GIFT","CALL","VOICE_OVER","SEND_PHOTO","SCENE_CREATE","SCENE_COVER_GENERATE","STUDIO_IMAGE","STUDIO_VIDEO","MODEL_CONTENT","SHOW_SCENE_IMAGE","SHOW_SCENE_VIDEO","SHORT_EPISODE","UPSCALE_IMAGE","UPSCALE_VIDEO"],"nullable":true,"description":"Feature spent on (OUT/SPENDING rows), else null"},"quotaAmount":{"type":"number","nullable":true,"description":"Portion drawn from / granted to the monthly quota bucket (v2)"},"purchasedAmount":{"type":"number","nullable":true,"description":"Portion drawn from / granted to the purchased/bonus bucket (v2)"},"status":{"type":"string","enum":["PENDING","COMPLETED","FAILED"]}},"required":["id","createdAt","direction","reason","amount","feature","quotaAmount","purchasedAmount","status"]},"UserCoinTransactionsHistoryResponseDto":{"type":"object","properties":{"userId":{"type":"string","description":"User id"},"transactions":{"type":"array","items":{"$ref":"#/components/schemas/CoinTransactionHistoryItemDto"}},"total":{"type":"number","description":"Total number of transactions"},"page":{"type":"number","description":"Current page"},"limit":{"type":"number","description":"Page size"},"totalPages":{"type":"number","description":"Total number of pages"}},"required":["userId","transactions","total","page","limit","totalPages"]},"AdminDepositCoinsDtoClass":{"type":"object","properties":{"amount":{"type":"number","description":"Amount of coins to deposit","example":100,"minimum":1},"reason":{"type":"string","description":"Transaction reason","enum":["PURCHASE","PROMO","REFUND","SPENDING","SUBSCRIPTION","RESTORE","QUOTA_RESET","WELCOME_BONUS"],"example":"PURCHASE"},"direction":{"type":"string","description":"Transaction direction","enum":["IN","OUT"],"example":"IN"}},"required":["amount","reason","direction"]},"AdminDepositCoinsResponseDto":{"type":"object","properties":{"transactionId":{"type":"string","description":"Transaction ID","example":"transaction-uuid-here"},"userId":{"type":"string","description":"User ID","example":"user-uuid-here"},"amount":{"type":"number","description":"Amount deposited","example":100},"reason":{"type":"string","description":"Transaction reason","example":"PURCHASE"},"direction":{"type":"string","description":"Transaction direction","example":"IN"},"status":{"type":"string","description":"Transaction status","example":"COMPLETED"}},"required":["transactionId","userId","amount","reason","direction","status"]},"GrantV2SubscriptionDtoClass":{"type":"object","properties":{"planId":{"type":"string","description":"Plan id to grant. Tier and billing period are derived from it.","example":"plan-uuid-here"},"provider":{"type":"string","description":"Subscription provider to attribute the grant to","enum":["funnelfox","stripe","apple","unlimit","upgate"],"example":"funnelfox"}},"required":["planId","provider"]},"ChangeV2PlanDtoClass":{"type":"object","properties":{"planId":{"type":"string","description":"New plan id. Resets the billing period and re-grants quota.","example":"plan-uuid-here"}},"required":["planId"]},"ShiftV2PeriodDtoClass":{"type":"object","properties":{"currentPeriodStart":{"type":"string","description":"New billing period start (ISO 8601)","example":"2026-06-01T00:00:00.000Z"},"currentPeriodEnd":{"type":"string","description":"New billing period end (ISO 8601)","example":"2026-07-01T00:00:00.000Z"}},"required":["currentPeriodStart","currentPeriodEnd"]},"ExpireV2SubscriptionDtoClass":{"type":"object","properties":{"force":{"type":"boolean","description":"When true, force-expire now (sets period end to now, disables renewal). Otherwise mark expired only.","example":false}}},"SetV2AutoRenewDtoClass":{"type":"object","properties":{"enable":{"type":"boolean","description":"Enable (true) or disable (false) auto-renewal","example":false}},"required":["enable"]},"UserChatDto":{"type":"object","properties":{"id":{"type":"string","description":"Chat ID","example":"chat-uuid-here"},"model":{"description":"Model information","allOf":[{"$ref":"#/components/schemas/GetModelByIdResponseDto"}]},"lastMessage":{"description":"Last message in the chat","nullable":true,"allOf":[{"$ref":"#/components/schemas/MessageDto"}]},"createdAt":{"format":"date-time","type":"string","description":"Created At","example":"2024-01-01T00:00:00Z"},"updatedAt":{"format":"date-time","type":"string","description":"Updated At","example":"2024-01-01T00:00:00Z"}},"required":["id","model","lastMessage","createdAt","updatedAt"]},"UserChatsResponseDto":{"type":"object","properties":{"userId":{"type":"string","description":"User ID","example":"user-uuid-here"},"chats":{"description":"List of user chats","type":"array","items":{"$ref":"#/components/schemas/UserChatDto"}},"total":{"type":"number","description":"Total number of chats","example":25},"page":{"type":"number","description":"Current page number","example":1},"limit":{"type":"number","description":"Number of items per page","example":10},"totalPages":{"type":"number","description":"Total number of pages","example":3}},"required":["userId","chats","total","page","limit","totalPages"]},"UserChatMessagesResponseDto":{"type":"object","properties":{"userId":{"type":"string","description":"User ID","example":"user-uuid-here"},"chatId":{"type":"string","description":"Chat ID","example":"chat-uuid-here"},"items":{"description":"List of messages","type":"array","items":{"$ref":"#/components/schemas/MessageDto"}},"nextCursor":{"type":"string","description":"Next cursor for pagination","example":"base64-encoded-cursor"},"prevCursor":{"type":"string","description":"Previous cursor for pagination","example":"base64-encoded-cursor"},"hasNext":{"type":"boolean","description":"Whether there are more messages to load","example":true},"hasPrev":{"type":"boolean","description":"Whether there are previous messages to load","example":false}},"required":["userId","chatId","items","hasNext","hasPrev"]},"UsersSortByMessageCountItemDto":{"type":"object","properties":{"userId":{"type":"string"},"messageCount":{"type":"number"},"chatCount":{"type":"number"},"useGiftSuggestions":{"type":"boolean"},"isAnonymous":{"type":"boolean"}},"required":["userId","messageCount","chatCount","useGiftSuggestions","isAnonymous"]},"UsersSortByMessageCountResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/UsersSortByMessageCountItemDto"}}},"required":["items"]},"AttachmentListItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Attachment ID","example":"attachment-uuid-here"},"messageId":{"type":"string","description":"Message ID","example":"message-uuid-here"},"chatId":{"type":"string","description":"Chat ID","example":"chat-uuid-here"},"userId":{"type":"string","description":"User ID","example":"user-uuid-here"},"prompt":{"type":"string","description":"Prompt used for generation","example":"A beautiful sunset over the ocean","nullable":true},"type":{"type":"string","description":"Attachment type","enum":["image","video","audio","gift","all"],"example":"image"},"explicit":{"type":"boolean","description":"Explicit content flag","example":false},"s3Key":{"type":"string","description":"S3 key","example":"chat-images/attachment-key.jpg"},"mimeType":{"type":"string","description":"MIME type","example":"image/jpeg","nullable":true},"status":{"type":"string","description":"Status","enum":["IN_QUEUE","IN_PROGRESS","COMPLETED","FAILED","CANCELLED","TIMED_OUT"],"example":"COMPLETED"},"url":{"type":"string","description":"CDN URL","example":"https://cdn.example.com/chat-images/attachment-key.jpg"},"videoUrl":{"type":"string","description":"Video CDN URL","example":"https://cdn.example.com/chat-images/video-key.mp4","nullable":true},"createdAt":{"format":"date-time","type":"string","description":"Created At","example":"2024-01-01T00:00:00Z"},"updatedAt":{"format":"date-time","type":"string","description":"Updated At","example":"2024-01-01T00:00:00Z"},"isFromUser":{"type":"boolean","description":"Whether the attachment was sent by the user","example":false}},"required":["id","messageId","chatId","userId","prompt","type","explicit","s3Key","mimeType","status","url","videoUrl","createdAt","updatedAt","isFromUser"]},"AttachmentsListResponseDto":{"type":"object","properties":{"attachments":{"description":"List of attachments","type":"array","items":{"$ref":"#/components/schemas/AttachmentListItemDto"}},"total":{"type":"number","description":"Total number of attachments","example":150},"page":{"type":"number","description":"Current page number","example":1},"limit":{"type":"number","description":"Number of items per page","example":10},"totalPages":{"type":"number","description":"Total number of pages","example":15}},"required":["attachments","total","page","limit","totalPages"]},"FilteredAttachmentsStatsDto":{"type":"object","properties":{"total":{"type":"number","description":"Total number of filtered attachments","example":50},"images":{"type":"number","description":"Number of filtered image attachments","example":40},"videos":{"type":"number","description":"Number of filtered video attachments","example":10},"explicit":{"type":"number","description":"Number of filtered explicit content attachments","example":5},"failed":{"type":"number","description":"Number of filtered failed attachments","example":2},"successful":{"type":"number","description":"Number of filtered successful attachments","example":48}},"required":["total","images","videos","explicit","failed","successful"]},"AttachmentsStatsResponseDto":{"type":"object","properties":{"total":{"type":"number","description":"Total number of attachments","example":150},"images":{"type":"number","description":"Number of image attachments","example":120},"videos":{"type":"number","description":"Number of video attachments","example":30},"explicit":{"type":"number","description":"Number of explicit content attachments","example":10},"failed":{"type":"number","description":"Number of failed attachments","example":5},"successful":{"type":"number","description":"Number of successful attachments","example":145},"filtered":{"description":"Filtered statistics (when filters are applied)","allOf":[{"$ref":"#/components/schemas/FilteredAttachmentsStatsDto"}]}},"required":["total","images","videos","explicit","failed","successful"]},"ReportItemDto":{"type":"object","properties":{"id":{"type":"number","description":"Report ID","example":1},"userId":{"type":"string","description":"User ID who submitted the report","example":"user-uuid"},"reasonId":{"type":"string","description":"Report reason","example":"too_expensive"},"additionalDetails":{"type":"string","description":"Additional details","example":"This content violates guidelines"},"createdAt":{"format":"date-time","type":"string","description":"When the report was created","example":"2024-01-15T10:30:00Z"},"type":{"type":"string","description":"Type of report","example":"model"},"targetId":{"type":"string","description":"ID of the reported content","example":"content-uuid"}},"required":["id","userId","reasonId","createdAt","type"]},"ReportsListResponseDto":{"type":"object","properties":{"items":{"description":"List of reports","type":"array","items":{"$ref":"#/components/schemas/ReportItemDto"}},"page":{"type":"number","description":"Current page number","example":1},"limit":{"type":"number","description":"Items per page","example":20},"total":{"type":"number","description":"Total number of reports","example":150}},"required":["items","page","limit","total"]},"IapAggregatesResponseDto":{"type":"object","properties":{"averageIapCountPerUser":{"type":"number"},"medianIapCountPerUser":{"type":"number"},"averageIapUsdPerUser":{"type":"number"},"medianIapUsdPerUser":{"type":"number"},"averageIapCountPerSubscriber":{"type":"number"},"medianIapCountPerSubscriber":{"type":"number"},"averageIapUsdPerSubscriber":{"type":"number"},"medianIapUsdPerSubscriber":{"type":"number"},"totalUsersConsidered":{"type":"number"},"totalSubscribersConsidered":{"type":"number"},"totalOneTimeCharges":{"type":"number"},"timeRange":{"type":"object","description":"Applied time range if any"}},"required":["averageIapCountPerUser","medianIapCountPerUser","averageIapUsdPerUser","medianIapUsdPerUser","averageIapCountPerSubscriber","medianIapCountPerSubscriber","averageIapUsdPerSubscriber","medianIapUsdPerSubscriber","totalUsersConsidered","totalSubscribersConsidered","totalOneTimeCharges","timeRange"]},"ChatLengthResponseDto":{"type":"object","properties":{"avgMessagesPerChat":{"type":"number"},"medianMessagesPerChat":{"type":"number"},"totalChatsConsidered":{"type":"number"},"timeRange":{"type":"object","description":"Applied time range if any"}},"required":["avgMessagesPerChat","medianMessagesPerChat","totalChatsConsidered","timeRange"]},"ExplicitAttachmentRatioResponseDto":{"type":"object","properties":{"totalUsers":{"type":"number","description":"Total registered users"},"usersWithExplicitContent":{"type":"number","description":"Users with useExplicitContent flag enabled"},"ratio":{"type":"number","description":"Ratio of explicit users to total users"},"ratioPercent":{"type":"number","description":"Ratio as percentage"},"timeRange":{"type":"object","description":"Applied time range if any"}},"required":["totalUsers","usersWithExplicitContent","ratio","ratioPercent","timeRange"]},"IapByProductItemDto":{"type":"object","properties":{"productId":{"type":"string"},"purchasesCount":{"type":"number"},"totalUsd":{"type":"number"},"uniqueBuyers":{"type":"number"},"coinsSold":{"type":"number"}},"required":["productId","purchasesCount","totalUsd","uniqueBuyers"]},"IapByProductResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/IapByProductItemDto"}}},"required":["items"]},"DateRangePairDto":{"type":"object","properties":{"dateStart":{"type":"string"},"dateEnd":{"type":"string"}},"required":["dateStart","dateEnd"]},"TrendPointDto":{"type":"object","properties":{"periodLabel":{"type":"string","description":"Label for this period (e.g. \"Mar 25\")"},"value":{"type":"number","description":"Metric value for this period"}},"required":["periodLabel","value"]},"MetricWithDeltaDto":{"type":"object","properties":{"current":{"type":"number","description":"Value for the current period"},"previous":{"type":"number","description":"Value for the comparison period"},"delta":{"type":"number","description":"Absolute delta (current - previous)"},"deltaPercent":{"type":"number","description":"Percentage change ((current - previous) / previous * 100)"},"trend":{"description":"Values for past N periods + current, for sparkline rendering","type":"array","items":{"$ref":"#/components/schemas/TrendPointDto"}}},"required":["current","previous","delta","deltaPercent","trend"]},"DashboardMetricsResponseDto":{"type":"object","properties":{"period":{"$ref":"#/components/schemas/DateRangePairDto"},"comparisonPeriod":{"$ref":"#/components/schemas/DateRangePairDto"},"newSignups":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"newSubscribers":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"iapPurchases":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"activeUsers":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"newChatsStarted":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"avgChatLength":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"totalCoinsSpent":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"totalGiftsSent":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"studioImagesTotal":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"studioImagesCustomPrompt":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"signupToMessage":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"signupToContentRequest":{"$ref":"#/components/schemas/MetricWithDeltaDto"},"chatToGift":{"$ref":"#/components/schemas/MetricWithDeltaDto"}},"required":["period","comparisonPeriod"]},"UpsertGiftTranslationDto":{"type":"object","properties":{"name":{"type":"string","description":"Translated gift name"}}},"AdminLoginDto":{"type":"object","properties":{}},"ChangePasswordDto":{"type":"object","properties":{}},"ConstantItemDto":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"},"type":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","key","value","createdAt","updatedAt"]},"ConstantsListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ConstantItemDto"}},"total":{"type":"number"}},"required":["items","total"]},"IntegrationApiKeyItemDto":{"type":"object","properties":{"key":{"type":"string","example":"MCP_MARKETING_API_KEY"},"label":{"type":"string","example":"MCP marketing key"},"description":{"type":"string","description":"What the key unlocks and what breaks if wrong"},"header":{"type":"string","example":"x-mcp-api-key"},"source":{"type":"string","enum":["constant","env","none"],"description":"Where the value in force comes from: the runtime constant, the env var (AWS Secrets Manager), or nowhere"},"maskedValue":{"type":"string","nullable":true,"example":"••••••••a91f","description":"Dots plus the last four characters; null when unconfigured"},"envConfigured":{"type":"boolean","description":"Whether the env var still holds a value"},"updatedAt":{"format":"date-time","type":"string","nullable":true,"description":"When the constant was last set"}},"required":["key","label","description","header","source","maskedValue","envConfigured","updatedAt"]},"IntegrationApiKeysListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/IntegrationApiKeyItemDto"}}},"required":["items"]},"RevealIntegrationApiKeyResponseDto":{"type":"object","properties":{"key":{"type":"string","example":"MCP_MARKETING_API_KEY"},"value":{"type":"string","nullable":true,"description":"The constant value. Null when the key is served from the env var — those stay in AWS Secrets Manager and are not viewable here."},"source":{"type":"string","enum":["constant","env","none"]}},"required":["key","value","source"]},"SetIntegrationApiKeyDtoClass":{"type":"object","properties":{"value":{"type":"string","description":"The new secret, stored verbatim (trimmed)"}},"required":["value"]},"IntegrationApiKeyMutationResponseDto":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},"CreateConstantDtoClass":{"type":"object","properties":{"key":{"type":"string","description":"Unique constant key","example":"IMAGE_REQUEST_PRICE"},"value":{"type":"string","description":"Constant value (stored as text)","example":"4"},"type":{"type":"string","description":"Optional type hint","example":"number"},"description":{"type":"string","description":"Optional description"}},"required":["key","value"]},"UpdateConstantDtoClass":{"type":"object","properties":{"value":{"type":"string","description":"Constant value (stored as text)","example":"5"},"type":{"type":"string","description":"Optional type hint","example":"number"},"description":{"type":"string","description":"Optional description"}},"required":["value"]},"DeleteConstantResponseDto":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},"BatchGenerateImageGenTranslationsDtoClass":{"type":"object","properties":{}},"MigrateImageGenTranslationsDtoClass":{"type":"object","properties":{}},"ImageGenItemDto":{"type":"object","properties":{"id":{"type":"number"},"name":{"type":"string"},"label":{"type":"string"},"prompt":{"type":"string"},"s3Key":{"type":"string"},"videoLoraName":{"type":"string","description":"Only for poses"},"videoLoraActionName":{"type":"string","description":"Only for poses"},"videoLoraNames":{"description":"Only for poses","type":"array","items":{"type":"string"}},"videoHighNoiseLoraNames":{"description":"Only for poses","type":"array","items":{"type":"string"}},"videoLowNoiseLoraNames":{"description":"Only for poses","type":"array","items":{"type":"string"}},"emojiLabel":{"type":"string"},"order":{"type":"number"},"sfw":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","name","prompt","emojiLabel","order","sfw","createdAt","updatedAt"]},"ImageGenListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ImageGenItemDto"}},"total":{"type":"number"}},"required":["items","total"]},"CreateImageGenDtoClass":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"prompt":{"type":"string"},"s3Key":{"type":"string"},"videoLoraName":{"type":"string","description":"Only for poses"},"videoLoraActionName":{"type":"string","description":"Only for poses"},"videoLoraNames":{"type":"object","description":"Only for poses: LoRA names (array or comma-separated string), max 3"},"videoHighNoiseLoraNames":{"type":"object","description":"Only for poses: High noise LoRA names (array or comma-separated string), max 3"},"videoLowNoiseLoraNames":{"type":"object","description":"Only for poses: Low noise LoRA names (array or comma-separated string), max 3"},"emojiLabel":{"type":"string"},"order":{"type":"number","default":0},"sfw":{"type":"boolean","default":false}},"required":["name","prompt","emojiLabel"]},"UpdateImageGenDtoClass":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"prompt":{"type":"string"},"s3Key":{"type":"string"},"videoLoraName":{"type":"string","description":"Only for poses"},"videoLoraActionName":{"type":"string","description":"Only for poses"},"videoLoraNames":{"type":"object","description":"Only for poses: LoRA names (array or comma-separated string), max 3"},"videoHighNoiseLoraNames":{"type":"object","description":"Only for poses: High noise LoRA names (array or comma-separated string), max 3"},"videoLowNoiseLoraNames":{"type":"object","description":"Only for poses: Low noise LoRA names (array or comma-separated string), max 3"},"emojiLabel":{"type":"string"},"order":{"type":"number"},"isDeleted":{"type":"boolean"},"sfw":{"type":"boolean"}}},"GenerateImageGenLocalizationsResponseDto":{"type":"object","properties":{"parameterType":{"type":"string"},"localesProcessed":{"type":"number"},"sourceItemsCount":{"type":"number"},"generatedTranslationsCount":{"type":"number"}},"required":["parameterType","localesProcessed","sourceItemsCount","generatedTranslationsCount"]},"ImageGenTranslationDto":{"type":"object","properties":{"id":{"type":"string"},"parameterType":{"type":"string","example":"clothes"},"parameterId":{"type":"number"},"locale":{"type":"string","example":"es-419"},"name":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","parameterType","parameterId","locale","name","createdAt","updatedAt"]},"UpsertImageGenTranslationDtoClass":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]},"GenerateVideoDtoClass":{"type":"object","properties":{"model":{"type":"string","enum":["wavespeed-ai/wan-2.1/i2v-480p-lora","wavespeed-ai/wan-2.2/i2v-480p","wavespeed-ai/wan-2.2/i2v-480p-lora","kwaivgi/kling-v2.1-i2v-standard","kwaivgi/kling-v2.5-turbo-std/image-to-video","bytedance/seedance-v1-pro-i2v-720p"],"example":"wavespeed-ai/wan-2.2/i2v-480p"},"image":{"type":"string","example":"https://example.com/image.png"},"prompt":{"type":"string","example":"A cat walking in a garden"},"negative_prompt":{"type":"string"},"last_image":{"type":"string"},"duration":{"type":"number","example":5},"seed":{"type":"number","example":-1},"guidance_scale":{"type":"number","example":0.5},"loras":{"type":"array"},"high_noise_loras":{"type":"array"},"low_noise_loras":{"type":"array"},"safety_checker":{"type":"boolean","example":false},"size":{"type":"string","example":"832*480","enum":["832*480","480*832"],"description":"Size for Wan 2.1 I2V 480p LoRA model"},"num_inference_steps":{"type":"number","example":30,"description":"Number of inference steps for Wan 2.1 I2V 480p LoRA model"},"flow_shift":{"type":"number","example":3,"description":"Flow shift for Wan 2.1 I2V 480p LoRA model"}},"required":["model","image","prompt"]},"VideoGenerationTaskDto":{"type":"object","properties":{"id":{"type":"string"},"externalJobId":{"type":"string"},"model":{"type":"string"},"status":{"type":"string","enum":["created","processing","completed","failed"]},"parameters":{"type":"object","description":"Generation parameters"},"s3Key":{"type":"string"},"resultUrl":{"type":"string"},"error":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","model","status"]},"VideoGenerationHistoryResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/VideoGenerationTaskDto"}},"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"totalPages":{"type":"number"}},"required":["items","total","page","limit","totalPages"]},"AdminSceneResponseDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"contextPrompt":{"type":"string"},"coverImage":{"type":"string"},"genre":{"type":"string"},"place":{"type":"string"},"timePeriod":{"type":"string"},"backstory":{"type":"string"},"goal":{"type":"string"},"isPublic":{"type":"boolean"},"userId":{"type":"string"},"isDeleted":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","name","description","contextPrompt","isPublic","isDeleted","createdAt","updatedAt"]},"AdminScenesListResponseDto":{"type":"object","properties":{"scenes":{"type":"array","items":{"$ref":"#/components/schemas/AdminSceneResponseDto"}},"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"totalPages":{"type":"number"}},"required":["scenes","total","page","limit","totalPages"]},"AdminCreateSceneDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"contextPrompt":{"type":"string"},"coverImage":{"type":"string"},"genre":{"type":"string"},"place":{"type":"string"},"timePeriod":{"type":"string"},"backstory":{"type":"string"},"goal":{"type":"string"},"isPublic":{"type":"boolean"}},"required":["name","description","contextPrompt"]},"AdminUpdateSceneDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"contextPrompt":{"type":"string"},"coverImage":{"type":"string"},"genre":{"type":"string"},"place":{"type":"string"},"timePeriod":{"type":"string"},"backstory":{"type":"string"},"goal":{"type":"string"},"isPublic":{"type":"boolean"},"isDeleted":{"type":"boolean"}}},"AdminGenerateCoverDto":{"type":"object","properties":{"prompt":{"type":"string","description":"Prompt for cover generation"}},"required":["prompt"]},"AdminGenerateCoverResponseDto":{"type":"object","properties":{"coverUrl":{"type":"string"},"s3Key":{"type":"string"}},"required":["coverUrl","s3Key"]},"AdminGenerateDescriptionResponseDto":{"type":"object","properties":{"description":{"type":"string"}},"required":["description"]},"AdminGenerateContextPromptResponseDto":{"type":"object","properties":{"contextPrompt":{"type":"string"}},"required":["contextPrompt"]},"AdminGenerateFromDataDto":{"type":"object","properties":{"genre":{"type":"string"},"place":{"type":"string"},"timePeriod":{"type":"string"},"backstory":{"type":"string"},"goal":{"type":"string"}}},"GenerateGalleryUploadUrlDtoClass":{"type":"object","properties":{"filename":{"type":"string","example":"my-image.png"},"contentType":{"type":"string","example":"image/png"},"tags":{"type":"string","example":"reference,character"}}},"GalleryUploadUrlResponseDto":{"type":"object","properties":{"id":{"type":"string"},"uploadUrl":{"type":"string"},"s3Key":{"type":"string"}},"required":["id","uploadUrl","s3Key"]},"ConfirmGalleryUploadDtoClass":{"type":"object","properties":{"fileSize":{"type":"number","example":1024000}}},"GalleryImageDto":{"type":"object","properties":{"id":{"type":"string"},"s3Key":{"type":"string"},"url":{"type":"string","description":"Presigned URL for image access (expires in 1 hour)"},"filename":{"type":"string"},"contentType":{"type":"string"},"fileSize":{"type":"number"},"status":{"type":"string","enum":["pending","uploaded","deleted"]},"tags":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","s3Key","url","status","createdAt"]},"GalleryListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/GalleryImageDto"}},"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"totalPages":{"type":"number"}},"required":["items","total","page","limit","totalPages"]},"UpdateGalleryImageDtoClass":{"type":"object","properties":{"filename":{"type":"string"},"tags":{"type":"string"}}},"LoraFileDto":{"type":"object","properties":{"key":{"type":"string","description":"S3 key for the LoRA file"},"name":{"type":"string","description":"File name"},"size":{"type":"number","description":"File size in bytes"},"lastModified":{"type":"string","description":"Last modified date"},"folder":{"type":"string","description":"Folder/prefix the file belongs to"}},"required":["key","name"]},"LorasListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/LoraFileDto"}},"total":{"type":"number"}},"required":["items","total"]},"LoraPresignedUrlResponseDto":{"type":"object","properties":{"key":{"type":"string","description":"S3 key"},"url":{"type":"string","description":"Presigned URL (1 hour TTL)"}},"required":["key","url"]},"SendTestPushDto":{"type":"object","properties":{"userId":{"type":"string","description":"User ID to send test push notification to"},"title":{"type":"string","description":"Optional custom title (defaults to model name)"},"body":{"type":"string","description":"Optional custom body text"}},"required":["userId"]},"SendTestPushResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the push was successfully sent"},"modelName":{"type":"string","description":"Model name from most recent chat","nullable":true},"avatarUrl":{"type":"string","description":"Avatar URL used in the notification","nullable":true},"fcmToken":{"type":"string","description":"Masked FCM token (last 8 chars)","nullable":true},"successCount":{"type":"number","description":"Number of successful deliveries"},"failureCount":{"type":"number","description":"Number of failed deliveries"}},"required":["success","modelName","avatarUrl","fcmToken","successCount","failureCount"]},"ApiKeyItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier"},"name":{"type":"string","description":"Name of the API key"},"description":{"type":"string","description":"Description"},"keyPrefix":{"type":"string","description":"Key prefix for identification (first 8 chars)"},"isActive":{"type":"boolean","description":"Whether the key is active"},"lastUsedAt":{"type":"string","description":"Last time the key was used"},"expiresAt":{"type":"string","description":"Expiration date"},"createdAt":{"type":"string","description":"Creation timestamp"},"updatedAt":{"type":"string","description":"Update timestamp"}},"required":["id","name","keyPrefix","isActive","createdAt","updatedAt"]},"ApiKeysListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyItemDto"}},"total":{"type":"number","description":"Total count of items"}},"required":["items","total"]},"CreateApiKeyDtoClass":{"type":"object","properties":{"name":{"type":"string","description":"Name for the API key","example":"GPU Server Production"},"description":{"type":"string","description":"Description of the API key purpose"},"expiresAt":{"type":"string","description":"Expiration date in ISO format"}},"required":["name"]},"CreateApiKeyResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier"},"name":{"type":"string","description":"Name of the API key"},"description":{"type":"string","description":"Description"},"keyPrefix":{"type":"string","description":"Key prefix for identification (first 8 chars)"},"isActive":{"type":"boolean","description":"Whether the key is active"},"lastUsedAt":{"type":"string","description":"Last time the key was used"},"expiresAt":{"type":"string","description":"Expiration date"},"createdAt":{"type":"string","description":"Creation timestamp"},"updatedAt":{"type":"string","description":"Update timestamp"},"rawKey":{"type":"string","description":"The raw API key - ONLY SHOWN ONCE"}},"required":["id","name","keyPrefix","isActive","createdAt","updatedAt","rawKey"]},"UpdateApiKeyDtoClass":{"type":"object","properties":{"name":{"type":"string","description":"Name for the API key"},"description":{"type":"string","description":"Description of the API key purpose"},"isActive":{"type":"boolean","description":"Whether the key is active"},"expiresAt":{"type":"string","description":"Expiration date in ISO format, or null to remove"}}},"DeleteApiKeyResponseDto":{"type":"object","properties":{"message":{"type":"string","description":"Success message"}},"required":["message"]},"CreateAdminUserBody":{"type":"object","properties":{}},"UpdateAdminUserBody":{"type":"object","properties":{}},"ResetPasswordBody":{"type":"object","properties":{}},"CoinProductItemDto":{"type":"object","properties":{"id":{"type":"number"},"productId":{"type":"string"},"coinsAmount":{"type":"number"}},"required":["id","productId","coinsAmount"]},"CreateCoinProductDtoClass":{"type":"object","properties":{"productId":{"type":"string","description":"Unique coin product identifier from the store","example":"com.miaaichat.app.100coins.9.99"},"coinsAmount":{"type":"number","description":"Amount of coins deposited by this product","example":100}},"required":["productId","coinsAmount"]},"SubscriptionProductItemDto":{"type":"object","properties":{"id":{"type":"number"},"productId":{"type":"string"},"initialCoinsAmount":{"type":"number"},"recurringCoinsAmount":{"type":"number"},"recurringDepositInterval":{"type":"number"}},"required":["id","productId","initialCoinsAmount","recurringCoinsAmount","recurringDepositInterval"]},"CreateSubscriptionProductDtoClass":{"type":"object","properties":{"productId":{"type":"string","description":"Unique subscription product identifier from the store","example":"com.miaaichat.app.month.14.99"},"initialCoinsAmount":{"type":"number","description":"Coins deposited once at subscription activation","example":100},"recurringCoinsAmount":{"type":"number","description":"Coins deposited on each recurring interval","example":0},"recurringDepositInterval":{"type":"number","description":"Recurring deposit interval in days","example":0}},"required":["productId","initialCoinsAmount","recurringCoinsAmount","recurringDepositInterval"]},"WebCoinProductItemDto":{"type":"object","properties":{"id":{"type":"number"},"productId":{"type":"string"},"coinsAmount":{"type":"number"},"priceAmount":{"type":"number"},"currency":{"type":"string"},"provider":{"type":"string"},"funnelFoxPricePointIdent":{"type":"string","nullable":true},"stripePriceId":{"type":"string","nullable":true},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"}},"required":["id","productId","coinsAmount","priceAmount","currency","provider","funnelFoxPricePointIdent","stripePriceId","isActive","sortOrder"]},"CreateWebCoinProductDtoClass":{"type":"object","properties":{"productId":{"type":"string","description":"Unique web coin product identifier","example":"web_100coins_9.99"},"coinsAmount":{"type":"number","description":"Amount of coins deposited by this product","example":100},"priceAmount":{"type":"number","description":"Price in minor units (e.g. cents)","example":999},"currency":{"type":"string","description":"Currency code (ISO 4217)","example":"usd"},"provider":{"type":"string","description":"Payment provider","example":"funnelfox","enum":["stripe","funnelfox"]},"funnelFoxPricePointIdent":{"type":"string","description":"FunnelFox price point ident (required for funnelfox provider)","example":"coins_100_9.99"},"stripePriceId":{"type":"string","description":"Stripe price ID (required for stripe provider)","example":"price_1234567890"},"isActive":{"type":"boolean","description":"Whether the product is active","example":true},"sortOrder":{"type":"number","description":"Sort order for display","example":0}},"required":["productId","coinsAmount","priceAmount","currency","provider"]},"UpdateWebCoinProductDtoClass":{"type":"object","properties":{"coinsAmount":{"type":"number"},"priceAmount":{"type":"number"},"currency":{"type":"string"},"funnelFoxPricePointIdent":{"type":"string"},"stripePriceId":{"type":"string"},"isActive":{"type":"boolean"},"sortOrder":{"type":"number"}}},"StripeProductDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"active":{"type":"boolean"},"description":{"type":"string","nullable":true},"defaultPriceId":{"type":"string","nullable":true},"metadata":{"type":"object"}},"required":["id","name","active","description","defaultPriceId","metadata"]},"StripePriceDto":{"type":"object","properties":{"id":{"type":"string"},"productId":{"type":"string"},"productName":{"type":"string","nullable":true},"active":{"type":"boolean"},"currency":{"type":"string"},"unitAmount":{"type":"number","nullable":true},"unitAmountDecimal":{"type":"string","nullable":true},"nickname":{"type":"string","nullable":true},"lookupKey":{"type":"string","nullable":true},"interval":{"type":"string"},"intervalCount":{"type":"number"},"metadata":{"type":"object"}},"required":["id","productId","productName","active","currency","unitAmount","unitAmountDecimal","nickname","lookupKey","interval","intervalCount","metadata"]},"CurrencyDto":{"type":"object","properties":{"code":{"type":"string","example":"USD"},"minorUnits":{"type":"number","example":2},"title":{"type":"string","example":"US Dollar"},"symbol":{"type":"string","example":"$"}},"required":["code","minorUnits","title","symbol"]},"PricePointDto":{"type":"object","properties":{"ident":{"type":"string","example":"monthly-premium"},"currency":{"$ref":"#/components/schemas/CurrencyDto"},"introType":{"type":"string","enum":["no_intro","free_trial","paid_trial"],"example":"free_trial"},"lifetimePrice":{"type":"object","example":"99.99"},"introFreeTrialPeriod":{"type":"number","example":7},"introFreeTrialPeriodDuration":{"type":"string","enum":["minutes","days","weeks","months","years"]},"introPaidTrialPrice":{"type":"object","example":"4.99"},"introPaidTrialPeriod":{"type":"number","example":7},"introPaidTrialPeriodDuration":{"type":"string","enum":["minutes","days","weeks","months","years"]},"nextPrice":{"type":"object","example":"9.99"},"nextPeriod":{"type":"number","example":1},"nextPeriodDuration":{"type":"string","enum":["minutes","days","weeks","months","years"]},"features":{"example":["premium-access"],"type":"array","items":{"type":"string"}}},"required":["ident","currency","features"]},"CreatePricePointDto":{"type":"object","properties":{"ident":{"type":"string","description":"Unique identifier","example":"monthly-premium"},"currencyCode":{"type":"string","description":"Currency code","example":"USD"},"introType":{"type":"string","description":"Intro offer type","enum":["no_intro","free_trial","paid_trial"]},"lifetimePrice":{"type":"object","description":"Lifetime purchase price"},"introFreeTrialPeriod":{"type":"number","description":"Free trial period length"},"introFreeTrialPeriodDuration":{"type":"string","description":"Free trial period duration","enum":["minutes","days","weeks","months","years"]},"introPaidTrialPrice":{"type":"object","description":"Paid trial price"},"introPaidTrialPeriod":{"type":"number","description":"Paid trial period length"},"introPaidTrialPeriodDuration":{"type":"string","description":"Paid trial period duration","enum":["minutes","days","weeks","months","years"]},"nextPrice":{"type":"object","description":"Recurring price"},"nextPeriod":{"type":"number","description":"Recurring period length"},"nextPeriodDuration":{"type":"string","description":"Recurring period duration","enum":["minutes","days","weeks","months","years"]},"descriptor":{"type":"string","description":"Payment descriptor","maxLength":22},"features":{"description":"Feature identifiers","type":"array","items":{"type":"string"}}},"required":["ident","currencyCode","introType","features"]},"LocalizePricePointsDto":{"type":"object","properties":{"baseIdents":{"description":"Base (USD) price point idents to localize. Defaults to the live funnel products.","type":"array","items":{"type":"string"}},"currencies":{"description":"Target currency codes. Defaults to all Tier-1 currencies.","type":"array","items":{"type":"string"}},"dryRun":{"type":"boolean","description":"Preview only; do not create or update anything.","default":true},"overwrite":{"type":"boolean","description":"Update existing variants instead of skipping them.","default":false}}},"UpdatePricePointDto":{"type":"object","properties":{"originIdent":{"type":"string","description":"Original price point identifier"},"ident":{"type":"string","description":"New price point identifier"},"currencyCode":{"type":"string","description":"Currency code","example":"USD"},"introType":{"type":"string","description":"Intro offer type","enum":["no_intro","free_trial","paid_trial"]},"lifetimePrice":{"type":"object","description":"Lifetime purchase price"},"introFreeTrialPeriod":{"type":"number","description":"Free trial period length"},"introFreeTrialPeriodDuration":{"type":"string","description":"Free trial period duration","enum":["minutes","days","weeks","months","years"]},"introPaidTrialPrice":{"type":"object","description":"Paid trial price"},"introPaidTrialPeriod":{"type":"number","description":"Paid trial period length"},"introPaidTrialPeriodDuration":{"type":"string","description":"Paid trial period duration","enum":["minutes","days","weeks","months","years"]},"nextPrice":{"type":"object","description":"Recurring price"},"nextPeriod":{"type":"number","description":"Recurring period length"},"nextPeriodDuration":{"type":"string","description":"Recurring period duration","enum":["minutes","days","weeks","months","years"]},"descriptor":{"type":"string","description":"Payment descriptor","maxLength":22},"features":{"description":"Feature identifiers","type":"array","items":{"type":"string"}}}},"FeatureDto":{"type":"object","properties":{"ident":{"type":"string","example":"premium-access"},"type":{"type":"string","enum":["timebased","lifetime","consumable"]},"title":{"type":"string"}},"required":["ident"]},"ListFeaturesResponseDto":{"type":"object","properties":{"features":{"type":"array","items":{"$ref":"#/components/schemas/FeatureDto"}}},"required":["features"]},"CreateFeatureDto":{"type":"object","properties":{"ident":{"type":"string","description":"Feature identifier","example":"premium-access"},"featureType":{"type":"string","description":"Feature type","enum":["timebased","lifetime","consumable"]}},"required":["ident","featureType"]},"WebSubscriptionProductItemDto":{"type":"object","properties":{"id":{"type":"number"},"planId":{"type":"string"},"priceId":{"type":"string","nullable":true},"provider":{"type":"string","enum":["stripe","funnelfox"]},"initialCoinsAmount":{"type":"number"},"recurringCoinsAmount":{"type":"number"},"recurringDepositInterval":{"type":"number"}},"required":["id","planId","priceId","provider","initialCoinsAmount","recurringCoinsAmount","recurringDepositInterval"]},"CreateWebSubscriptionProductDtoClass":{"type":"object","properties":{"planId":{"type":"string","description":"Logical plan id (matches frontend plan id / webhook metadata)","example":"premium-month-39-99"},"priceId":{"type":"string","description":"Stripe Price ID when provider is stripe; null for FunnelFox-only rows","example":"price_1SjhrOACV98ZMZrYIVHGbuNs","nullable":true},"provider":{"type":"string","description":"Billing provider for this row","enum":["stripe","funnelfox"],"default":"stripe"},"initialCoinsAmount":{"type":"number","example":100},"recurringCoinsAmount":{"type":"number","example":0},"recurringDepositInterval":{"type":"number","description":"Recurring coin deposit interval in days","example":0}},"required":["planId","initialCoinsAmount","recurringCoinsAmount","recurringDepositInterval"]},"UpdateWebSubscriptionProductDtoClass":{"type":"object","properties":{"planId":{"type":"string"},"priceId":{"type":"string","nullable":true},"provider":{"type":"string","enum":["stripe","funnelfox"]},"initialCoinsAmount":{"type":"number"},"recurringCoinsAmount":{"type":"number"},"recurringDepositInterval":{"type":"number"}}},"BillingRefundDtoClass":{"type":"object","properties":{"amountMinor":{"type":"number","description":"Partial refund amount in MINOR units (cents). Omit or null to refund the whole remaining amount.","example":999},"reason":{"type":"string","description":"Why the refund was issued — stored in the provider ledger.","example":"support_goodwill"},"initiatedBy":{"type":"string","description":"Support agent identity for the audit trail. Defaults to \"mcp\" when the caller sends none.","example":"agent@genai-apps.com"},"revokeAccess":{"type":"boolean","description":"Whether the refund should also revoke access. Only honoured by providers with accessRevocationControl; others reject false rather than ignore it.","default":true,"example":true}}},"BillingSuspendDtoClass":{"type":"object","properties":{"initiatedBy":{"type":"string","description":"Support agent identity for the audit trail.","example":"agent@genai-apps.com"},"reason":{"type":"string","description":"Why the subscription was ended on the spot.","example":"chargeback_threat"}}},"BillingAutorenewDtoClass":{"type":"object","properties":{"enable":{"type":"boolean","description":"true = enable auto-renewal, false = disable it","example":false},"initiatedBy":{"type":"string","description":"Support agent identity for the audit trail.","example":"agent@genai-apps.com"},"reason":{"type":"string","description":"Why the change was made.","example":"support_request"}},"required":["enable"]},"McpSetCreativeStatusDtoClass":{"type":"object","properties":{"status":{"type":"string","description":"draft | review | live | rejected | archived"}},"required":["status"]},"BroadcastNotificationDto":{"type":"object","properties":{"type":{"type":"string","enum":["system","promo","model","studio","chat"]},"localizedContent":{"type":"object","description":"Localized content keyed by locale. en-US is required as fallback.","additionalProperties":{"$ref":"#/components/schemas/LocalizedContentItem"}},"imageUrl":{"type":"string"},"actionUrl":{"type":"string"},"metadata":{"type":"object"},"expiresAt":{"type":"string","description":"ISO date string"}},"required":["type","localizedContent"]},"FunnelConfigItemDto":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"config":{"type":"object"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","slug","name","isActive","config","createdAt","updatedAt"]},"FunnelConfigsListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/FunnelConfigItemDto"}},"total":{"type":"number"}},"required":["items","total"]},"CreateFunnelConfigDtoClass":{"type":"object","properties":{"slug":{"type":"string","description":"URL-safe slug","example":"hot-funnel-v1"},"name":{"type":"string","description":"Admin display name","example":"Hot Funnel V1"},"config":{"type":"object","description":"Funnel configuration payload"}},"required":["slug","name","config"]},"UpdateFunnelConfigDtoClass":{"type":"object","properties":{"name":{"type":"string","description":"Admin display name"},"slug":{"type":"string","description":"URL-safe slug"},"isActive":{"type":"boolean","description":"Whether funnel is active"},"config":{"type":"object","description":"Funnel configuration payload"}}},"DeleteFunnelConfigResponseDto":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},"GenerateModelContentUploadUrlDto":{"type":"object","properties":{"target":{"type":"string","enum":["content","story","pack-preview"]},"contentType":{"type":"string"}},"required":["target"]},"ModelContentUploadUrlResponseDto":{"type":"object","properties":{"uploadUrl":{"type":"string"},"s3Key":{"type":"string"}},"required":["uploadUrl","s3Key"]},"ConfirmModelContentUploadDto":{"type":"object","properties":{"target":{"type":"string","enum":["content","story","pack-preview"]},"s3Key":{"type":"string"},"contentType":{"type":"string"}},"required":["target","s3Key"]},"ModelContentUploadConfirmResponseDto":{"type":"object","properties":{"s3Key":{"type":"string"},"videoS3Key":{"type":"string"},"url":{"type":"string"},"previewS3Key":{"type":"string"},"previewUrl":{"type":"string"},"nsfwThumbnailS3Key":{"type":"string"},"nsfwThumbnailUrl":{"type":"string"},"lockedCoverS3Key":{"type":"string"},"lockedCoverUrl":{"type":"string"},"durationSec":{"type":"number"},"previewVideoS3Key":{"type":"string"},"previewVideoUrl":{"type":"string"},"previewVideoPosterS3Key":{"type":"string"},"previewVideoPosterUrl":{"type":"string"}},"required":["s3Key","url"]},"BackfillModelContentLockedCoversDto":{"type":"object","properties":{"modelId":{"type":"string","description":"Restrict the backfill to a single model."},"contentIds":{"description":"Restrict the backfill to specific content ids.","type":"array","items":{"type":"string"}},"scope":{"type":"string","description":"pack-covers processes only the cover media of each pack; all-media processes every media row.","enum":["pack-covers","all-media"],"default":"pack-covers"},"limit":{"type":"number","default":100},"force":{"type":"boolean","default":false,"description":"Regenerate covers that already exist."},"dryRun":{"type":"boolean","default":false}}},"BackfillModelContentVideoDurationsDto":{"type":"object","properties":{"modelId":{"type":"string","description":"Restrict the backfill to a single model."},"contentIds":{"description":"Restrict the backfill to specific content ids.","type":"array","items":{"type":"string"}},"limit":{"type":"number","default":50},"force":{"type":"boolean","default":false,"description":"Re-measure media that already carries a duration."},"dryRun":{"type":"boolean","default":false}}},"AdminPackModelDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"avatarUrl":{"type":"string","nullable":true}},"required":["id","name","slug"]},"AdminPackListItemDto":{"type":"object","properties":{"id":{"type":"string"},"model":{"$ref":"#/components/schemas/AdminPackModelDto"},"title":{"type":"string","nullable":true},"status":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]},"price":{"type":"number"},"photosCount":{"type":"number"},"videosCount":{"type":"number"},"totalVideoDurationSec":{"type":"number"},"unlocksCount":{"type":"number"},"likesCount":{"type":"number"},"coverUrl":{"type":"string","nullable":true},"hasPreviewVideo":{"type":"boolean"},"previewVideoUrl":{"type":"string","nullable":true},"isExplicit":{"type":"boolean"},"isPrivateContentListed":{"type":"boolean"},"publishedAt":{"type":"string","nullable":true},"updatedAt":{"type":"string"}},"required":["id","model","status","price","photosCount","videosCount","totalVideoDurationSec","unlocksCount","likesCount","hasPreviewVideo","isExplicit","isPrivateContentListed","updatedAt"]},"AdminPacksListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AdminPackListItemDto"}},"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"totalPages":{"type":"number"}},"required":["items","total","page","limit","totalPages"]},"AdminModelContentMediaViewDto":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["PHOTO","VIDEO"]},"url":{"type":"string"},"videoUrl":{"type":"string"},"previewUrl":{"type":"string"},"nsfwThumbnailUrl":{"type":"string"},"lockedCoverUrl":{"type":"string"},"duration":{"type":"number"},"order":{"type":"number"},"price":{"type":"number"},"isPurchased":{"type":"boolean"},"isLocked":{"type":"boolean"},"s3Key":{"type":"string"},"videoS3Key":{"type":"string"},"previewS3Key":{"type":"string"},"nsfwThumbnailS3Key":{"type":"string"},"lockedCoverS3Key":{"type":"string"}},"required":["id","type","order","price","isPurchased","isLocked","s3Key"]},"AdminModelContentViewDto":{"type":"object","properties":{"id":{"type":"string"},"modelId":{"type":"string"},"modelName":{"type":"string"},"modelAvatarUrl":{"type":"string"},"type":{"type":"string","enum":["POST","PACK"]},"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]},"price":{"type":"number"},"isPurchased":{"type":"boolean"},"isExplicit":{"type":"boolean"},"order":{"type":"number"},"publishedAt":{"type":"string"},"createdAt":{"type":"string"},"media":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMediaViewDto"}},"baseLikes":{"type":"number"},"baseDislikes":{"type":"number"},"baseUnlocks":{"type":"number"},"likesCount":{"type":"number"},"dislikesCount":{"type":"number"},"unlocksCount":{"type":"number"},"positiveRatePercent":{"type":"number","description":"Null until at least one reaction exists."},"photosCount":{"type":"number"},"videosCount":{"type":"number"},"totalVideoDurationSec":{"type":"number"},"coverMediaId":{"type":"string"},"isPrivateContentListed":{"type":"boolean"},"previewVideoS3Key":{"type":"string","nullable":true,"description":"Looping blurred teaser shown while the pack is locked."},"previewVideoUrl":{"type":"string","nullable":true},"previewVideoPosterS3Key":{"type":"string","nullable":true},"previewVideoPosterUrl":{"type":"string","nullable":true}},"required":["id","modelId","type","status","price","isPurchased","isExplicit","order","createdAt","media","baseLikes","baseDislikes","baseUnlocks","likesCount","dislikesCount","unlocksCount","photosCount","videosCount","totalVideoDurationSec","isPrivateContentListed"]},"AdminModelContentListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentViewDto"}},"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"totalPages":{"type":"number"}},"required":["items","total","page","limit","totalPages"]},"AdminModelContentMediaInputDto":{"type":"object","properties":{"id":{"type":"string","description":"Existing media id. Omit to create a new media row."},"type":{"type":"string","enum":["PHOTO","VIDEO"]},"s3Key":{"type":"string"},"videoS3Key":{"type":"string"},"previewS3Key":{"type":"string"},"nsfwThumbnailS3Key":{"type":"string"},"lockedCoverS3Key":{"type":"string"},"duration":{"type":"number"},"order":{"type":"number","default":0},"price":{"type":"number","default":0},"isLockedPreview":{"type":"boolean","default":false}},"required":["type","s3Key"]},"AdminModelContentMigrationContentDto":{"type":"object","properties":{"type":{"type":"string","enum":["POST","PACK"]},"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]},"price":{"type":"number","default":0},"publishedAt":{"type":"string"},"order":{"type":"number","default":0},"isExplicit":{"type":"boolean","default":false},"baseLikes":{"type":"number"},"baseDislikes":{"type":"number"},"baseUnlocks":{"type":"number"},"isPrivateContentListed":{"type":"boolean","default":true},"previewVideoS3Key":{"type":"string"},"previewVideoPosterS3Key":{"type":"string"},"coverS3Key":{"type":"string"},"media":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMediaInputDto"}}},"required":["type","status","media"]},"AdminModelContentMigrationStoryDto":{"type":"object","properties":{"status":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]},"publishedAt":{"type":"string"},"expiresAt":{"type":"string"},"archivedAt":{"type":"string"},"order":{"type":"number","default":0},"isExplicit":{"type":"boolean","default":false},"media":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMediaInputDto"}}},"required":["status","media"]},"AdminModelContentMigrationSnapshotDto":{"type":"object","properties":{"name":{"type":"string"},"sourceStaticFilesBucket":{"type":"string"},"content":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMigrationContentDto"}},"stories":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMigrationStoryDto"}}},"required":["name","sourceStaticFilesBucket","content","stories"]},"ApplyAdminModelContentMigrationDto":{"type":"object","properties":{"name":{"type":"string"},"sourceStaticFilesBucket":{"type":"string"},"content":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMigrationContentDto"}},"stories":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMigrationStoryDto"}},"mode":{"type":"string","enum":["upsert","replace"],"default":"upsert"},"replaceExisting":{"type":"boolean"},"dryRun":{"type":"boolean","default":false}},"required":["name","sourceStaticFilesBucket","content","stories"]},"CreateAdminModelContentDto":{"type":"object","properties":{"modelId":{"type":"string"},"type":{"type":"string","enum":["POST","PACK"]},"title":{"type":"string"},"description":{"type":"string"},"price":{"type":"number","default":0},"order":{"type":"number","default":0},"isExplicit":{"type":"boolean","default":false},"baseLikes":{"type":"number","description":"Synthetic social-proof baseline; real reactions are added on top. Omit on a pack to have the server generate one."},"baseDislikes":{"type":"number"},"baseUnlocks":{"type":"number","description":"Synthetic unlock baseline; real purchases are added on top. Omit on a pack to have the server generate one."},"coverMediaId":{"type":"string","description":"Media used as the pack cover. Defaults to the first media by order."},"isPrivateContentListed":{"type":"boolean","default":true,"description":"Show this pack in the Private Content catalog."},"previewVideoS3Key":{"type":"string","nullable":true,"description":"Key returned by confirm-upload with target=pack-preview. Null clears the teaser."},"previewVideoPosterS3Key":{"type":"string","nullable":true},"media":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMediaInputDto"}}},"required":["modelId","type"]},"UpdateAdminModelContentDto":{"type":"object","properties":{"type":{"type":"string","enum":["POST","PACK"]},"title":{"type":"string"},"description":{"type":"string"},"price":{"type":"number"},"order":{"type":"number"},"isExplicit":{"type":"boolean"},"baseLikes":{"type":"number"},"baseDislikes":{"type":"number"},"baseUnlocks":{"type":"number"},"coverMediaId":{"type":"string"},"isPrivateContentListed":{"type":"boolean"},"previewVideoS3Key":{"type":"string","nullable":true,"description":"Null clears the teaser video."},"previewVideoPosterS3Key":{"type":"string","nullable":true},"media":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMediaInputDto"}}}},"ModelStoryDto":{"type":"object","properties":{"id":{"type":"string"},"modelId":{"type":"string"},"status":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]},"isExplicit":{"type":"boolean"},"order":{"type":"number"},"publishedAt":{"type":"string"},"expiresAt":{"type":"string"},"archivedAt":{"type":"string"},"createdAt":{"type":"string"},"isViewed":{"type":"boolean"},"viewedAt":{"type":"string"},"media":{"type":"array","items":{"$ref":"#/components/schemas/ModelContentMediaDto"}}},"required":["id","modelId","status","isExplicit","order","createdAt","isViewed","media"]},"AdminModelStoriesListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ModelStoryDto"}},"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"totalPages":{"type":"number"}},"required":["items","total","page","limit","totalPages"]},"CreateAdminModelStoryDto":{"type":"object","properties":{"modelId":{"type":"string"},"publishedAt":{"type":"string"},"expiresAt":{"type":"string"},"order":{"type":"number","default":0},"isExplicit":{"type":"boolean","default":false},"media":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMediaInputDto"}}},"required":["modelId"]},"UpdateAdminModelStoryDto":{"type":"object","properties":{"publishedAt":{"type":"string"},"expiresAt":{"type":"string"},"order":{"type":"number"},"isExplicit":{"type":"boolean"},"media":{"type":"array","items":{"$ref":"#/components/schemas/AdminModelContentMediaInputDto"}}}},"BannerAdminImageDto":{"type":"object","properties":{"key":{"type":"string","description":"Full S3 key in the static bucket."},"url":{"type":"string","description":"Full CDN URL."}},"required":["key","url"]},"BannerAdminImagesDto":{"type":"object","properties":{"desktop":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/BannerAdminImageDto"}]},"tablet":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/BannerAdminImageDto"}]},"mobile":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/BannerAdminImageDto"}]}}},"BannerAdminDto":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]},"order":{"type":"number"},"isExplicit":{"type":"boolean"},"targetRoute":{"type":"string"},"altText":{"type":"string","nullable":true},"publishedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"images":{"$ref":"#/components/schemas/BannerAdminImagesDto"}},"required":["id","slug","status","order","isExplicit","targetRoute","createdAt","updatedAt","images"]},"BannerAdminListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/BannerAdminDto"}}},"required":["items"]},"BannerMigrationSnapshotDto":{"type":"object","properties":{"slug":{"type":"string"},"status":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]},"order":{"type":"number"},"isExplicit":{"type":"boolean"},"targetRoute":{"type":"string"},"altText":{"type":"string","nullable":true},"imageDesktopKey":{"type":"string","nullable":true},"imageTabletKey":{"type":"string","nullable":true},"imageMobileKey":{"type":"string","nullable":true}},"required":["slug","status","order","isExplicit","targetRoute"]},"BannerMigrationSnapshotResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/BannerMigrationSnapshotDto"}}},"required":["items"]},"BannerMigrationItemsDto":{"type":"object","properties":{}},"GenerateBannerUploadUrlDto":{"type":"object","properties":{"variant":{"type":"string","enum":["desktop","tablet","mobile"]},"contentType":{"type":"string"}},"required":["variant","contentType"]},"BannerUploadUrlResponseDto":{"type":"object","properties":{"uploadUrl":{"type":"string"},"key":{"type":"string","description":"Full S3 key of the raw uploaded object."}},"required":["uploadUrl","key"]},"BulkUpdateBannersOrderDto":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"order":{"type":"number"}}}}},"required":["items"]},"CreateBannerDto":{"type":"object","properties":{"slug":{"type":"string"},"targetRoute":{"type":"string"},"altText":{"type":"string"},"isExplicit":{"type":"boolean","default":false}},"required":["slug","targetRoute"]},"UpdateBannerDto":{"type":"object","properties":{"slug":{"type":"string"},"targetRoute":{"type":"string"},"altText":{"type":"string","nullable":true},"isExplicit":{"type":"boolean"},"status":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]}}},"ConfirmBannerUploadDto":{"type":"object","properties":{"variant":{"type":"string","enum":["desktop","tablet","mobile"]},"key":{"type":"string","description":"Key returned by upload-url."}},"required":["variant","key"]},"FacebookPurchaseEventLogsResponseDto":{"type":"object","properties":{}},"ScreensAnalyticsDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"data":{"description":"Uploaded screens analytics JSON payload","oneOf":[{"type":"object","additionalProperties":true},{"type":"array","items":{}}]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","data","createdAt","updatedAt"]},"GetScreensAnalyticsResponseDto":{"type":"object","properties":{"analytics":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ScreensAnalyticsDto"}]}},"required":["analytics"]},"OpenRouterConfigItemDto":{"type":"object","properties":{"id":{"type":"string"},"modelSlug":{"type":"string"},"name":{"type":"string","nullable":true},"providers":{"type":"array","items":{"type":"string"}},"allowFallbacks":{"type":"boolean"},"sort":{"type":"string","nullable":true,"enum":["price","throughput","latency"]},"isActive":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","modelSlug","name","providers","allowFallbacks","sort","isActive","createdAt","updatedAt"]},"OpenRouterConfigsListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/OpenRouterConfigItemDto"}},"total":{"type":"number"}},"required":["items","total"]},"CreateOpenRouterConfigDtoClass":{"type":"object","properties":{"modelSlug":{"type":"string","description":"OpenRouter model slug this config applies to","example":"deepseek/deepseek-v4-flash"},"name":{"type":"string","description":"Admin display label"},"providers":{"description":"Ordered list of provider base slugs → provider.order","example":["deepinfra","novita"],"type":"array","items":{"type":"string"}},"allowFallbacks":{"type":"boolean","description":"provider.allow_fallbacks (false = only the listed providers)","default":true},"sort":{"type":"string","description":"provider.sort strategy","enum":["price","throughput","latency"]}},"required":["modelSlug","providers","allowFallbacks"]},"UpdateOpenRouterConfigDtoClass":{"type":"object","properties":{"modelSlug":{"type":"string"},"name":{"type":"string"},"providers":{"type":"array","items":{"type":"string"}},"allowFallbacks":{"type":"boolean"},"sort":{"type":"string","enum":["price","throughput","latency"]},"isActive":{"type":"boolean"}}},"DeleteOpenRouterConfigResponseDto":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},"ReviewModerationFeedbackBody":{"type":"object","properties":{}},"ChatModeExperimentDto":{"type":"object","properties":{"variantId":{"type":"string","description":"Assigned variant of the chat-mode choice experiment (GEN-1517). `roleplay_only` both for the control branch and for users outside the experiment.","example":"roleplay_only"},"modeChoiceEnabled":{"type":"boolean","description":"Whether the user may start a Realistic chat with a character that declares both modes. Single-mode characters are unaffected.","example":false}},"required":["variantId","modeChoiceEnabled"]},"TrackPushOpenDto":{"type":"object","properties":{}},"TrackPushOpenResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tracking was successful"}},"required":["success"]},"AcquisitionAttributionDto":{"type":"object","properties":{"utmSource":{"type":"string","nullable":true},"utmMedium":{"type":"string","nullable":true},"utmCampaign":{"type":"string","nullable":true},"utmContent":{"type":"string","nullable":true},"utmTerm":{"type":"string","nullable":true},"fbclid":{"type":"string","nullable":true},"gclid":{"type":"string","nullable":true},"ttclid":{"type":"string","nullable":true},"referrer":{"type":"string","nullable":true},"adMetadata":{"type":"object","description":"Additional ad/campaign metadata captured from the landing URL — all values are strings"}}},"StartAcquisitionSessionDto":{"type":"object","properties":{"funnelId":{"type":"string","description":"Stable funnel slug (e.g. \"quiz-3-v2-adaptive\")"},"firebaseUid":{"type":"string","description":"Firebase Auth UID (anonymous or registered). When present, we attach this session to the existing lead with the same UID."},"locale":{"type":"string","description":"Optional locale override. Defaults to \"en\"."},"attribution":{"$ref":"#/components/schemas/AcquisitionAttributionDto"},"experiments":{"type":"object","description":"Map of experimentId → assignment captured at funnel entry"},"deviceType":{"type":"string","enum":["mobile","tablet","desktop"]},"userAgent":{"type":"string"},"country":{"type":"string"},"ipAddress":{"type":"string"},"amplitudeDeviceId":{"type":"string"},"facebookPixelId":{"type":"string"},"tiktokPixelId":{"type":"string"}},"required":["funnelId"]},"AcquisitionSessionDto":{"type":"object","properties":{"id":{"type":"string"},"leadId":{"type":"string"},"funnelId":{"type":"string"},"attemptNumber":{"type":"number"},"status":{"type":"string","enum":["in_progress","completed","purchased","abandoned"]},"answers":{"type":"object"},"attribution":{"$ref":"#/components/schemas/AcquisitionAttributionDto"},"experiments":{"type":"object"},"locale":{"type":"string"},"deviceType":{"type":"string","enum":["mobile","tablet","desktop"],"nullable":true},"userAgent":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"ipAddress":{"type":"string","nullable":true},"amplitudeDeviceId":{"type":"string","nullable":true},"facebookPixelId":{"type":"string","nullable":true},"tiktokPixelId":{"type":"string","nullable":true},"startedAt":{"format":"date-time","type":"string"},"lastTouchAt":{"format":"date-time","type":"string"},"completedAt":{"format":"date-time","type":"string","nullable":true},"purchasedAt":{"format":"date-time","type":"string","nullable":true},"abandonedAt":{"format":"date-time","type":"string","nullable":true},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","leadId","funnelId","attemptNumber","status","answers","attribution","experiments","locale","startedAt","lastTouchAt","createdAt","updatedAt"]},"StartAcquisitionSessionResponseDto":{"type":"object","properties":{"session":{"$ref":"#/components/schemas/AcquisitionSessionDto"},"created":{"type":"boolean","description":"True when a brand-new session was created; false when an existing in-progress session was returned."},"leadIsCustomer":{"type":"boolean","description":"True if the lead is a known paying customer. The funnel should redirect to the app instead of continuing."}},"required":["session","created","leadIsCustomer"]},"CheckAcquisitionEmailDto":{"type":"object","properties":{"email":{"type":"string"},"sessionId":{"type":"string","description":"Current acquisition session id. When supplied, the check excludes the lead this session is attached to from `duplicate_lead` detection."}},"required":["email"]},"CheckAcquisitionEmailResponseDto":{"type":"object","properties":{"status":{"type":"string","enum":["available","existing_customer","duplicate_lead"]},"hint":{"type":"string","enum":["continue","sign_in_suggested","continue_silently"],"description":"Recommended front-end behaviour. The status is informational; the funnel never blocks the user purely because the email exists."}},"required":["status","hint"]},"UpdateAcquisitionSessionDto":{"type":"object","properties":{"answers":{"type":"object","description":"Partial answers map — merged with existing answers using shallow object merge."},"attribution":{"$ref":"#/components/schemas/AcquisitionAttributionDto"},"experiments":{"type":"object"},"locale":{"type":"string"},"deviceType":{"type":"string","enum":["mobile","tablet","desktop"]},"firebaseUid":{"type":"string","description":"Latest Firebase UID seen during this session — useful when Firebase Anonymous is initialised after the first request."}}},"CaptureAcquisitionEmailDto":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]},"CaptureAcquisitionEmailResponseDto":{"type":"object","properties":{"sessionId":{"type":"string"},"leadId":{"type":"string"},"status":{"type":"string","enum":["available","existing_customer","duplicate_lead"]},"hint":{"type":"string","enum":["continue","sign_in_suggested","continue_silently"]}},"required":["sessionId","leadId","status","hint"]},"AcquisitionLeadSummaryDto":{"type":"object","properties":{"lead":{"$ref":"#/components/schemas/AcquisitionLeadDto"},"latestSession":{"nullable":true,"description":"The most recent session for this lead (any funnel). Use this to resume an in-progress run.","allOf":[{"$ref":"#/components/schemas/AcquisitionSessionDto"}]},"sessions":{"type":"array","items":{"$ref":"#/components/schemas/AcquisitionSessionDto"}}},"required":["lead","latestSession","sessions"]},"AcquisitionContentAccessDto":{"type":"object","properties":{"hasAccess":{"type":"boolean","description":"True when the identity (Firebase UID, email, or a completed funnel run recorded for that UID) belongs to a user who came through a web acquisition funnel."},"source":{"type":"string","enum":["firebase_uid","email","web_user_funnel","none","feature_disabled"],"description":"Which identifier matched a funnel origin. `web_user_funnel` means no lead matched but the funnel tracking row (`web_users`) for this UID shows a completed run — the lead was created under the pre-registration UID. `none` means no funnel origin was found. `feature_disabled` means the decision was forced by the global NSFW_CONTENT_ACCESS_MODE ('all' grants everyone, 'none' denies everyone) without a lookup."},"leadId":{"type":"string","nullable":true,"description":"The acquisition lead id that granted access, when matched."}},"required":["hasAccess","source"]},"LinkAcquisitionLeadToUserDto":{"type":"object","properties":{"firebaseUid":{"type":"string","description":"Firebase UID of the now-registered user. Used to look up the matching row in the `users` table."},"appUserId":{"type":"string","description":"Pre-resolved `users.id` to short-circuit the Firebase lookup. Optional."}},"required":["firebaseUid"]},"CheckoutWalletHintsDto":{"type":"object","properties":{"applePay":{"type":"boolean","description":"Device can run Apple Pay (`ApplePaySession.canMakePayments()`)."},"googlePay":{"type":"boolean","description":"Device can run Google Pay. Only an explicit `false` has an effect (drops GOOGLE_PAY)."}}},"CreateCheckoutDto":{"type":"object","properties":{"purpose":{"type":"string","enum":["subscription","coins","card_update"]},"productId":{"type":"string","description":"What to buy: provider price point id for subscriptions, web coin productId for coins. Not required for card_update."},"returnPath":{"type":"string","description":"Relative frontend path to return the user to after a redirect payment."},"customerEmail":{"type":"string","description":"Buyer email captured client-side (funnel store). Last-resort fallback when the user record has no email yet — anonymous funnel users are provisioned after the funnel email sync ran."},"funnelId":{"type":"string","description":"Funnel the checkout was started from (the `/ob/:funnelId` segment). Upgate resolves the payment methods it asks for per funnel (UPGATE_CHECKOUT_METHODS_BY_FUNNEL) — omitted (in-app surfaces) means the default set. Ignored by the other providers.","example":"lvhuiq5"},"wallets":{"description":"What the client's device can run: `applePay` = `ApplePaySession.canMakePayments()`. Upgate keeps at most one wallet in the form — Apple Pay when available, Google Pay otherwise (GEN-1277). Omitted = no narrowing. Ignored by the other providers.","allOf":[{"$ref":"#/components/schemas/CheckoutWalletHintsDto"}]}},"required":["purpose"]},"CheckoutResponseDto":{"type":"object","properties":{"provider":{"type":"string","enum":["funnelfox","unlimit","upgate"]},"mode":{"type":"string","enum":["embedded","redirect"]},"pricePointIdent":{"type":"string","description":"FunnelFox price point to mount the embedded SDK with."},"orderId":{"type":"string","description":"Payment order id (redirect mode / upgate)."},"redirectUrl":{"type":"string","description":"Hosted payment page URL to redirect the user to."},"checkoutUrl":{"type":"string","description":"Upgate checkout session URL — the embedded payment element mounts with it (also works as a hosted page)."},"availableMethods":{"description":"Payment methods the Upgate form will render ('CARD', 'APPLE_PAY', …), as the gateway echoed them. Upgate silently drops methods disabled on the merchant account, so this — not the requested list — tells whether the buyer is offered a wallet. Absent for the other providers.","type":"array","items":{"type":"string"}}},"required":["provider","mode"]},"PaymentOrderStatusResponseDto":{"type":"object","properties":{"orderId":{"type":"string"},"purpose":{"type":"string","enum":["subscription","coins","card_update"]},"status":{"type":"string","enum":["pending","completed","declined","cancelled","expired"]},"provider":{"type":"string"},"declineCode":{"type":"string"},"returnPath":{"type":"string"}},"required":["orderId","purpose","status","provider"]},"OneClickPurchaseDto":{"type":"object","properties":{"productId":{"type":"string","description":"Web coin productId to charge for."}},"required":["productId"]},"OneClickPurchaseResponseDto":{"type":"object","properties":{"status":{"type":"string","enum":["completed","pending","declined","no_payment_method"]},"orderId":{"type":"string"},"declineCode":{"type":"string"},"balance":{"type":"number","description":"Coin balance after a completed charge."}},"required":["status"]},"PaymentProviderResponseDto":{"type":"object","properties":{"provider":{"type":"string","enum":["funnelfox","stripe","apple","unlimit","upgate"]},"reason":{"type":"string","enum":["subscription","assignment","default"]},"funnelId":{"type":"string","description":"Funnel slug the checkout should carry (affiliate visitors, GEN-1317)"}},"required":["provider","reason"]},"CreateConceptDto":{"type":"object","properties":{"code":{"type":"string","example":"ASMR"},"name":{"type":"string","example":"ASMR triggers"},"description":{"type":"string"},"status":{"type":"string","enum":["active","testing","archived"]},"author":{"type":"string","example":"Лёша"}},"required":["code","name"]},"UpdateConceptDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["active","testing","archived"]},"author":{"type":"string","example":"Катя"}}},"CreateVariantDto":{"type":"object","properties":{"code":{"type":"string","example":"black_curly_female"},"note":{"type":"string"}},"required":["code"]},"UpdateVariantDto":{"type":"object","properties":{"note":{"type":"string"}}},"CreateLocalizationDto":{"type":"object","properties":{"locale":{"type":"string","example":"en_US"},"title":{"type":"string","example":"She whispers just for you…"},"description":{"type":"string"},"canonicalName":{"type":"string","example":"12_06_fr_hand_1"}},"required":["locale","title"]},"UpdateLocalizationDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["draft","review","live","archived"]}}},"SetTestWinnerDto":{"type":"object","properties":{"winner":{"type":"boolean"}},"required":["winner"]},"UpdateDraftStatusDto":{"type":"object","properties":{"status":{"type":"string","enum":["draft","handed_off","live","rejected"]},"rejectReason":{"type":"string"}},"required":["status"]},"CreateFbAccountDto":{"type":"object","properties":{"provider":{"type":"string","example":"IG"},"product":{"type":"string","example":"MIA"},"metaAdAccountId":{"type":"string","example":"act_238491027465"},"facebookPageId":{"type":"string","example":"104829301182736"},"accessToken":{"type":"string"},"reuseTokenFromAccountId":{"type":"string"},"proxyId":{"type":"string"}},"required":["provider","product","metaAdAccountId","facebookPageId","proxyId"]},"UpdateFbAccountDto":{"type":"object","properties":{"status":{"type":"string","enum":["active","inactive"]},"hiddenInV2":{"type":"boolean"},"proxyId":{"type":"string"},"accessToken":{"type":"string"},"reuseTokenFromAccountId":{"type":"string"},"facebookPageId":{"type":"string"}}},"ImportV1AccountsDto":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"accountId":{"type":"string"},"provider":{"type":"string"},"product":{"type":"string"}}}}},"required":["items"]},"CreateProxyDto":{"type":"object","properties":{"protocol":{"type":"string","enum":["http","https","socks5"]},"host":{"type":"string","example":"45.155.68.12"},"port":{"type":"number","example":8000},"username":{"type":"string"},"password":{"type":"string"},"expiresAt":{"type":"string","example":"2026-11-01"}},"required":["protocol","host","port"]},"UpdateProxyDto":{"type":"object","properties":{"status":{"type":"string","enum":["active","inactive"]},"expiresAt":{"type":"string"}}},"CreateFbAdDto":{"type":"object","properties":{"adsetId":{"type":"string"},"campaignId":{"type":"string"}},"required":["adsetId"]},"RunInsightsSyncDto":{"type":"object","properties":{"days":{"type":"number","example":3},"force":{"type":"boolean"}}},"RegisterConceptDto":{"type":"object","properties":{"external_id":{"type":"string","example":"H-041"},"name":{"type":"string","example":"Отказ читается как незаинтересованность"},"description":{"type":"string"},"status":{"type":"string","enum":["active","testing","archived"]},"author":{"type":"string","example":"Лёша"}},"required":["external_id","name"]},"RegisterCreativeDto":{"type":"object","properties":{"concept_external_id":{"type":"string","example":"H-041"},"external_id":{"type":"string","example":"H-041-C2"},"ad_name":{"type":"string","example":"H-041-C2__rejection_fear__seedance_video__motion__w31"},"title":{"type":"string","example":"Отказ читается как незаинтересованность"},"description":{"type":"string"},"locale":{"type":"string","example":"es_MX","description":"ll_CC of the localized creative; en_US when omitted"},"parent_external_id":{"type":"string","example":"H-016-C3","description":"external_id of the base creative — the localization joins its variant (external_id would then be e.g. H-016-C3-ES-MX)"}},"required":["concept_external_id","external_id","ad_name","title"]},"RegisterMediaDto":{"type":"object","properties":{"creative_external_id":{"type":"string","example":"H-041-C2"},"source_url":{"type":"string","description":"Presigned https URL on an allowed host"},"s3_key":{"type":"string","description":"Key of an object the sender already PUT into the marketing bucket"},"source_ref":{"type":"string","description":"Sender's file id — makes redelivery idempotent"},"content_type":{"type":"string","example":"video/mp4"},"size_bytes":{"type":"number","example":41234567},"slug":{"type":"string","example":"ferma-s-kozoy"},"original_filename":{"type":"string"}},"required":["creative_external_id","source_ref","content_type","size_bytes"]},"RecallCreativeDto":{"type":"object","properties":{"creative_external_id":{"type":"string","example":"H-041-C2"}},"required":["creative_external_id"]},"MarkTestWinnerDto":{"type":"object","properties":{"creative_external_id":{"type":"string","example":"H-041-C2"},"winner":{"type":"boolean","description":"false clears the mark"}},"required":["creative_external_id","winner"]},"SnapshotBodyDto":{"type":"object","properties":{"creative_ids":{"type":"array","items":{"type":"string"}},"ad_names":{"type":"array","items":{"type":"string"}},"with_days":{"type":"boolean","default":false}}},"LiveActionModelDto":{"type":"object","properties":{"id":{"type":"string","description":"Model ID"},"slug":{"type":"string","description":"Model URL slug","example":"anna-smith-x7f2k"},"name":{"type":"string","description":"Model name"},"age":{"type":"number","description":"Model age"},"photo":{"type":"string","description":"Model photo URL"},"avatarIcon":{"type":"string","description":"Model avatar icon URL"},"previewVideoUrl":{"type":"string","description":"CDN URL of the initial state video for the live action scenario"},"soundOn":{"type":"boolean","description":"Whether this live action scenario has sound"},"longVideos":{"type":"boolean","description":"Whether this live action scenario uses long videos"}},"required":["id","slug","name","age","photo","soundOn","longVideos"]},"GetLiveActionModelsResponseDto":{"type":"object","properties":{"total":{"type":"number"},"pageSize":{"type":"number"},"page":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/LiveActionModelDto"}}},"required":["total","pageSize","page","items"]},"ScenarioStateDtoSchema":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"category":{"type":"string","enum":["CLASSIC","NSFW"]},"price":{"type":"number"},"isNext":{"type":"boolean"},"isPurchased":{"type":"boolean"},"stateVideoUrl":{"type":"string"},"previewImageUrl":{"type":"string"},"order":{"type":"number"}},"required":["id","label","category","isNext","isPurchased","order"]},"ScenarioResponseDtoSchema":{"type":"object","properties":{"id":{"type":"string"},"modelId":{"type":"string"},"name":{"type":"string"},"initialStateId":{"type":"string"},"intermediateVideoUrl":{"type":"string"},"shortInitialVideoUrl":{"type":"string"},"soundOn":{"type":"boolean"},"longVideos":{"type":"boolean"},"classic":{"type":"array","items":{"$ref":"#/components/schemas/ScenarioStateDtoSchema"}},"nsfw":{"type":"array","items":{"$ref":"#/components/schemas/ScenarioStateDtoSchema"}},"hiddenNsfwCount":{"type":"number"},"unlockAllPrice":{"type":"number"},"unlockAllFullPrice":{"type":"number"},"isPurchaseAllAvailable":{"type":"boolean"}},"required":["id","modelId","soundOn","longVideos","classic","nsfw","hiddenNsfwCount","isPurchaseAllAvailable"]},"PurchaseStateDto":{"type":"object","properties":{"stateId":{"type":"string"}},"required":["stateId"]},"PurchaseResponseDtoSchema":{"type":"object","properties":{"purchased":{"type":"boolean"},"purchaseId":{"type":"string","nullable":true},"balance":{"type":"number"}},"required":["purchased","purchaseId","balance"]},"LiveActionsMigrationScenarioDto":{"type":"object","properties":{"name":{"type":"string"},"intermediateVideoKey":{"type":"string"},"shortInitialVideoKey":{"type":"string"},"soundOn":{"type":"boolean","default":false},"longVideos":{"type":"boolean","default":false},"order":{"type":"number","default":0},"initialStateVideoKey":{"type":"string"}},"required":["soundOn","longVideos","order"]},"LiveActionsMigrationStateDto":{"type":"object","properties":{"label":{"type":"string"},"category":{"type":"string","enum":["CLASSIC","NSFW"]},"price":{"type":"number","default":0},"stateVideoKey":{"type":"string"},"previewImageKey":{"type":"string"},"order":{"type":"number","default":0}},"required":["label","category","price","stateVideoKey","order"]},"ApplyLiveActionsMigrationDto":{"type":"object","properties":{"name":{"type":"string"},"sourceBucket":{"type":"string"},"scenario":{"$ref":"#/components/schemas/LiveActionsMigrationScenarioDto"},"states":{"type":"array","items":{"$ref":"#/components/schemas/LiveActionsMigrationStateDto"}},"removeMissingStates":{"type":"boolean","default":true},"dryRun":{"type":"boolean","default":false}},"required":["name","sourceBucket","scenario","states"]},"ScenarioOrderUpdateItem":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the live action scenario","example":"123e4567-e89b-12d3-a456-426614174000"},"order":{"type":"number","description":"The new order position (0-indexed)","example":0,"minimum":0}},"required":["id","order"]},"UpdateScenariosOrderDto":{"type":"object","properties":{"updates":{"description":"Array of scenario ID and order pairs to update","type":"array","items":{"$ref":"#/components/schemas/ScenarioOrderUpdateItem"}}},"required":["updates"]},"StateOrderUpdateItem":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the live action state","example":"123e4567-e89b-12d3-a456-426614174000"},"category":{"type":"string","description":"Category the state belongs to after the update","enum":["CLASSIC","NSFW"]},"order":{"type":"number","description":"The new order position inside the category (0-indexed)","example":0,"minimum":0}},"required":["id","category","order"]},"UpdateStatesOrderDto":{"type":"object","properties":{"updates":{"description":"Full layout of the scenario states after the edit","type":"array","items":{"$ref":"#/components/schemas/StateOrderUpdateItem"}}},"required":["updates"]},"MediaRenditionResponseDto":{"type":"object","properties":{"codec":{"type":"string","enum":["av1","hevc"]},"status":{"type":"string","enum":["pending","processing","ready","skipped","failed"]},"outputKey":{"type":"string"},"outputBytes":{"type":"number","description":"Size of the derivative in bytes"},"presetUsed":{"type":"string","description":"Preset the encoder actually used"},"skipReason":{"type":"string","enum":["source-already-in-codec","output-not-smaller"]},"encodeMs":{"type":"number"},"attempts":{"type":"number"},"error":{"type":"string"}},"required":["codec","status","outputKey","attempts"]},"MediaAssetResponseDto":{"type":"object","properties":{"id":{"type":"string"},"bucket":{"type":"string"},"sourceKey":{"type":"string"},"cdnPrefix":{"type":"string"},"profile":{"type":"string"},"status":{"type":"string","enum":["pending","processing","ready","partial","failed"]},"sourceBytes":{"type":"number"},"durationMs":{"type":"number"},"width":{"type":"number"},"height":{"type":"number"},"sourceCodec":{"type":"string"},"renditions":{"type":"array","items":{"$ref":"#/components/schemas/MediaRenditionResponseDto"}},"savedBytes":{"type":"number","description":"Bytes the best available derivative saves against the source, or null while nothing is ready"},"createdAt":{"format":"date-time","type":"string"},"completedAt":{"format":"date-time","type":"string"}},"required":["id","bucket","sourceKey","cdnPrefix","profile","status","renditions","createdAt"]},"MediaAssetListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/MediaAssetResponseDto"}},"total":{"type":"number"},"countsByStatus":{"type":"object","description":"Asset counts per status across the whole filtered set","example":{"pending":2,"processing":1,"ready":40,"partial":0,"failed":1}}},"required":["items","total","countsByStatus"]},"ShortListItemDtoSchema":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"coverImageUrl":{"type":"string"},"order":{"type":"number"},"episodeCount":{"type":"number"}},"required":["id","slug","title","order","episodeCount"]},"GetShortsResponseDto":{"type":"object","properties":{"total":{"type":"number"},"pageSize":{"type":"number"},"page":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ShortListItemDtoSchema"}}},"required":["total","pageSize","page","items"]},"ShortCastModelDtoSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"photo":{"type":"string"}},"required":["id","name","photo"]},"ShortEpisodeSubtitleDtoSchema":{"type":"object","properties":{"locale":{"type":"string"},"url":{"type":"string"},"format":{"type":"string"}},"required":["locale","url","format"]},"ShortEpisodeDtoSchema":{"type":"object","properties":{"id":{"type":"string"},"episodeNumber":{"type":"number"},"order":{"type":"number"},"title":{"type":"string"},"price":{"type":"number"},"isLocked":{"type":"boolean"},"isNext":{"type":"boolean"},"isPurchased":{"type":"boolean"},"previewImageUrl":{"type":"string"},"durationSeconds":{"type":"number"},"videoUrl":{"type":"string"},"subtitles":{"type":"array","items":{"$ref":"#/components/schemas/ShortEpisodeSubtitleDtoSchema"}}},"required":["id","episodeNumber","order","isLocked","isNext","isPurchased"]},"ShortResponseDtoSchema":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"coverImageUrl":{"type":"string"},"order":{"type":"number"},"isPublished":{"type":"boolean"},"cast":{"type":"array","items":{"$ref":"#/components/schemas/ShortCastModelDtoSchema"}},"episodes":{"type":"array","items":{"$ref":"#/components/schemas/ShortEpisodeDtoSchema"}}},"required":["id","slug","title","order","isPublished","cast","episodes"]},"GenerateShortTranslationsDto":{"type":"object","properties":{}},"LiveActionChatResponseDto":{"type":"object","properties":{}},"SendLiveActionMessageDto":{"type":"object","properties":{}},"V2SceneDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"coverImage":{"type":"string"},"mood":{"type":"string"},"genre":{"type":"string"},"setting":{"type":"string"},"avatarUrl":{"type":"string","description":"CDN URL of the scene avatar icon"},"category":{"type":"string"}},"required":["id","name","description"]},"V2CreateChatResponseDto":{"type":"object","properties":{"id":{"type":"string"},"model":{"$ref":"#/components/schemas/GetModelByIdResponseDto"},"scene":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/V2SceneDto"}]},"lastMessage":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/MessageDto"}]},"suggestionsEnabled":{"type":"boolean","description":"Whether suggestions are enabled for this chat","default":true},"mode":{"type":"string","description":"Mode this chat runs in (GEN-1386). Part of the chat identity — the same user can hold one chat per mode with the same model.","enum":["ROLEPLAY","REALISTIC"],"default":"ROLEPLAY"},"unreadMessageCount":{"type":"number","description":"Number of unread non-user messages in this chat (character / assistant)","default":0},"isOnboarding":{"type":"boolean","description":"Whether this chat is in onboarding mode"},"onboardingStep":{"type":"number","description":"Current onboarding step (1-6) if in onboarding mode"},"onboardingScenarioId":{"type":"string","description":"Onboarding scenario ID if in onboarding mode"},"onboardingAskSuggestions":{"description":"Localized prompt suggestions for the Ask feature during onboarding","type":"array","items":{"type":"string"}},"isDemo":{"type":"boolean"},"demoMessageCount":{"type":"number"},"demoStockPhotosDelivered":{"type":"boolean"},"demoAssistantTextTurns":{"type":"number"}},"required":["id","model","lastMessage","suggestionsEnabled","mode","unreadMessageCount"]},"UnreadMessagesCountDto":{"type":"object","properties":{"count":{"type":"number","description":"Total number of unread non-user messages across all user chats","default":0}},"required":["count"]},"TextMessageQuotaResponseDto":{"type":"object","properties":{"used":{"type":"number","description":"Number of non-billable text messages the user has sent today (UTC)","example":1900},"limit":{"type":"number","description":"Maximum number of non-billable text messages per user per day","example":2000},"remaining":{"type":"number","description":"Number of text messages left before the daily limit","example":100},"warningThreshold":{"type":"number","description":"How many messages before the limit the warning notification is sent","example":100},"limitReached":{"type":"boolean","description":"Whether the user has reached the daily text message limit","example":false},"resetsAt":{"type":"string","description":"ISO timestamp of the next UTC midnight when the daily counter resets","example":"2026-06-13T00:00:00.000Z"},"resetsInSeconds":{"type":"number","description":"Seconds left until the daily counter resets","example":36000}},"required":["used","limit","remaining","warningThreshold","limitReached","resetsAt","resetsInSeconds"]},"AffiliateQuotaResponseDto":{"type":"object","properties":{"active":{"type":"boolean","description":"Whether affiliate limits apply to this user"},"messageLimit":{"type":"number"},"messagesUsed":{"type":"number"},"requiresRegistration":{"type":"boolean","description":"Media generation needs a linked account first — the client opens sign-up instead of the paywall."}},"required":["active"]},"V2CreateChatDto":{"type":"object","properties":{"modelId":{"type":"string","description":"The model ID","example":"123e4567-e89b-12d3-a456-426614174000"},"sceneId":{"type":"string","description":"The scene ID (optional - for scene-based chats)","example":"123e4567-e89b-12d3-a456-426614174001"},"onboardingScenarioId":{"type":"string","description":"Onboarding scenario ID (optional - for first-chat onboarding)","example":"elevator"},"mode":{"type":"string","description":"Chat mode (GEN-1386). Must be one of the model's allowedChatModes. Omitted means \"the conversation with this character\": an existing chat in any mode is returned, and a brand-new one starts in the model's first allowed mode.","enum":["ROLEPLAY","REALISTIC"]}},"required":["modelId"]},"V2SendMessageDto":{"type":"object","properties":{"clientId":{"type":"string","description":"Client-provided message ID for idempotency","example":"550e8400-e29b-41d4-a716-446655440000"},"text":{"type":"string","description":"Message text","example":"Hello"},"preferVideo":{"type":"boolean","description":"Prefer video generation over image generation for media requests","example":false},"imageRequestCount":{"type":"number","description":"Number of media items to generate (for image/video requests), 1..8","example":1}},"required":["text"]},"V2SendMessageResponseDto":{"type":"object","properties":{"userMessage":{"$ref":"#/components/schemas/MessageDto"},"balance":{"type":"number","description":"User coin balance after operation"}},"required":["userMessage","balance"]},"V2RequestImageDto":{"type":"object","properties":{"clientId":{"type":"string","description":"Client-provided message ID for idempotency","example":"550e8400-e29b-41d4-a716-446655440000"},"surroundingsId":{"type":"number","description":"Surroundings id","example":1},"clothesId":{"type":"number","description":"Clothes id","example":1},"emotionsId":{"type":"number","description":"Emotions id","example":1},"posesId":{"type":"number","description":"Poses id","example":1},"imageRequestCount":{"type":"number","description":"Number of images to generate","example":1},"offerSessionId":{"type":"string","description":"Optional offer session id for analytics correlation","example":"b1b3d2f0-5a4c-4e2f-9a1b-1234567890ab"}},"required":["surroundingsId","clothesId","emotionsId","posesId","imageRequestCount"]},"V2ShowSceneDto":{"type":"object","properties":{"clientId":{"type":"string","description":"Client-provided message ID for idempotency","example":"550e8400-e29b-41d4-a716-446655440000"}}},"V2ShowSceneVideoDto":{"type":"object","properties":{"clientId":{"type":"string","description":"Client-provided message ID for idempotency","example":"550e8400-e29b-41d4-a716-446655440000"},"offerSessionId":{"type":"string","description":"Optional offer session id for analytics correlation","example":"b1b3d2f0-5a4c-4e2f-9a1b-1234567890ab"}}},"V2SendGiftDto":{"type":"object","properties":{"clientId":{"type":"string","description":"Client-provided message ID for idempotency","example":"550e8400-e29b-41d4-a716-446655440000"},"giftId":{"type":"string","description":"Gift ID","example":"uuid"},"text":{"type":"string","description":"Optional user text"},"offerSessionId":{"type":"string","description":"Optional offer session id for analytics correlation","example":"b1b3d2f0-5a4c-4e2f-9a1b-1234567890ab"}},"required":["giftId"]},"V2AnimateAttachmentsRequestDto":{"type":"object","properties":{"clientId":{"type":"string","description":"Client-provided message ID for idempotency","example":"550e8400-e29b-41d4-a716-446655440000"},"attachmentIds":{"description":"IDs of image attachments to animate","type":"array","items":{"type":"string"}},"promptOverride":{"type":"string","description":"Optional prompt override for animation"},"offerSessionId":{"type":"string","description":"Optional offer session id for analytics correlation","example":"b1b3d2f0-5a4c-4e2f-9a1b-1234567890ab"}},"required":["attachmentIds"]},"V2SendVoiceMessageDto":{"type":"object","properties":{"clientId":{"type":"string","description":"Client-provided message ID for idempotency","example":"550e8400-e29b-41d4-a716-446655440000"},"audioS3Key":{"type":"string","description":"S3 key of the uploaded audio file","example":"voice-messages/abc123.mp3"},"text":{"type":"string","description":"Optional text message/comment to send with the voice message","example":"Listen to this!"},"durationSeconds":{"type":"number","description":"Duration of the audio in seconds (client-provided)","example":5.2}},"required":["audioS3Key"]},"GenerateSuggestionsResponseDto":{"type":"object","properties":{"suggestions":{"description":"Array of three suggested answers","example":["That sounds amazing!","Tell me more about it","I love that idea"],"type":"array","items":{"type":"string"}}},"required":["suggestions"]},"V2SendPhotoDto":{"type":"object","properties":{"clientId":{"type":"string","description":"Client-provided message ID for idempotency","example":"550e8400-e29b-41d4-a716-446655440000"},"photoS3Key":{"type":"string","description":"S3 key of the uploaded photo","example":"chat-images/abc123.jpg"},"text":{"type":"string","description":"Optional text message/comment to send with the photo","example":"Look at this!"}},"required":["photoS3Key"]},"GetSuggestionsStatusResponseDto":{"type":"object","properties":{"suggestionsEnabled":{"type":"boolean","description":"Whether suggestions are enabled for this chat","example":true}},"required":["suggestionsEnabled"]},"ToggleSuggestionsDto":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether to enable or disable suggestions for this chat","example":true}},"required":["enabled"]},"ToggleSuggestionsResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Success status","example":true},"suggestionsEnabled":{"type":"boolean","description":"The new state of suggestions","example":true}},"required":["success","suggestionsEnabled"]},"MarkMessagesReadDto":{"type":"object","properties":{"messageIds":{"description":"Array of message IDs to mark as read","example":["message-id-1","message-id-2"],"type":"array","items":{"type":"string"}}},"required":["messageIds"]},"ElevenLabsTtsRequestDto":{"type":"object","properties":{"voiceId":{"type":"string","description":"ElevenLabs voice identifier","example":"21m00Tcm4TlvDq8ikWAM"},"text":{"type":"string","description":"Text to synthesize","example":"Hello from ElevenLabs"}},"required":["voiceId","text"]},"ElevenLabsSttRequestDto":{"type":"object","properties":{"key":{"type":"string","description":"S3 object key under stt/ prefix (e.g., 2025-10-23/file.mp3)","example":"sample.mp3"},"languageCode":{"type":"string","description":"Optional language code (e.g., eng)","example":"eng"}},"required":["key"]},"CreateCallRequestDto":{"type":"object","properties":{"modelId":{"type":"string","description":"Model ID to create assistant for","example":"8c7f6e9a-2d3b-4c5a-9f8e-1a2b3c4d5e6f"},"overrides":{"type":"object","description":"Optional overrides for assistant creation"}},"required":["modelId"]},"Assistant":{"type":"object","properties":{}},"SayMessageRequestDto":{"type":"object","properties":{"callId":{"type":"string","description":"Vapi Call ID","example":"817242d9-ca6a-4dc5-b203-3858fc8c905c"},"message":{"type":"string","description":"Message to speak into the call","example":"Welcome to Vapi!"},"endCallAfterSpoken":{"type":"boolean","description":"End call after the message is spoken","default":false}},"required":["callId","message"]},"EndCallRequestDto":{"type":"object","properties":{"callId":{"type":"string","description":"Vapi Call ID","example":"817242d9-ca6a-4dc5-b203-3858fc8c905c"}},"required":["callId"]},"CallReportReasonDto":{"type":"object","properties":{"id":{"type":"string","description":"Report reason identifier","enum":["poor_voice_quality","uninteresting_or_irrelevant","slow_response","technical_issue","boring_lacked_emotions","other"]},"label":{"type":"string","description":"Human-readable label for the report reason"},"description":{"type":"string","description":"Detailed description of the report reason"}},"required":["id","label","description"]},"GetCallReportReasonsResponseDto":{"type":"object","properties":{"reasons":{"description":"List of available call report reasons","type":"array","items":{"$ref":"#/components/schemas/CallReportReasonDto"}}},"required":["reasons"]},"CreateCallReportRequestDto":{"type":"object","properties":{"callId":{"type":"string","description":"ID of the call being reported","example":"b2a4d3e6-1c2b-4a5f-9d8e-7f6a5b4c3d2e"},"reasonId":{"type":"string","description":"Reason for reporting the call","enum":["poor_voice_quality","uninteresting_or_irrelevant","slow_response","technical_issue","boring_lacked_emotions","other"],"example":"poor_voice_quality"},"additionalDetails":{"type":"string","description":"Additional details about the report (optional)","example":"Audio was very distorted and hard to understand"}},"required":["callId","reasonId"]},"CreateCallReportResponseDto":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the report was created successfully","example":true},"message":{"type":"string","description":"Response message","example":"Call reported successfully"},"reportId":{"type":"number","description":"ID of the created report","example":1}},"required":["success","message"]},"UtmParamsDto":{"type":"object","properties":{"utmSource":{"type":"string"},"utmMedium":{"type":"string"},"utmCampaign":{"type":"string"},"utmContent":{"type":"string"},"utmTerm":{"type":"string"},"fbclid":{"type":"string"}}},"GeolocationDto":{"type":"object","properties":{"ipAddress":{"type":"string"},"country":{"type":"string"}},"required":["ipAddress"]},"CreateWebUserDto":{"type":"object","properties":{"firebaseUid":{"type":"string"},"email":{"type":"string"},"funnelType":{"type":"string","description":"Funnel variant identifier (e.g., \"main\", \"dialogue\", \"quick\")"},"funnelAnswers":{"type":"object"},"utmParams":{"$ref":"#/components/schemas/UtmParamsDto"},"geolocation":{"$ref":"#/components/schemas/GeolocationDto"},"amplitudeDeviceId":{"type":"string"},"facebookPixelId":{"type":"string"},"locale":{"type":"string"},"userAgent":{"type":"string"},"referrer":{"type":"string"},"deviceType":{"type":"string","enum":["mobile","tablet","desktop"]},"adMetadata":{"type":"object","description":"Ad/campaign metadata - all URL query params from ad links (e.g. campaign_id, adset_id, ad_id, affid, _subid, _token, etc.)"},"experiments":{"type":"object","description":"A/B test experiment assignments (experimentId -> assignment)"}},"required":["funnelType","funnelAnswers","geolocation","locale","userAgent"]},"CreateWebUserResponseDto":{"type":"object","properties":{"webUserId":{"type":"string"},"created":{"type":"boolean"}},"required":["webUserId","created"]},"UpdateWebUserDto":{"type":"object","properties":{"webUserId":{"type":"string"},"firebaseUid":{"type":"string"},"email":{"type":"string"},"funnelAnswers":{"type":"object"},"amplitudeDeviceId":{"type":"string"},"facebookPixelId":{"type":"string"},"sessionsCount":{"type":"number"},"locale":{"type":"string","description":"Current UI locale (2-letter code). Re-sent when the visitor switches language on the funnel so emails follow the latest choice"},"adMetadata":{"type":"object","description":"Ad/campaign metadata - all URL query params from ad links (e.g. campaign_id, adset_id, ad_id, affid, _subid, _token, etc.)"},"experiments":{"type":"object","description":"A/B test experiment assignments (experimentId -> assignment)"}}},"TrackFacebookPurchaseEventDto":{"type":"object","properties":{"webUserId":{"type":"string"},"firebaseUid":{"type":"string"},"eventId":{"type":"string"},"eventName":{"type":"string","default":"Purchase"},"tier":{"type":"string"},"productId":{"type":"string"},"purchaseSource":{"type":"string"},"currency":{"type":"string"},"value":{"type":"number"},"purchasedAt":{"type":"string"},"metadata":{"type":"object"}}},"TrackFacebookPurchaseEventResponseDto":{"type":"object","properties":{"id":{"type":"string"},"webUserId":{"type":"string","nullable":true},"userId":{"type":"string","nullable":true},"firebaseUid":{"type":"string","nullable":true}},"required":["id","webUserId","userId","firebaseUid"]},"InitDemoDto":{"type":"object","properties":{"webUserId":{"type":"string","description":"Web user ID from the funnel"}},"required":["webUserId"]},"InitDemoResponseDto":{"type":"object","properties":{"firebaseCustomToken":{"type":"string","description":"Firebase custom token for anonymous auth"},"modelId":{"type":"string","description":"Selected demo model ID"}},"required":["firebaseCustomToken","modelId"]},"AffiliateStartDto":{"type":"object","properties":{"modelSlug":{"type":"string","description":"Public slug of the model to open a chat with. Omitted by the home-screen entry (`/aff`, GEN-1561): the visitor picks a character in the catalog instead."},"affiliateId":{"type":"string","description":"Alias of `utm_source` for partner links written as `?affid=` (GEN-1561). Used only when the link carries no utm_source of its own — the reports know one partner field, not two."},"funnelType":{"type":"string","description":"Funnel type recorded on the web user / acquisition session. Defaults to \"aff_1\"."},"attribution":{"$ref":"#/components/schemas/AcquisitionAttributionDto"},"locale":{"type":"string"},"userAgent":{"type":"string"},"amplitudeDeviceId":{"type":"string"}}},"AffiliateStartResponseDto":{"type":"object","properties":{"firebaseCustomToken":{"type":"string","description":"Firebase custom token for anonymous auth"},"modelId":{"type":"string","nullable":true,"description":"Resolved model ID for the chat, or null when the link carried no model slug (home-screen entry)."}},"required":["firebaseCustomToken","modelId"]},"DemoWithPreferencesDto":{"type":"object","properties":{"ethnicity":{"type":"string","enum":["caucasian","asian","arab","latina","african"],"description":"Used with age to pick the demo model; other body/voice fields are ignored."},"age":{"type":"string","enum":["18","20","30","40"],"description":"Maps to three demo age bands: 18 → young; 20 and 30 → mid; 40 → mature."},"body":{"type":"string","enum":["xs","s","m","l"]},"butt":{"type":"string","enum":["xs","s","m","l"]},"breast":{"type":"string","enum":["xs","s","m","l"]},"voice":{"type":"string","enum":["innocent","standard","seductress","dominant"]},"temperament":{"type":"string","enum":["submissive","romantic","provocative","dominant"]}},"required":["ethnicity","age"]},"WebUserDto":{"type":"object","properties":{"id":{"type":"string"},"firebaseUid":{"type":"string"},"email":{"type":"string"},"funnelType":{"type":"string","description":"Funnel variant identifier"},"funnelAnswers":{"type":"object"},"funnelCompleted":{"type":"boolean"},"funnelCompletedAt":{"format":"date-time","type":"string"},"utmSource":{"type":"string"},"utmMedium":{"type":"string"},"utmCampaign":{"type":"string"},"utmContent":{"type":"string"},"utmTerm":{"type":"string"},"fbclid":{"type":"string"},"referrer":{"type":"string"},"ipAddress":{"type":"string"},"country":{"type":"string"},"amplitudeDeviceId":{"type":"string"},"facebookPixelId":{"type":"string"},"firstVisit":{"format":"date-time","type":"string"},"sessionsCount":{"type":"number"},"emailCaptured":{"type":"boolean"},"emailCapturedAt":{"format":"date-time","type":"string"},"signedUp":{"type":"boolean"},"signedUpAt":{"format":"date-time","type":"string"},"purchased":{"type":"boolean"},"purchasedAt":{"format":"date-time","type":"string"},"adMetadata":{"type":"object","description":"Ad/campaign metadata - all URL query params from ad links"},"locale":{"type":"string"},"userAgent":{"type":"string"},"deviceType":{"type":"string","enum":["mobile","tablet","desktop"]},"metadata":{"type":"object"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","funnelType","funnelAnswers","funnelCompleted","firstVisit","sessionsCount","emailCaptured","signedUp","purchased","adMetadata","locale","metadata","createdAt","updatedAt"]},"TrackEventDto":{"type":"object","properties":{"event":{"type":"string","enum":["email_captured","signup_completed","funnel_completed","purchase_completed"]},"email":{"type":"string"},"firebaseUid":{"type":"string"},"eventId":{"type":"string"}},"required":["event"]},"CreatePaymentIntentDto":{"type":"object","properties":{"amount":{"type":"number","description":"Amount in cents (e.g., 5999 = $59.99)","example":5999},"currency":{"type":"string","description":"ISO currency code (lowercase)","example":"usd"},"planId":{"type":"string","description":"Internal plan identifier","example":"premium_monthly"},"planName":{"type":"string","description":"Display name for receipts","example":"Premium Monthly"},"email":{"type":"string","description":"Customer email for receipt","example":"user@example.com"},"userId":{"type":"string","description":"Firebase user ID if authenticated"},"webUserId":{"type":"string","description":"Web user ID from tracking system"}},"required":["amount","currency","planId","planName"]},"CreatePaymentIntentResponseDto":{"type":"object","properties":{"clientSecret":{"type":"string","description":"Client secret for Stripe.js confirmPayment","example":"pi_xxx_secret_xxx"},"paymentIntentId":{"type":"string","description":"Payment Intent ID for reference","example":"pi_xxx"}},"required":["clientSecret","paymentIntentId"]},"CreateSubscriptionDto":{"type":"object","properties":{"priceId":{"type":"string","description":"Stripe Price ID","example":"price_xxx"},"planId":{"type":"string","description":"Internal plan identifier","example":"premium_yearly"},"email":{"type":"string","description":"Customer email","example":"user@example.com"},"userId":{"type":"string","description":"Firebase user ID"},"webUserId":{"type":"string","description":"Web user ID from tracking system"},"customerId":{"type":"string","description":"Existing Stripe Customer ID (if customer was created earlier)","example":"cus_xxx"},"promotionCode":{"type":"string","description":"Stripe Promotion Code ID (promo_...) applied to the first invoice. If set, takes precedence over `coupon`.","example":"promo_1TN5jQACV98ZMZrYJdJO9PgM"},"coupon":{"type":"string","description":"Stripe Coupon ID applied to the subscription (e.g. percent_off, duration once). Ignored if `promotionCode` is set.","example":"1UVQZFHK"}},"required":["priceId","planId","email","userId"]},"CreateSubscriptionResponseDto":{"type":"object","properties":{"clientSecret":{"type":"string","description":"Client secret from subscription latest invoice","example":"pi_xxx_secret_xxx"},"subscriptionId":{"type":"string","description":"Stripe Subscription ID","example":"sub_xxx"},"customerId":{"type":"string","description":"Stripe Customer ID","example":"cus_xxx"}},"required":["clientSecret","subscriptionId","customerId"]},"CreateCustomerDto":{"type":"object","properties":{"email":{"type":"string","description":"Customer email address","example":"user@example.com"},"webUserId":{"type":"string","description":"Web user ID from tracking system","example":"uuid-here"}},"required":["email"]},"CreateCustomerResponseDto":{"type":"object","properties":{"customerId":{"type":"string","description":"Stripe Customer ID","example":"cus_xxx"},"isNew":{"type":"boolean","description":"Whether the customer was newly created","example":true}},"required":["customerId","isNew"]},"WebSubscriptionProductDto":{"type":"object","properties":{"planId":{"type":"string","description":"Plan ID used to identify the product","example":"premium-week-23-99"},"priceId":{"type":"string","description":"Stripe Price ID for the plan","example":"price_1Sjhr1ACV98ZMZrYHr3BkDSe"},"initialCoinsAmount":{"type":"number","description":"Initial coins granted when subscribing","example":50},"recurringCoinsAmount":{"type":"number","description":"Recurring coins amount per interval","example":0},"recurringDepositInterval":{"type":"number","description":"Recurring deposit interval in days","example":0}},"required":["planId","priceId","initialCoinsAmount","recurringCoinsAmount","recurringDepositInterval"]},"WebSubscriptionProductsResponseDto":{"type":"object","properties":{"products":{"description":"List of available web subscription products","type":"array","items":{"$ref":"#/components/schemas/WebSubscriptionProductDto"}}},"required":["products"]},"SubscriptionInfoDto":{"type":"object","properties":{"isSubscribed":{"type":"boolean","description":"Whether the user has an active subscription"},"source":{"type":"string","description":"Subscription source (stripe, apple, funnelfox, unlimit, or null)","enum":["stripe","apple","funnelfox","unlimit",null],"nullable":true},"planName":{"type":"string","description":"Subscription plan name","nullable":true},"status":{"type":"string","description":"Subscription status","nullable":true},"currentPeriodEnd":{"format":"date-time","type":"string","description":"Current period end date","nullable":true},"cancelAtPeriodEnd":{"type":"boolean","description":"Whether subscription will cancel at period end","nullable":true},"email":{"type":"string","description":"User email","nullable":true}},"required":["isSubscribed","source","planName","status","currentPeriodEnd","cancelAtPeriodEnd","email"]},"SubscriptionManagementLinkResponseDto":{"type":"object","properties":{"url":{"type":"string","description":"URL for the subscription management webview","example":"https://api.example.com/v1/payments/subscription-portal?token=abc123"},"expiresIn":{"type":"number","description":"Token expiration time in seconds","example":3600},"subscription":{"description":"Subscription information for the client to display","allOf":[{"$ref":"#/components/schemas/SubscriptionInfoDto"}]},"provider":{"type":"string","description":"Payment provider (stripe, apple, funnelfox, unlimit, or null if no subscription)","enum":["stripe","apple","funnelfox","unlimit",null],"nullable":true,"example":"stripe"},"canManageOnline":{"type":"boolean","description":"Whether user can manage subscription via Stripe portal","example":true}},"required":["url","expiresIn","subscription","provider","canManageOnline"]},"StudioPriceDto":{"type":"object","properties":{"imagePrice":{"type":"number","description":"Price per image generation in coins","example":4},"videoPrice":{"type":"number","description":"Price per video generation (image + video pipeline) in coins","example":10},"animatePrice":{"type":"number","description":"Price per animate (existing image to video) in coins","example":6},"upscaleImagePrice":{"type":"number","description":"Price per image upscale in coins","example":4},"upscaleVideoPrice":{"type":"number","description":"Price per video upscale in coins","example":10}},"required":["imagePrice","videoPrice","animatePrice","upscaleImagePrice","upscaleVideoPrice"]},"StudioCharactersResponseDto":{"type":"object","properties":{"total":{"type":"number","description":"Total number of characters available","example":100},"pageSize":{"type":"number","description":"Number of characters per page","example":20},"page":{"type":"number","description":"Current page number","example":1},"items":{"description":"Array of characters (models)","type":"array","items":{"$ref":"#/components/schemas/GetModelByIdResponseDto"}}},"required":["total","pageSize","page","items"]},"StudioRequestImageDto":{"type":"object","properties":{"modelId":{"type":"string","description":"Model (AI partner) ID","example":"uuid"},"surroundingsId":{"type":"number","description":"Surroundings id","example":1},"clothesId":{"type":"number","description":"Clothes id","example":1},"emotionsId":{"type":"number","description":"Emotions id","example":1},"posesId":{"type":"number","description":"Poses id","example":1},"count":{"type":"number","description":"Number of images to generate","example":1}},"required":["modelId","surroundingsId","clothesId","emotionsId","posesId"]},"StudioGenerationResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Attachment ID"},"type":{"description":"Type of media being generated","allOf":[{"$ref":"#/components/schemas/AttachmentType"}]},"status":{"description":"Current status of generation","allOf":[{"$ref":"#/components/schemas/RunpodJobState"}]},"modelId":{"type":"string","description":"Model ID associated with this generation"},"createdAt":{"format":"date-time","type":"string","description":"Creation timestamp"}},"required":["id","type","status","modelId","createdAt"]},"StudioRequestVideoDto":{"type":"object","properties":{"modelId":{"type":"string","description":"Model (AI partner) ID","example":"uuid"},"surroundingsId":{"type":"number","description":"Surroundings id","example":1},"clothesId":{"type":"number","description":"Clothes id","example":1},"emotionsId":{"type":"number","description":"Emotions id","example":1},"posesId":{"type":"number","description":"Poses id","example":1},"count":{"type":"number","description":"Number of videos to generate (1-10)","example":1,"default":1}},"required":["modelId","surroundingsId","clothesId","emotionsId","posesId"]},"StudioRequestCustomImageDto":{"type":"object","properties":{"modelId":{"type":"string","description":"Model (AI partner) ID","example":"uuid"},"prompt":{"type":"string","description":"Custom prompt for image generation","example":"Sitting on a park bench, wearing a fitted jumpsuit, with a confident smile."},"count":{"type":"number","description":"Number of images to generate (1-10)","example":1,"default":1}},"required":["modelId","prompt"]},"StudioRequestCustomVideoDto":{"type":"object","properties":{"modelId":{"type":"string","description":"Model (AI partner) ID","example":"uuid"},"prompt":{"type":"string","description":"Custom prompt for video generation","example":"Sitting on a park bench, wearing a fitted jumpsuit, with a confident smile."},"count":{"type":"number","description":"Number of videos to generate (1-10)","example":1,"default":1}},"required":["modelId","prompt"]},"StudioAnimateImageDto":{"type":"object","properties":{"attachmentId":{"type":"string","description":"Attachment ID of the image to animate","example":"uuid"},"promptOverride":{"type":"string","description":"Optional prompt override for the animation","example":"Bring this photo to life with gentle movement"}},"required":["attachmentId"]},"StudioUpscaleAttachmentDto":{"type":"object","properties":{"attachmentId":{"type":"string","description":"Attachment ID of the image or video to upscale","example":"uuid"}},"required":["attachmentId"]},"CreateHyperLoraRequestDto":{"type":"object","properties":{"race":{"type":"string","enum":["afro","arab","asian","caucasian","latina"]},"ageSpan":{"type":"string","enum":["18","20","30","40","50"]},"referencePhotoContentType":{"type":"string","enum":["image/jpeg","image/png","image/webp"],"description":"Content type for the reference photo"}},"required":["race","ageSpan"]},"CreateExternalModelRequestDtoClass":{"type":"object","properties":{"name":{"type":"string","example":"Chloe Brennan"},"gender":{"type":"string","enum":["male","female"],"example":"female"},"description":{"type":"string","example":"The girl with the pink hair and the five-year plan."},"age":{"type":"number","example":26,"minimum":18,"maximum":99},"ethnicity":{"type":"string","example":"caucasian"},"occupation":{"type":"string","example":"Brand Strategist"},"hobbies":{"type":"string","example":"color-coded planners, 5am runs, late-night baking experiments"},"hair":{"type":"object","example":{"color":"pink","technique":"solid","silhouette":"long_loose"}},"body_type":{"type":"string","example":"average"},"breast_size":{"type":"string","example":"large"},"butt_size":{"type":"string","example":"medium"},"eyes_color":{"type":"string","example":"blue"},"personality_type":{"type":"string","example":"Innocent"},"relationship":{"type":"string","example":"Stranger"},"archetype":{"type":"string","example":"Girl Next Door"},"category":{"type":"string","example":"realistic_gfe"},"first_message":{"type":"string","example":"Oh my god. You were NOT supposed to see that."},"initial_image_prompt":{"type":"string","example":"a pink-haired woman in a sundress at a convenience store"},"system_prompt":{"type":"string","example":"You are Chloe Brennan, a 26-year-old brand strategist..."},"default_scenario_initial_message":{"type":"string","example":"The 24-hour convenience store is dead except for you and a girl in the frozen aisle."},"default_scenario_prompt":{"type":"string","example":"You are at a 24-hour convenience store late at night..."},"first_message_media_type":{"type":"string","enum":["image","video"],"example":"image"},"personality":{"example":["innocent","ambitious"],"description":"Optional. Array, empty array, null, or a single string.","type":"array","items":{"type":"string"}},"drive":{"type":"string","example":"fears_losing_control"},"in_bed":{"example":["passionate"],"description":"Optional. Array, empty array, null, or a single string.","type":"array","items":{"type":"string"}},"secret":{"type":"string","example":"secretly_insatiable"},"kinks":{"example":["overstimulation","public_risk"],"description":"Optional. Array, empty array, null, or a single string.","type":"array","items":{"type":"string"}},"availability_status":{"type":"string","example":"single"},"relationship_context":{"type":"string","example":"stranger"},"scenario_title":{"type":"string","example":"Aisle Seven, One A.M."},"scenario_hook":{"type":"string","example":"The 24-hour convenience store is dead..."}},"required":["name","gender","description","age","ethnicity","occupation","hobbies","hair","body_type","breast_size","butt_size","eyes_color","personality_type","relationship","archetype","category","first_message","initial_image_prompt","system_prompt","default_scenario_initial_message","default_scenario_prompt","first_message_media_type"]},"CreateExternalModelStoryRequestDto":{"type":"object","properties":{"media_type":{"type":"string","enum":["photo","video"],"example":"photo"},"content_type":{"type":"string","example":"image/webp","description":"Defaults to image/webp for photo stories and video/mp4 for video stories."},"expires_at":{"type":"string","example":"2026-05-08T12:00:00.000Z","nullable":true},"order":{"type":"number","default":0},"is_explicit":{"type":"boolean","default":false},"duration":{"type":"number","description":"Video duration in seconds, if known.","nullable":true}},"required":["media_type"]},"CreateExternalModelStoryResponseDto":{"type":"object","properties":{"story_id":{"type":"string"},"media_id":{"type":"string"},"model_id":{"type":"string"},"upload_url":{"type":"string"},"s3_key":{"type":"string"},"final_url":{"type":"string"},"expires_in":{"type":"number"}},"required":["story_id","media_id","model_id","upload_url","s3_key","final_url","expires_in"]},"CreateUploadUrlRequestDto":{"type":"object","properties":{"modelId":{"type":"string","description":"Model identifier, used as the top-level S3 folder"},"packId":{"type":"string","description":"Pack identifier, used as the second-level S3 folder"},"fileName":{"type":"string","description":"File name to store under the pack. When omitted, a UUID-based name is generated (with an extension derived from contentType, if provided)"},"contentType":{"type":"string","description":"MIME type the client will send in the PUT request, e.g. image/png or video/mp4"}},"required":["modelId","packId"]},"VerifyTurnstileDto":{"type":"object","properties":{"token":{"type":"string","description":"Cloudflare Turnstile token from the client widget","maxLength":2048}},"required":["token"]},"VerifyTurnstileResponseDto":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]},"ModelContentDto":{"type":"object","properties":{"id":{"type":"string"},"modelId":{"type":"string"},"modelName":{"type":"string"},"modelAvatarUrl":{"type":"string"},"type":{"type":"string","enum":["POST","PACK"]},"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["DRAFT","PUBLISHED","ARCHIVED"]},"price":{"type":"number"},"isPurchased":{"type":"boolean"},"isExplicit":{"type":"boolean"},"order":{"type":"number"},"publishedAt":{"type":"string"},"createdAt":{"type":"string"},"media":{"type":"array","items":{"$ref":"#/components/schemas/ModelContentMediaDto"}}},"required":["id","modelId","type","status","price","isPurchased","isExplicit","order","createdAt","media"]},"ModelContentFeedResponseDto":{"type":"object","properties":{"total":{"type":"number"},"pageSize":{"type":"number"},"page":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ModelContentDto"}}},"required":["total","pageSize","page","items"]},"PurchasedModelContentGalleryItemDto":{"type":"object","properties":{"kind":{"type":"string","enum":["PHOTO","VIDEO","PACK"]},"contentId":{"type":"string"},"mediaId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"price":{"type":"number"},"purchasedAt":{"type":"string"},"publishedAt":{"type":"string"},"media":{"type":"array","items":{"$ref":"#/components/schemas/ModelContentMediaDto"}}},"required":["kind","contentId","price","purchasedAt","media"]},"PurchasedModelContentGalleryResponseDto":{"type":"object","properties":{"total":{"type":"number"},"pageSize":{"type":"number"},"page":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/PurchasedModelContentGalleryItemDto"}}},"required":["total","pageSize","page","items"]},"ModelStoriesResponseDto":{"type":"object","properties":{"total":{"type":"number"},"pageSize":{"type":"number"},"page":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ModelStoryDto"}}},"required":["total","pageSize","page","items"]},"ModelContentPurchaseDto":{"type":"object","properties":{"id":{"type":"string"},"contentId":{"type":"string"},"priceAtPurchase":{"type":"number"},"transactionId":{"type":"string"},"createdAt":{"type":"string"},"content":{"$ref":"#/components/schemas/ModelContentDto"}},"required":["id","contentId","priceAtPurchase","createdAt","content"]},"ModelContentPurchasesResponseDto":{"type":"object","properties":{"total":{"type":"number"},"pageSize":{"type":"number"},"page":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ModelContentPurchaseDto"}}},"required":["total","pageSize","page","items"]},"ModelStoriesTrayItemDto":{"type":"object","properties":{"modelId":{"type":"string"},"name":{"type":"string"},"photo":{"type":"string"},"avatarIcon":{"type":"string"},"storyCount":{"type":"number"},"viewedStoryCount":{"type":"number"},"unviewedStoryCount":{"type":"number"},"isViewed":{"type":"boolean"},"latestPublishedAt":{"type":"string"},"stories":{"type":"array","items":{"$ref":"#/components/schemas/ModelStoryDto"}}},"required":["modelId","name","storyCount","viewedStoryCount","unviewedStoryCount","isViewed","stories"]},"ModelStoriesTrayResponseDto":{"type":"object","properties":{"total":{"type":"number"},"pageSize":{"type":"number"},"page":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ModelStoriesTrayItemDto"}}},"required":["total","pageSize","page","items"]},"ModelStoryViewResponseDto":{"type":"object","properties":{"storyId":{"type":"string"},"isViewed":{"type":"boolean"},"viewedAt":{"type":"string"}},"required":["storyId","isViewed","viewedAt"]},"ModelContentPurchaseResponseDto":{"type":"object","properties":{"purchased":{"type":"boolean"},"purchaseId":{"type":"string","nullable":true},"balance":{"type":"number"}},"required":["purchased","purchaseId","balance"]},"PrivateContentModelDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Localized model name."},"slug":{"type":"string","description":"Also the source of the @handle on the client."},"avatarUrl":{"type":"string"}},"required":["id","name","slug"]},"PrivateContentPackCardDto":{"type":"object","properties":{"contentId":{"type":"string"},"model":{"$ref":"#/components/schemas/PrivateContentModelDto"},"title":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"price":{"type":"number","description":"Price in coins."},"photosCount":{"type":"number"},"videosCount":{"type":"number"},"totalVideoDurationSec":{"type":"number","description":"Summed duration of every video in the pack."},"positiveRatePercent":{"type":"number","nullable":true,"description":"Null until the pack has at least one reaction."},"likesCount":{"type":"number","description":"Synthetic baseline plus real likes. This — not the percentage — is what the catalog card renders."},"dislikesCount":{"type":"number"},"unlocksCount":{"type":"number"},"myReaction":{"type":"string","enum":["LIKE","DISLIKE"],"nullable":true},"coverUrl":{"type":"string","nullable":true,"description":"Clean cover. Present only when the pack is unlocked."},"lockedCoverUrl":{"type":"string","nullable":true,"description":"Pre-blurred 640px teaser cover, safe to serve while locked. Falls back to the nsfw thumbnail."},"previewVideoUrl":{"type":"string","nullable":true,"description":"Tiny blurred clip to loop under the play badge. Present only while the pack is locked; null means the card keeps the still cover."},"previewVideoPosterUrl":{"type":"string","nullable":true,"description":"First frame of the teaser, for the video poster."},"hasVideo":{"type":"boolean","description":"Whether to render a play badge on the cover."},"isExplicit":{"type":"boolean"},"isPurchased":{"type":"boolean"},"isLocked":{"type":"boolean"},"access":{"$ref":"#/components/schemas/ModelContentPurchaseStateDto"},"publishedAt":{"type":"string","nullable":true},"purchasedAt":{"type":"string","nullable":true,"description":"Only set in the collection tab."}},"required":["contentId","model","price","photosCount","videosCount","totalVideoDurationSec","likesCount","dislikesCount","unlocksCount","hasVideo","isExplicit","isPurchased","isLocked","access"]},"PrivateContentPacksResponseDto":{"type":"object","properties":{"total":{"type":"number"},"pageSize":{"type":"number"},"page":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/PrivateContentPackCardDto"}}},"required":["total","pageSize","page","items"]},"PrivateContentPackDetailsDto":{"type":"object","properties":{"contentId":{"type":"string"},"model":{"$ref":"#/components/schemas/PrivateContentModelDto"},"title":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"price":{"type":"number","description":"Price in coins."},"photosCount":{"type":"number"},"videosCount":{"type":"number"},"totalVideoDurationSec":{"type":"number","description":"Summed duration of every video in the pack."},"positiveRatePercent":{"type":"number","nullable":true,"description":"Null until the pack has at least one reaction."},"likesCount":{"type":"number","description":"Synthetic baseline plus real likes. This — not the percentage — is what the catalog card renders."},"dislikesCount":{"type":"number"},"unlocksCount":{"type":"number"},"myReaction":{"type":"string","enum":["LIKE","DISLIKE"],"nullable":true},"coverUrl":{"type":"string","nullable":true,"description":"Clean cover. Present only when the pack is unlocked."},"lockedCoverUrl":{"type":"string","nullable":true,"description":"Pre-blurred 640px teaser cover, safe to serve while locked. Falls back to the nsfw thumbnail."},"previewVideoUrl":{"type":"string","nullable":true,"description":"Tiny blurred clip to loop under the play badge. Present only while the pack is locked; null means the card keeps the still cover."},"previewVideoPosterUrl":{"type":"string","nullable":true,"description":"First frame of the teaser, for the video poster."},"hasVideo":{"type":"boolean","description":"Whether to render a play badge on the cover."},"isExplicit":{"type":"boolean"},"isPurchased":{"type":"boolean"},"isLocked":{"type":"boolean"},"access":{"$ref":"#/components/schemas/ModelContentPurchaseStateDto"},"publishedAt":{"type":"string","nullable":true},"purchasedAt":{"type":"string","nullable":true,"description":"Only set in the collection tab."},"teaserMedia":{"description":"Single teaser media. Locked packs get it without url/videoUrl — only the blurred cover.","type":"array","items":{"$ref":"#/components/schemas/ModelContentMediaDto"}}},"required":["contentId","model","price","photosCount","videosCount","totalVideoDurationSec","likesCount","dislikesCount","unlocksCount","hasVideo","isExplicit","isPurchased","isLocked","access","teaserMedia"]},"PrivateContentGalleryPackDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"model":{"$ref":"#/components/schemas/PrivateContentModelDto"},"likesCount":{"type":"number"},"dislikesCount":{"type":"number"},"positiveRatePercent":{"type":"number","nullable":true},"myReaction":{"type":"string","enum":["LIKE","DISLIKE"],"nullable":true}},"required":["id","model","likesCount","dislikesCount"]},"PrivateContentPackGalleryResponseDto":{"type":"object","properties":{"total":{"type":"number"},"pageSize":{"type":"number"},"page":{"type":"number"},"pack":{"$ref":"#/components/schemas/PrivateContentGalleryPackDto"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ModelContentMediaDto"}}},"required":["total","pageSize","page","pack","items"]},"SetPrivateContentReactionDto":{"type":"object","properties":{"value":{"type":"string","enum":["LIKE","DISLIKE"],"nullable":true,"description":"Null clears the reaction."}},"required":["value"]},"PrivateContentReactionResponseDto":{"type":"object","properties":{"contentId":{"type":"string"},"myReaction":{"type":"string","enum":["LIKE","DISLIKE"],"nullable":true},"likesCount":{"type":"number"},"dislikesCount":{"type":"number"},"positiveRatePercent":{"type":"number","nullable":true}},"required":["contentId","likesCount","dislikesCount"]},"RewindCompanionDto":{"type":"object","properties":{"id":{"type":"string","description":"Model ID"},"chatId":{"type":"string","description":"Chat ID to open the conversation with her"},"name":{"type":"string","description":"Model name"},"photo":{"type":"string","description":"Model avatar CDN URL"}},"required":["id","chatId","name","photo"]},"RewindMessagesDto":{"type":"object","properties":{"total":{"type":"number","description":"Total non-deleted messages across all chats"},"chats":{"type":"number","description":"Number of chats the user has"},"companions":{"type":"number","description":"Number of distinct companions the user has messaged"},"topCompanions":{"description":"1–5 top companions by message volume (avatar cluster)","type":"array","items":{"$ref":"#/components/schemas/RewindCompanionDto"}}},"required":["total","chats","companions","topCompanions"]},"RewindVideosDto":{"type":"object","properties":{"total":{"type":"number","description":"Total videos generated"},"lastVideoUrl":{"type":"string","description":"CDN URL of the most recently generated video"},"lastPreviewUrl":{"type":"string","description":"CDN URL of the poster/preview image for the last video"},"explicit":{"type":"boolean","description":"Whether the last video is explicit content"}},"required":["total"]},"RewindPhotosDto":{"type":"object","properties":{"total":{"type":"number","description":"Total photos generated & saved"},"lastPhotoUrl":{"type":"string","description":"CDN URL of the most recent photo received from a companion"},"explicit":{"type":"boolean","description":"Whether the last photo is explicit content"}},"required":["total"]},"RewindAffinityDto":{"type":"object","properties":{"score":{"type":"number","description":"Affinity score 0-100 — percentile of the user by engagement among all users"},"topPercent":{"type":"number","description":"Top percentile bucket, e.g. 1 means \"Top 1%\""}},"required":["score","topPercent"]},"RewindGirlfriendItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Model ID"},"name":{"type":"string","description":"Model name"}},"required":["id","name"]},"RewindGirlfriendsDto":{"type":"object","properties":{"total":{"type":"number","description":"Total number of custom AI girlfriends created"},"items":{"description":"1–4 most recently created girlfriend model IDs (no avatar URLs)","type":"array","items":{"$ref":"#/components/schemas/RewindGirlfriendItemDto"}}},"required":["total","items"]},"RewindPackItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Pack (model content) ID"},"title":{"type":"string","description":"Pack title"},"coverUrl":{"type":"string","description":"CDN URL of the pack cover image"},"explicit":{"type":"boolean","description":"Whether the pack is explicit content"}},"required":["id","title","explicit"]},"RewindPacksDto":{"type":"object","properties":{"total":{"type":"number","description":"Total Private Content packs the user unlocked"},"photos":{"type":"number","description":"Photos inside the unlocked packs"},"videos":{"type":"number","description":"Videos inside the unlocked packs"},"items":{"description":"1–4 most recently unlocked packs (cover grid)","type":"array","items":{"$ref":"#/components/schemas/RewindPackItemDto"}}},"required":["total","photos","videos","items"]},"RewindLiveActionsDto":{"type":"object","properties":{"total":{"type":"number","description":"Total live actions the user unlocked"},"companions":{"type":"number","description":"Number of distinct companions those live actions belong to"},"lastVideoUrl":{"type":"string","description":"CDN URL of the most recently unlocked live action clip"},"lastPreviewUrl":{"type":"string","description":"CDN URL of the poster/preview image for that clip"},"explicit":{"type":"boolean","description":"Whether the last unlocked live action is explicit content"}},"required":["total","companions"]},"RewindDto":{"type":"object","properties":{"messages":{"$ref":"#/components/schemas/RewindMessagesDto"},"videos":{"$ref":"#/components/schemas/RewindVideosDto"},"photos":{"$ref":"#/components/schemas/RewindPhotosDto"},"affinity":{"$ref":"#/components/schemas/RewindAffinityDto"},"girlfriends":{"$ref":"#/components/schemas/RewindGirlfriendsDto"},"packs":{"$ref":"#/components/schemas/RewindPacksDto"},"liveActions":{"$ref":"#/components/schemas/RewindLiveActionsDto"},"generatedAt":{"type":"string","description":"ISO timestamp of when these stats were computed"}},"required":["messages","videos","photos","affinity","girlfriends","packs","liveActions","generatedAt"]},"PlanFeatureKey":{"type":"string","enum":["chat","custom_character","audio_messages","photo_generation","video_generation","live_action","audio_calls","media_studio"],"description":"Feature identifier"},"PlanFeatureDto":{"type":"object","properties":{"key":{"description":"Feature identifier","allOf":[{"$ref":"#/components/schemas/PlanFeatureKey"}]},"used":{"type":"boolean","description":"Whether the user has ever used this feature"}},"required":["key","used"]},"PlanFeaturesDto":{"type":"object","properties":{"usedCount":{"type":"number","description":"Number of features the user has used"},"totalCount":{"type":"number","description":"Total number of features"},"features":{"description":"Feature usage flags in a stable order","type":"array","items":{"$ref":"#/components/schemas/PlanFeatureDto"}}},"required":["usedCount","totalCount","features"]},"BannerImagesDto":{"type":"object","properties":{"desktop":{"type":"string","description":"Full CDN URL of the desktop image."},"tablet":{"type":"string","description":"Full CDN URL of the tablet image."},"mobile":{"type":"string","description":"Full CDN URL of the mobile image."}},"required":["desktop","tablet","mobile"]},"BannerDto":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"targetRoute":{"type":"string","description":"Internal web-app route the banner links to."},"altText":{"type":"string","nullable":true},"isExplicit":{"type":"boolean"},"images":{"$ref":"#/components/schemas/BannerImagesDto"}},"required":["id","slug","targetRoute","isExplicit","images"]},"BannersResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/BannerDto"}}},"required":["items"]}}},"security":[{"bearer":[]}]}