On September 25th, 2024, we released v2 of the Apps SDK. To learn what’s new and how to upgrade, see Migration FAQ and Migration guide.

Implement premium apps features

Learn how to add premium features to your app.

This article is for apps that have been accepted into the Premium Apps Program(opens in a new tab or window). If your app hasn't been accepted into the program, this article does not apply to you.

This guide shows you how to implement the premium apps API in your app.

Our design guidelines help you create a high-quality app that easily passes app review.

Step 1: Decide what Premium apps features to implement

  1. Before you start implementing premium app features, send us a Slack message to provide us with:

    • A list of the distinct premium features you'd like to build — or let us know if you plan to make the whole app paywalled.
    • The App ID(s) you're using. If you're using separate apps for different stages of your development workflow (for example: development, staging, production), provide all relevant App IDs.
  2. For each premium feature you provide, we'll add a specific billable action to your app. For a list of possible billable actions, see Billable actions.

  3. After you receive the assigned billable action for each of your premium features, you can start implementing them in your app.

Step 2: Install the Premium apps SDK

  1. Download and unzip the ZIP file that Canva has shared with you.

  2. Move the user folder into the starter kit's sdk folder. If an sdk folder doesn’t exist in your starter kit, create it.

  3. In the starter kit's package.json file, replace the dependency for @canva/user with the local package "@canva/user": "./sdk/user":

    {
    ...
    "dependencies": {
    ...
    - "@canva/user": "^2.0.0",
    + "@canva/user": "./sdk/user"
    ...
    },
    "devDependencies": {
    ...
    }
    }
    DIFF
  4. In the starter kit’s tsconfig.json file, add @canva/user to the paths array:

    {
    "compilerOptions": {
    ...
    "paths": {
    "assets": ["./assets"],
    "styles": ["./styles"],
    + "@canva/user": ["./sdk/user"]
    }
    },
    ...
    }
    DIFF
  5. If you're upgrading from the previous apps bundling experiment, make sure to remove any references to the previous experiment API. For example, remove references to UserCapabilitiesToken, UserInfo, and so on.

  6. Run the following command to install the dependencies:

    npm install
    SHELL

Step 3: Check whether a user can perform a billable action

Before rendering a premium feature, you first need to check whether a user can perform the billable action.

This is done using the monetization API, which has an isEnabled method that you can use to check whether a user is enabled for a billable action.

import { monetization } from "@canva/user";
const checkBillableAction = async (action: BillableAction) => {
const canPerformAction = await monetization.isEnabled(action);
if (canPerformAction) {
// The user is enabled for the billable action
} else {
// The user is not enabled for the billable action
}
};
TYPESCRIPT

Step 4: Display your UI

When displaying your app UI, you must customize what is shown depending on whether the user is enabled for a billable action.

In general, you should use the included components in @canva/user/components with isEnabled. This especially includes displaying the pro badge and the premium crown icon.

For details on the components and what UI to display, make sure to follow the design guidelines that have been provided to you.

For example:

const [isActionEnabled, setIsActionEnabled] = useState<boolean>(false);
useEffect(() => {
const checkIsActionEnabled = async () => {
setIsActionEnabled(await monetization.isEnabled("generate_image"))
};
checkIsActionEnabled().catch(e => {
// handle error
});
}, [])
// In JSX
// Premium badge usage
<PremiumBadge enabled={isActionEnabled} />
// Premium icon usage
{isActionEnabled ? <PremiumFilledIcon /> : <PremiumFilledGoldIcon />}
TYPESCRIPT

Step 5: Handle not-enabled users executing premium features

If a user isn't enabled for a billable action and they attempt to use a premium feature, you should prompt them to upgrade to a paid Canva plan. You can do this using the monetization.requestEnableBillableAction API. For example:

const performBillableAction = async (action: BillableAction) => {
const canPerformAction = await monetization.isEnabled(action);
if (canPerformAction) {
// Do the billable action
} else {
const res = await monetization.requestEnableBillableAction(action);
if (res.status !== "granted") {
// Deny access to the billable action
return;
}
// Do the billable action
}
};
TYPESCRIPT

Step 6: Execute premium features

If a user is enabled for a billable action, you can execute the premium feature.

Configure your backend

We recommend that apps have a backend for performing billable actions. If your app doesn't have a backend, checking that a user is enabled with monetization.isEnabled is sufficient. For more information, see Security considerations.

To configure your backend, you must do the following:

  • Set up separate endpoints for each billable action.
  • For HTTP requests going to your backend, verify the requests with the CanvaUserToken JWT.

Set up endpoints for each billable action

To prevent abuse, you must avoid sending the billable action's name in HTTP requests. Instead, we recommend that you configure dedicated endpoints for each billable action, and then follow the next section to check that the billableActions claim has the matching action.

Verify HTTP requests

When making fetch calls to your app's backend, you must verify the HTTP request using the CanvaUserToken JWT.

For apps with billable actions, the CanvaUserToken includes an extra JWT claim: billableActions.

Follow the process described in HTTP request verification to verify the HTTP request in your backend, with one important difference: when checking the claims, you must also check the billableActions claim. For example, the following code example checks for the generate_image billable action:

if (
!verified.aud ||
!verified.brandId ||
!verified.userId ||
!verified.billableActions
) {
return response.sendStatus(401);
}
if (!verified.billableActions.includes("generate_image")) {
return response.sendStatus(403);
}
TYPESCRIPT

Run your code in a tracking session

An important part of executing a premium feature for a billable action is using a tracking session. This allows Canva to correctly attribute your app's usage.

When using tracking sessions, it's very important to do the following:

  • Use the correct billable action when opening a tracking session.
  • Only open a tracking session immediately before you start work on a billable action. Valid work includes things like relevant SDK calls or calling fetch for tasks directly related to executing the premium feature.
  • Defer any calls that don't contribute to the billable action until after you close the tracking session. Examples of what to defer include:
    • Analytics calls.
    • Calls to your backend that aren't directly related to the billable action.
    • Non-billable calls which don't require the user to be enabled for a billable action. For example, uploading non-premium assets.
  • Close a tracking session immediately after you finish work on a billable action.

You can also continue a tracking session after closing it if you need to do non-billable work while a tracking session is open.

Basic tracking session example

For example, a simple app that imports premium assets should open a tracking session using monetization.openTrackingSession with the import_image billable action before uploading that image and adding it to the design. It should close the tracking session immediately afterwards.

const importPremiumAsset = async () => {
const session = await monetization.openTrackingSession({
action: "import_image",
});
const image = await upload({
type: "image",
mimeType: "image/jpeg",
url: "https://www.canva.dev/example-assets/image-import/image.jpg",
thumbnailUrl:
"https://www.canva.dev/example-assets/image-import/thumbnail.jpg",
aiDisclosure: "none",
});
await addElementAtPoint({
type: "image",
ref: image.ref,
});
session.closeTrackingSession();
};
TYPESCRIPT

HTTP requests with tracking sessions

In this example, a more complex app (such as an AI image generation app) might need to make an HTTP request. After opening a tracking session, make sure you do the following:

  1. Add the Canva-Premium-Usage-Id header to the fetch call, with the tracking session's ID as the value. Only pass this header with HTTP requests that directly contribute to the completion of a billable action. Don't attach this header to non-essential calls, such as analytics.
  2. Add the CanvaUserToken in the Authorization header, and make sure to verify the request and the billable action name in your backend.
async function generateImage(): Promise<TrackingId> {
const token = await auth.getCanvaUserToken();
// Start tracking the generation of an image
const generationSession = monetization.openTrackingSession({
action: "generate_image",
});
try {
const response = await fetch("https://example.com/generate", {
headers: {
Authorization: `Bearer ${token}`,
"Canva-Premium-Usage-Id": generationSession.id,
},
});
// Do things with the response
// Close the session after image generation completes
await generationSession.closeTrackingSession();
return generationSession.id;
} catch (error) {
// Log the error
}
}
TYPESCRIPT

Continue a closed tracking session

There may be situations when you need to do tasks not related to a billable action while a tracking session is open. You can close a session and keep track of the ID, and then continue with the same session later.

// Start a tracking for generation of an image
const generationSession = monetization.openTrackingSession({
action: "generate_image",
});
// Do some billable work
// Close the session and keep the ID
await generationSession.closeTrackingSession();
// Do some non-billable work
// Continue the previous session
const imageAdditionSession = monetization.openTrackingSession({
id: generationSession.id,
});
// Do some more billable work
// Close the session after the work is done
await imageAdditionSession.closeTrackingSession();
TYPESCRIPT

Security considerations

As with building any app on Canva, you must comply with the Security guidelines and Shared responsibility model for Canva Apps. For premium apps, there are some additional security considerations when using Monetization API.

Apps without a backend

Apps that do work for a billable action only in the frontend are inherently vulnerable to users tampering with the JavaScript bundle to access the feature for free.

Canva has measures to limit these attacks, such as obfuscating the JavaScript bundles, but because of the nature of JavaScript, these attacks are always possible.

If you suspect a user is fraudulently accessing your premium app features, contact us and provide as much evidence as possible. We will investigate and take appropriate action.

Where practical, consider implementing high-value parts of a billable action in a backend which is better protected.

Check CanvaUserToken when making fetch calls

As mentioned previously, when your app makes fetch calls to your backend, you must verify the HTTP request using the CanvaUserToken, and when checking claims, also check the billableActions claim.

If it done incorrectly, you might expose your app to fraudulent use.

Check for the correct billable actions

It's the responsibility of the app to check and use the correct billable action for a premium feature. If you use the incorrect billable action, you may allow ineligible users to access your app's premium features. Canva is also unable to accurately attribute your app's premium usage.

Test your app's billable actions

You can use the developer menu to override your own account's billable actions and test when certain billable actions are on or off. This saves you from having to switch between Canva Free and Pro accounts to see how your app behaves.

  1. In the developer menu, enable Billable actions override.

    Billable actions override

  2. Use the menu to select which billable actions are enabled or disabled. You can enable all billable actions, disable all billable actions, or enable specific billable actions.

    Whatever you select will override the billable actions for your account only. This includes removing any billable actions that Canva has enabled for you.

    Billable actions override menu

  3. When you are finished testing, disable Billable actions override to return to your account's normal billable actions.