User post-authentication workflow
Workflows
When users downgrade or cancel plans, you want to check that they are able to do so without exceeding usage limits of the plan they are downgrading to.
In this tutorial, we’ll focus on how to deny cancellations for Enterprise plans, ensuring that customers who have signed legal agreements can only cancel by contacting support - while still allowing self-serve cancellation for other plans.
enterprise)http://localhost:3000/dashboard in the Return URL field.Fork the workflow base template repo into your GitHub account by selecting Use this template > Create a new repository.
Clone the repo into your computer with the following Git command.
git clone https://github.com/your_github_username/workflow-base-template.gitChange into the directory.
cd workflow-base-templateRemove the sample workflow with the command.
rm -rf kindeSrc/environment/workflows/postUserAuthenticationCreate a new workflow with the following command.
You can name it anything that resonates with your project structure; we are calling it denyPlanCancel.
mkdir kindeSrc/environment/workflows/denyPlanCanceltouch kindeSrc/environment/workflows/denyPlanCancel/Workflow.tsOpen the new file with your preferred code editor (e.g., VS Code) and add the following workflow code into the file and save changes.
import { onPlanCancellationRequest, WorkflowSettings, WorkflowTrigger, denyPlanCancellation, getEnvironmentVariable,} from "@kinde/infrastructure"
// The setting for this workflowexport const workflowSettings: WorkflowSettings = { id: "onUserPlanCancellationRequest", trigger: WorkflowTrigger.PlanCancellationRequest, name: "Deny Plan Cancellation", failurePolicy: { action: "stop", }, bindings: { "kinde.plan": {}, "kinde.env": {}, url: {}, },}
// The workflow code to be executed when the event is triggeredexport default async function Workflow(event: onPlanCancellationRequest) { const { currentPlanCode } = event.context.billing
// Configurable list of enterprise-like SKUs (comma-separated), default to "enterprise" const ENTERPRISE_CODES = ( getEnvironmentVariable("ENTERPRISE_PLAN_CODES").value || "enterprise" ) .split(",") .map((s) => s.trim().toLowerCase())
const isEnterprise = !!currentPlanCode && ENTERPRISE_CODES.includes(String(currentPlanCode).toLowerCase())
if (isEnterprise) { denyPlanCancellation( "Your subscription is managed by our team. To make changes, please contact support." ) }}Optional: Install the Kinde infrastructure dependency with the following bash command for TypeScript intellisense.
npm installRun the following git commands in your terminal to push the changes to your GitHub repo:
git add .git commit -m "add new deny plan cancellation workflow"git pushHere’s a quick breakdown of what the code does.
Imports
The top of the file brings in useful methods and types from the @kinde/infrastructure package:
onPlanCancellationRequest – the event type for plan cancellation requests.WorkflowSettings and WorkflowTrigger – used to configure when the workflow runs.denyPlanCancellation – stops a cancellation and shows a message to the user.getEnvironmentVariable – retrieves environment variables set in the Kinde dashboard.Workflow settings
The workflowSettings object defines how and when the workflow executes:
id is a unique identifier for the workflow.trigger is set to PlanCancellationRequest, meaning the workflow runs whenever a customer tries to cancel their plan.name is for easy reference in the dashboard.failurePolicy with action: "stop" ensures the workflow halts cleanly if something goes wrong.bindings enable access to Kinde plan info, environment variables, and URLs inside the workflow.Main workflow logic
The Workflow function is executed on each cancellation attempt:
It extracts the currentPlanCode from the billing context.
It loads a configurable list of Enterprise-like SKUs from the ENTERPRISE_PLAN_CODES environment variable. If none is set, it defaults to "enterprise".
It checks whether the current plan matches one of the Enterprise SKUs.
If the plan is Enterprise, it calls denyPlanCancellation() and shows a custom message to the user:
“Your subscription is managed by our team. To make changes, please contact support.”
Result
Enterprise customers won’t be able to cancel through the self-serve portal. Instead, they’ll see a clear message directing them to sales or support. All other customers (Free, Pro, etc.) continue with the normal cancellation flow.
Go to Workflows.
If you already have your workflow repo connected, go straight to step 4.
Select Connect repo > Connect GitHub and follow the onboarding flow to authorize Kinde to access your GitHub account.
From the dropdown, select your GitHub repository that contains the Kinde workflow code, choose the main branch, and click Next.
If you already have a repo connected and want to change it, go to Settings > Git repo > Change repo.
Select Sync code to pull your latest workflow code from GitHub into your Kinde project.
Go to Workflows to see your workflow listed inside the dashboard.
Go to Settings > Env variables and select Add environment variable.
ENTERPRISE_PLAN_CODESenterprise,enterprise_plus)Setup a sample Kinde project and run it.
Open your Kinde project and go through the sign up process. You will see the plan selection page.
Select the SDR Enterprise Standard plan. Use sample credit card numbers:
4242 4242 4242 424212/34123Now select My Account > Plan > Cancel plan. You will be restricted from canceling the plan with the message:
Your subscription is managed by our team. To make changes, please contact support.
By adding a workflow to handle cancellation requests, you can create clear rules for cancelling plans directly in the self-serve portal.
In our example, we blocked Enterprise customers from canceling on their own, ensuring they follow the proper sales and support channels defined in their agreements. With Kinde, you can combine these types of rules with your other billing workflows to build a billing experience that fits your business model.