diff --git a/apps/studio/components/interfaces/Billing/ChargeBreakdown.tsx b/apps/studio/components/interfaces/Billing/ChargeBreakdown.tsx new file mode 100644 index 00000000000..38ce73ec9b6 --- /dev/null +++ b/apps/studio/components/interfaces/Billing/ChargeBreakdown.tsx @@ -0,0 +1,58 @@ +import { cn } from 'ui' + +import { formatCurrency } from '@/lib/helpers' + +export interface ChargeBreakdownProps { + subtotal: number + total: number + tax?: { amount: number; percentage: number } + taxStatus?: 'calculated' | 'failed' | 'not_applicable' + isFetching: boolean +} + +export const ChargeBreakdown = ({ + subtotal, + total, + tax, + taxStatus, + isFetching, +}: ChargeBreakdownProps) => { + return ( +
+ {total !== subtotal && ( +
+
Subtotal
+
+ {formatCurrency(subtotal)} +
+
+ )} + + {taxStatus === 'calculated' && tax && tax.amount > 0 && ( +
+
Tax ({tax.percentage}%)
+
+ {formatCurrency(tax.amount)} +
+
+ )} + + {taxStatus === 'failed' && ( +
+
+ Tax could not be estimated and may be applied separately +
+
+ )} + +
+
Total due today
+
+ {formatCurrency(total)} +
+
+
+ ) +} diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/CreditTopUp.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/CreditTopUp.tsx index 6b8f1843e7e..ae7c6050744 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/CreditTopUp.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/CreditTopUp.tsx @@ -4,6 +4,7 @@ import { Elements } from '@stripe/react-stripe-js' import { loadStripe, PaymentIntentResult } from '@stripe/stripe-js' import { PermissionAction, SupportCategories } from '@supabase/shared-types/out/constants' import { useQueryClient } from '@tanstack/react-query' +import { useDebounce } from '@uidotdev/usehooks' import { AlertCircle, Info } from 'lucide-react' import { useTheme } from 'next-themes' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' @@ -32,12 +33,15 @@ import { z } from 'zod' import type { PaymentMethodElementRef } from '../../Billing/Payment/PaymentMethods/NewPaymentMethodElement' import PaymentMethodSelection from './Subscription/PaymentMethodSelection' +import { ChargeBreakdown } from '@/components/interfaces/Billing/ChargeBreakdown' import { getStripeElementsAppearanceOptions } from '@/components/interfaces/Billing/Payment/Payment.utils' import { PaymentConfirmation } from '@/components/interfaces/Billing/Payment/PaymentConfirmation' import { NO_PROJECT_MARKER } from '@/components/interfaces/Support/SupportForm.utils' import { SupportLink } from '@/components/interfaces/Support/SupportLink' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { useOrganizationCreditTopUpMutation } from '@/data/organizations/organization-credit-top-up-mutation' +import { useCreditTopUpPreview } from '@/data/organizations/organization-credit-top-up-preview' +import type { CustomerAddress, CustomerTaxId } from '@/data/organizations/types' import { subscriptionKeys } from '@/data/subscriptions/keys' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { STRIPE_PUBLIC_KEY } from '@/lib/constants' @@ -45,12 +49,14 @@ import { STRIPE_PUBLIC_KEY } from '@/lib/constants' const stripePromise = loadStripe(STRIPE_PUBLIC_KEY) const FORM_ID = 'credit-top-up' +const MIN_TOP_UP_AMOUNT = 300 +const MAX_TOP_UP_AMOUNT = 2000 const FormSchema = z.object({ amount: z.coerce .number() - .gte(300, 'Amount must be between $300 - $2000.') - .lte(2000, 'Amount must be between $300 - $2000.') + .gte(MIN_TOP_UP_AMOUNT, `Amount must be between $${MIN_TOP_UP_AMOUNT} - $${MAX_TOP_UP_AMOUNT}.`) + .lte(MAX_TOP_UP_AMOUNT, `Amount must be between $${MIN_TOP_UP_AMOUNT} - $${MAX_TOP_UP_AMOUNT}.`) .int('Amount must be a whole number.'), paymentMethod: z.string(), }) @@ -87,6 +93,52 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { const [topUpModalVisible, setTopUpModalVisible] = useState(false) const [useAsDefaultBillingAddress, setUseAsDefaultBillingAddress] = useState(true) const [paymentConfirmationLoading, setPaymentConfirmationLoading] = useState(false) + + const [latestAddress, setLatestAddress] = useState() + const [latestTaxId, setLatestTaxId] = useState() + + const billingAddress = useAsDefaultBillingAddress ? latestAddress : undefined + const billingTaxId = useAsDefaultBillingAddress ? latestTaxId : null + const debouncedAddress = useDebounce(billingAddress, 1000) + const debouncedTaxId = useDebounce(billingTaxId, 1000) + + const watchedAmount = form.watch('amount') + const debouncedAmount = useDebounce(watchedAmount, 1000) + const parsedAmount = Number(debouncedAmount) + const validAmount = + !Number.isNaN(parsedAmount) && + Number.isInteger(parsedAmount) && + parsedAmount >= MIN_TOP_UP_AMOUNT && + parsedAmount <= MAX_TOP_UP_AMOUNT + ? parsedAmount + : undefined + + const isPreviewStale = + watchedAmount !== debouncedAmount || + billingAddress !== debouncedAddress || + billingTaxId !== debouncedTaxId + + const handleAddressChange = useCallback((address: CustomerAddress) => { + setLatestAddress(address) + }, []) + + const handleTaxIdChange = useCallback((taxId: CustomerTaxId | null) => { + setLatestTaxId(taxId) + }, []) + + const { + data: creditPreview, + isFetching: creditPreviewIsFetching, + isSuccess: creditPreviewInitialized, + } = useCreditTopUpPreview( + { + slug, + amount: validAmount, + address: debouncedAddress, + taxId: debouncedTaxId ?? undefined, + }, + { enabled: topUpModalVisible && !!validAmount } + ) const [captchaToken, setCaptchaToken] = useState(null) const [captchaRef, setCaptchaRef] = useState(null) @@ -175,6 +227,8 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { setCaptchaRef(null) setPaymentIntentConfirmation(undefined) setPaymentIntentSecret('') + setLatestAddress(undefined) + setLatestTaxId(null) } } @@ -288,6 +342,8 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { readOnly={executingTopUp || paymentConfirmationLoading} useAsDefaultBillingAddress={useAsDefaultBillingAddress} onUseAsDefaultBillingAddressChange={setUseAsDefaultBillingAddress} + onAddressChange={handleAddressChange} + onTaxIdChange={handleTaxIdChange} /> )} /> @@ -324,6 +380,25 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { )} + + {creditPreviewInitialized && !!validAmount && ( +
+ +
+ )} {!paymentIntentConfirmation?.paymentIntent && ( @@ -332,6 +407,7 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { htmlType="submit" type="primary" loading={executingTopUp || paymentConfirmationLoading} + disabled={isPreviewStale || creditPreviewIsFetching} > Top Up diff --git a/apps/studio/data/organizations/keys.ts b/apps/studio/data/organizations/keys.ts index d41cec69d64..62d4ff7a1df 100644 --- a/apps/studio/data/organizations/keys.ts +++ b/apps/studio/data/organizations/keys.ts @@ -32,4 +32,8 @@ export const organizationKeys = { ) => ['organizations', 'creation-preview', tier, params] as const, previewCreditCode: (slug: string | undefined, code: string) => ['organizations', slug, 'preview-credit-code', code] as const, + creditTopUpPreview: ( + slug: string | undefined, + params?: { amount?: number; address?: Record; taxId?: Record } + ) => ['organizations', slug, 'credits', 'preview', params] as const, } diff --git a/apps/studio/data/organizations/organization-credit-top-up-preview.ts b/apps/studio/data/organizations/organization-credit-top-up-preview.ts new file mode 100644 index 00000000000..4cea861e5be --- /dev/null +++ b/apps/studio/data/organizations/organization-credit-top-up-preview.ts @@ -0,0 +1,57 @@ +import { keepPreviousData, useQuery } from '@tanstack/react-query' + +import { organizationKeys } from './keys' +import type { CustomerAddress, CustomerTaxId } from './types' +import { handleError, post } from '@/data/fetchers' +import type { ResponseError, UseCustomQueryOptions } from '@/types' + +export type CreditTopUpPreviewVariables = { + slug?: string + amount?: number + address?: CustomerAddress + taxId?: CustomerTaxId +} + +export async function previewCreditTopUp({ + slug, + amount, + address, + taxId, +}: CreditTopUpPreviewVariables) { + if (!slug) throw new Error('slug is required') + if (!amount) throw new Error('amount is required') + + const { data, error } = await post(`/platform/organizations/{slug}/billing/credits/preview`, { + params: { path: { slug } }, + body: { + amount, + ...(address && { address }), + ...(taxId && { tax_id: taxId }), + }, + }) + + if (error) handleError(error) + + return data +} + +export type CreditTopUpPreviewData = Awaited> + +export const useCreditTopUpPreview = ( + { slug, amount, address, taxId }: CreditTopUpPreviewVariables, + { + enabled = true, + ...options + }: UseCustomQueryOptions = {} +) => + useQuery({ + queryKey: organizationKeys.creditTopUpPreview(slug, { + amount, + address: address as Record | undefined, + taxId: taxId as Record | undefined, + }), + queryFn: () => previewCreditTopUp({ slug, amount, address, taxId }), + enabled: enabled && typeof slug !== 'undefined' && typeof amount !== 'undefined', + placeholderData: keepPreviousData, + ...options, + }) diff --git a/packages/api-types/types/api.d.ts b/packages/api-types/types/api.d.ts index 4f84a15c31c..3163eb5c0db 100644 --- a/packages/api-types/types/api.d.ts +++ b/packages/api-types/types/api.d.ts @@ -5625,6 +5625,27 @@ export interface operations { } content?: never } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } 'v1-revoke-token': { @@ -5688,6 +5709,27 @@ export interface operations { 'application/json': components['schemas']['OrganizationResponseV1'][] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Unexpected error listing organizations */ 500: { headers: { @@ -5718,6 +5760,27 @@ export interface operations { 'application/json': components['schemas']['OrganizationResponseV1'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Unexpected error creating an organization */ 500: { headers: { @@ -5954,6 +6017,27 @@ export interface operations { 'application/json': components['schemas']['OrganizationProjectsResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to retrieve projects */ 500: { headers: { @@ -5999,6 +6083,27 @@ export interface operations { 'application/json': components['schemas']['V1ProjectWithDatabaseResponse'][] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } 'v1-create-a-project': { @@ -6022,6 +6127,27 @@ export interface operations { 'application/json': components['schemas']['V1ProjectResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } 'v1-get-project': { @@ -7252,27 +7378,6 @@ export interface operations { 'application/json': components['schemas']['BranchResponse'][] } } - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Forbidden action */ - 403: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Rate limit exceeded */ - 429: { - headers: { - [name: string]: unknown - } - content?: never - } /** @description Failed to retrieve database branches */ 500: { headers: { @@ -7306,27 +7411,6 @@ export interface operations { 'application/json': components['schemas']['BranchResponse'] } } - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Forbidden action */ - 403: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Rate limit exceeded */ - 429: { - headers: { - [name: string]: unknown - } - content?: never - } /** @description Failed to create database branch */ 500: { headers: { @@ -7405,27 +7489,6 @@ export interface operations { 'application/json': components['schemas']['BranchResponse'] } } - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Forbidden action */ - 403: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Rate limit exceeded */ - 429: { - headers: { - [name: string]: unknown - } - content?: never - } /** @description Failed to fetch database branch */ 500: { headers: { @@ -12898,6 +12961,27 @@ export interface operations { 'application/json': components['schemas']['SnippetList'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to list user's SQL snippets */ 500: { headers: { @@ -12926,6 +13010,27 @@ export interface operations { 'application/json': components['schemas']['SnippetResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to retrieve SQL snippet */ 500: { headers: { diff --git a/packages/api-types/types/platform.d.ts b/packages/api-types/types/platform.d.ts index 8961dec7e87..d2501aff841 100644 --- a/packages/api-types/types/platform.d.ts +++ b/packages/api-types/types/platform.d.ts @@ -1077,6 +1077,23 @@ export interface paths { patch?: never trace?: never } + '/platform/organizations/{slug}/billing/credits/preview': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Preview for credit top-up */ + post: operations['OrgCreditsController_previewTopUp'] + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/platform/organizations/{slug}/billing/credits/redeem': { parameters: { query?: never @@ -1888,6 +1905,23 @@ export interface paths { patch?: never trace?: never } + '/platform/organizations/onboarding-survey': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Submit onboarding survey for a newly created organization */ + post: operations['OrganizationsController_handleOnboardingSurvey'] + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/platform/organizations/preview-creation': { parameters: { query?: never @@ -4988,11 +5022,24 @@ export interface components { workdir: string } CreateInvitationBody: { - email: string + /** + * Format: email + * @deprecated + */ + email?: string + emails?: string[] require_sso?: boolean role_id: number role_scoped_projects?: string[] } + CreateInvitationResponse: { + failed: { + /** Format: email */ + email: string + error: string + }[] + succeeded: string[] + } | null CreateNamespaceBody: { namespace: string } @@ -5222,6 +5269,8 @@ export interface components { | 'action_runs_read' | 'action_runs_write' | 'advisors_read' + | 'analytics_config_read' + | 'analytics_config_write' | 'analytics_logs_read' | 'analytics_usage_read' | 'api_gateway_keys_read' @@ -5685,6 +5734,8 @@ export interface components { | 'action_runs_read' | 'action_runs_write' | 'advisors_read' + | 'analytics_config_read' + | 'analytics_config_write' | 'analytics_logs_read' | 'analytics_usage_read' | 'api_gateway_keys_read' @@ -7631,6 +7682,13 @@ export interface components { )[] website: string } + OnboardingSurveyBody: { + building?: string + heard_from?: string + kind?: string + size?: string + slug: string + } OrganizationProjectsResponse: { pagination: { /** @description Total number of projects. Use this to calculate the total number of pages. */ @@ -8271,6 +8329,36 @@ export interface components { name: string schema: string } + PreviewCreditTopUpRequest: { + address?: { + city?: string | null + country: string + line1: string + line2?: string | null + postal_code?: string | null + state?: string | null + } + amount: number + tax_id?: { + country: string + type: string + value: string + } + } + PreviewCreditTopUpResponse: { + amount: number + currency: string + tax: { + currency: string + tax_amount: number + tax_rate_percentage: number + total_amount_excluding_tax: number + total_amount_including_tax: number + } | null + /** @enum {string} */ + tax_status: 'calculated' | 'not_applicable' | 'failed' + total: number + } PreviewOrganizationCreationBody: { address?: { city?: string | null @@ -8648,7 +8736,7 @@ export interface components { | '48xlarge_optimized_cpu' | '48xlarge_high_memory' inserted_at: string - integration_source?: string | null + integration_source: string | null is_branch_enabled: boolean is_physical_backups_enabled: boolean lastDatabaseResizeAt?: string @@ -10591,6 +10679,8 @@ export interface components { | 'action_runs_read' | 'action_runs_write' | 'advisors_read' + | 'analytics_config_read' + | 'analytics_config_write' | 'analytics_logs_read' | 'analytics_usage_read' | 'api_gateway_keys_read' @@ -14002,6 +14092,27 @@ export interface operations { 'application/json': components['schemas']['ListPlatformAppsResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppsController_createPlatformApp: { @@ -14028,6 +14139,27 @@ export interface operations { 'application/json': components['schemas']['CreatePlatformAppResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppsController_getPlatformApp: { @@ -14051,6 +14183,27 @@ export interface operations { 'application/json': components['schemas']['GetPlatformAppResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppsController_deletePlatformApp: { @@ -14072,6 +14225,27 @@ export interface operations { } content?: never } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppsController_updatePlatformApp: { @@ -14099,6 +14273,27 @@ export interface operations { 'application/json': components['schemas']['GetPlatformAppResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppSigningKeysController_listPlatformAppSigningKeys: { @@ -14122,6 +14317,27 @@ export interface operations { 'application/json': components['schemas']['ListPlatformAppSigningKeysResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppSigningKeysController_createPlatformAppSigningKey: { @@ -14145,6 +14361,27 @@ export interface operations { 'application/json': components['schemas']['CreatePlatformAppSigningKeyResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppSigningKeysController_deletePlatformAppSigningKey: { @@ -14167,6 +14404,27 @@ export interface operations { } content?: never } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppInstallationsController_listPlatformAppInstallations: { @@ -14189,6 +14447,27 @@ export interface operations { 'application/json': components['schemas']['ListPlatformAppInstallationsResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppInstallationsController_installPlatformApp: { @@ -14215,6 +14494,27 @@ export interface operations { 'application/json': components['schemas']['InstallPlatformAppResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppInstallationsController_getPlatformAppInstallation: { @@ -14238,6 +14538,27 @@ export interface operations { 'application/json': components['schemas']['GetPlatformAppInstallationResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppInstallationsController_uninstallPlatformInstallation: { @@ -14259,6 +14580,27 @@ export interface operations { } content?: never } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } PlatformAppInstallationsController_updatePlatformAppInstallation: { @@ -14282,6 +14624,27 @@ export interface operations { 'application/json': components['schemas']['UpdatePlatformAppInstallationResponse'] } } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } } } OrgAuditLogsController_getAuditLogs: { @@ -14393,6 +14756,54 @@ export interface operations { } } } + OrgCreditsController_previewTopUp: { + parameters: { + query?: never + header?: never + path: { + /** @description Organization slug */ + slug: string + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['PreviewCreditTopUpRequest'] + } + } + responses: { + /** @description Credit top-up preview with tax calculation. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['PreviewCreditTopUpResponse'] + } + } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden action */ + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content?: never + } + } + } OrgCreditsController_redeemCode: { parameters: { query?: never @@ -15910,11 +16321,14 @@ export interface operations { } } responses: { + /** @description Batch invitations result when using "emails" field. null/no body when using legacy "email" field. */ 201: { headers: { [name: string]: unknown } - content?: never + content: { + 'application/json': components['schemas']['CreateInvitationResponse'] + } } /** @description Unauthorized */ 401: { @@ -17462,6 +17876,27 @@ export interface operations { } } } + OrganizationsController_handleOnboardingSurvey: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['OnboardingSurveyBody'] + } + } + responses: { + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + } + } OrganizationsController_previewOrganizationCreation: { parameters: { query?: never