{"openapi":"3.0.0","paths":{"/api/v1/health":{"get":{"operationId":"HealthController_check_v1","parameters":[],"responses":{"200":{"description":"The Health Check is successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}},"503":{"description":"The Health Check is not successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"error"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"},"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}}},"tags":["Health"]}},"/api/v1/health/ready":{"get":{"operationId":"HealthController_ready_v1","parameters":[],"responses":{"200":{"description":"The Health Check is successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}},"503":{"description":"The Health Check is not successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"error"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"},"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}}},"tags":["Health"]}},"/api/v1/health/live":{"get":{"operationId":"HealthController_live_v1","parameters":[],"responses":{"200":{"description":"The Health Check is successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}},"503":{"description":"The Health Check is not successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"error"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"},"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}}},"tags":["Health"]}},"/api/v1/metrics":{"get":{"operationId":"PrometheusController_index_v1","parameters":[],"responses":{"200":{"description":""}},"tags":["Prometheus"]}},"/api/v1/payments/setup-intent":{"post":{"description":"Creates a Stripe Setup Intent. Use the returned client secret with Stripe Elements to collect card details.","operationId":"PaymentMethodsController_createSetupIntent_v1","parameters":[],"responses":{"201":{"description":"Setup Intent created successfully","content":{"application/json":{"schema":{"example":{"clientSecret":"seti_1234567890_secret_abcdefg","setupIntentId":"seti_1234567890"}}}}},"401":{"description":"Unauthorized - Invalid or missing auth token"},"500":{"description":"Internal server error"}},"security":[{"bearer":[]}],"summary":"Create Setup Intent for adding payment method","tags":["Payment Methods"]}},"/api/v1/payments/methods":{"get":{"description":"Retrieves all payment methods saved by the user. Indicates which is the default.","operationId":"PaymentMethodsController_listPaymentMethods_v1","parameters":[],"responses":{"200":{"description":"Payment methods retrieved successfully","content":{"application/json":{"schema":{"example":{"paymentMethods":[{"id":"pm_1234567890","brand":"visa","last4":"4242","exp_month":12,"exp_year":2025,"isDefault":true},{"id":"pm_0987654321","brand":"mastercard","last4":"5555","exp_month":6,"exp_year":2026,"isDefault":false}]}}}}},"401":{"description":"Unauthorized - Invalid or missing auth token"},"500":{"description":"Internal server error"}},"security":[{"bearer":[]}],"summary":"List saved payment methods","tags":["Payment Methods"]}},"/api/v1/payments/methods/{paymentMethodId}":{"delete":{"description":"Removes a saved payment method from the user account.","operationId":"PaymentMethodsController_deletePaymentMethod_v1","parameters":[{"name":"paymentMethodId","required":true,"in":"path","description":"Stripe Payment Method ID","schema":{"example":"pm_1234567890","type":"string"}}],"responses":{"200":{"description":"Payment method deleted successfully","content":{"application/json":{"schema":{"example":{"success":true,"message":"Payment method deleted"}}}}},"401":{"description":"Unauthorized - Invalid or missing auth token"},"403":{"description":"Forbidden - Payment method does not belong to user"},"404":{"description":"Not found - Payment method does not exist"},"500":{"description":"Internal server error"}},"security":[{"bearer":[]}],"summary":"Delete payment method","tags":["Payment Methods"]}},"/api/v1/payments/methods/{paymentMethodId}/default":{"put":{"description":"Sets the specified payment method as default for future purchases.","operationId":"PaymentMethodsController_setDefaultPaymentMethod_v1","parameters":[{"name":"paymentMethodId","required":true,"in":"path","description":"Stripe Payment Method ID","schema":{"example":"pm_1234567890","type":"string"}}],"responses":{"200":{"description":"Default payment method updated successfully","content":{"application/json":{"schema":{"example":{"success":true,"message":"Default payment method updated"}}}}},"401":{"description":"Unauthorized - Invalid or missing auth token"},"403":{"description":"Forbidden - Payment method does not belong to user"},"404":{"description":"Not found - Payment method does not exist"},"500":{"description":"Internal server error"}},"security":[{"bearer":[]}],"summary":"Set default payment method","tags":["Payment Methods"]}},"/api/v1/payment-links/public/{slug}":{"get":{"operationId":"PaymentLinkController_getPublicLink_v1","parameters":[{"name":"slug","required":true,"in":"path","description":"Payment link slug","schema":{"type":"string"}}],"responses":{"200":{"description":"Payment link details"},"404":{"description":"Not found"}},"summary":"Get payment link for checkout page (public)","tags":["Payment Links"]}},"/api/v1/payment-links/public/{slug}/pay":{"post":{"operationId":"PaymentLinkController_payLink_v1","parameters":[{"name":"slug","required":true,"in":"path","description":"Payment link slug","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayPaymentLinkDto"}}}},"responses":{"200":{"description":"Payment intent created with clientSecret"},"400":{"description":"Link not active or invalid amount"},"404":{"description":"Not found"}},"summary":"Pay a payment link (creates Stripe PaymentIntent)","tags":["Payment Links"]}},"/api/v1/payment-links":{"post":{"operationId":"PaymentLinkController_create_v1","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentLinkDto"}}}},"responses":{"201":{"description":"Payment link created with shareable URL"},"401":{"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Create a payment link","tags":["Payment Links"]},"get":{"operationId":"PaymentLinkController_list_v1","parameters":[],"responses":{"200":{"description":"Payment links retrieved"}},"security":[{"bearer":[]}],"summary":"List your payment links","tags":["Payment Links"]}},"/api/v1/payment-links/{id}":{"get":{"operationId":"PaymentLinkController_getById_v1","parameters":[{"name":"id","required":true,"in":"path","description":"Payment link UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Payment link found"},"404":{"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Get a payment link by ID","tags":["Payment Links"]},"patch":{"operationId":"PaymentLinkController_update_v1","parameters":[{"name":"id","required":true,"in":"path","description":"Payment link UUID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePaymentLinkDto"}}}},"responses":{"200":{"description":"Updated"}},"security":[{"bearer":[]}],"summary":"Update a payment link (title, description, expiry)","tags":["Payment Links"]},"delete":{"operationId":"PaymentLinkController_cancel_v1","parameters":[{"name":"id","required":true,"in":"path","description":"Payment link UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Cancelled or deleted"}},"security":[{"bearer":[]}],"summary":"Cancel or delete a payment link","tags":["Payment Links"]}},"/api/v1/payments":{"post":{"description":"Initiates a new payment for token/NFT purchase or marketplace trade. Returns clientSecret for in-app payment collection.","operationId":"PaymentController_createPayment_v1","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentDto"}}}},"responses":{"201":{"description":"Payment created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponseDto"}}}},"400":{"description":"Invalid payment data"},"401":{"description":"Unauthorized"},"409":{"description":"Duplicate payment (idempotency key conflict)"}},"security":[{"clerk-jwt":[]}],"summary":"Create a new payment","tags":["payments"]},"get":{"description":"List all payments with optional filters","operationId":"PaymentController_listPayments_v1","parameters":[{"name":"customerId","required":false,"in":"query","description":"Filter by customer ID","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter by status","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"example":10,"type":"number"}},{"name":"offset","required":false,"in":"query","schema":{"example":0,"type":"number"}}],"responses":{"200":{"description":"Payments retrieved successfully"}},"security":[{"clerk-jwt":[]}],"summary":"List payments","tags":["payments"]}},"/api/v1/payments/{id}":{"get":{"description":"Retrieves detailed payment information","operationId":"PaymentController_getPayment_v1","parameters":[{"name":"id","required":true,"in":"path","description":"Payment ID (UUID)","schema":{"example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}}],"responses":{"200":{"description":"Payment found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponseDto"}}}},"404":{"description":"Payment not found"}},"security":[{"clerk-jwt":[]}],"summary":"Get payment by ID","tags":["payments"]}},"/api/v1/subscriptions":{"post":{"description":"Creates a Stripe Subscription for a campaign. Returns client secret for payment confirmation.","operationId":"SubscriptionController_createSubscription_v1","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionDto"}}}},"responses":{"201":{"description":"Subscription created successfully. Athlete receives 100% of pledge amount, customer pays pledge + 8% platform fee.","content":{"application/json":{"schema":{"example":{"subscriptionId":"sub_1234567890","clientSecret":"pi_1234567890_secret_abcdefg","status":"incomplete","pledgeAmount":4.99,"totalCharged":5.39,"platformFee":0.4,"currency":"USD"}}}}},"400":{"description":"Campaign does not have PLEDGE enabled"},"401":{"description":"Unauthorized - Invalid or missing auth token"},"404":{"description":"Campaign not found"}},"security":[{"bearer":[]}],"summary":"Create PLEDGE subscription","tags":["Subscriptions"]},"get":{"description":"Retrieves all PLEDGE subscriptions for the authenticated user.","operationId":"SubscriptionController_listSubscriptions_v1","parameters":[],"responses":{"200":{"description":"Subscriptions retrieved successfully","content":{"application/json":{"schema":{"example":{"subscriptions":[{"id":"sub_1234567890","campaignId":"campaign-uuid","campaignName":"Artist Campaign","status":"active","amount":9.99,"currency":"USD","currentPeriodEnd":"2025-01-09T00:00:00Z","cancelAtPeriodEnd":false}]}}}}}},"security":[{"bearer":[]}],"summary":"List user subscriptions","tags":["Subscriptions"]}},"/api/v1/subscriptions/{subscriptionId}":{"delete":{"description":"Cancels a PLEDGE subscription at the end of the current billing period.","operationId":"SubscriptionController_cancelSubscription_v1","parameters":[{"name":"subscriptionId","required":true,"in":"path","description":"Stripe Subscription ID","schema":{"example":"sub_1234567890","type":"string"}}],"responses":{"200":{"description":"Subscription cancelled successfully","content":{"application/json":{"schema":{"example":{"success":true,"message":"Subscription will be cancelled at end of billing period","cancelAt":"2025-01-09T00:00:00Z"}}}}}},"security":[{"bearer":[]}],"summary":"Cancel subscription","tags":["Subscriptions"]}},"/api/v1/subscriptions/{subscriptionId}/reactivate":{"post":{"description":"Reactivates a PLEDGE subscription that was set to cancel.","operationId":"SubscriptionController_reactivateSubscription_v1","parameters":[{"name":"subscriptionId","required":true,"in":"path","description":"Stripe Subscription ID","schema":{"example":"sub_1234567890","type":"string"}}],"responses":{"200":{"description":"Subscription reactivated successfully","content":{"application/json":{"schema":{"example":{"success":true,"message":"Subscription reactivated","status":"active"}}}}}},"security":[{"bearer":[]}],"summary":"Reactivate subscription","tags":["Subscriptions"]}},"/api/v1/webhooks/clerk":{"post":{"operationId":"ClerkWebhookController_handleWebhook_v1","parameters":[{"name":"svix-id","required":true,"in":"header","schema":{"type":"string"}},{"name":"svix-timestamp","required":true,"in":"header","schema":{"type":"string"}},{"name":"svix-signature","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook processed successfully"},"400":{"description":"Invalid webhook signature"}},"summary":"Handle Clerk webhook events","tags":["Webhooks"]}},"/api/v1/connect/onboard":{"post":{"description":"Initiates Stripe Express account creation and returns onboarding URL for vendor verification","operationId":"ConnectController_onboardConnectedAccount_v1","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardConnectedAccountDto"}}}},"responses":{"201":{"description":"Onboarding link created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingResponseDto"}}}},"400":{"description":"Invalid input data"},"409":{"description":"Company already has an active Stripe account"}},"security":[{"bearer":[]}],"summary":"Start Stripe Connect onboarding","tags":["Stripe Connect"]}},"/api/v1/connect/sync":{"post":{"description":"Internal endpoint for admin-gateway to sync Stripe accounts created via its own Stripe SDK into the payments database. Idempotent — returns existing record if already synced.","operationId":"ConnectController_syncConnectedAccount_v1","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncConnectedAccountDto"}}}},"responses":{"200":{"description":"Account synced successfully (or already existed)"},"401":{"description":"Invalid internal API key"}},"security":[{"bearer":[]}],"summary":"Sync a Stripe Connect account from admin-gateway","tags":["Stripe Connect"]}},"/api/v1/connect/{accountId}/status":{"get":{"description":"Retrieves current verification status and capabilities of a Stripe Connect account","operationId":"ConnectController_getAccountStatus_v1","parameters":[{"name":"accountId","required":true,"in":"path","description":"Connected account ID","schema":{"example":"ca_abc123","type":"string"}}],"responses":{"200":{"description":"Account status retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountStatusResponseDto"}}}},"404":{"description":"Connected account not found"}},"security":[{"bearer":[]}],"summary":"Check connected account status","tags":["Stripe Connect"]}},"/api/v1/connect/{accountId}/refresh-link":{"post":{"description":"Generates new onboarding URL if original link expired (after 24 hours)","operationId":"ConnectController_refreshOnboardingLink_v1","parameters":[{"name":"accountId","required":true,"in":"path","description":"Connected account ID","schema":{"example":"ca_abc123","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshOnboardingLinkDto"}}}},"responses":{"200":{"description":"Onboarding link refreshed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingLinkResponseDto"}}}},"400":{"description":"Account already active - no refresh needed"},"404":{"description":"Connected account not found"}},"security":[{"bearer":[]}],"summary":"Refresh expired onboarding link","tags":["Stripe Connect"]}},"/api/v1/connect/company/{companyId}/status":{"get":{"description":"Retrieves Stripe Connect status for a company. Used to check if company can receive payments.","operationId":"ConnectController_getCompanyConnectStatus_v1","parameters":[{"name":"companyId","required":true,"in":"path","description":"Company UUID","schema":{"example":"4e18cf2e-b05f-4a8a-8e85-d0258e117597","type":"string"}}],"responses":{"200":{"description":"Connect account found"},"404":{"description":"Company has not completed Stripe Connect onboarding"}},"security":[{"bearer":[]}],"summary":"Get connected account status by company ID","tags":["Stripe Connect"]}},"/api/v1/connect/company/{companyId}/balance":{"get":{"description":"Retrieves Stripe balance for a company. Shows available funds to withdraw.","operationId":"ConnectController_getCompanyBalance_v1","parameters":[{"name":"companyId","required":true,"in":"path","description":"Company UUID","schema":{"example":"4e18cf2e-b05f-4a8a-8e85-d0258e117597","type":"string"}}],"responses":{"200":{"description":"Balance retrieved successfully"},"404":{"description":"Company has not completed Stripe Connect onboarding"}},"security":[{"bearer":[]}],"summary":"Get connected account balance by company ID","tags":["Stripe Connect"]}},"/api/v1/connect/company/{companyId}/disconnect":{"post":{"description":"Internal endpoint called by admin-gateway when an athlete disconnects Stripe. Soft-deletes the record in payments_db.","operationId":"ConnectController_disconnectCompanyAccount_v1","parameters":[{"name":"companyId","required":true,"in":"path","description":"Company UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Account disconnected (or not found)"},"401":{"description":"Invalid internal API key"}},"security":[{"bearer":[]}],"summary":"Disconnect (soft-delete) a connected account by company ID","tags":["Stripe Connect"]}},"/api/v1/connect/company/{companyId}/pending-payouts":{"get":{"description":"Returns total pending payouts for creators who have not connected Stripe","operationId":"ConnectController_getCompanyPendingPayouts_v1","parameters":[{"name":"companyId","required":true,"in":"path","description":"Company UUID","schema":{"example":"4e18cf2e-b05f-4a8a-8e85-d0258e117597","type":"string"}}],"responses":{"200":{"description":"Pending payouts retrieved successfully"}},"security":[{"bearer":[]}],"summary":"Get pending payouts for company","tags":["Stripe Connect"]}},"/api/v1/connect/company/{companyId}/payouts":{"get":{"description":"Pulls the last N Stripe payouts on the connected account plus the payout schedule and YTD sum.","operationId":"ConnectController_getCompanyPayouts_v1","parameters":[{"name":"companyId","required":true,"in":"path","description":"Company UUID","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Recent payouts, schedule, and YTD total","tags":["Stripe Connect"]}},"/api/v1/stripe/connect/onboarding":{"post":{"description":"Creates Stripe Connect account and returns onboarding URL. CompanyId extracted from JWT.","operationId":"StripeConnectPublicController_startOnboarding_v1","parameters":[],"responses":{"200":{"description":"Onboarding URL created","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","example":"https://connect.stripe.com/setup/c/..."},"expiresAt":{"type":"string","format":"date-time"}}}}}},"401":{"description":"Not authenticated"},"403":{"description":"No company associated with user"},"409":{"description":"Company already has Stripe account"}},"security":[{"bearer":[]}],"summary":"Start Stripe Connect onboarding","tags":["Stripe Connect (Public)"]}},"/api/v1/stripe/connect/status":{"get":{"description":"Returns whether company has completed Stripe onboarding. CompanyId extracted from JWT.","operationId":"StripeConnectPublicController_getOnboardingStatus_v1","parameters":[],"responses":{"200":{"description":"Status retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"onboardingComplete":{"type":"boolean","example":false},"stripeAccountId":{"type":"string","example":"acct_1234567890","nullable":true},"status":{"type":"string","enum":["PENDING","ONBOARDING","ACTIVE","RESTRICTED","SUSPENDED"]},"chargesEnabled":{"type":"boolean"},"payoutsEnabled":{"type":"boolean"}}}}}},"401":{"description":"Not authenticated"},"403":{"description":"No company associated with user"},"404":{"description":"No Stripe account found for company"}},"security":[{"bearer":[]}],"summary":"Check Stripe Connect onboarding status","tags":["Stripe Connect (Public)"]}},"/api/v1/stripe/connect/refresh":{"post":{"description":"Generates new onboarding URL if the previous one expired (24hr expiry)","operationId":"StripeConnectPublicController_refreshOnboardingLink_v1","parameters":[],"responses":{"200":{"description":"New onboarding link created","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}}}}}},"400":{"description":"Onboarding already complete"},"404":{"description":"No Stripe account found"}},"security":[{"bearer":[]}],"summary":"Refresh expired onboarding link","tags":["Stripe Connect (Public)"]}},"/api/v1/payouts/trigger":{"post":{"operationId":"PayoutController_trigger_v1","parameters":[{"name":"X-Internal-Api-Key","in":"header","description":"Internal API key for admin access","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerPayoutDto"}}}},"responses":{"200":{"description":"Payout run result"}},"summary":"Trigger smart payout manually","tags":["Payouts"]}},"/api/v1/payouts/preview":{"get":{"operationId":"PayoutController_preview_v1","parameters":[{"name":"X-Internal-Api-Key","in":"header","description":"Internal API key for admin access","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Payout preview result"}},"summary":"Preview payout without executing (always dry run)","tags":["Payouts"]}},"/api/v1/payouts/status":{"get":{"operationId":"PayoutController_status_v1","parameters":[{"name":"X-Internal-Api-Key","in":"header","description":"Internal API key for admin access","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Payout config status"}},"summary":"Get current payout configuration","tags":["Payouts"]}}},"info":{"title":"PXL8 Payments API","description":"Production-grade payment processing service for PXL8 tokenized benefits platform.\n\n**Features:**\n- Stripe payment processing with fee calculation\n- Invoice generation and management\n- Event sourcing for regulatory compliance\n- Webhook handling for payment status updates\n\n**Authentication:** Use X-API-Key header for API access.","version":"1.0.0","contact":{"name":"PXL8 Support","url":"https://pxl8.io","email":"support@pxl8.io"}},"tags":[{"name":"payments","description":"Payment processing operations"},{"name":"invoices","description":"Invoice management"},{"name":"webhooks","description":"Stripe webhook handlers"},{"name":"health","description":"Service health checks"}],"servers":[{"url":"https://api.pxl8.io","description":"Production"},{"url":"http://localhost:3001","description":"Local Development"}],"components":{"securitySchemes":{"api-key":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Production API Key for documentation and endpoint access"}},"schemas":{"PayPaymentLinkDto":{"type":"object","properties":{"payerName":{"type":"string","description":"Payer full name","example":"John Smith"},"payerEmail":{"type":"string","description":"Payer email address","example":"john@sponsor.com"},"payerCompany":{"type":"string","description":"Payer company name","example":"Acme Corp"},"amountCents":{"type":"number","description":"Amount in cents (required if payment link has no fixed amount)","example":50000},"paymentMethodId":{"type":"string","description":"Stripe payment method ID","example":"pm_xxx"}},"required":["payerName","payerEmail"]},"CreatePaymentLinkDto":{"type":"object","properties":{"title":{"type":"string","description":"Title of the payment link","example":"Fight Night Sponsorship"},"description":{"type":"string","description":"Description for the payer","example":"Main event sponsorship package"},"amountCents":{"type":"number","description":"Fixed amount in cents. If null, payer enters their own amount.","example":50000},"currency":{"type":"string","description":"Currency (ISO 4217)","example":"USD","default":"USD"},"category":{"type":"string","description":"Payment category","enum":["SPONSORSHIP","MERCHANDISE","LICENSING","APPEARANCE","PURSE_PAY","CUSTOM"],"example":"SPONSORSHIP"},"campaignId":{"type":"string","description":"Campaign ID (PASS) associated with this payment link"},"expiresAt":{"type":"string","description":"Expiration date (ISO 8601)","example":"2026-04-01T00:00:00.000Z"}},"required":["title","category","campaignId"]},"UpdatePaymentLinkDto":{"type":"object","properties":{"title":{"type":"string","description":"Updated title"},"description":{"type":"string","description":"Updated description"},"expiresAt":{"type":"string","description":"Updated expiration date (ISO 8601)"}}},"CreatePaymentDto":{"type":"object","properties":{"companyId":{"type":"string","description":"Company ID (tenant) for this payment","example":"550e8400-e29b-41d4-a716-446655440000"},"customerId":{"type":"string","description":"Customer ID from authentication system (Clerk user ID)","example":"user_2abc123def456","minLength":1,"maxLength":255},"orderId":{"type":"string","description":"Order ID (token/NFT/trade ID)","example":"550e8400-e29b-41d4-a716-446655440000"},"orderType":{"type":"string","description":"Type of order being paid for","enum":["token_purchase","nft_purchase","marketplace_trade","marketplace_purchase","token_transfer"],"example":"token_purchase"},"amountCents":{"type":"number","description":"Payment amount in cents (to avoid floating-point errors)","example":10000,"minimum":50,"maximum":99999999},"currency":{"type":"string","description":"Currency code (ISO 4217)","example":"USD","minLength":3,"maxLength":3},"idempotencyKey":{"type":"string","description":"Idempotency key to prevent duplicate payments","example":"idem_1234567890abcdef"},"receiptEmail":{"type":"string","description":"Email address to send Stripe receipt to","example":"customer@example.com"},"description":{"type":"string","description":"Description shown on Stripe receipt (e.g., \"Campaign Name - 5 Tokens\")","example":"Gearbox Records - 2 GEAR Tokens"},"metadata":{"type":"object","description":"Additional metadata (vendor info, campaign details, etc.)","example":{"campaignId":"camp_123","vendorId":"vendor_456","tokenType":"membership"}},"destinationAccountId":{"type":"string","description":"Stripe Connected Account ID for destination charges. When provided, payment goes to this account.","example":"acct_1RyEfVGBeq2KOeAG"},"applicationFeeCents":{"type":"number","description":"Platform fee in cents (what PXL8 keeps). Only used with destinationAccountId.","example":0,"minimum":0}},"required":["companyId","customerId","orderType","amountCents","currency"]},"PaymentResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Payment ID","example":"550e8400-e29b-41d4-a716-446655440000"},"customerId":{"type":"string","description":"Customer ID","example":"user_2abc123def456"},"orderId":{"type":"string","description":"Order ID","example":"550e8400-e29b-41d4-a716-446655440001"},"orderType":{"type":"string","description":"Order type","example":"token_purchase"},"amount":{"type":"object","description":"Payment amount","example":{"cents":10000,"dollars":100,"currency":"USD","formatted":"$100.00"}},"feeCalculation":{"type":"object","description":"Fee breakdown","example":{"originalAmount":{"cents":10000,"dollars":100,"currency":"USD","formatted":"$100.00"},"platformFee":{"cents":300,"dollars":3,"currency":"USD","formatted":"$3.00"},"stripeFee":{"cents":320,"dollars":3.2,"currency":"USD","formatted":"$3.20"},"totalFees":{"cents":620,"dollars":6.2,"currency":"USD","formatted":"$6.20"},"netAmount":{"cents":9380,"dollars":93.8,"currency":"USD","formatted":"$93.80"}}},"status":{"type":"string","description":"Payment status","example":"PENDING","enum":["PENDING","REQUIRES_ACTION","PROCESSING","AUTHORIZED","SUCCEEDED","FAILED","CANCELLED","REFUNDED","PARTIALLY_REFUNDED","DISPUTED"]},"stripePaymentIntentId":{"type":"string","description":"Stripe Payment Intent ID","example":"pi_3abc123def456"},"stripeChargeId":{"type":"string","description":"Stripe Charge ID","example":"ch_3abc123def456"},"failureCode":{"type":"string","description":"Failure code (if payment failed)","example":"card_declined"},"failureMessage":{"type":"string","description":"Failure message (if payment failed)","example":"Your card was declined"},"metadata":{"type":"object","description":"Additional metadata","example":{"campaignId":"camp_123"}},"createdAt":{"format":"date-time","type":"string","description":"Payment creation timestamp","example":"2025-10-18T12:00:00.000Z"},"updatedAt":{"format":"date-time","type":"string","description":"Payment last update timestamp","example":"2025-10-18T12:05:00.000Z"}},"required":["id","customerId","orderType","amount","feeCalculation","status","createdAt","updatedAt"]},"CreateSubscriptionDto":{"type":"object","properties":{}},"OnboardConnectedAccountDto":{"type":"object","properties":{"companyId":{"type":"string","description":"Company ID to onboard","example":"550e8400-e29b-41d4-a716-446655440000"},"email":{"type":"string","description":"Business email address","example":"business@example.com"},"businessName":{"type":"string","description":"Business name","example":"Acme Corporation"},"businessUrl":{"type":"string","description":"Business website URL","example":"https://www.acme.com"},"returnUrl":{"type":"string","description":"Return URL after successful onboarding","example":"https://app.pxl8.io/settings/payments/success"},"refreshUrl":{"type":"string","description":"Refresh URL if onboarding is incomplete","example":"https://app.pxl8.io/settings/payments/refresh"},"accountType":{"type":"string","description":"Account type (express, standard, custom)","enum":["express","standard","custom"],"default":"express"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)","example":"US","default":"US"},"businessType":{"type":"string","description":"Business type for Stripe account. Use \"individual\" for sole traders/personal accounts, \"company\" for registered businesses (Ltd, LLC, etc.)","enum":["individual","company"],"example":"individual"}},"required":["companyId","email","returnUrl","refreshUrl"]},"OnboardingResponseDto":{"type":"object","properties":{"accountId":{"type":"string","description":"Connected account ID","example":"550e8400-e29b-41d4-a716-446655440000"},"stripeAccountId":{"type":"string","description":"Stripe account ID","example":"acct_1234567890"},"onboardingUrl":{"type":"string","description":"Onboarding URL (redirect user here)","example":"https://connect.stripe.com/setup/s/abc123..."},"expiresAt":{"type":"string","description":"Link expiration timestamp (ISO 8601)","example":"2025-10-23T12:00:00.000Z"},"status":{"type":"string","description":"Account status","enum":["PENDING","ONBOARDING","RESTRICTED","ACTIVE","REJECTED","SUSPENDED"],"example":"ONBOARDING"}},"required":["accountId","stripeAccountId","onboardingUrl","expiresAt","status"]},"SyncConnectedAccountDto":{"type":"object","properties":{"companyId":{"type":"string","description":"Company ID that owns this Stripe account","example":"550e8400-e29b-41d4-a716-446655440000"},"stripeAccountId":{"type":"string","description":"Stripe Connect account ID","example":"acct_1234567890"},"email":{"type":"string","description":"Email associated with the Stripe account","example":"athlete@example.com"},"country":{"type":"string","description":"Country code (ISO 3166-1 alpha-2)","example":"GB"},"businessType":{"type":"string","description":"Business type","enum":["individual","company"],"example":"individual"},"accountType":{"type":"string","description":"Stripe account type (express, custom, standard)","enum":["express","custom","standard"],"example":"express"}},"required":["companyId","stripeAccountId"]},"AccountStatusResponseDto":{"type":"object","properties":{"accountId":{"type":"string","description":"Connected account ID","example":"550e8400-e29b-41d4-a716-446655440000"},"stripeAccountId":{"type":"string","description":"Stripe account ID","example":"acct_1234567890"},"status":{"type":"string","description":"Account status","enum":["PENDING","ONBOARDING","RESTRICTED","ACTIVE","REJECTED","SUSPENDED"],"example":"ACTIVE"},"chargesEnabled":{"type":"boolean","description":"Can accept charges","example":true},"payoutsEnabled":{"type":"boolean","description":"Can receive payouts","example":true},"detailsSubmitted":{"type":"boolean","description":"Has submitted all required details","example":true},"businessName":{"type":"object","description":"Business name","example":"Acme Corporation"},"businessType":{"type":"object","description":"Business type","example":"company"},"country":{"type":"string","description":"Country code","example":"US"},"defaultCurrency":{"type":"string","description":"Default currency","example":"USD"},"verifiedAt":{"type":"object","description":"Account verification timestamp","example":"2025-10-22T15:30:00.000Z"},"createdAt":{"type":"string","description":"Account creation timestamp","example":"2025-10-20T10:00:00.000Z"},"updatedAt":{"type":"string","description":"Last update timestamp","example":"2025-10-22T16:45:00.000Z"}},"required":["accountId","stripeAccountId","status","chargesEnabled","payoutsEnabled","detailsSubmitted","country","defaultCurrency","verifiedAt","createdAt","updatedAt"]},"RefreshOnboardingLinkDto":{"type":"object","properties":{"returnUrl":{"type":"string","description":"Return URL after successful onboarding","example":"https://app.pxl8.io/settings/payments/success"},"refreshUrl":{"type":"string","description":"Refresh URL if onboarding is incomplete","example":"https://app.pxl8.io/settings/payments/refresh"}},"required":["returnUrl","refreshUrl"]},"OnboardingLinkResponseDto":{"type":"object","properties":{"onboardingUrl":{"type":"string","description":"New onboarding URL","example":"https://connect.stripe.com/setup/s/xyz789..."},"expiresAt":{"type":"string","description":"Link expiration timestamp (ISO 8601)","example":"2025-10-23T13:00:00.000Z"}},"required":["onboardingUrl","expiresAt"]},"TriggerPayoutDto":{"type":"object","properties":{}}}}}