Skip to content

Billing endpoints

59 endpoints at https://api.vaif.studio, generated from the API's OpenAPI document. The REST API page covers authentication, and All endpoints lists every group.

POST /billing/checkout

Request body (application/json, required)
{
  orgId: string (uuid);
  plan: "entry" | "entry_plus" | "starter" | "pro" | "agency" | "studio_plus";
  interval?: "monthly" | "yearly";
  promoCode?: string;
  successUrl?: string (uri);
  cancelUrl?: string (uri);
}
Response 200 (application/json)
{
  url: string | null;
}

Errors: 400, 401, 403, 503, with the error body.

GET /billing/checkout/verify/{sessionId}

ParameterInTypeRequired
sessionIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
  type: string;
  status: null | "complete" | "expired" | "open";
  paymentStatus: "no_payment_required" | "paid" | "unpaid";
  creditAmount: number;
  packageId: string;
  fulfilled: boolean;
} | {
  ok: boolean;
  type: string;
  status: null | "complete" | "expired" | "open";
  paymentStatus: "no_payment_required" | "paid" | "unpaid";
  fulfilled: boolean;
} | {
  ok: boolean;
  status: null | "complete" | "expired" | "open";
  paymentStatus: "no_payment_required" | "paid" | "unpaid";
  plan: "agency" | "entry" | "entry_plus" | "pro" | "starter" | "studio_plus";
  interval: string;
  subscriptionId: null | string;
  subscriptionStatus: null | "active" | "canceled" | "incomplete" | "incomplete_expired" | "past_due" | "paused" | "trialing" | "unpaid";
  currentPeriodEnd: null | string (date-time);
  taxStatus: null | "complete" | "failed" | "requires_location_inputs";
  taxWarning: null | {
    code: "TaxCalculationFailed" | "TaxLocationRequired";
    message: string;
  };
  founder: null | {
    status: string;
    founderNumber?: null | number;
  };
}

POST /billing/hosting/checkout

Request body (application/json, required)
{
  orgId: string (uuid);
  interval?: "monthly" | "yearly";
  quantity?: number;
  successUrl?: string (uri);
  cancelUrl?: string (uri);
}
Response 200 (application/json)
{
  url: string | null;
}

Errors: 400, 401, 403, with the error body.

GET /billing/org/{orgId}/hosting

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
Response 200 (application/json)
{
  packs: {
    quantity: number;
    interval: string;
    status: string;
    currentPeriodEnd: string | null;
  } | null;
  allowance: {
    maxServices: number;
    alwaysOnLimit: number;
    alwaysOnInUse: number;
    servicesInUse: number;
    alwaysOnAllowed: boolean;
  };
  egress: {
    usedBytes: number;
    includedBytes: number;
  };
  pack: {
    monthlyPriceCents: number;
    yearlyPriceCents: number;
    servicesPerPack: number;
    egressGiBPerPack: number;
    maxQuantityPerCheckout: number;
  };
}

Errors: 401, 403, with the error body.

GET /billing/org/{orgId}/infrastructure

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
Response 200 (application/json)
{
  databases: {
    id: string;
    name: string;
    projectId: string;
    projectName: string;
    kind: "primary" | "replica";
    sizeLabel: string;
    status: "running" | "provisioning" | "resizing" | "stopped" | "failed";
    monthlyCents: number;
  }[];
  totalMonthlyCents: number;
}

Errors: 401, 403, with the error body.

POST /billing/portal

Request body (application/json, required)
{
  orgId: string (uuid);
  returnUrl?: string (uri);
}
Response 200 (application/json)
{
  url: string | null;
}

Errors: 400, 401, 403, 404, with the error body.

POST /billing/org/{orgId}/portal

ParameterInTypeRequired
orgIdpathstringYes
Request body (application/json)
{
  returnUrl?: string;
}
Response 200 (application/json)
{
  url: string;
} | {
  ok: boolean;
  url: string;
}

GET /billing/org/{orgId}/summary

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
  orgId: string;
  managed: boolean;
  plan: string;
  status: string;
  paymentStatus: any;
  paymentFailedAt: any;
  billingInterval: any;
  currentPeriodEnd: null | string (date-time);
  cancelAtPeriodEnd: boolean;
  stripeCustomerIdPresent: boolean;
  stripeSubscriptionIdPresent: boolean;
  pauseStartedAt: any;
  pauseExpiresAt: any;
  pauseCooldownEndsAt: any;
  pauseCount: any;
}

GET /billing/org/{orgId}/subscription

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
Response 200 (application/json)
{
  ok: true;
  plan: string;
  interval: "monthly" | "yearly";
  priceCents: number | null;
  currency: string;
  founder: boolean;
  managed: boolean;
  period: {
    start: string;
    end: string;
  } | null;
  state: "free" | "active" | "trialing" | "canceling" | "past_due" | "frozen" | "paused" | "retention" | "cooldown" | "canceled";
  trialEndsAt: string | null;
  cancelAt: string | null;
  pausedSince: string | null;
  freePauseEndsAt: string | null;
  retentionPriceCents: number | null;
  cooldownEndsAt: string | null;
  payBy: string | null;
  upcomingInvoices: {
    subscription: "plan" | "hosting";
    date: string;
    amountCents: number;
    currency: string;
    lines: {
      kind: "plan" | "hosting_pack" | "addon" | "retention" | "other";
      label: string;
      quantity: number;
      amountCents: number;
    }[];
  }[] | null;
  unpaidInvoice: {
    id: string;
    number: string | null;
    amountCents: number;
    dueDate: string | null;
    nextRetryAt: string | null;
    hostedInvoiceUrl: string | null;
  } | null;
  paymentMethod: {
    brand: string;
    last4: string;
    expMonth: number;
    expYear: number;
  } | null;
  lastPaymentError: {
    at: string | null;
    message: string;
  } | null;
  billingEmail: string | null;
  pausePolicy: {
    freeDays: number;
    retentionPercent: number;
    cooldownDays: number;
  };
  partial: ("period" | "upcomingInvoices" | "unpaidInvoice" | "lastPaymentError" | "paymentMethod" | "billingEmail")[];
}

Errors: 401, 403, with the error body.

POST /billing/webhook

Response 200 (application/json)
{
  received: boolean;
}

GET /billing/org/{orgId}/usage-alerts

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
  plan: "agency" | "enterprise" | "free" | "pro" | "starter" | "studio_plus";
  alerts: {
    type: string;
    severity: "critical" | "warning";
    message: string;
    usage: number;
    limit: number;
  }[];
  usage: {
    aiCredits: {
      used: number;
      limit: number;
      remaining: number;
      percentUsed: number;
    };
  };
}

POST /billing/org/{orgId}/usage-alerts

ParameterInTypeRequired
orgIdpathstringYes
Request body (application/json, required)
{
  metric: "verament_credits" | "api_requests" | "function_invocations" | "file_storage" | "hosting_egress" | "database_rows" | "mau" | "realtime_peak" | "ai_credits" | "storage" | "storage_bytes" | "bandwidth" | "realtime_connections";
  threshold: number;
  notifyEmail?: boolean;
}
Response 200 (application/json)
{
  ok: true;
  alertId: string;
}

Errors: 401, 403, with the error body.

GET /billing/org/{orgId}/usage-alerts/configured

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: true;
  alerts: {
    id: string;
    metric: string;
    threshold: number;
    enabled: boolean;
    emailOn: boolean;
    notifyEmail: boolean;
    limitNow: number | null;
    createdAt: string;
  }[];
  meters: {
    metric: "verament_credits" | "api_requests" | "function_invocations" | "file_storage" | "hosting_egress" | "database_rows" | "mau" | "realtime_peak";
    name: string;
    unit: string;
    limitNow: number | null;
  }[];
}

Errors: 401, 403, with the error body.

PATCH /billing/org/{orgId}/usage-alerts/{alertId}

ParameterInTypeRequired
orgIdpathstringYes
alertIdpathstringYes
Request body (application/json, required)
{
  enabled?: boolean;
  threshold?: number;
  notifyEmail?: boolean;
}
Response 200 (application/json)
{
  ok: true;
}

Errors: 401, 403, with the error body.

DELETE /billing/org/{orgId}/usage-alerts/{alertId}

ParameterInTypeRequired
orgIdpathstringYes
alertIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
}

GET /billing/org/{orgId}/usage-alerts/history

ParameterInTypeRequired
limitquerynumberNo
beforequerystringNo
orgIdpathstringYes
Response 200 (application/json)
{
  ok: true;
  history: {
    id: string;
    alertId: string;
    firedAt: string;
    metric: string;
    threshold: number;
    usage: number;
    limit: number;
    emailStatus: "sent" | "not_sent" | "no_recipients";
    meterName: string | null;
    unit: string | null;
    currentUsage: number;
    limitValue: number;
    notificationSent: boolean;
    triggeredAt: string;
  }[];
  nextCursor: string | null;
}

Errors: 400, 401, 403, with the error body.

GET /billing/plans

Response 200 (application/json)
{
  ok: boolean;
  plans: {
    name: string;
    displayName: string;
    description: string;
    priceCents: null | number;
    priceYearlyCents: null | number;
    stripePriceId: null | string;
    stripeYearlyPriceId: null | string;
    popular: boolean;
    features: string[];
    limits: {
      projectsMax: number;
      environmentsMax: number;
      teamMembersMax: number;
      mauIncluded: number;
      mauOveragePrice: number;
      mauHardLimit: boolean;
      apiRequestsMonthly: number;
      storageGb: number;
      databaseRowsMax: number;
      realtimeEnabled: boolean;
      realtimeMessagesMonthly: number;
      realtimeConnectionsMax: number;
      functionsEnabled: boolean;
      functionInvocationsMonthly: number;
      functionConcurrency: number;
      aiEnabled: boolean;
      aiPromptsEnabled: boolean;
      aiCodeExportEnabled: boolean;
      aiFunctionGenerationEnabled: boolean;
      aiEndpointGenerationEnabled: boolean;
      aiDocsGenerationEnabled: boolean;
      aiCreditsMonthly: number;
      aiMaxRequestTokens: number;
      aiMaxOutputTokens: number;
      aiOveragesEnabled: boolean;
      aiOverageMultiplier: number;
      aiSpendCapEnabled: boolean;
      aiMaxConcurrentRequests: number;
      aiCreditRolloverCap: number;
      aiEnterpriseRoutingEnabled: boolean;
      aiBringYourOwnKeysEnabled: boolean;
      aiModelTier: "enterprise" | "free" | "paid";
      aiMaxContextTokens: number;
      aiMaxMonthlyTokens: number;
      aiWorkspaceSessionsMax: number;
      aiBackendGenerationEnabled: boolean;
      aiClarificationEnabled: boolean;
      aiReasoningDisplayEnabled: boolean;
      teamRolesEnabled: boolean;
      auditLogsEnabled: boolean;
      auditLogRetentionDays: number;
      ssoEnabled: boolean;
      samlEnabled: boolean;
      scimEnabled: boolean;
      webhooksEnabled: boolean;
      webhookDlqEnabled: boolean;
      ciTokensEnabled: boolean;
      customDomainsEnabled: boolean;
      customDomainsMax: number;
      templatesEnabled: boolean;
      deploymentsEnabled: boolean;
      branchDeploymentsEnabled: boolean;
      branchDeploymentsMax: number;
      observabilityHubEnabled: boolean;
      advancedMetricsEnabled: boolean;
      regionalRoutingEnabled: boolean;
      dedicatedDbEnabled: boolean;
      dedicatedDbTiers: any[];
      dedicatedDbCustomSizing: boolean;
      dedicatedDbMaxStorageGb: number;
      dedicatedStackEnabled: boolean;
      liveDeploymentsEnabled: boolean;
      maxLiveDeployments: number;
      liveDeploymentMemoryOptions: string[];
      liveDeploymentCpuOptions: string[];
      liveDeploymentMaxInstances: number;
      includedAlwaysOn: number;
      sitesMax: number;
      staticSitesOnly: boolean;
      dailyBuildsMax: number;
      dedicatedDbFreeTier: null | "2xl" | "3xl" | "4xl" | "custom" | "high-memory-large" | "high-memory-medium" | "high-memory-xl" | "large" | "medium" | "micro" | "small" | "xl";
      dedicatedDbFreeCount: number;
      dedicatedDbIncludedTier: null | "2xl" | "3xl" | "4xl" | "custom" | "high-memory-large" | "high-memory-medium" | "high-memory-xl" | "large" | "medium" | "micro" | "small" | "xl";
      haIncluded: boolean;
      readReplicasPerDatabase: number;
      oauthProvidersMax: number;
      extensionsMax: number;
      wrappersMax: number;
      alertRulesMax: number;
      slaSupportEnabled: boolean;
      dedicatedSupportEnabled: boolean;
    };
    monthlyPriceCents: null | number;
    annualPriceCents: null | number;
    annualMonthlyCents: null | number;
    annualSavingsCents: null | number;
  }[];
}

GET /billing/credits/packages

Response 200 (application/json)
{
  ok: boolean;
  packages: {
    id: string;
    amount: number;
    priceInCents: number;
    label: string;
    description: string;
    bonus: string;
  } | {
    id: string;
    amount: number;
    priceInCents: number;
    label: string;
    description: string;
    bonus?: any;
  }[];
}

GET /billing/credits/rates

Response 200 (application/json)
{
  tier: "lite" | "standard" | "max";
  creditsPer1kTokens: number;
  availableOn: string[];
}[]

POST /billing/org/{orgId}/change-plan

ParameterInTypeRequired
orgIdpathstringYes
Request body (application/json, required)
{
  plan: "entry" | "entry_plus" | "starter" | "pro" | "agency" | "studio_plus";
  interval?: "monthly" | "yearly";
  prorationDate?: number;
}
Response 200 (application/json)
{
  ok: true;
  newPlan: string;
  newInterval: string;
  effectiveDate: string | string (date-time);
  status: string;
}

Errors: 400, 401, 402, 403, 409, 500, 503, with the error body.

POST /billing/org/{orgId}/change-plan/preview

ParameterInTypeRequired
orgIdpathstringYes
Request body (application/json, required)
{
  plan: "entry" | "entry_plus" | "starter" | "pro" | "agency" | "studio_plus";
  interval?: "monthly" | "yearly";
}
Response 200 (application/json)
{
  ok: true;
  plan: string;
  interval: "monthly" | "yearly";
  currentInterval: "monthly" | "yearly";
  intervalChange: boolean;
  chargedNow: boolean;
  currency: string;
  amountDue: number;
  changeAmount: number;
  invoiceDate: string;
  prorationCredit: number;
  prorationCharge: number;
  creditToBalance: number;
  recurringAmount: number | null;
  renewsAt: string | null;
  tax: number | null;
  discountEnds: boolean;
  founderAnnualOffer: boolean;
  discountAmount: number;
  itemsMoved: number;
  prorationDate: number;
}

Errors: 400, 401, 403, 409, 500, 503, with the error body.

POST /billing/org/{orgId}/cancel

ParameterInTypeRequired
orgIdpathstringYes
Request body (application/json, required)
{
  cancelImmediately?: boolean;
}
Response 200 (application/json)
{
  ok: true;
  cancelAtPeriodEnd: boolean;
  currentPeriodEnd: string | string (date-time);
}

Errors: 400, 401, 403, 500, with the error body.

POST /billing/org/{orgId}/reactivate

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
  status: "active" | "canceled" | "incomplete" | "incomplete_expired" | "past_due" | "paused" | "trialing" | "unpaid";
  currentPeriodEnd: string (date-time);
}

GET /billing/org/{orgId}/overages

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
  summary: {
    orgId: string;
    periodStart: string (date-time);
    periodEnd: string (date-time);
    overages: {
      metric: string;
      metricName: string;
      currentUsage: number;
      limit: number;
      overage: number;
      unitSize: number;
      overageUnits: number;
      pricePerUnit: number;
      totalChargeCents: number;
    }[];
    totalOverageCents: number;
    creditsApplied: number;
    netChargeCents: number;
  };
  pricing: {
    [key: string]: {
      unitSize: number;
      priceCents: number;
    };
  };
}

GET /billing/org/{orgId}/overages/history

ParameterInTypeRequired
orgIdpathstringYes
limitquerystringNo
Response 200 (application/json)
{
  ok: boolean;
  overages: any[];
}

GET /billing/org/{orgId}/tax-info

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: true;
  taxInfo: {
    taxId: string | null;
    taxIdType: string | null;
    taxIdVerified: boolean;
    taxIdVerification: "verified" | "pending" | "unverified" | "not_applicable";
    businessName: string | null;
    address: {
      line1: string | null;
      line2: string | null;
      city: string | null;
      state: string | null;
      postalCode: string | null;
      country: string | null;
    };
  };
  taxIdTypes: {
    code: string;
    label: string;
  }[];
}

Errors: 401, 403, with the error body.

PUT /billing/org/{orgId}/tax-info

ParameterInTypeRequired
orgIdpathstringYes
Request body (application/json, required)
{
  taxId?: string | null;
  taxIdType?: string | null;
  businessName?: string | null;
  address?: {
    line1?: string | null;
    line2?: string | null;
    city?: string | null;
    state?: string | null;
    postalCode?: string | null;
    country?: string | null;
  };
}
Response 200 (application/json)
{
  ok: true;
  stripeSynced: boolean;
  taxIdSubmitted: boolean;
  taxIdVerified: boolean;
  warning: string | null;
}

Errors: 401, 403, with the error body.

POST /billing/org/{orgId}/pause

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
  status: string;
  pauseStartedAt: string;
  pauseExpiresAt: string;
}

POST /billing/org/{orgId}/resume

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
  status: string;
  resumedAt: string;
  pauseCooldownEndsAt: string;
}

GET /billing/org/{orgId}/invoices

ParameterInTypeRequired
qquerystringNo
statusquery"paid" | "due" | "overdue" | "void" | ("paid" | "due" | "overdue" | "void")[]No
sincequerystring (date-time)No
untilquerystring (date-time)No
startingAfterquerystringNo
limitquerynumberNo
orgIdpathstring (uuid)Yes
Response 200 (application/json)
{
  ok: true;
  canPay: boolean;
  invoices: {
    id: string;
    number: string | null;
    status: string | null;
    displayStatus: "paid" | "due" | "overdue" | "void" | "draft";
    description: string | null;
    amountCents: number;
    amountDue: number;
    amountPaid: number;
    currency: string;
    createdAt: string;
    dueDate: string | null;
    paidAt: string | null;
    hostedInvoiceUrl: string | null;
    invoicePdf: string | null;
    plan: string | null;
    interval: string | null;
    affectsPlan: boolean;
    source: "stripe" | "contract";
  }[];
  hasMore: boolean;
  nextCursor: string | null;
}

Errors: 400, 401, 403, 500, with the error body.

GET /billing/org/{orgId}/cost-breakdown

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  period: {
    start: string;
    end: string;
  };
  projects: {
    sharePercent: number;
    planAllocatedCents: number;
    veramentCredits: number;
    dedicatedDatabasesCents: number;
    totalCents: number;
    projectId: string;
    name: string;
  }[];
  deletedProjects: null | {
    sharePercent: number;
    planAllocatedCents: number;
    veramentCredits: number;
    dedicatedDatabasesCents: number;
    totalCents: number;
  };
  notTiedToProject: {
    lines: {
      label: string;
      cents: number;
    }[];
    cents: number;
    veramentCredits: number;
  };
  totals: {
    planCents: number;
    veramentCredits: number;
    dedicatedDatabasesCents: number;
    totalCents: number;
  };
  method: "metered_share";
}

GET /billing/org/{orgId}/usage/realtime

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
  apiRequests: {
    used: number;
    limit: number;
    percent: number;
  };
  storage: {
    usedBytes: number;
    limitBytes: number;
    percent: number;
  };
  aiCredits: {
    used: number;
    limit: number;
    percent: number;
    daysUntilReset: number;
  };
  functions: {
    invocations: number;
    limit: number;
    percent: number;
  };
  realtimeConnections: {
    current: number;
    limit: number;
    percent: number;
  };
  databaseRows: {
    current: number;
    limit: number;
    percent: number;
  };
  mau: {
    current: number;
    limit: number;
    percent: number;
  };
  trend24h: {
    apiRequests: number[];
    aiCredits: number[];
  };
}

GET /billing/org/{orgId}/invoices/{invoiceId}/pdf

ParameterInTypeRequired
orgIdpathstringYes
invoiceIdpathstringYes
Response 200

No body.

GET /billing/org/{orgId}/credits/auto-topup

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: true;
  settings: {
    enabled: boolean;
    packageId: string;
    monthlyCapUsd: number;
    updatedAt: string | null;
  };
  eligible: boolean;
  ineligibleReason: string | null;
  canEdit: boolean;
  periodKey: string;
  chargedThisPeriodCents: number;
  usedThisMonthCents: number;
  remainingCapCents: number;
  notifyAtPercent: number;
  paymentMethodOnFile: boolean | null;
  lastTopUp: {
    at: string;
    credits: number;
    amountCents: number;
    status: "charged" | "requires_action" | "failed";
  } | null;
  packages: {
    id: string;
    label: string;
    description: string;
    priceInCents: number;
    amount: number;
  }[];
}

Errors: 401, 403, with the error body.

PUT /billing/org/{orgId}/credits/auto-topup

ParameterInTypeRequired
orgIdpathstringYes
Request body (application/json, required)
{
  enabled: boolean;
  packageId: "credits_10" | "credits_25" | "credits_50" | "credits_100";
  monthlyCapUsd: number;
}
Response 200 (application/json)
{
  ok: true;
  settings: {
    enabled: boolean;
    packageId: string;
    monthlyCapUsd: number;
    updatedAt: string | null;
  };
  eligible: boolean;
  ineligibleReason: string | null;
  canEdit: boolean;
  periodKey: string;
  chargedThisPeriodCents: number;
  usedThisMonthCents: number;
  remainingCapCents: number;
  notifyAtPercent: number;
  paymentMethodOnFile: boolean | null;
  lastTopUp: {
    at: string;
    credits: number;
    amountCents: number;
    status: "charged" | "requires_action" | "failed";
  } | null;
  packages: {
    id: string;
    label: string;
    description: string;
    priceInCents: number;
    amount: number;
  }[];
}

Errors: 400, 401, 403, with the error body.

POST /billing/org/{orgId}/credits/purchase

ParameterInTypeRequired
orgIdpathstringYes
Request body (application/json, required)
{
  packageId: "credits_10" | "credits_25" | "credits_50" | "credits_100";
  successUrl?: string (uri);
  cancelUrl?: string (uri);
}
Response 200 (application/json)
{
  ok: true;
  checkoutUrl: string | null;
}

Errors: 400, 401, 403, 500, with the error body.

GET /billing/org/{orgId}/credits/transactions

ParameterInTypeRequired
typequery"usage" | "plan" | "purchase" | "auto_topup" | "adjustment"No
periodquery"current" | "previous" | "all"No
beforequerystringNo
limitquerynumberNo
orgIdpathstring (uuid)Yes
Response 200 (application/json)
{
  rows: {
    id: string;
    date: string;
    type: "usage" | "plan" | "purchase" | "auto_topup" | "adjustment";
    description: string;
    project: {
      id: string;
      name: string;
    } | null;
    tier: "lite" | "standard" | "max" | "verament";
    credits: number;
    amountCents: number | null;
    actor: {
      name: string;
    } | null;
  }[];
  nextCursor: string | null;
}

Errors: 400, 401, 403, with the error body.

GET /billing/org/{orgId}/contacts

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
Response 200 (application/json)
{
  ok: true;
  contacts: {
    id: string;
    name: string;
    email: string;
    phone: string | null;
    isPrimary: boolean;
    receiveInvoices: boolean;
    receiveAlerts: boolean;
    createdAt: string;
  }[];
}

Errors: 401, 403, with the error body.

POST /billing/org/{orgId}/contacts

ParameterInTypeRequired
orgIdpathstringYes
Request body (application/json, required)
{
  name: string;
  email: string (email);
  phone?: string;
  receiveInvoices?: boolean;
  receiveAlerts?: boolean;
}
Response 200 (application/json)
{
  ok: true;
  contactId: string;
  isPrimary: boolean;
}

Errors: 401, 403, 409, with the error body.

PATCH /billing/org/{orgId}/contacts/{contactId}

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
contactIdpathstring (uuid)Yes
Request body (application/json, required)
{
  name?: string;
  phone?: string | null;
  receiveInvoices?: boolean;
  receiveAlerts?: boolean;
  isPrimary?: true;
}
Response 200 (application/json)
{
  ok: true;
  contact: {
    id: string;
    name: string;
    email: string;
    phone: string | null;
    isPrimary: boolean;
    receiveInvoices: boolean;
    receiveAlerts: boolean;
    createdAt: string;
  };
}

Errors: 400, 401, 403, 404, with the error body.

DELETE /billing/org/{orgId}/contacts/{contactId}

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
contactIdpathstring (uuid)Yes
Response 200 (application/json)
{
  ok: true;
}

Errors: 401, 403, 404, with the error body.

POST /billing/enterprise/inquiry

Request body (application/json, required)
{
  orgId: string (uuid);
  name: string;
  email: string (email);
  company: string;
  phone?: string;
  teamSize?: "1-10" | "11-50" | "51-200" | "201-1000" | "1000+";
  message?: string;
}
Response 200 (application/json)
{
  ok: true;
  sentAt: string;
}

Errors: 401, 403, 429, with the error body.

POST /billing/promo-codes/validate

Request body (application/json, required)
{
  code: string;
  plan?: "entry" | "entry_plus" | "starter" | "pro" | "agency" | "studio_plus";
  interval?: "monthly" | "yearly";
}
Response 200 (application/json)
{
  ok: true;
  code: string;
  discountType: string;
  discountValue: number;
  duration: string;
  durationMonths: number | null;
  applicablePlans: string[] | null;
  expiresAt: string | string (date-time);
  founder?: {
    offer: "launch25" | "founder_annual";
    summary: string;
    otherIntervalSummary: string;
    seatsRemaining: number;
    seatsTotal: number;
    foundingPriceForever: true;
  };
}

Errors: 400, 404, 410, 429, 500, with the error body.

POST /billing/redeem-promo

Request body (application/json, required)
{
  orgId: string (uuid);
  code: string;
}
Response 200 (application/json)
{
  ok: true;
  code: string;
  creditsAdded: number;
}

Errors: 401, 403, 404, 429, 500, with the error body.

GET /billing/org/{orgId}/founder

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
Response 200 (application/json)
{
  founder: {
    number: number | null;
    since: string;
    offer: "launch25" | "founder_annual";
    plan: string | null;
    priceLock: "forever";
    priceLockUntil: "null";
    foundingPrices: {
      plan: string;
      interval: string;
      unitAmountCents: number | null;
      currency: string | null;
    }[];
    wall: {
      optIn: boolean;
      name: string | null;
      url: string | null;
    };
  } | null;
  pendingClaim: {
    offer: "launch25" | "founder_annual";
    reservedUntil: string;
  } | null;
  earlyAccess: {
    hostedMcp: boolean;
    websiteHosting: boolean;
  };
  availability: {
    open: boolean;
    seatsTotal: number;
    seatsRemaining: number;
    code: string;
    offers: {
      launch25: {
        interval: "monthly" | "yearly";
        percentOff: number;
        durationMonths: number;
        summary: string;
      };
      founder_annual: {
        interval: "monthly" | "yearly";
        percentOff: number;
        durationMonths: number;
        summary: string;
      };
    };
  };
}

Errors: 401, 403, with the error body.

PUT /billing/org/{orgId}/founder/wall

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
Request body (application/json, required)
{
  optIn: boolean;
  name?: string | null;
  url?: string | null;
}
Response 200 (application/json)
{
  ok: true;
  wall: {
    optIn: boolean;
    name: string | null;
    url: string | null;
  };
}

Errors: 400, 401, 403, with the error body.

GET /billing/founders/wall

Response 200 (application/json)
{
  availability: {
    open: boolean;
    seatsTotal: number;
    seatsRemaining: number;
    code: string;
    offers: {
      launch25: {
        interval: "monthly" | "yearly";
        percentOff: number;
        durationMonths: number;
        summary: string;
      };
      founder_annual: {
        interval: "monthly" | "yearly";
        percentOff: number;
        durationMonths: number;
        summary: string;
      };
    };
  };
  founders: {
    number: number | null;
    name: string;
    url: string | null;
  }[];
}

GET /billing/org/{orgId}/card-check

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
Response 200 (application/json)
{
  required: boolean;
  verified: boolean;
  verifiedAt: string | null;
  card: {
    brand: string | null;
    last4: string | null;
  } | null;
  lastAttemptRefused: boolean;
}

Errors: 401, 403, with the error body.

POST /billing/org/{orgId}/card-check/session

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
Request body (application/json, required)
{
  successUrl?: string (uri);
  cancelUrl?: string (uri);
}
Response 200 (application/json)
{
  ok: true;
  url: string;
  sessionId: string;
}

Errors: 400, 401, 403, 409, 502, with the error body.

POST /billing/org/{orgId}/card-check/confirm

ParameterInTypeRequired
orgIdpathstring (uuid)Yes
Request body (application/json, required)
{
  sessionId: string;
}
Response 200 (application/json)
{
  ok: true;
  verified: true;
  card: {
    brand: string | null;
    last4: string | null;
  };
}

Errors: 400, 401, 403, 409, 502, with the error body.

GET /billing/addons/catalog

ParameterInTypeRequired
orgIdquerystringNo
Response 200 (application/json)
{
  ok: boolean;
  addons: {
    type: "ai_credits" | "always_on" | "bandwidth" | "compute" | "database" | "storage";
    name: string;
    description: string;
    unitLabel: string;
    unitPriceCents: number;
    minQuantity: number;
    maxQuantity: number;
    quantityStep: number;
    availableOnPlans: ("agency" | "enterprise" | "entry" | "entry_plus" | "free" | "pro" | "starter" | "studio_plus")[];
    retired?: true;
    annualUnitPriceCents: number;
  }[];
  billingInterval: "monthly" | "yearly";
  dedicatedDatabases: {
    soldSeparately: boolean;
    href: string;
    message: string;
  };
  currentPlan: string;
}

GET /billing/org/{orgId}/addons

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
  addons: {
    id: string;
    addonType: string;
    name: string;
    description: null | string;
    quantity: number;
    unitPriceCents: number;
    totalPriceCents: number;
    status: string;
    effectiveAt: string;
    createdAt: string;
  }[];
  totalMonthlyCents: number;
}

POST /billing/org/{orgId}/addons

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  ok: boolean;
  addonId: string;
  addonType: "ai_credits" | "always_on" | "bandwidth" | "compute" | "storage";
  name: string;
  quantity: number;
  unitPriceCents: number;
  totalPriceCents: number;
  stripeItemId: string;
}
Response 201 (application/json)
{
  ok: boolean;
  addonId: string;
  addonType: "ai_credits" | "always_on" | "bandwidth" | "compute" | "storage";
  name: string;
  quantity: number;
  unitPriceCents: number;
  totalPriceCents: number;
  stripeItemId: string;
}

POST /billing/org/{orgId}/addons/preview

ParameterInTypeRequired
orgIdpathstringYes
Request body (application/json)
{
  addonType: "ai_credits" | "always_on" | "bandwidth" | "compute" | "storage";
  quantity: number;
}
Response 200 (application/json)
{
  addonType: "ai_credits" | "always_on" | "bandwidth" | "compute" | "storage";
  quantity: number;
  newQuantity: number;
  dueTodayCents: number;
  nextInvoiceDeltaCents: number;
  currency: string;
  interval: "monthly" | "yearly";
  paymentMethod: null | {
    brand: string;
    last4: string;
  };
}

POST /billing/org/{orgId}/addons/{addonId}/preview

ParameterInTypeRequired
orgIdpathstringYes
addonIdpathstringYes
Request body (application/json)
{
  quantity: number;
}
Response 200 (application/json)
{
  quantity: number;
  wasQuantity: number;
  monthlyCents: number;
  wasMonthlyCents: number;
  dueTodayCents: number;
  creditCents: number;
  nextInvoiceDeltaCents: number;
  currency: string;
  interval: "monthly" | "yearly";
  paymentMethod: null;
  effect: string;
} | {
  quantity: number;
  wasQuantity: number;
  monthlyCents: number;
  wasMonthlyCents: number;
  dueTodayCents: number;
  creditCents: number;
  nextInvoiceDeltaCents: number;
  currency: string;
  interval: "monthly" | "yearly";
  paymentMethod: null | {
    brand: string;
    last4: string;
  };
  effect: null | string;
}

PATCH /billing/org/{orgId}/addons/{addonId}

ParameterInTypeRequired
orgIdpathstringYes
addonIdpathstringYes
Request body (application/json)
{
  quantity: number;
}
Response 200 (application/json)
{
  wasQuantity?: number;
  monthlyCents?: number;
  wasMonthlyCents?: number;
  dueTodayCents?: number;
  creditCents?: number;
  nextInvoiceDeltaCents?: number;
  currency?: string;
  interval?: "monthly" | "yearly";
  effect?: null | string;
  ok: boolean;
  addonId: string;
  quantity: number;
  totalPriceCents: number;
}

DELETE /billing/org/{orgId}/addons/{addonId}

ParameterInTypeRequired
orgIdpathstringYes
addonIdpathstringYes
Response 200 (application/json)
{
  quantity?: number;
  wasQuantity?: number;
  monthlyCents?: number;
  wasMonthlyCents?: number;
  dueTodayCents?: number;
  creditCents?: number;
  nextInvoiceDeltaCents?: number;
  currency?: string;
  interval?: "monthly" | "yearly";
  effect?: null | string;
  ok: boolean;
}

GET /billing/org/{orgId}/usage

ParameterInTypeRequired
orgIdpathstringYes
Response 200 (application/json)
{
  plan: "agency" | "enterprise" | "free" | "pro" | "starter" | "studio_plus";
  period: {
    start: string;
    end: string;
    source: "calendar";
  };
  projects: {
    used: number;
    max: number;
  };
  members: {
    used: number;
    max: number;
  };
  mau: {
    current: number;
    included: number;
    hardLimit: boolean;
  };
  realtimePeak: {
    peak: number;
    max: number;
  };
  usage: {
    storage: {
      usedGb: number;
      planLimitGb: number;
      addonGb: number;
      effectiveLimitGb: number;
    };
    aiCredits: {
      used: number;
      planLimit: number;
      addonCredits: number;
      effectiveLimit: number;
      remaining: number;
      balance: number;
      percentUsed: number;
    };
    compute: {
      invocations: number;
      planLimit: number;
      addonInvocations: number;
      effectiveLimit: number;
    };
    apiRequests: {
      used: number;
      limit: number;
    };
    bandwidth: {
      usedGb: number;
      addonGb: number;
    };
    hostingEgress: {
      usedBytes: number;
      includedBytes: number;
    };
    databaseRows: {
      used: number;
      limit: number;
    };
  };
  addons: {
    active: number;
    totalMonthlyCents: number;
    byType: {
      [key: string]: number;
    };
  };
  ok: boolean;
}

GET /billing/org/{orgId}/usage/history

ParameterInTypeRequired
orgIdpathstringYes
monthsquerynumberNo
Response 200 (application/json)
{
  ok: boolean;
  history: {
    period: {
      start: string;
      end: string;
    };
    inProgress: boolean;
    apiRequests: null | number;
    functionInvocations: null | number;
    veramentCredits: null | number;
    storagePeakBytes: null | number;
    egressBytes: null | number;
    limits: null | {
      apiRequests: number;
      functionInvocations: number;
      veramentCredits: number;
      storagePeakBytes: number;
      egressBytes: number;
    };
  }[];
}