Docs navigation
Customer + Subscription
The shapes the SDK accepts on its customer and subscriptions props. Pass these in analytics mode and in Direct-mode token sessions. With a connected billing provider, you pass only the IDs and Churnkey looks up the rest from the provider.
The Direct prefix marks these as data passed directly from your code, as opposed to the equivalents Churnkey assembles from a connected provider.
DirectCustomer
interface DirectCustomer {
id: string
email?: string
name?: string
lastName?: string
phone?: string
currency?: string // ISO 4217
addresses?: DirectAddress[]
metadata?: Record<string, unknown>
}| Field | Notes |
|---|---|
id | Required. Sessions are keyed by this. With a connected provider, use the provider's customer ID (cus_xxx for Stripe). In Direct mode, use your internal ID. |
email | Forwarded to the Churnkey session payload. Used by Feedback AI and email automations on the Intelligence tier. |
name, lastName, phone | Available on the customer object inside your handlers and listeners. Not forwarded to the session payload — set them in the token payload when you mint if you need them in the dashboard. |
currency | Used to format prices when DirectPrice.amount.currency isn't set. |
metadata | Forwarded to analytics and webhook payloads. Use it for plan tier, account age, or any field your segments target. |
DirectAddress
interface DirectAddress {
line1?: string
line2?: string
city?: string
state?: string
postalCode?: string
country?: string // ISO 3166-1 alpha-2
}Used by tax-aware offer rendering in jurisdictions that require it.
DirectSubscription
interface DirectSubscription {
id: string
customerId?: string // optional cross-reference
start: Date | string // ISO 8601 or Date
status: SubscriptionStatus // tagged union, see below
items: DirectSubscriptionItem[]
duration?: DurationConfig
end?: Date | string
discounts?: DirectDiscount[]
metadata?: Record<string, unknown>
}
interface DirectSubscriptionItem {
id?: string
price: DirectPrice
quantity?: number
}
interface DurationConfig {
interval: 'day' | 'week' | 'month' | 'year'
intervalCount?: number // defaults to 1
}
interface DirectDiscount {
id?: string
coupon?: DirectCoupon
start?: Date | string
end?: Date | string
}
interface DirectCoupon {
id?: string
name?: string
percentOff?: number
amountOff?: number // smallest currency unit (cents for USD)
currency?: string
duration?: 'once' | 'repeating' | 'forever'
durationInMonths?: number
metadata?: Record<string, unknown>
}SubscriptionStatus
type SubscriptionStatus =
| { name: 'active'; currentPeriod: { start: Date | string; end: Date | string } }
| { name: 'trial'; trial: { start: Date | string; end: Date | string };
currentPeriod?: { start: Date | string; end: Date | string } }
| { name: 'paused'; pause: { start: Date | string; end?: Date | string };
currentPeriod?: { start: Date | string; end: Date | string } }
| { name: 'canceled'; canceledAt: Date | string }
| { name: 'unpaid'; currentPeriod?: { start: Date | string; end: Date | string } }
| { name: 'future'; currentPeriod?: { start: Date | string; end: Date | string } }The status tagged union carries the subscription's lifecycle state. The default Confirm component reads currentPeriod.end to render a "Your access continues until X" notice when present; in connected mode the dashboard can target offers at specific statuses through segments. The SDK does not suppress or substitute offers client-side based on status — your steps and the dashboard config decide what gets shown.
DirectPrice
interface DirectPrice {
id: string
type?: 'standalone' | 'product'
active?: boolean
productId?: string
name?: string
description?: string
duration?: DurationConfig
amount: {
model?: 'fixed' | 'tiered'
value: number // smallest currency unit (cents for USD)
currency?: string // ISO 4217
}
metadata?: Record<string, unknown>
}amount.value is in the smallest currency unit — cents for USD, pence for GBP, yen for JPY (no division). The SDK formats prices based on currency.
A complete example
<CancelFlow
appId="app_xxx"
customer={{
id: 'cus_123',
email: 'jane@acme.com',
name: 'Jane',
metadata: { plan: 'pro', signupDate: '2024-03-12' },
}}
subscriptions={[{
id: 'sub_456',
start: '2024-03-12',
status: {
name: 'active',
currentPeriod: {
start: '2025-04-01',
end: '2025-05-01',
},
},
items: [{
price: {
id: 'price_pro',
name: 'Pro monthly',
amount: { value: 2999, currency: 'USD' },
duration: { interval: 'month' },
},
}],
discounts: [{
coupon: {
id: 'STRIPE_LOYAL10',
percentOff: 10,
},
end: '2025-07-01',
}],
}]}
steps={steps}
handleCancel={handleCancel}
/>Next steps
<CancelFlow>reference — wherecustomerandsubscriptionsare passed.- Integration levels — when to provide each prop.
- Server-side tokens — Direct mode token minting.