---
title: Churnkey Direct
description: Integrate Churnkey with any billing system using Direct mode
---

Churnkey Direct lets you integrate retention flows with any billing system. Instead of connecting to Stripe or Chargebee, you pass customer and subscription data directly and handle billing operations through callbacks.

**Use Direct mode when:**

- You use a custom or unsupported billing system
- You have strict data privacy requirements
- You're managing trial or freemium users without subscriptions
- You need to test flows before setting up a full integration

## How it works

1. **Pass customer data** when initializing Churnkey
2. **Customer interacts** with your retention flow
3. **Your handlers execute** billing operations (cancel, discount, pause, etc.)
4. **Churnkey tracks results** in analytics automatically

## Quick Start

```javascript
window.churnkey.init('show', {
  appId: 'your_app_id',
  mode: 'live',
  provider: 'direct',
  authHash: 'hmac_signature', // See Authentication section

  customer: {
    id: 'cus_12345',
    email: 'customer@example.com',
  },

  subscriptions: [
    {
      id: 'sub_67890',
      start: new Date('2024-06-01'), // When subscription started
      status: {
        name: 'active',
        currentPeriod: {
          start: new Date('2025-01-01'),
          end: new Date('2025-02-01'),
        },
      },
      items: [
        {
          price: {
            id: 'price_pro',
            amount: { value: 2999, currency: 'usd' },
            interval: 'month',
            intervalCount: 1,
          },
        },
      ],
    },
  ],

  async handleCancel(customer, surveyChoice, feedback, followupResponse) {
    await yourAPI.cancelSubscription(customer.id);
    return { message: 'Subscription canceled.' };
  },
});
```

For script installation and HMAC authentication, see the [Quick Start Guide](/cancel-flows/quick-start-guide).

## Customer object

Identifies who is interacting with the retention flow. Only `id` is required.

:field-schema{schema="/types/direct/customer-direct.type"}

**Usage:** Additional fields like `email`, `name`, and `metadata` are used for segmentation, Slack notifications, merge fields, and analytics.

## Subscription object

Describes the customer's billing state and determines which offers are shown.

:field-schema{schema="/types/direct/subscription-direct.type"}

### Subscription status

The `status` field determines which offers are available. For example, trial extensions only appear for trial subscriptions.

::code-group

```javascript [Active]
status: {
  name: 'active',
  currentPeriod: {
    start: new Date('2025-01-01'),
    end: new Date('2025-02-01'),
  },
}
```

```javascript [Trial]
status: {
  name: 'trial',
  trial: {
    start: new Date('2025-01-01'),
    end: new Date('2025-01-14'),
  },
  // currentPeriod is optional - defaults to trial period
}
```

```javascript [Paused]
status: {
  name: 'paused',
  pause: {
    start: new Date('2025-01-15'),
    end: new Date('2025-02-15'), // Omit for indefinite pause
  },
  currentPeriod: {
    start: new Date('2025-01-01'),
    end: new Date('2025-02-01'),
  },
}
```

```javascript [Canceled]
status: {
  name: 'canceled',
  canceledAt: new Date('2025-01-15'),
}
```

```javascript [Unpaid]
status: {
  name: 'unpaid',
  currentPeriod: {
    start: new Date('2025-01-01'),
    end: new Date('2025-02-01'),
  },
}
```

::

### Subscription items

Items represent the products and pricing on a subscription:

::code-group

```javascript [Monthly]
items: [
  {
    id: 'si_basic',
    price: {
      id: 'price_basic_monthly',
      amount: { value: 2999, currency: 'usd' }, // $29.99
      interval: 'month',
      intervalCount: 1,
    },
    quantity: 1,
    product: {
      id: 'prod_basic',
      name: 'Basic Plan',
    },
  },
];
```

```javascript [Annual]
items: [
  {
    id: 'si_pro',
    price: {
      id: 'price_pro_annual',
      amount: { value: 29900, currency: 'usd' }, // $299/year
      interval: 'year',
      intervalCount: 1,
    },
    quantity: 1,
    product: {
      id: 'prod_pro',
      name: 'Pro Plan',
    },
  },
];
```

```javascript [Per-seat]
items: [
  {
    id: 'si_team',
    price: {
      id: 'price_per_seat',
      amount: { value: 1500, currency: 'usd' }, // $15/seat
      interval: 'month',
      intervalCount: 1,
    },
    quantity: 10, // 10 seats
    product: {
      id: 'prod_team',
      name: 'Team Plan',
    },
  },
];
```

::

See [Direct mode examples](/billing-providers/direct-connect/direct-examples) for usage-based billing, paused subscriptions, and complex scenarios.

## Handlers

Handlers execute billing operations when customers accept offers. **Each offer requires its corresponding handler**—if you configure a pause offer in the [Flow Builder](https://app.churnkey.co/cancellation/builder) but don't provide `handlePause`, the offer won't appear.

::code-group

```javascript [Cancel (required)]
async handleCancel(customer, surveyChoice, feedback, followupResponse) {
  // surveyChoice: Selected cancellation reason (string)
  // feedback: Additional text feedback (string | null)
  // followupResponse: Follow-up question answers (object | null)

  try {
    await yourAPI.cancelSubscription(customer.id);
    return { message: 'Your subscription has been canceled.' };
  } catch (error) {
    // Error messages are shown to customers
    throw new Error('Unable to cancel. Please contact support.');
  }
}
```

```javascript [Pause]
async handlePause(customer, pauseOptions) {
  // pauseOptions: {
  //   pauseEndDate: Date,
  //   pauseDurationDays: number
  // }

  await yourAPI.pauseSubscription(
    customer.id,
    pauseOptions.pauseEndDate
  );

  return {
    message: `Paused until ${pauseOptions.pauseEndDate.toLocaleDateString()}`,
  };
}
```

```javascript [Discount]
async handleDiscount(customer, coupon) {
  // coupon: {
  //   id: string,
  //   percentOff?: number,
  //   amountOff?: number,
  //   duration: 'once' | 'repeating' | 'forever',
  //   durationInMonths?: number
  // }

  await yourAPI.applyDiscount(customer.id, {
    percentOff: coupon.percentOff,
    duration: coupon.duration,
  });

  return { message: `${coupon.percentOff}% discount applied!` };
}
```

```javascript [Change Plan]
async handlePlanChange(customer, planOptions) {
  // planOptions: {
  //   newPriceId: string,
  //   oldPriceId: string
  // }

  await yourAPI.updateSubscriptionPlan(
    customer.id,
    planOptions.newPriceId
  );

  return { message: 'Plan updated successfully' };
}
```

```javascript [Trial Extension]
async handleTrialExtension(customer, trialOptions) {
  // trialOptions: {
  //   trialExtensionDays: number,
  //   newTrialEndDate: Date
  // }

  await yourAPI.extendTrial(
    customer.id,
    trialOptions.newTrialEndDate
  );

  return { message: `Trial extended ${trialOptions.trialExtensionDays} days` };
}
```

```javascript [Support]
handleSupportRequest(customer) {
  // Open your support widget or redirect
  if (window.Intercom) {
    window.Intercom('show');
  } else {
    window.location.href = '/support';
  }
}
```

```javascript [Redirect]
handleRedirect(customer, url) {
  window.location.href = url;
}
```

::

## Configuring offers

Configure offers in the [Flow Builder](https://app.churnkey.co/cancellation/builder). Churnkey automatically shows or hides offers based on:

- **Subscription status** — Trial extensions only appear for trial subscriptions
- **Billing interval** — Annual customers won't see pause offers by default
- **Handler availability** — Offers only appear if you've implemented the handler

You configure offer values (discount percentages, pause durations, etc.) in the Flow Builder, then implement the billing changes in your handlers.

## Segmentation

Create targeted flows for different customer segments based on:

- **Subscription attributes** — Status, billing interval, subscription duration
- **Customer metadata** — Custom attributes via `customer.metadata` and `subscription.metadata`
- **Billing amounts** — MRR, subscription value, pricing tier

Configure segments in the Flow Builder under **Audience Targeting**. [Learn more about segmentation →](https://churnkey.co/blog/launch-customer-segmentation)
