---
title: Cancel - Action
description: Cancels a subscription at specific date. 
---

To implement the `Cancel` action, you need to implement an endpoint and define `features`

If you don't implement this action, the :badge{label='Cancel Flow'} will not be available to your customers, even if you implemented other actions.

## Prerequisites
::grid{cols="2"}
    ::card-baseline{title="Subscriptions - Controller" to="/integrations/controllers/subscriptions"}
      You must implement a `Subscriptions` controller first.
      ::flex{class="mt-4"}
        :badge{label="Required" color="green"}
      ::
    ::
::

## SDK

If you are using the SDK, you can implement the `Cancel` action by following the code example below. You don't need to get into the details of the API endpoints, the SDK will take care of that for you.

::code-group
```typescript [Typescript]
import { Integrator } from '@churnkey/sdk'
import { Subscriptions } from '../controllers/Subscriptions'

export const Cancel = Integrator.Cancel.config({
    Subscriptions: Subscriptions,
    features: {
        // define which schedules are supported, at least one is required
        schedules: { 
            [Integrator.Cancel.Schedule.Immediate]: true,
            [Integrator.Cancel.Schedule.EndOfPeriod]: true
        }
    },
    async handle(ctx, options) {
        const subscription = await this.subscriptions.retrieve({
            customerId: options.customerId,
            id: options.subscriptionId
        })

        switch (options.scheduledAt) {
            case Integrator.Cancel.Schedule.Immediate:
                await ctx.db.cancelSubscription(subscription)
                break
            case Integrator.Cancel.Schedule.EndOfPeriod:
                await ctx.db.cancelSubscriptionAtThePeriodEnd(subscription)
                break
        }
    }
})
```
::

## Endpoints

### Handle :badge{label="Required" color="green"}

`POST /churnkey/actions/subscription/cancel`

This endpoints handles the single subscription cancellation. You should find the subscription by `customerId` and `subscriptionId` and cancel it.

Options for cancellation, provided in the request body.

::collapsible{name="request body"}
  :field-schema{schema="/types/actions/cancel/request.type.json"}
::

::collapsible{name="response"}
    ::tabs
        ::div{label="200"}
        Must return empty response.
        ::
        ::div{label="Error"}
        See [Error Responses](/integrations/general#error-responses).
        ::
    ::
::

::collapsible{name="code example"}
  ::code-group
  ```typescript [Typescript Express]
    app.post('/churnkey/actions/subscription/cancel', async (req, res) => {
        const subscription = await db.findSubscription(req.body.customerId, req.body.subscriptionId)

        switch (req.body.scheduledAt) {
            case 'immediate':
                await db.cancelSubscription(subscription)
                break
            case 'end_of_period':
                await db.cancelSubscriptionAtThePeriodEnd(subscription)
                break
        }

        res.send()
    })
  ```
  ::
::

### Handle All :badge{label="optional"}

`POST /churnkey/actions/customer/cancel`

This endpoint handles cancellation of all customer's subscriptions. You should find all subscriptions by `customerId` and cancel them.

This endpoint is optional. By default, when we need to cancel all subscriptions, we will call the `Handle` endpoint for each subscription. You can implement this endpoint to reduce the number of API calls and improve performance/end-user UX.

Options for cancellation, provided in the request body.

::collapsible{name="request body"}
  :field-schema{schema="/types/actions/cancel/request-all.type.json"}
::

::collapsible{name="response"}
    ::tabs
        ::div{label="200"}
        Must return empty response.
        ::
        ::div{label="Error"}
        See [Error Responses](/integrations/general#error-responses).
        ::
    ::
::

::collapsible{name="code example"}
  ::code-group
  ```typescript [Typescript Express]
    app.post('/churnkey/actions/customer/cancel', async (req, res) => {
        const subscriptions = await db.findSubscriptionsByCustomerId(req.body.customerId)

        switch (req.body.scheduledAt) {
            case 'immediate':
                await db.cancelSubscriptions(subscriptions)
                break
            case 'end_of_period':
                await db.cancelSubscriptionsAtThePeriodEnd(subscriptions)
                break
        }

        res.send()
    })
  ```
  ::
::

## Features

Features define which behavior is supported for the `Cancel` action. Depending on the features you enabled, requests body will have different options.

For example, if you enable only `end-of-period` schedule, the `request.body.scheduledAt` will be always `end-of-period`. If you enable both schedules, `request.body.scheduledAt` can be either `immediate` or `end-of-period`.

::collapsible{name="schema"}
  :field-schema{schema="/types/actions/cancel/features.type.json"}
::
::collapsible{name="code example"}
  ::code-group
  ```typescript [Typescript]
  export interface Features {
    enabled: boolean
    schedules: {
      immediate: boolean
      end_of_period: boolean
    }
  }

  export const features: Features = {
    enabled: true,
    schedules: {
      immediate: true,
      end_of_period: false // this schedule will be disabled
    }
  }
  ```
::