---
title: Publish your Integration
description: Learn how to make your integration available to Churnkey.
---

Before following this guide, make sure that you have implemented all the required controllers and actions.

## Get Integration Token

To obtain your `Integration Token`, go to the Churnkey dashboard -> Settings -> Billing Providers:

![Quick start guide](/img/placeholder-black.png)

Choose `Custom Provider` and copy the `Integration Token`:

![Quick start guide](/img/placeholder-black.png)

Never store your `Integration Token` in your codebase. Use environment variables or a secure vault to store it.

## Make integration publicly available
::tabs
  ::div{label="With SDK"}
    First you need to create `Integration` instance, where you should pass your `Context` and all the controllers and actions you implemented.

    ::code-group
    ```typescript [Typescript]
    import { Integrator } from '@churnkey/sdk'
    import { Context } from './Context'
    import { Customers } from './controllers/Customers'
    import { Prices } from './controllers/Prices'
    import { Subscriptions } from './controllers/Subscriptions'
    import { Coupons } from './controllers/Coupons' // optional
    import { Cancel } from './actions/Cancel' // optional
    import { ApplyCoupon } from './actions/ApplyCoupon' // optional
    import { ExtendTrial } from './actions/ExtendTrial' // optional
    import { ChangePrice } from './actions/ChangePrice' // optional
    import { Pause } from './actions/Pause' // optional

    export const Integration = new Integrator.Integration({
        ctx: Context,
        name: 'YourCompanyName',
        modules: {
            controllers: {
                Customers,
                Prices,
                Subscriptions,
                Coupons // optional
            },
            actions: {
                Cancel, // optional
                ApplyCoupon, // optional
                ExtendTrial, // optional
                ChangePrice, // optional
                Pause // optional
            }
        }
    })
    ```
    ::

    Next, in your router file, you should expose the `Integration` to the internet. Authentication and features manifest are handled by the SDK, so you don't need to worry about it.

    ::code-group
    ```typescript [Typescript Express]
    import express from 'express'
    import { Integration } from './churnkey/Integration'
    import { Context } from './churnkey/Context'

    const app = express()
    Integration.expose({
        app: app, // express app instance
        token: process.env.CK_INTEGRATION_TOKEN, // your integration token
        ctx(req, req) {
            return new Context(
                // initialize your context here
                // parameters can vary depending on your implementation
            )
        } 
    })
    ```
    ::
  ::
  ::div{label="Without SDK"}
    If SDK is not available in your preferred programming language, you should follow this guide to make your integration available to Churnkey.

    ### Features
    You should add an endpoint which will return a list of supported features, we call it `Feature Manifest`. This manifest tells us what `Controllers`, `Actions`
    and their behavior are supported by your integration.

    ::collapsible{name="schema"}
    :field-schema{schema="/types/features.type.json"}
    ::

    ::collapsible{name="code example"}
    ```typescript [Typescript Express]
    import { features } from '../actions/Cancel'

    app.get('/churnkey/features', async (req, res) => {
        res.send({
            controllers: {
                customers: true,
                prices: {
                    enabled: true,
                    type: 'product'
                }
                subscriptions: true,
                coupons: true // optional
                products: { // optional
                    enabled: true,
                    type: 'family'
                },
                families: true // optional
            },
            actions: {
                cancel: features, // optional
                ... // other actions features if you have them
            }
        })
    })
    ```
    ::

    ### Authentication
    Each request to your implemented endpoints will contain a `Authorization` header with a Bearer token. You should compare this token with your `Integration Token`, if they match you should consider request authorized. 

    ::collapsible{name="code example"}
    ```typescript [Typescript Express]
    // add express middleware before all your routes
    app.use(async (req, res, next) => {
        if (req.headers.authorization !== process.env.CK_INTEGRATION_TOKEN) {
            return res.status(401).send({ code: 401, message: 'Unauthorized' })
        }
        next()
    })
    ```
    ::

    ### Verify endpoints
    Make sure that you implemented all required `Controller` and `Action` endpoints and your auth middleware is protecting them from unauthorized access.
  ::
::


## Verify your Integration

Go to the Churnkey dashboard -> Settings -> Billing Providers. You should see that your `Custom Provider` is pending verification:
![Quick start guide](/img/placeholder-black.png)

Add your `API URL` to the `Custom Provider`. This is your API root URL, we will append the endpoint with `/churnkey` prefix to it automatically:
![Quick start guide](/img/placeholder-black.png)

Click on the `Verify` button to verify your integration:
![Quick start guide](/img/placeholder-black.png)

You should see results and list of supported features soon:
![Quick start guide](/img/placeholder-black.png)

## Troubleshooting
If you see any errors during the verification process, the most common issues are:
- Incorrect `Integration Token`
- Incorrect data format (e.g. wrong `Model` implementation)
- Incorrect endpoint implementation (e.g. wrong url or wrong response)
- `Features` endpoint is not implemented

Generally, you should be able to fix most of the issues by following the error message you see. If you are still stuck, feel free to reach out to our support team, we are happy to help you.

Keep in mind that we're following modular architecture, so if one of the optional modules doesn't work, other modules should work fine.

## Updating integration
Over time you may want to add new features or fix bugs in your integration. 

We cache the `Feature Manifest`, so if your update includes changes to the `Feature Manifest`, you should re-verify your integration. If you only update the implementation, you don't need to re-verify it.

### Manual verification
You can go to the Churnkey dashboard -> Settings -> Billing Providers and click on the `Verify` button to re-verify your integration.

### Automatic verification
You can call the verify endpoint programmatically from your CI/CD or during the server start. This way, you can ensure that your integration is always up-to-date and verified.

::collapsible{name="code example"}
::code-group
```bash [CI/CD]
curl -X POST https://api.churnkey.co/v2/verify -H "Authorization: Bearer $CK_API_KEY"
```
```typescript [Typescript]
// in your server file
fetch('https://api.churnkey.co/v2/verify', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${process.env.CK_API_KEY}`
    }
})
```
```typescript [Typescript SDK]
import { Integrator } from '@churnkey/sdk'
Integrator.verify(process.env.CK_API_KEY)
```
::
