/** * MarginFront SDK Types * TypeScript interfaces for all API requests and responses */ /** * Log levels supported by the SDK */ type LogLevel = "debug" | "info" | "warn" | "error"; /** * Custom log handler function */ type LogHandler = (level: LogLevel, message: string, data?: unknown) => void; /** * Logging configuration options */ interface LoggingOptions { /** Whether logging is enabled */ enabled?: boolean; /** Minimum log level to output */ level?: LogLevel; /** Custom log handler function */ handler?: LogHandler; } /** * Telemetry handler function */ type TelemetryHandler = (metrics: RequestMetrics) => void; /** * Telemetry configuration options */ interface TelemetryOptions { /** Whether telemetry is enabled */ enabled?: boolean; /** Sample rate for telemetry (0-1), 1 = track every request */ sampleRate?: number; /** Custom handler for telemetry events */ handler?: TelemetryHandler; } /** * Request metrics collected for telemetry */ interface RequestMetrics { /** Unique ID for the request */ requestId: string; /** Request method (GET, POST, etc) */ method: string; /** Normalized request path */ path: string; /** Timestamp when request started */ startTime: number; /** Timestamp when request ended */ endTime?: number; /** Duration of the request in milliseconds */ duration?: number; /** HTTP status code */ statusCode?: number; /** Whether the request was successful */ success?: boolean; /** Error message if request failed */ errorMessage?: string; /** Error type if request failed */ errorType?: string; /** Number of retry attempts */ retryCount?: number; } /** * Telemetry statistics */ interface TelemetryStats { requestCount: number; successCount: number; errorCount: number; successRate: number; averageDuration: number; errorBreakdown: Record; } /** * Configuration options for the MarginFront client */ interface MarginFrontConfig { /** API key for authentication (format: mf_sk_* or mf_pk_*) */ apiKey: string; /** Base URL for the API */ baseUrl?: string; /** @deprecated Use baseUrl instead */ baseURL?: string; /** Request timeout in milliseconds (default: 5000) */ timeout?: number; /** Number of retry attempts for failed requests (default: 3) */ retries?: number; /** Delay between retry attempts in milliseconds (default: 300) */ retryDelay?: number; /** Custom headers to include in all requests */ headers?: Record; /** Enable debug logging (shorthand for logging.enabled) */ debug?: boolean; /** Logging configuration */ logging?: LoggingOptions; /** Telemetry configuration for performance tracking */ telemetry?: TelemetryOptions; /** When true (default), usage.record() never throws. network errors go to * a local retry buffer, validation errors log a warning and drop. Set to * false if you want to handle errors yourself. */ fireAndForget?: boolean; } /** * Client options (config without apiKey) */ type ClientOptions = Omit; interface Organization { id: string; name: string; } interface VerifyResponse { organization: Organization; verified: boolean; createdAt: string; updatedAt: string; } /** * One service contribution within a multi-service usage event. * * Use this when a single business outcome (one report generated, one * call answered) is backed by more than one underlying service. For * example, a sourcing report that uses Claude for the LLM work and * Google Maps for places lookups would have two ServiceEntry items * inside one UsageRecord. */ interface ServiceEntry { /** Model identifier from your provider (e.g. "gpt-4o", "claude-sonnet-4-6", "twilio-sms") */ model: string; /** Provider name in lowercase (e.g. "openai", "anthropic", "google", "twilio") */ modelProvider: string; /** Number of input (prompt) tokens. required for LLM cost tracking, optional for non-LLM */ inputTokens?: number; /** Number of output (completion) tokens. required for LLM cost tracking, optional for non-LLM */ outputTokens?: number; /** * Number of prompt-cache READ tokens (cache hits). Optional. Pass your * provider response's cache-read count (Anthropic `cache_read_input_tokens`, * OpenAI `prompt_tokens_details.cached_tokens`). Priced at the cheaper * cache-read rate instead of the full input rate. */ cacheReadTokens?: number; /** * Number of prompt-cache WRITE tokens (cache creation). Optional. Pass your * provider response's cache-creation count (Anthropic * `cache_creation_input_tokens`). Priced at the cache-write rate. */ cacheWriteTokens?: number; /** Quantity for non-LLM services (must be >= 0) */ quantity?: number; } /** * Environment in which the usage event occurred. Drives the COGS vs R&D * accounting classification: `production` events flow into COGS, `development` * and `testing` events flow into R&D. `staging` is intentionally ambiguous — * the platform leaves the signal's `costCategory` untouched. When omitted, * the platform falls back to the signal's manually set classification. */ type UsageEnvironment = "production" | "staging" | "development" | "testing"; /** * Fields shared by both single-service and multi-service usage events. */ interface CommonEventFields { /** Customer's external ID in your system */ customerExternalId: string; /** External code of the agent/product being used (set in the UI when creating an agent) */ agentCode: string; /** Name of the signal/metric being tracked */ signalName: string; /** Optional date for the usage event (defaults to now) */ usageDate?: string | Date; /** Optional metadata to attach to the usage event */ metadata?: Record; /** * Environment the event occurred in. Used by the platform to auto-classify * the parent signal as `PRODUCTION_COGS` or `DEVELOPMENT_RD` for accounting. * If omitted, the signal's existing `costCategory` is left unchanged. */ environment?: UsageEnvironment; } /** * Single-service usage record. One business outcome backed by ONE * underlying service (the most common case). Existing integrations * use this shape unchanged. */ interface SingleServiceEvent extends CommonEventFields { /** Model identifier from your provider (e.g. "gpt-4o", "claude-sonnet-4-6", "twilio-sms") */ model: string; /** Provider name in lowercase (e.g. "openai", "anthropic", "google", "twilio") */ modelProvider: string; /** Number of input (prompt) tokens. required for LLM cost tracking, optional for non-LLM */ inputTokens?: number; /** Number of output (completion) tokens. required for LLM cost tracking, optional for non-LLM */ outputTokens?: number; /** * Number of prompt-cache READ tokens (cache hits). Optional, LLM-only. * Pass your provider response's cache-read count (Anthropic * `cache_read_input_tokens`). Priced at the cheaper cache-read rate. */ cacheReadTokens?: number; /** * Number of prompt-cache WRITE tokens (cache creation). Optional, LLM-only. * Pass your provider response's cache-creation count (Anthropic * `cache_creation_input_tokens`). Priced at the cache-write rate. */ cacheWriteTokens?: number; /** Quantity to record (must be >= 0) */ quantity?: number; } /** * Multi-service usage record. One business outcome backed by MULTIPLE * underlying services (e.g. one report = Claude tokens + Google Maps * queries). MarginFront aggregates per-service cost into the parent * event and exposes the per-service breakdown for analytics. */ interface MultiServiceEvent extends CommonEventFields { /** Two or more services attributed to this single business outcome. */ services: ServiceEntry[]; } /** * A single usage record to be tracked. Either single-service (one * service per event, the historical shape) or multi-service (one event * carrying multiple services). * * The discriminator is the presence of `services`: if provided, the * record is multi-service; otherwise it's single-service. Both shapes * accept the same usage event metadata (customerExternalId, agentCode, * signalName, usageDate, metadata). */ type UsageRecord = SingleServiceEvent | MultiServiceEvent; /** * Parameters for tracking events - supports single record or batch */ type TrackEventParams = UsageRecord | { records: UsageRecord[]; }; /** * Response for a single tracked event */ interface SingleEventResponse { /** ID of the recorded usage */ id: string; /** Whether the recording was successful */ success: boolean; /** Additional response data */ [key: string]: unknown; } /** * Response for batch event tracking */ interface BatchEventResponse { /** Whether the batch operation was successful */ success: boolean; /** Total number of records processed */ totalRecords: number; /** Number of records successfully processed */ successCount: number; /** Number of records that failed to process */ failureCount: number; /** Results for each record */ results: Array<{ /** Whether this record was successfully processed */ success: boolean; /** The data for the record, if successful */ responseData?: SingleEventResponse; /** Error message if the record failed */ error?: string; /** The original data that was submitted */ originalData?: Record; }>; } /** * Response from event tracking - can be single or batch */ type TrackEventResponse = SingleEventResponse | BatchEventResponse; interface UsageRecordSuccess { customerExternalId: string; agentCode: string; signalName: string; quantity: number; eventId: string; rawEventId: string; timestamp: string; } interface UsageRecordFailure { record: UsageRecord; error: string; } interface UsageRecordResponse { processed: number; successful: number; failed: number; results: { success: UsageRecordSuccess[]; failed: UsageRecordFailure[]; }; } type CustomerStatus = "active" | "inactive" | "suspended"; /** * Data for creating a new customer */ interface CreateCustomerData { /** Customer name (required) */ name: string; /** Unique external ID in your system */ externalId?: string; /** Customer email address */ email?: string; /** Customer phone number */ phone?: string; /** Customer timezone (default: UTC) */ timezone?: string; /** Customer status */ status?: CustomerStatus; /** Billing contact name */ billingContactName?: string; /** Billing contact email */ billingContactEmail?: string; /** Net payment terms in days (default: 30) */ netTerms?: number; /** List of email addresses to receive invoices */ invoiceEmailRecipients?: string[]; /** Street address line 1 */ addressLine1?: string; /** Street address line 2 */ addressLine2?: string; /** City */ city?: string; /** State or province */ state?: string; /** ZIP or postal code */ zipCode?: string; /** Country */ country?: string; /** Custom metadata */ metadata?: Record; /** Agent code (external ID set in the UI) - if provided, auto-creates a subscription */ agentCode?: string; } /** Alias for CreateCustomerData */ type CustomerCreateParams = CreateCustomerData; /** * Data for updating an existing customer */ interface UpdateCustomerData { name?: string; email?: string; phone?: string; timezone?: string; status?: CustomerStatus; billingContactName?: string; billingContactEmail?: string; netTerms?: number; invoiceEmailRecipients?: string[]; metadata?: Record | null; } /** Alias for UpdateCustomerData */ type CustomerUpdateParams = UpdateCustomerData; interface CustomerSubscription { id: string; status: string; startDate: string; endDate: string | null; agent: { id: string; name: string; agentCode: string; }; plan: { id: string; name: string; }; } interface Customer { id: string; name: string; externalId: string | null; email: string | null; phone: string | null; timezone: string; status: CustomerStatus; billingContactName: string | null; billingContactEmail: string | null; netTerms: number; invoiceEmailRecipients: string[]; metadata: Record | null; subscriptions?: CustomerSubscription[]; createdAt: string; updatedAt: string; } /** * Parameters for listing customers */ interface CustomerListParams { /** Page number for pagination */ page?: number; /** Number of items per page */ limit?: number; /** Filter by external ID */ externalId?: string; /** Filter by email */ email?: string; /** Search query string */ query?: string; } /** * Response for customer listing */ interface CustomerListResponse { /** Array of customers */ data: Customer[]; /** Total number of results */ totalResults: number; /** Current page number */ page: number; /** Number of items per page */ limit: number; /** Whether there's a next page */ hasMore: boolean; } type InvoiceStatus = "draft" | "issued" | "paid" | "overdue" | "void" | "refunded"; interface InvoiceLineItem { id: string; description: string; quantity: number; /** Unit price. Always a number since SDK 0.9.0 (P1A-2). */ unitPrice: number; /** Line-item total (quantity × unitPrice). Always a number since SDK 0.9.0. */ amount: number; signalName?: string; } interface InvoiceCustomer { id: string; name: string; email: string | null; externalId: string | null; } interface Invoice { id: string; invoiceNumber: string; status: InvoiceStatus; invoiceDate: string; dueDate: string; /** Invoice total. Always a number since SDK 0.9.0 (P1A-2). */ totalAmount: number; /** Amount paid to date. Always a number since SDK 0.9.0. */ amountPaid: number; /** Amount still due. Always a number since SDK 0.9.0. */ amountDue: number; currency: string; customer: InvoiceCustomer; lineItems: InvoiceLineItem[]; createdAt: string; } interface InvoiceDetail extends Invoice { payments?: Array<{ id: string; /** Payment amount. Always a number since SDK 0.9.0 (P1A-2). */ amount: number; paymentDate: string; paymentMethod: string; }>; subscription?: { id: string; status: string; agent: { id: string; name: string; }; plan: { id: string; name: string; }; }; } interface ListInvoicesParams { /** Filter by customer ID */ customerId?: string; /** Filter by customer external ID */ customerExternalId?: string; /** Filter by invoice status */ status?: InvoiceStatus; /** Page number (default: 1) */ page?: number; /** Results per page (default: 20) */ limit?: number; } interface ListInvoicesResponse { invoices: Invoice[]; page: number; limit: number; totalPages: number; totalResults: number; } /** * Options for `client.invoices.send()`. All fields are optional — by default the * invoice is emailed to the customer's stored email with auto-generated subject + body. */ interface SendInvoiceParams { /** Override the destination address. Falls back to the customer's stored email. */ recipientEmail?: string; /** Legacy alias for `recipientEmail` — kept for backward compat. */ email?: string; /** Custom subject line. Default: "Invoice {number} from {your business name}". */ subject?: string; /** Optional note prepended to the email body, shown in a callout above the invoice details. */ message?: string; } /** * Response from `client.invoices.send()`. If the invoice was a draft when this call * landed, the API also auto-finalizes it (status flips draft → issued) as a side effect. */ interface SendInvoiceResponse { success: boolean; message: string; /** Resend message ID for tracking delivery (null if the email provider returned no ID). */ emailId: string | null; /** The address the email was actually sent to (after applying any overrides). */ recipientEmail: string; } type GroupBy = "daily" | "weekly" | "monthly"; interface UsageAnalyticsParams { /** Start date (ISO format, required) */ startDate: string; /** End date (ISO format, required) */ endDate: string; /** Grouping interval (default: daily) */ groupBy?: GroupBy; /** Filter by customer ID */ customerId?: string; /** Filter by customer external ID */ customerExternalId?: string; /** Filter by agent ID */ agentId?: string; /** Filter by signal ID */ signalId?: string; /** Filter by subscription ID (added in SDK 0.9.0 for filter-scope parity) */ subscriptionId?: string; } interface UsageAnalyticsSummary { totalEvents: number; totalQuantity: number; totalCost: number; uniqueCustomers: number; uniqueAgents: number; uniqueSignals: number; } interface UsageAnalyticsDataPoint { date: string; quantity: number; cost: number; eventCount: number; } interface UsageAnalyticsResponse { dateRange: { start: string; end: string; groupBy: GroupBy; }; summary: UsageAnalyticsSummary; data: UsageAnalyticsDataPoint[]; } /** One entry per active subscription in scope for the window. */ interface SubscriptionRevenue { subscriptionId: string; customerId: string; agentId: string; planId: string; revenue: number; cost: number; margin: number; usageRevenue: number; recurringRevenue: number; seatRevenue: number; onetimeRevenue: number; eventCount: number; } /** One entry per pricing strategy that contributed revenue in the window. */ interface StrategyRevenue { strategyId: string; chargeType: ChargeType; pricingModel: PricingModel | null; signalId: string | null; revenue: number; /** For usage strategies: SUM(quantity) in the window. 0 for non-usage. */ quantity: number; } /** * The canonical revenue shape. Returned by: * - `client.analytics.revenue()` * - `client.customers.getWithRevenue()` (embedded) * - `client.subscriptions.getWithRevenue()` (embedded) * * `marginPercent` is `null` when revenue === 0 (rendered as "-"), never * 0%, never NaN%, never Infinity%. per canon M1. */ interface RevenueMetrics { revenue: number; cost: number; margin: number; marginPercent: number | null; usageRevenue: number; recurringRevenue: number; seatRevenue: number; onetimeRevenue: number; eventCount: number; /** Events with usageCost IS NULL. counted in eventCount, excluded from cost. Per C1. */ eventCountWithNullCost: number; bySubscription: SubscriptionRevenue[]; byStrategy: StrategyRevenue[]; } /** * Invoice-totals for a window. Realized (invoice-materialized) amounts, not * the revenue formula. Per I1, non-draft invoices reconcile to * RevenueMetrics by construction. so billed/collected/outstanding are * consumable directly without re-deriving from events. */ interface InvoiceTotals { /** I2: SUM(totalAmount) WHERE status IN ('issued','overdue') AND invoiceDate ∈ window. */ billed: number; /** I2: SUM(totalAmount) WHERE status = 'paid' AND invoiceDate ∈ window. */ collected: number; /** I2: SUM(totalAmount) WHERE status = 'draft' AND invoiceDate ∈ window. Inventory only. */ draft: number; /** billed − collected. Age-independent. */ outstanding: number; /** Current-state (no date filter). SUM(totalAmount) WHERE status = 'overdue'. */ overdueAmount: number; overdueCount: number; /** COUNT WHERE status IN ('issued','paid','overdue') AND invoiceDate ∈ window. */ invoicesSentCount: number; } /** * MRR per canon MRR1. Fixed window = first-of-previous-calendar-month to * first-of-current-calendar-month. ARR = MRR × 12. */ interface Mrr { mrr: number; arr: number; } /** Per-subscription breakdown for run-rate MRR (MRR2). */ interface RunRateMrrBreakdownRow { subscriptionId: string; customerId: string; agentId: string; planId: string; /** Recurring fees monthly-normalized (divide by 12 if sub is yearly). */ recurring: number; /** Seat fees: max(seatsCount, minCommit) × rate, monthly-normalized. */ seatBased: number; /** Usage: actual last-30-days events through calcRouted(strategy, qty). */ usage: number; /** Sum of the three. this sub's run-rate MRR contribution. */ total: number; } /** Run-Rate MRR (MRR2). "trajectory if current 30-day pace continues". */ interface RunRateMrrResult { mrr: number; breakdown: RunRateMrrBreakdownRow[]; } /** Per-subscription breakdown for committed MRR (MRR3). Shape matches RunRate. */ interface CommittedMrrBreakdownRow { subscriptionId: string; customerId: string; agentId: string; planId: string; recurring: number; seatBased: number; /** Usage floor: calcRouted(strategy, minimumCommitment). 0 when no min. */ usage: number; total: number; } /** Committed MRR (MRR3). "contractual floor regardless of usage". */ interface CommittedMrrResult { mrr: number; breakdown: CommittedMrrBreakdownRow[]; } /** Per-agent cost breakdown row. */ interface CostByAgentRow { agentId: string; cost: number; eventCount: number; } /** Per-customer cost breakdown row. */ interface CostByCustomerRow { customerId: string; cost: number; eventCount: number; } /** Per-signal cost breakdown row. `signalId` is null for orphan events. */ interface CostBySignalRow { signalId: string | null; cost: number; eventCount: number; } /** * Daily cost bucket. one row per UTC day with activity. * `date` is ISO string on the wire (JSON doesn't carry Date); convert at * the consumer if you need a Date object. */ interface CostByDayRow { date: string; cost: number; eventCount: number; } /** Per-plan cost breakdown row. `planId: null` captures orphan events. */ interface CostByPlanRow { planId: string | null; cost: number; eventCount: number; } /** Per-LLM-model cost breakdown row. `model: null` covers non-LLM + missing-model events. */ interface CostByModelRow { model: string | null; cost: number; eventCount: number; } /** * Canonical cost shape. Returned by `client.analytics.costBreakdown()`. * * `prior` is populated only when the caller sets `includePriorWindow: true` *. an optional period-over-period trend block with the same shape (minus * `prior` itself). */ interface CostMetrics { /** SUM(usageCost) for the window. Null contributions count as 0. */ cost: number; /** All events in window, including those with NULL usageCost. */ eventCount: number; /** Events with usageCost IS NULL. "needs attention" indicator. Per C1. */ eventCountWithNullCost: number; byAgent: CostByAgentRow[]; byCustomer: CostByCustomerRow[]; bySignal: CostBySignalRow[]; byDay: CostByDayRow[]; byPlan: CostByPlanRow[]; byModel: CostByModelRow[]; /** Populated only when `includePriorWindow` was true on the request. */ prior?: Omit; } /** Per-strategy breakdown for Agent-Earned. One row per usage strategy in the window. */ interface AgentEarnedStrategyRow { strategyId: string; pricingModel: PricingModel | null; signalId: string | null; /** Events × rate routed through pricingModel (flat/graduated/volume/credit_pool). */ revenue: number; /** SUM(signal_events.quantity) for this strategy's signal in the window. */ quantity: number; } /** Per-subscription breakdown for Agent-Earned. activity revenue per sub. */ interface AgentEarnedSubscriptionRow { subscriptionId: string; customerId: string; agentId: string; planId: string; revenue: number; eventCount: number; } /** * Agent-Earned (canon R1 pure). Events × pricing-strategy rate only. no * recurring, no seat, no prorations, no onetime. This is the leading * indicator at the top of the Earned → Billed → Collected funnel. */ interface AgentEarned { /** Total activity-only revenue for the window. */ revenue: number; /** Events that produced revenue (usage events attributable to a usage strategy). */ eventCount: number; bySubscription: AgentEarnedSubscriptionRow[]; byStrategy: AgentEarnedStrategyRow[]; } /** Parameters for `client.analytics.revenue()`. */ interface RevenueMetricsParams { /** Start date (ISO format, required). */ startDate: string; /** End date (ISO format, required). */ endDate: string; /** Filter by customer ID. Narrowing by ANY entity triggers canonical shape. */ customerId?: string; /** Filter by agent ID. */ agentId?: string; /** Filter by signal ID. */ signalId?: string; /** Filter by subscription ID. */ subscriptionId?: string; } /** Parameters for `client.analytics.costBreakdown()`. */ interface CostMetricsParams { /** Start date (ISO format, required). */ startDate: string; /** End date (ISO format, required). */ endDate: string; /** Filter by customer ID. */ customerId?: string; /** Filter by agent ID. */ agentId?: string; /** Filter by signal ID. */ signalId?: string; /** Filter by subscription ID. */ subscriptionId?: string; /** When true, adds `prior` field with symmetric-span trend data (one extra DB query). */ includePriorWindow?: boolean; } /** Parameters for `client.analytics.invoiceTotals()`. */ interface InvoiceTotalsParams { /** Start date (ISO format, required). */ startDate: string; /** End date (ISO format, required). */ endDate: string; /** Optional customer scope. the single place per-customer billed/collected math runs. */ customerId?: string; } /** Parameters for `client.analytics.mrr()`, `runRateMrr()`, `committedMrr()`. */ interface MrrParams { /** Filter by customer ID. */ customerId?: string; /** Filter by subscription ID. */ subscriptionId?: string; } /** Parameters for `client.analytics.agentEarned()`. */ interface AgentEarnedParams { /** Start date (ISO format, required). */ startDate: string; /** End date (ISO format, required). */ endDate: string; customerId?: string; agentId?: string; signalId?: string; subscriptionId?: string; } /** * Response shape for `client.subscriptions.getWithRevenue()`. Wraps the * regular SubscriptionDetail with canonical revenue metrics for the window. */ interface SubscriptionDetailWithRevenue { subscription: SubscriptionDetail; revenue: RevenueMetrics; } /** * Response shape for `client.customers.getWithRevenue()`. Wraps the * regular Customer with canonical revenue metrics for the window. */ interface CustomerDetailWithRevenue { customer: Customer; revenue: RevenueMetrics; } type SubscriptionStatus = "upcoming" | "active" | "ended" | "canceled" | "expired" | "pending"; interface SubscriptionCustomer { id: string; name: string; email: string | null; externalId: string | null; phone?: string | null; } interface SubscriptionAgent { id: string; name: string; agentCode: string; description?: string; } interface SubscriptionPlan { id: string; name: string; description?: string; features?: string[]; } interface Subscription { id: string; status: SubscriptionStatus; startDate: string; endDate: string | null; billingCycle: string; customer: SubscriptionCustomer; agent: SubscriptionAgent; plan: SubscriptionPlan; createdAt: string; } interface SubscriptionUsage { totalQuantity: number; totalEvents: number; periodStart: string; periodEnd: string; } interface SubscriptionDetail extends Subscription { timezone: string; usage: SubscriptionUsage; updatedAt: string; } interface ListSubscriptionsParams { /** Filter by customer ID */ customerId?: string; /** Filter by customer external ID */ customerExternalId?: string; /** Filter by agent ID */ agentId?: string; /** Filter by subscription status */ status?: SubscriptionStatus; /** Page number (default: 1) */ page?: number; /** Results per page (default: 20) */ limit?: number; } interface ListSubscriptionsResponse { subscriptions: Subscription[]; page: number; limit: number; totalPages: number; totalResults: number; } /** * Available features in a portal session */ type PortalFeature = "invoices" | "subscriptions" | "usage" | "profile"; /** * Parameters for creating a portal session * Requires a secret key (mf_sk_*) */ interface CreatePortalSessionParams { /** Customer ID (internal MarginFront ID) */ customerId?: string; /** Customer external ID (your system's ID) */ customerExternalId?: string; /** URL to redirect customer after portal session */ returnUrl?: string; /** Features to enable in the portal (default: all) */ features?: PortalFeature[]; } /** * Portal session response (single-use magic link). * * `token` and `url` are only surfaced once — at creation. List/get responses * return ListedPortalSession instead, which intentionally OMITS these fields. */ interface PortalSession { /** Portal session ID */ id: string; /** Object type identifier */ object: "portal_session"; /** Full URL for the customer portal (single-use; mgl_ prefix on token) */ url: string; /** Portal session token (mgl_*) */ token: string; /** Customer ID */ customerId: string; /** Customer name (omitted when null) */ customerName?: string; /** Customer email (omitted when null) */ customerEmail?: string; /** Session expiration time (ISO 8601 string on the wire) */ expiresAt: string; /** Enabled features (informational in v1; single-page portal renders all) */ features: PortalFeature[]; /** Return URL after session (omitted when null) */ returnUrl?: string; /** Session creation time */ createdAt: string; } /** * Listed portal session — used by list() and get() responses. * * Token is intentionally omitted for security. URL is omitted because once a * single-use link is created and either redeemed or expired, sharing the URL * is meaningless. */ interface ListedPortalSession { /** Portal session ID */ id: string; /** Object type identifier */ object: "portal_session"; /** Customer ID */ customerId: string; /** Customer name (omitted when null) */ customerName?: string; /** Customer email (omitted when null) */ customerEmail?: string; /** Session expiration time (ISO 8601 string on the wire) */ expiresAt: string; /** Whether the magic link has expired */ isExpired: boolean; /** Whether the magic link has been redeemed */ isUsed: boolean; /** When it was redeemed (omitted when not yet used) */ usedAt?: string; /** Session creation time */ createdAt: string; /** Enabled features */ features: PortalFeature[]; } /** * List portal sessions parameters */ interface ListPortalSessionsParams { /** Filter by customer ID */ customerId?: string; /** Maximum number of sessions to return */ limit?: number; /** Include expired sessions */ includeExpired?: boolean; } /** * List portal sessions response (flat array — no envelope). */ type ListPortalSessionsResponse = ListedPortalSession[]; /** How the strategy charges: per-usage-event, fixed recurring, one-time setup, or per-seat. Mirrors Prisma enum `enum_pricing_strategies_chargeType`. */ type ChargeType = "usage" | "recurring" | "onetime" | "seat_based"; /** How the rate math works for a given strategy */ type PricingModel = "flat" | "graduated" | "volume" | "credit_pool"; /** How often the strategy bills (DB supports monthly/yearly; quarterly/weekly reserved for future) */ type BillingFrequency = "monthly" | "yearly" | "quarterly" | "weekly"; /** A single tier in a tiered pricing strategy */ interface PricingTier { /** Lower bound of this tier (inclusive for display, 0 for first tier) */ lower: number; /** Upper bound of this tier (null = unbounded / infinity) */ upper: number | null; /** Rate for this tier. per-unit for graduated/volume, flat fee for credit_pool pool tier */ rate: number; } /** An agent linked to a pricing plan */ interface LinkedAgent { id: string; name: string; agentCode: string; } /** A pricing strategy (the actual pricing rules inside a plan) */ interface PricingStrategy { id: string; name: string; agentId: string; signalId: string | null; chargeType: ChargeType; pricingModel: PricingModel | null; billingFrequency: BillingFrequency | null; tiers: PricingTier[] | null; rate: string | null; tags: string[]; active: boolean; minimumCommitment: number | null; signal?: { id: string; name: string; shortName: string | null; type?: string; } | null; createdAt: string; updatedAt: string; } /** A pricing plan (container for strategies, linked to agents) */ interface PricingPlan { id: string; name: string; description: string | null; featuresList: Record | null; isActive: boolean; agents: LinkedAgent[]; pricingStrategies?: PricingStrategy[]; createdAt: string; updatedAt: string; } /** Parameters for creating a pricing plan */ interface CreatePricingPlanData { name: string; description?: string; featuresList?: Record; agentId?: string; isActive?: boolean; } /** Parameters for updating a pricing plan */ interface UpdatePricingPlanData { name?: string; description?: string; featuresList?: Record; isActive?: boolean; } /** Query parameters for listing pricing plans */ interface PricingPlanListParams { name?: string; agentId?: string; isActive?: boolean; includeStrategies?: boolean; sortBy?: "name" | "createdAt" | "updatedAt"; sortOrder?: "asc" | "desc"; page?: number; limit?: number; } /** Paginated response for listing pricing plans */ interface PricingPlanListResponse { results: PricingPlan[]; page: number; limit: number; totalPages: number; totalResults: number; } /** Parameters for copying a pricing plan (with optional discount) */ interface CopyPricingPlanData { newName: string; newDescription?: string; /** Percentage discount applied to all rates (0-100) */ discount?: number; /** Convert monthly billing to yearly */ requestedYearly?: boolean; } /** Parameters for creating a pricing strategy */ interface CreatePricingStrategyData { name: string; agentId: string; chargeType: ChargeType; signalId?: string; tags?: string[]; pricingModel?: PricingModel; billingFrequency?: BillingFrequency; tiers?: PricingTier[]; rate?: number; active?: boolean; minimumCommitment?: number; } /** Parameters for updating a pricing strategy */ interface UpdatePricingStrategyData { name?: string; agentId?: string; signalId?: string; chargeType?: ChargeType; pricingModel?: PricingModel; billingFrequency?: BillingFrequency; tiers?: PricingTier[]; rate?: number; tags?: string[]; active?: boolean; minimumCommitment?: number; } /** Query parameters for listing pricing strategies */ interface PricingStrategyListParams { name?: string; chargeType?: ChargeType; pricingModel?: PricingModel; active?: boolean; sortBy?: "name" | "chargeType" | "createdAt" | "updatedAt"; sortOrder?: "asc" | "desc"; page?: number; limit?: number; } /** Paginated response for listing pricing strategies */ interface PricingStrategyListResponse { results: PricingStrategy[]; page: number; limit: number; totalPages: number; totalResults: number; } /** Parameters for bulk update + create in one call */ interface BulkUpdatePricingStrategiesData { strategies: { new?: CreatePricingStrategyData[]; update?: (UpdatePricingStrategyData & { id: string; })[]; }; } /** * Response shape for `client.pricingPlans.linkAgent()`. The API returns a * simple success message after creating the junction row. no envelope * object or created record, just confirmation. */ interface LinkAgentResponse { message: string; } /** * Response shape for `client.pricingStrategies.bulkUpdate()`. Wraps the * partial result (created + updated counts plus the actual strategy * records) with a message + top-level count summary for convenience. */ interface BulkUpdatePricingStrategiesResponse { message: string; updated: number; created: number; result: { created: { count: number; strategies: PricingStrategy[]; }; updated: { count: number; strategies: PricingStrategy[]; }; }; } /** * One entry from the global service_pricing catalog. Returned by * `client.services.list()` and `client.services.get()`. Customers use this to * discover canonical model + modelProvider names BEFORE firing usage events, * avoiding NEEDS_COST_BACKFILL on first hit. */ interface ServiceCatalogEntry { /** UUID of the catalog entry. */ id: string; /** Source-specific identifier (e.g. "openai/gpt-4o", "api/cloud-run-cpu-second"). */ externalId: string; /** The lowercase canonical name to send as `model` in `usage.record(...)`. */ canonicalName: string; /** Human-readable display name. */ displayName: string; /** Lowercase provider name to send as `modelProvider` (e.g. "openai", "google", "twilio"). */ provider: string; /** Category label (e.g. "LLM", "Web Search", "Geocoding", "Compute", "Vector Database"). */ serviceType: string; /** * Cost per unit on the input side (LLM input tokens, or per-call rate for * non-LLM services). Returned as a string-formatted decimal so JS Number * precision can't truncate fractional cents. Null when the service does not * publish an input-side rate. */ inputCost: string | null; /** Cost per unit on the output side (LLM output tokens). Null for non-LLM services. */ outputCost: string | null; /** Unit the rates are denominated in (e.g. "1M tokens", "1K requests", "1K vCPU-seconds"). */ costUnit: string; /** LLM context window size in tokens. Null for non-LLM services. */ contextWindow: number | null; /** Provenance: "openrouter", "litellm", or "curated". */ source: string; /** True for non-LLM API entries (Cloud Run, Twilio, Google Places, etc.). */ isApi: boolean; /** False if the entry has been deactivated. The list endpoint filters these out by default. */ isActive: boolean; } /** * Optional filters for `client.services.list(...)`. */ interface ListServicesParams { /** Filter by lowercase provider (e.g. "google", "openai"). */ provider?: string; /** Filter by serviceType (e.g. "LLM", "Geocoding", "Compute"). */ serviceType?: string; /** Filter by LLM (`false`) vs non-LLM API (`true`). Omit for both. */ isApi?: boolean; /** Case-insensitive search across canonicalName and displayName. */ search?: string; /** 1-based page number. */ page?: number; /** Page size, 1-100. Defaults to 50. */ limit?: number; } /** * Paginated response from `client.services.list(...)`. */ interface ListServicesResponse { data: ServiceCatalogEntry[]; pagination: { page: number; limit: number; total: number; totalPages: number; }; } interface ApiErrorResponse { error: string; message: string; statusCode: number; details?: Record; } /** * Input Validation Utilities */ /** * API key type */ type ApiKeyType = "secret" | "publishable"; /** * Parsed API key information */ interface ParsedApiKey { type: ApiKeyType; } /** * Parse an API key prefix to infer a display hint for the key type. * * This is a convenience utility for UIs that want to show "secret" or * "publishable" based on the key prefix. The server is the authoritative * source of key validity and type — this function does NOT gate access. * * @returns ParsedApiKey with type hint, or null if prefix is unrecognized */ declare function parseApiKey(apiKey: string): ParsedApiKey | null; /** * Check if an API key looks like a secret key based on its prefix. * Cosmetic hint only — the server is the authoritative source. */ declare function isSecretKey(apiKey: string): boolean; /** * Check if an API key looks like a publishable key based on its prefix. * Cosmetic hint only — the server is the authoritative source. */ declare function isPublishableKey(apiKey: string): boolean; /** * HTTP Client Wrapper * Handles all API requests with proper error handling, authentication, retry logic, and telemetry */ /** * HTTP client for making API requests to MarginFront */ declare class HttpClient { private readonly client; private readonly logger; private readonly telemetry; private readonly retries; private readonly retryDelay; constructor(config: MarginFrontConfig); /** * Make a request with retry logic */ private request; /** * Make a GET request */ get(path: string, params?: Record): Promise; /** * Make a POST request */ post(path: string, data?: unknown): Promise; /** * Make a PUT request */ put(path: string, data?: unknown): Promise; /** * Make a PATCH request */ patch(path: string, data?: unknown): Promise; /** * Make a DELETE request */ delete(path: string): Promise; /** * Get telemetry statistics */ getTelemetryStats(): TelemetryStats; /** * Reset telemetry statistics */ resetTelemetryStats(): void; } /** * Retry Buffer * * Holds failed usage events in memory and retries them in the background. * Only active when there are events to retry — zero overhead when everything * is working fine. * * Give-up policy: * - Max 5 attempts per event, then drop with a warning. * - Backoff between flushes: 10s → 20s → 40s → 60s ceiling. Resets on success. * - Buffer cap: 1,000 events. Oldest drop when full. */ declare class RetryBuffer { private readonly sendFn; private readonly logger; private buffer; private flushTimer; private currentIntervalMs; private destroying; constructor(sendFn: (records: UsageRecord[]) => Promise, logger: { warn: (msg: string, data?: unknown) => void; }); push(record: UsageRecord): void; get size(): number; flush(): Promise; destroy(): void; private ensureTimer; private stopTimer; } /** * Usage Resource * Handles usage tracking and event recording */ /** * Resource for recording usage events */ declare class UsageResource { private readonly http; private readonly fireAndForget; private readonly buffer; private readonly logger; constructor(http: HttpClient, fireAndForget?: boolean, buffer?: RetryBuffer | null, logger?: { warn: (msg: string, data?: unknown) => void; }); /** * Track events for a customer - supports both single record and batch operations */ trackEvent(params: TrackEventParams): Promise; /** * Record a single usage event */ record(record: UsageRecord): Promise; /** * Record multiple usage events in a batch */ recordBatch(records: UsageRecord[]): Promise; /** * Push failed records into the retry buffer (if one exists) */ private bufferRecords; /** * Normalize a usage record */ private normalizeRecord; /** * Convert native response to BatchEventResponse format */ private convertToBatchResponse; } /** * Customers Resource * Handles customer CRUD operations */ /** * Optional window for `client.customers.getWithRevenue()`. Both fields * together or neither. omit to default to the last 30 days. */ interface CustomerRevenueWindow { /** Start date (ISO format). */ startDate?: string; /** End date (ISO format). */ endDate?: string; } /** * Resource for managing customers */ declare class CustomersResource { private readonly http; constructor(http: HttpClient); /** * Create a new customer * * @param params - Customer creation parameters * @returns The created customer * * @example * ```typescript * const customer = await client.customers.create({ * name: 'Acme Corp', * externalId: 'acme_123', * email: 'billing@acme.com', * agentCode: 'agent_abc' // auto-creates subscription * }); * ``` */ create(params: CreateCustomerData): Promise; /** * Get a single customer by ID * * @param id - The customer's ID * @returns The customer with subscriptions * * @example * ```typescript * const customer = await client.customers.get('cust_123'); * console.log(customer.name); * ``` */ get(id: string): Promise; /** * Update an existing customer * * @param id - The customer's ID * @param params - Fields to update * @returns The updated customer * * @example * ```typescript * const customer = await client.customers.update('cust_123', { * name: 'New Company Name', * email: 'new-email@company.com' * }); * ``` */ update(id: string, params: UpdateCustomerData): Promise; /** * Delete a customer * * @param id - The customer's ID * * @example * ```typescript * await client.customers.delete('cust_123'); * ``` */ delete(id: string): Promise; /** * List customers with pagination and filtering * * @param params - List parameters * @returns Paginated list of customers * * @example * ```typescript * const { data, totalResults, hasMore } = await client.customers.list({ * limit: 10, * page: 1 * }); * ``` */ list(params?: CustomerListParams): Promise; /** * Get a customer together with canonical revenue metrics for a window. * * Added in SDK 0.9.0 (P1A-2). Makes two API calls in parallel. the * standard customer fetch plus the canonical per-customer revenue * endpoint. and returns both in one object. Lets SDK consumers build * a customer-detail page in one round-trip without picking which call * happens first. * * If `window.startDate` / `window.endDate` are omitted, the revenue * call defaults to the last 30 days UTC (matches the dashboard default). * * @param id - The customer's ID * @param window - Optional date window for the revenue block * @returns Object with `customer` (the usual Customer shape) and `revenue` (canonical RevenueMetrics) * * @example * ```typescript * // Default window (last 30 days) * const { customer, revenue } = await client.customers.getWithRevenue('cust_123'); * console.log(`${customer.name}: $${revenue.revenue} revenue, ${revenue.eventCount} events`); * * // Explicit window * const result = await client.customers.getWithRevenue('cust_123', { * startDate: '2024-01-01', * endDate: '2024-01-31', * }); * ``` */ getWithRevenue(id: string, window?: CustomerRevenueWindow): Promise; } /** * Invoices Resource * Handles invoice retrieval operations */ /** * Resource for managing invoices */ declare class InvoicesResource { private readonly http; constructor(http: HttpClient); /** * List invoices with optional filters * * @param params - Optional filter parameters * @returns Paginated list of invoices * * @example * ```typescript * const { invoices, totalResults } = await client.invoices.list({ * customerId: 'cust_123', * status: 'issued', * page: 1, * limit: 20 * }); * ``` */ list(params?: ListInvoicesParams): Promise; /** * Get a single invoice by ID * * @param invoiceId - The invoice's ID * @returns The invoice with full details including line items and payments * * @example * ```typescript * const invoice = await client.invoices.get('inv_abc'); * console.log(`Invoice ${invoice.invoiceNumber}: ${invoice.totalAmount}`); * ``` */ get(invoiceId: string): Promise; /** * Generate a draft invoice from a subscription's billing period usage events. * * This is the one-call path from "498 ingested events in April" to "$249 draft invoice * with N line items, ready to send." Composes server-side: creates a draft shell, then * populates line items + totals by running the period's signal_events through the * subscription's pricing strategy. Use this instead of `create()` when the line items * should come from real tracked usage rather than being hand-supplied. * * When `billingPeriodStart` / `billingPeriodEnd` are omitted, the subscription's current * billing period is used — that's the right default for cron-style auto-billing and for * the dashboard's "Generate from Subscription" button. * * @param params.customerId - The customer being billed (their MarginFront UUID) * @param params.subscriptionId - The subscription whose usage we're invoicing * @param params.billingPeriodStart - Optional override; defaults to subscription's current period start * @param params.billingPeriodEnd - Optional override; defaults to subscription's current period end * @returns The generated draft invoice with line items and totals * * @example * ```typescript * const draft = await client.invoices.generate({ * customerId: 'cust_abc', * subscriptionId: 'sub_xyz', * }); * console.log(`Draft ${draft.invoiceNumber}: $${draft.totalAmount}`); * ``` */ generate(params: { customerId: string; subscriptionId: string; billingPeriodStart?: string | Date; billingPeriodEnd?: string | Date; }): Promise; /** * Send an invoice email to the customer with a Stripe Checkout deeplink. * * Use after `generate()` to deliver a draft (which also auto-finalizes the draft * status from "draft" to "issued" as a side effect), or to re-send an already-issued * invoice. Customer email defaults to the address stored on the customer record; * pass `recipientEmail` to override (e.g. for billing-contact routing). * * The recipient receives a branded email with line items, total amount, and a * "Pay Now" button that opens Stripe Checkout pre-filled with the invoice details. * After Stripe confirms payment, the invoice status flips to "paid" automatically * via webhook — no extra SDK call needed. * * @param invoiceId - The invoice's UUID (from `generate()` or `list()`) * @param options - Optional overrides. Omit entirely to use stored customer email + defaults. * @returns Send confirmation with the Resend message ID and the actual recipient address used * * @example * ```typescript * // Generate + send in one go * const draft = await client.invoices.generate({ * customerId: 'cust_abc', * subscriptionId: 'sub_xyz', * }); * await client.invoices.send(draft.id); * * // Send to a different email + with a custom note * await client.invoices.send('inv_abc', { * recipientEmail: 'billing@customer.com', * subject: "May usage invoice — auto-charged in 5 days", * message: 'See attached. Card on file will be charged automatically.', * }); * ``` */ send(invoiceId: string, options?: SendInvoiceParams): Promise; } /** * Analytics Resource * * Handles usage analytics + canonical revenue/cost/MRR reporting. The * canonical methods on this resource (everything below `usage()`) all * wrap the api-nest `/v1/analytics/*` endpoints added in P1A-1 and hit * the shared `getRevenueMetrics` / `getCostMetrics` / `getMrr` library * in `@unitpay/db`. Response shapes match the canonical TS types exactly - * no shape drift between SDK and dashboard. */ /** * Resource for accessing analytics data. legacy usage rollups plus the * canonical revenue/cost/MRR surface added in SDK 0.9.0 (P1A-2). */ declare class AnalyticsResource { private readonly http; constructor(http: HttpClient); /** * Get usage analytics (events, quantity, cost) for a date range. * * Legacy method. Returns the simple time-series shape the dashboard uses * for daily/weekly activity charts. not the canonical revenue metric. * For canonical revenue with cost + margin, use `.revenue()` instead. * * @example * ```typescript * const analytics = await client.analytics.usage({ * startDate: '2024-01-01', * endDate: '2024-01-31', * groupBy: 'daily', * subscriptionId: 'sub_abc', // filter added in SDK 0.9.0 * }); * ``` */ usage(params: UsageAnalyticsParams): Promise; /** * Canonical revenue metrics (canon R1 + R2). * * Returns the full `RevenueMetrics` shape. revenue, cost, margin, * marginPercent, usage/recurring/seat/onetime breakdown, bySubscription, * byStrategy. Scoped to the date window and any combination of entity * filters. * * Whenever a filter (customerId / subscriptionId / signalId) is set the * API always returns canonical shape. For the org-wide call (no filters) * the SDK passes `variant=canonical` so you get `RevenueMetrics` rather * than the dashboard's legacy MRR-trio breakdown. * * @example * ```typescript * // Org-wide revenue for last 30 days * const metrics = await client.analytics.revenue({ * startDate: '2024-01-01', * endDate: '2024-01-31', * }); * console.log(`Revenue: $${metrics.revenue.toFixed(2)}`); * console.log(`Margin: ${(metrics.marginPercent ?? 0).toFixed(1)}%`); * * // Per-customer revenue * const custMetrics = await client.analytics.revenue({ * startDate: '2024-01-01', * endDate: '2024-01-31', * customerId: 'cust_123', * }); * ``` */ revenue(params: RevenueMetricsParams): Promise; /** * Canonical cost metrics (canon C1). * * Returns `CostMetrics`. full cost breakdown by agent, customer, signal, * day, pricing plan, and LLM model. Includes `eventCountWithNullCost` * (the "needs attention" counter for events whose cost couldn't be * computed). * * Set `includePriorWindow: true` to also get period-over-period trend * data in the response's `prior` field (one extra DB query). * * @example * ```typescript * const cost = await client.analytics.costBreakdown({ * startDate: '2024-01-01', * endDate: '2024-01-31', * includePriorWindow: true, * }); * console.log(`Cost this month: $${cost.cost.toFixed(2)}`); * if (cost.prior) { * const delta = cost.cost - cost.prior.cost; * console.log(`Δ vs prior window: $${delta.toFixed(2)}`); * } * ``` */ costBreakdown(params: CostMetricsParams): Promise; /** * Canonical invoice totals (canon I2 + I3). * * Returns billed / collected / draft / outstanding / overdueAmount / * overdueCount / invoicesSentCount for the window. `outstanding` is * billed − collected, age-independent. `overdueAmount` is current-state * (no date filter). the ageing check. * * @example * ```typescript * const totals = await client.analytics.invoiceTotals({ * startDate: '2024-01-01', * endDate: '2024-01-31', * }); * console.log(`Billed: $${totals.billed}`); * console.log(`Collected: $${totals.collected}`); * console.log(`Outstanding: $${totals.outstanding}`); * if (totals.overdueCount > 0) { * console.log(`⚠️ ${totals.overdueCount} overdue ($${totals.overdueAmount})`); * } * ``` */ invoiceTotals(params: InvoiceTotalsParams): Promise; /** * Canonical MRR (MRR1). "what did we actually bill last calendar month?" * * Fixed window: first-of-previous-calendar-month through * first-of-current-calendar-month. Returns `{ mrr, arr }` where * `arr = mrr * 12`. Optional `customerId` / `subscriptionId` narrows. * * For forward-looking questions (run-rate, contractual floor) use * `runRateMrr()` or `committedMrr()`. * * @example * ```typescript * const { mrr, arr } = await client.analytics.mrr(); * console.log(`MRR: $${mrr}, ARR: $${arr}`); * ``` */ mrr(params?: MrrParams): Promise; /** * Run-Rate MRR (MRR2). "what would we bill per month if the last 30 days * kept going?" * * Contract commitments plus actual 30-day usage, monthly-normalized. * Returns `{ mrr, breakdown[] }` with per-subscription recurring / seat / * usage / total contributions. * * @example * ```typescript * const { mrr, breakdown } = await client.analytics.runRateMrr(); * for (const row of breakdown) { * console.log(`Sub ${row.subscriptionId}: $${row.total} (usage: $${row.usage})`); * } * ``` */ runRateMrr(params?: MrrParams): Promise; /** * Committed MRR (MRR3). "contractual floor regardless of usage." * * Same shape as run-rate but usage is replaced with * `strategy.minimumCommitment × rate` (0 when no minimum is set). The * floor under run-rate: committed ≤ runRate by definition. * * @example * ```typescript * const { mrr, breakdown } = await client.analytics.committedMrr(); * console.log(`Committed MRR floor: $${mrr}`); * ``` */ committedMrr(params?: MrrParams): Promise; /** * Agent-Earned (canon R1 pure). activity-only revenue. * * Events × pricing-strategy rate only. Excludes recurring fees, seat * fees, onetime fees, and prorations. This is the leading indicator at * the top of the Earned → Billed → Collected funnel. the "did my agent * do billable work?" number, independent of invoice cadence. * * @example * ```typescript * const earned = await client.analytics.agentEarned({ * startDate: '2024-01-01', * endDate: '2024-01-31', * agentId: 'agent_abc', * }); * console.log(`Activity revenue: $${earned.revenue}`); * console.log(`Events: ${earned.eventCount}`); * ``` */ agentEarned(params: AgentEarnedParams): Promise; } /** * Subscriptions Resource * Handles subscription management operations */ /** * Optional window for `client.subscriptions.getWithRevenue()`. Both fields * together or neither. omit to default to the last 30 days. */ interface SubscriptionRevenueWindow { /** Start date (ISO format). */ startDate?: string; /** End date (ISO format). */ endDate?: string; } /** * Resource for managing subscriptions */ declare class SubscriptionsResource { private readonly http; constructor(http: HttpClient); /** * List subscriptions with optional filters * * @param params - Optional filter parameters * @returns Paginated list of subscriptions * * @example * ```typescript * const { subscriptions, totalResults } = await client.subscriptions.list({ * status: 'active', * customerId: 'cust_123', * page: 1, * limit: 20 * }); * ``` */ list(params?: ListSubscriptionsParams): Promise; /** * Get a single subscription by ID * * @param subscriptionId - The subscription's ID * @returns The subscription with full details including usage summary * * @example * ```typescript * const sub = await client.subscriptions.get('sub_abc'); * console.log(`Status: ${sub.status}`); * console.log(`Usage this period: ${sub.usage.totalQuantity}`); * ``` */ get(subscriptionId: string): Promise; /** * Get a subscription together with canonical revenue metrics for a window. * * Added in SDK 0.9.0 (P1A-2). Makes two API calls in parallel. the * standard subscription fetch plus the canonical per-subscription revenue * endpoint. and returns both in one object. Lets SDK consumers build * a subscription-detail page in one round-trip. * * If `window.startDate` / `window.endDate` are omitted, the revenue * call defaults to the last 30 days UTC. * * @param subscriptionId - The subscription's ID * @param window - Optional date window for the revenue block * @returns Object with `subscription` (SubscriptionDetail) and `revenue` (canonical RevenueMetrics) * * @example * ```typescript * // Default window (last 30 days) * const { subscription, revenue } = await client.subscriptions.getWithRevenue('sub_abc'); * console.log(`Plan: ${subscription.plan.name}`); * console.log(`Revenue: $${revenue.revenue}, Margin: ${revenue.marginPercent ?? 0}%`); * * // Explicit window * const result = await client.subscriptions.getWithRevenue('sub_abc', { * startDate: '2024-01-01', * endDate: '2024-01-31', * }); * ``` */ getWithRevenue(subscriptionId: string, window?: SubscriptionRevenueWindow): Promise; } /** * Portal Sessions Resource * * Create and manage portal sessions for your customers. * Portal sessions provide secure, short-lived URLs that allow customers * to access their billing information without exposing your API key. * * This follows the Stripe/Chargebee pattern: * 1. Your backend creates a portal session using your secret key * 2. You redirect the customer to the portal URL * 3. Customer can view invoices, subscriptions, usage (1 hour expiry) * * @example * ```typescript * import { MarginFrontClient } from '@marginfront/sdk'; * * const client = new MarginFrontClient('mf_sk_your_secret_key'); * * // Create a portal session * const session = await client.portalSessions.create({ * customerId: 'cust_123', * returnUrl: 'https://myapp.com/account' * }); * * // Redirect customer to the portal * res.redirect(session.url); * ``` * * IMPORTANT: Portal session creation requires a secret key (mf_sk_*). * Publishable keys cannot create portal sessions. */ declare class PortalSessionsResource { private readonly http; private readonly assertSecretKey; constructor(http: HttpClient, assertSecretKey: () => void); /** * Create a new portal session * * Creates a short-lived portal session for a customer. * The returned URL can be used to redirect the customer to their billing portal. * * @param params - Portal session parameters * @returns Created portal session with URL and token * @throws ValidationError if neither customerId nor customerExternalId is provided * @throws AuthenticationError if using a publishable key * * @example * ```typescript * // By customer ID * const session = await client.portalSessions.create({ * customerId: 'cust_123', * returnUrl: 'https://myapp.com/account' * }); * * // By external ID * const session = await client.portalSessions.create({ * customerExternalId: 'your_customer_id', * features: ['invoices', 'usage'] * }); * * console.log(session.url); // Redirect customer here * ``` */ create(params: CreatePortalSessionParams): Promise; /** * Get a portal session by ID * * @param sessionId - Portal session ID * @returns Portal session details * * @example * ```typescript * const session = await client.portalSessions.get('ps_123'); * console.log(session.expiresAt); * ``` */ get(sessionId: string): Promise; /** * List portal sessions * * @param params - Optional filters * @returns List of portal sessions * * @example * ```typescript * // List all active sessions * const { data } = await client.portalSessions.list(); * * // List sessions for a specific customer * const { data } = await client.portalSessions.list({ * customerId: 'cust_123', * includeExpired: true * }); * ``` */ list(params?: ListPortalSessionsParams): Promise; /** * Revoke a portal session * * Immediately invalidates the session, preventing further access. * * @param sessionId - Portal session ID to revoke * * @example * ```typescript * await client.portalSessions.revoke('ps_123'); * ``` */ revoke(sessionId: string): Promise; } /** * Pricing Plans Resource * Handles pricing plan CRUD, copying, and agent linking */ /** * Resource for managing pricing plans * * Pricing plans are containers for pricing strategies. A plan holds the rules * (charge types, tiers, rates) that determine how a customer gets billed. * Plans can be linked to one or more agents and assigned to customers via * subscriptions. */ declare class PricingPlansResource { private readonly http; constructor(http: HttpClient); /** * Create a new pricing plan * * @param params - Plan creation parameters * @returns The created plan * * @example * ```typescript * const plan = await client.pricingPlans.create({ * name: 'Pro Plan', * description: 'Usage-based with overage', * }); * ``` */ create(params: CreatePricingPlanData): Promise; /** * List pricing plans with optional filters * * @param params - Optional filter and pagination parameters * @returns Paginated list of plans * * @example * ```typescript * const { results } = await client.pricingPlans.list({ * isActive: true, * includeStrategies: true, * }); * ``` */ list(params?: PricingPlanListParams): Promise; /** * Get a single pricing plan by ID * * @param planId - The plan's UUID * @returns The plan with linked agents and strategies * * @example * ```typescript * const plan = await client.pricingPlans.get('plan_uuid'); * console.log(plan.agents); // agents linked to this plan * ``` */ get(planId: string): Promise; /** * Update a pricing plan's metadata * * @param planId - The plan's UUID * @param params - Fields to update (name, description, featuresList, isActive) * @returns The updated plan * * @example * ```typescript * const plan = await client.pricingPlans.update('plan_uuid', { * name: 'Enterprise Plan', * isActive: true, * }); * ``` */ update(planId: string, params: UpdatePricingPlanData): Promise; /** * Soft-delete a pricing plan * * @param planId - The plan's UUID * * @example * ```typescript * await client.pricingPlans.delete('plan_uuid'); * ``` */ delete(planId: string): Promise; /** * Copy a pricing plan (with all its strategies) and optionally apply a discount * * @param planId - The source plan's UUID * @param params - Copy parameters (new name, optional discount percentage) * @returns The newly created plan with copied strategies * * @example * ```typescript * // Copy with a 20% discount for yearly billing * const yearlyPlan = await client.pricingPlans.copy('plan_uuid', { * newName: 'Pro Plan (Yearly)', * discount: 20, * requestedYearly: true, * }); * ``` */ copy(planId: string, params: CopyPricingPlanData): Promise; /** * Link a pricing plan to an agent (many-to-many) * * @param planId - The plan's UUID * @param agentId - The agent's UUID to link * @returns The updated link record * * @example * ```typescript * const { message } = await client.pricingPlans.linkAgent('plan_uuid', 'agent_uuid'); * console.log(message); // "Plan linked to agent successfully" * ``` */ linkAgent(planId: string, agentId: string): Promise; /** * Unlink a pricing plan from an agent * * Fails if active subscriptions exist using this plan+agent combination. * * @param planId - The plan's UUID * @param agentId - The agent's UUID to unlink * * @example * ```typescript * await client.pricingPlans.unlinkAgent('plan_uuid', 'agent_uuid'); * ``` */ unlinkAgent(planId: string, agentId: string): Promise; } /** * Pricing Strategies Resource * Handles pricing strategy CRUD (nested under pricing plans) */ /** * Resource for managing pricing strategies * * Strategies are the actual pricing rules inside a plan. the charge types, * pricing models, tiers, and rates that determine what a customer pays. * * All operations are scoped to a specific pricing plan (planId). */ declare class PricingStrategiesResource { private readonly http; constructor(http: HttpClient); /** * Create one or more pricing strategies on a plan * * The API accepts an array. send one strategy or many in one call. * * @param planId - The plan's UUID * @param strategies - Array of strategies to create * @returns The created strategies * * @example * ```typescript * // Create a credit pool strategy: 5,000 calls for $99, then $0.03 overage * const strategies = await client.pricingStrategies.createBulk('plan_uuid', [ * { * name: 'API Call Pool', * agentId: 'agent_uuid', * chargeType: 'usage', * pricingModel: 'credit_pool', * signalId: 'signal_uuid', * tiers: [ * { lower: 0, upper: 5000, rate: 99 }, * { lower: 5000, upper: null, rate: 0.03 }, * ], * }, * ]); * ``` */ createBulk(planId: string, strategies: CreatePricingStrategyData[]): Promise; /** * List pricing strategies for a plan * * @param planId - The plan's UUID * @param params - Optional filter and pagination parameters * @returns Paginated list of strategies * * @example * ```typescript * const { results } = await client.pricingStrategies.list('plan_uuid', { * chargeType: 'usage', * active: true, * }); * ``` */ list(planId: string, params?: PricingStrategyListParams): Promise; /** * Get a single pricing strategy by ID * * @param planId - The plan's UUID * @param strategyId - The strategy's UUID * @returns The strategy with signal details * * @example * ```typescript * const strategy = await client.pricingStrategies.get('plan_uuid', 'strategy_uuid'); * console.log(strategy.chargeType, strategy.pricingModel); * ``` */ get(planId: string, strategyId: string): Promise; /** * Update a single pricing strategy * * @param planId - The plan's UUID * @param strategyId - The strategy's UUID * @param params - Fields to update * @returns The updated strategy * * @example * ```typescript * const updated = await client.pricingStrategies.update( * 'plan_uuid', * 'strategy_uuid', * { rate: 0.05, active: true }, * ); * ``` */ update(planId: string, strategyId: string, params: UpdatePricingStrategyData): Promise; /** * Bulk update existing strategies and/or create new ones in one call * * @param planId - The plan's UUID * @param params - Object with `strategies.new` and/or `strategies.update` arrays * @returns Summary of created and updated strategies * * @example * ```typescript * const result = await client.pricingStrategies.bulkUpdate('plan_uuid', { * strategies: { * new: [{ name: 'New Fee', agentId: '...', chargeType: 'recurring', rate: 49 }], * update: [{ id: 'existing_uuid', rate: 59 }], * }, * }); * ``` */ bulkUpdate(planId: string, params: BulkUpdatePricingStrategiesData): Promise; /** * Soft-delete a pricing strategy * * @param planId - The plan's UUID * @param strategyId - The strategy's UUID * * @example * ```typescript * await client.pricingStrategies.delete('plan_uuid', 'strategy_uuid'); * ``` */ delete(planId: string, strategyId: string): Promise; } /** * Services Catalog Resource * * Read-only access to the global service_pricing catalog. Use this BEFORE * firing usage events with new model/provider names to discover what * MarginFront recognizes for cost calculation. If your service isn't in the * catalog, the event still lands but cost stays null (NEEDS_COST_BACKFILL) * until the catalog is updated. The map_model endpoint can then redirect to * an existing entry; it cannot create new rates. */ /** * Resource for browsing the MarginFront service catalog. */ declare class ServicesResource { private readonly http; constructor(http: HttpClient); /** * List entries from the global service catalog. * * @param params - Optional filters and pagination * @returns Paginated list of catalog entries * * @example * ```typescript * // Find every Google service in the catalog * const { data } = await client.services.list({ provider: 'google' }); * data.forEach(entry => { * console.log(`${entry.canonicalName} (${entry.serviceType}): ${entry.inputCost} per ${entry.costUnit}`); * }); * * // Find non-LLM APIs only * const { data } = await client.services.list({ isApi: true }); * * // Search by name * const { data } = await client.services.list({ search: 'cloud-run' }); * ``` */ list(params?: ListServicesParams): Promise; /** * Get a single catalog entry by its UUID. * * @param id - The catalog entry's UUID (from a prior `list()` call) * @returns The catalog entry * * @example * ```typescript * const entry = await client.services.get('df3c1acd-fdc4-4f6c-9d84-99697ffb76a2'); * console.log(`Send '${entry.canonicalName}' as model with provider '${entry.provider}'`); * ``` */ get(id: string): Promise; } /** * MarginFront Client * Main entry point for the MarginFront SDK */ /** * Organization information from API key verification */ interface OrganizationInfo { id: string; name: string; [key: string]: unknown; } /** * MarginFront SDK Client * * Provides access to all MarginFront API resources including usage tracking, * customer management, invoicing, analytics, and subscriptions. * * @example * ```typescript * import { MarginFrontClient } from '@marginfront/sdk'; * * const client = new MarginFrontClient('mf_sk_your_secret_key'); * await client.connect(); * * // Track usage * await client.trackEvent({ * agentCode: 'agent_123', * customerExternalId: 'customer_456', * signalName: 'api_call', * quantity: 1 * }); * * // Or with full configuration * const client = new MarginFrontClient('mf_sk_your_secret_key', { * timeout: 10000, * retries: 3, * logging: { enabled: true, level: 'info' }, * telemetry: { enabled: true } * }); * ``` */ declare class MarginFrontClient { private readonly http; private readonly _apiKey; private readonly _keyType; private readonly _buffer; private _orgInfo; /** * Usage tracking resource * * @example * ```typescript * await client.usage.record({ * customerExternalId: 'cust_123', * agentCode: 'agent_abc', * signalName: 'api_call', * quantity: 1 * }); * ``` */ readonly usage: UsageResource; /** * Customer management resource * * @example * ```typescript * const customer = await client.customers.create({ * name: 'Acme Corp', * externalId: 'acme_123' * }); * ``` */ readonly customers: CustomersResource; /** * Invoice retrieval resource * * @example * ```typescript * const { invoices } = await client.invoices.list({ * status: 'issued' * }); * ``` */ readonly invoices: InvoicesResource; /** * Analytics resource * * @example * ```typescript * const analytics = await client.analytics.usage({ * startDate: '2024-01-01', * endDate: '2024-01-31' * }); * ``` */ readonly analytics: AnalyticsResource; /** * Subscription management resource * * @example * ```typescript * const { subscriptions } = await client.subscriptions.list({ * status: 'active' * }); * ``` */ readonly subscriptions: SubscriptionsResource; /** * Portal sessions resource * Create and manage customer portal sessions * * IMPORTANT: Requires a secret key (mf_sk_*). Publishable keys cannot create portal sessions. * * @example * ```typescript * const session = await client.portalSessions.create({ * customerId: 'cust_123', * returnUrl: 'https://myapp.com/account' * }); * * // Redirect customer to the portal * res.redirect(session.url); * ``` */ readonly portalSessions: PortalSessionsResource; /** * Pricing plan management resource * * @example * ```typescript * const plan = await client.pricingPlans.create({ name: 'Pro Plan' }); * await client.pricingPlans.linkAgent(plan.id, 'agent_uuid'); * ``` */ readonly pricingPlans: PricingPlansResource; /** * Pricing strategy management resource (nested under plans) * * @example * ```typescript * await client.pricingStrategies.createBulk('plan_uuid', [ * { name: 'API Calls', agentId: '...', chargeType: 'usage', * pricingModel: 'credit_pool', * tiers: [{ lower: 0, upper: 5000, rate: 99 }, { lower: 5000, upper: null, rate: 0.03 }] }, * ]); * ``` */ readonly pricingStrategies: PricingStrategiesResource; /** * Service catalog browser. Use this to discover the canonical model + * provider names MarginFront recognizes for cost calculation BEFORE firing * usage events. Helps avoid NEEDS_COST_BACKFILL on first-hit ingestion. * * @example * ```typescript * const { data } = await client.services.list({ provider: 'google' }); * data.forEach(svc => console.log(svc.canonicalName, svc.costUnit)); * ``` */ readonly services: ServicesResource; /** * Create a new MarginFront client * * @param apiKeyOrConfig - Either an API key string or a configuration object * @param options - Optional client options (when first param is API key) * * @example * ```typescript * // Simple initialization * const client = new MarginFrontClient('mf_sk_your_secret_key'); * * // With options * const client = new MarginFrontClient('mf_sk_your_secret_key', { * timeout: 10000, * retries: 3 * }); * * // With config object * const client = new MarginFrontClient({ * apiKey: 'mf_sk_your_secret_key', * baseUrl: 'https://api.example.com/v1', * debug: true * }); * ``` */ constructor(apiKeyOrConfig: string | MarginFrontConfig, options?: ClientOptions); /** * No-op — the server enforces key type for secret-only operations. * Kept so PortalSessionsResource can call it without breaking. */ private assertSecretKey; /** * Connect to the MarginFront API and verify credentials * * This must be called once before using any API methods that require verification. * Optional but recommended for validating your API key. * * @returns Promise resolving to the client instance for chaining * @throws AuthenticationError if API key is invalid * @throws InitializationError for other initialization errors * * @example * ```typescript * const client = new MarginFrontClient('mf_sk_your_secret_key'); * * try { * await client.connect(); * console.log('Connected to MarginFront API'); * } catch (error) { * console.error('Failed to connect:', error); * } * ``` */ connect(): Promise; /** * Verify the API key and get organization details * * @returns Verification result with organization info * * @example * ```typescript * const result = await client.verify(); * if (result.verified) { * console.log(`Connected to ${result.organization.name}`); * } * ``` */ verify(): Promise; /** * Get organization information * * @returns Organization information from API key verification * @throws Error if organization info is not available (call connect() first) * * @example * ```typescript * await client.connect(); * const org = client.getOrganization(); * console.log(`Organization: ${org.name}`); * ``` */ getOrganization(): OrganizationInfo; /** * Track event for a customer (shorthand method) * * @param params - Event tracking parameters * @returns Tracked event data * * @example * ```typescript * // Simple event tracking * await client.trackEvent({ * agentCode: 'agent_123', * customerExternalId: 'customer_456', * signalName: 'api_call', * quantity: 1 * }); * * // Batch tracking * await client.trackEvent({ * records: [ * { customerExternalId: 'cust_1', agentCode: 'agent_123', signalName: 'api_call', quantity: 10 }, * { customerExternalId: 'cust_2', agentCode: 'agent_123', signalName: 'storage', quantity: 100 } * ] * }); * ``` */ trackEvent(params: TrackEventParams): Promise; /** * Re-verify the API key * * Useful if you suspect the API key status might have changed * * @returns Updated organization information */ verifyApiKey(): Promise; /** * Get current telemetry statistics * * @returns Telemetry statistics * * @example * ```typescript * const stats = client.getTelemetryStats(); * console.log(`Total Requests: ${stats.requestCount}`); * console.log(`Success Rate: ${(stats.successRate * 100).toFixed(2)}%`); * ``` */ getTelemetryStats(): TelemetryStats; /** * Reset telemetry statistics */ resetTelemetryStats(): void; /** * Get the API key type (secret or publishable) * * @returns 'secret' for mf_sk_* keys, 'publishable' for mf_pk_* keys * * @example * ```typescript * if (client.getKeyType() === 'secret') { * const session = await client.portalSessions.create({ ... }); * } * ``` */ getKeyType(): ApiKeyType; /** * Check if the API key is a secret key (mf_sk_*) * * @returns true if the key can perform server-side operations */ isSecretKey(): boolean; /** * Check if the API key is a publishable key (mf_pk_*) * * @returns true if the key is safe for frontend use */ isPublishableKey(): boolean; /** * Get a masked version of the API key for debugging * * @returns Masked API key showing only prefix and last 4 characters */ getMaskedApiKey(): string; } /** * MarginFront SDK Error Classes * Custom error types for different API error scenarios */ /** * Base error class for all MarginFront SDK errors */ declare class MarginFrontError extends Error { /** HTTP status code from the API response */ readonly statusCode: number; /** Error code for programmatic handling */ readonly code: string; /** Additional error details from the API */ readonly details?: Record; /** Additional error metadata */ readonly metadata: Record; /** Request ID associated with the error */ readonly requestId?: string; constructor(message: string, statusCode?: number, code?: string, details?: Record, metadata?: Record); } /** * Thrown when API authentication fails (401) * Usually indicates an invalid or expired API key */ declare class AuthenticationError extends MarginFrontError { constructor(message?: string, metadata?: Record); } /** * Thrown when the API key doesn't have permission for the requested action (403) */ declare class AuthorizationError extends MarginFrontError { constructor(message?: string, metadata?: Record); } /** * Thrown when a requested resource is not found (404) */ declare class NotFoundError extends MarginFrontError { /** The type of resource that was not found */ readonly resourceType?: string; /** The ID of the resource that was not found */ readonly resourceId?: string; constructor(message?: string, resourceType?: string, resourceId?: string, metadata?: Record); } /** * Thrown when request validation fails (400/422) * Contains details about which fields failed validation */ declare class ValidationError extends MarginFrontError { /** Object containing field-level validation errors */ readonly validationErrors?: Record; constructor(message?: string, validationErrors?: Record, metadata?: Record); } /** * Thrown when the API rate limit is exceeded (429) */ declare class RateLimitError extends MarginFrontError { /** Number of seconds to wait before retrying */ readonly retryAfter?: number; constructor(message?: string, retryAfter?: number, metadata?: Record); } /** * Thrown when there's a conflict with the current state (409) */ declare class ConflictError extends MarginFrontError { constructor(message?: string, metadata?: Record); } /** * Thrown when a network error occurs (no response from server) */ declare class NetworkError extends MarginFrontError { /** The original error that caused the network failure */ readonly originalError?: Error; constructor(message?: string, originalError?: Error, metadata?: Record); } /** * Thrown when a request times out */ declare class TimeoutError extends MarginFrontError { /** The timeout value in milliseconds that was exceeded */ readonly timeoutMs?: number; constructor(message?: string, timeoutMs?: number, metadata?: Record); } /** * Thrown when an internal server error occurs (500) */ declare class InternalError extends MarginFrontError { constructor(message?: string, metadata?: Record); } /** * Thrown when SDK initialization fails */ declare class InitializationError extends MarginFrontError { constructor(message?: string, metadata?: Record); } /** * Generic API error */ declare class ApiError extends MarginFrontError { /** API error code if available */ readonly errorCode?: string; constructor(message?: string, statusCode?: number, errorCode?: string, metadata?: Record); } /** * Maps HTTP status codes to appropriate error classes */ declare function createErrorFromResponse(statusCode: number, message: string, details?: Record, metadata?: Record): MarginFrontError; /** * Parse API error response into appropriate error object */ declare function parseApiError(error: unknown): MarginFrontError; export { type AgentEarned, type AgentEarnedParams, type AgentEarnedStrategyRow, type AgentEarnedSubscriptionRow, ApiError, type ApiErrorResponse, type ApiKeyType, AuthenticationError, AuthorizationError, type BatchEventResponse, type BillingFrequency, type BulkUpdatePricingStrategiesData, type BulkUpdatePricingStrategiesResponse, type ChargeType, type ClientOptions, type CommittedMrrBreakdownRow, type CommittedMrrResult, ConflictError, type CopyPricingPlanData, type CostByAgentRow, type CostByCustomerRow, type CostByDayRow, type CostByModelRow, type CostByPlanRow, type CostBySignalRow, type CostMetrics, type CostMetricsParams, type CreateCustomerData, type CreatePortalSessionParams, type CreatePricingPlanData, type CreatePricingStrategyData, type Customer, type CustomerCreateParams, type CustomerDetailWithRevenue, type CustomerListParams, type CustomerListResponse, type CustomerStatus, type CustomerSubscription, type CustomerUpdateParams, type GroupBy, InitializationError, InternalError, type Invoice, type InvoiceCustomer, type InvoiceDetail, type InvoiceLineItem, type InvoiceStatus, type InvoiceTotals, type InvoiceTotalsParams, type LinkAgentResponse, type LinkedAgent, type ListInvoicesParams, type ListInvoicesResponse, type ListPortalSessionsParams, type ListPortalSessionsResponse, type ListServicesParams, type ListServicesResponse, type ListSubscriptionsParams, type ListSubscriptionsResponse, type LogHandler, type LogLevel, type LoggingOptions, MarginFrontClient, type MarginFrontConfig, MarginFrontError, type Mrr, type MrrParams, NetworkError, NotFoundError, type Organization, type OrganizationInfo, type PortalFeature, type PortalSession, type PricingModel, type PricingPlan, type PricingPlanListParams, type PricingPlanListResponse, type PricingStrategy, type PricingStrategyListParams, type PricingStrategyListResponse, type PricingTier, RateLimitError, type RequestMetrics, type RevenueMetrics, type RevenueMetricsParams, type RunRateMrrBreakdownRow, type RunRateMrrResult, type SendInvoiceParams, type SendInvoiceResponse, type ServiceCatalogEntry, type SingleEventResponse, type StrategyRevenue, type Subscription, type SubscriptionAgent, type SubscriptionCustomer, type SubscriptionDetail, type SubscriptionDetailWithRevenue, type SubscriptionPlan, type SubscriptionRevenue, type SubscriptionStatus, type SubscriptionUsage, type TelemetryHandler, type TelemetryOptions, type TelemetryStats, TimeoutError, type TrackEventParams, type TrackEventResponse, type UpdateCustomerData, type UpdatePricingPlanData, type UpdatePricingStrategyData, type UsageAnalyticsDataPoint, type UsageAnalyticsParams, type UsageAnalyticsResponse, type UsageAnalyticsSummary, type UsageRecord, type UsageRecordFailure, type UsageRecordResponse, type UsageRecordSuccess, ValidationError, type VerifyResponse, createErrorFromResponse, isPublishableKey, isSecretKey, parseApiError, parseApiKey };