mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
feat: show tax preview for credit top-ups (#45005)
## Summary
- Add tax preview to the credit top-up flow by integrating the new `POST
/organizations/{slug}/billing/credits/preview` endpoint
- Show a price breakdown (credit amount, tax line item, total) in the
top-up dialog before the user confirms payment
- Handle all three `tax_status` states: show tax when `calculated`, hide
the line when `not_applicable`, show an inline warning when `failed`
## Behavior
- Preview fires as soon as a valid amount (300–2000) is entered; address
and tax ID are optional and refine the estimate
- Amount and address changes are debounced at 1s to avoid excessive API
calls
## Test plan
- [ ] Open credit top-up dialog - verify preview appears with default
$300 amount
- [ ] Change amount within 300–2000 - verify preview updates after
debounce
- [ ] Enter amount outside range (e.g. 100 or 3000) - verify preview
hides and validation error shows
- [ ] Add a new payment method with a billing address in a taxed region
- verify tax line item appears
- [ ] Add a new payment method with no tax jurisdiction - verify no tax
line, just total
- [ ] Complete a top-up - verify the charge goes through and dialog
closes
<img width="571" height="551" alt="image"
src="https://github.com/user-attachments/assets/d3357752-f913-4a4a-b84a-f78e2f457c7b"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Credit top-up preview with charge breakdown (credit, tax or tax note,
total).
* Onboarding survey endpoint and ISO 27001 certificate availability for
organizations.
* **Updates**
* Improved top-up UX: debounced address/tax inputs, enforced min/max
amount validation, preview-driven form state, and submit disabled while
preview is loading/stale.
* API docs wording changed to “temporary access configuration.”
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
03f6083b1b
commit
e6ca2766ff
6 files changed
+803
-68
No files matched your search
@@ -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 (
|
||||
<div
|
||||
className={cn('text-foreground-light text-sm transition-opacity', isFetching && 'opacity-50')}
|
||||
>
|
||||
{total !== subtotal && (
|
||||
<div className="flex items-center justify-between gap-2 border-b border-muted text-sm">
|
||||
<div className="py-2">Subtotal</div>
|
||||
<div className="py-2 text-right tabular-nums" translate="no">
|
||||
{formatCurrency(subtotal)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{taxStatus === 'calculated' && tax && tax.amount > 0 && (
|
||||
<div className="flex items-center justify-between gap-2 border-b border-muted text-sm">
|
||||
<div className="py-2">Tax ({tax.percentage}%)</div>
|
||||
<div className="py-2 text-right tabular-nums" translate="no">
|
||||
{formatCurrency(tax.amount)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{taxStatus === 'failed' && (
|
||||
<div className="flex items-center justify-between gap-2 border-b border-muted text-sm">
|
||||
<div className="py-2 text-foreground-lighter">
|
||||
Tax could not be estimated and may be applied separately
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between gap-2 text-foreground text-base">
|
||||
<div className="py-2">Total due today</div>
|
||||
<div className="py-2 text-right tabular-nums" translate="no">
|
||||
{formatCurrency(total)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -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<CustomerAddress>()
|
||||
const [latestTaxId, setLatestTaxId] = useState<CustomerTaxId | null>()
|
||||
|
||||
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<string | null>(null)
|
||||
const [captchaRef, setCaptchaRef] = useState<HCaptcha | null>(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 }) => {
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
|
||||
{creditPreviewInitialized && !!validAmount && (
|
||||
<div className="mt-4">
|
||||
<ChargeBreakdown
|
||||
subtotal={creditPreview.amount}
|
||||
total={creditPreview.total}
|
||||
tax={
|
||||
creditPreview.tax
|
||||
? {
|
||||
amount: creditPreview.tax.tax_amount,
|
||||
percentage: creditPreview.tax.tax_rate_percentage,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
taxStatus={creditPreview.tax_status}
|
||||
isFetching={creditPreviewIsFetching}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</DialogSection>
|
||||
|
||||
{!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
|
||||
</Button>
|
||||
|
||||
@@ -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<string, unknown>; taxId?: Record<string, unknown> }
|
||||
) => ['organizations', slug, 'credits', 'preview', params] as const,
|
||||
}
|
||||
@@ -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<ReturnType<typeof previewCreditTopUp>>
|
||||
|
||||
export const useCreditTopUpPreview = <TData = CreditTopUpPreviewData>(
|
||||
{ slug, amount, address, taxId }: CreditTopUpPreviewVariables,
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
}: UseCustomQueryOptions<CreditTopUpPreviewData, ResponseError, TData> = {}
|
||||
) =>
|
||||
useQuery<CreditTopUpPreviewData, ResponseError, TData>({
|
||||
queryKey: organizationKeys.creditTopUpPreview(slug, {
|
||||
amount,
|
||||
address: address as Record<string, unknown> | undefined,
|
||||
taxId: taxId as Record<string, unknown> | undefined,
|
||||
}),
|
||||
queryFn: () => previewCreditTopUp({ slug, amount, address, taxId }),
|
||||
enabled: enabled && typeof slug !== 'undefined' && typeof amount !== 'undefined',
|
||||
placeholderData: keepPreviousData,
|
||||
...options,
|
||||
})
|
||||
Vendored
+168
-63
@@ -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: {
|
||||
|
||||
+438
-3
@@ -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
|
||||
|
||||
Reference in new issue
Block a user