Extensibility with Scripts and Workflows
Advancing developer craft
Runtime
Complete form to watch full video
Scripts and Workflows let you build inside Stripe, not just on top of it. See how to program custom logic—from discount models to event-driven automations—with live demos and debugging strategies. Learn when to reach for each tool, when to code, when to configure, and how to extend Stripe’s capabilities.
Speakers
Tanya Boiteau, Product Manager, Extensibility, Stripe
Ben Smith, Staff Developer Advocate, Stripe
BEN SMITH: Hello everybody. Hello, good afternoon. My name’s Ben Smith. I’m a developer advocate here at Stripe, and I’m joined by Tanya, who is a product lead for the Stripe extensibility team. And actually Tanya and I have a long working relationship. We’ve been working together for about six or seven years now, I’d say. We were here at Sessions last year on stage presenting Stripe Workflows. Was anyone at that talk? Oh wow. Thanks for coming back. Wonderful. Good to see you again. The reason I mentioned that, oh, I should say, also we were at AWS as well, weren’t we? In the serverless product team as developer advocate and product manager. And I’m bringing that up for a reason, not just because it’s a cool story without an ending. We found one thing to be constant throughout this whole time working together. And that’s no matter how many developers we speak to, no matter how many customers we speak to, each with their own technology stack, their own challenges that they’re solving, we find that your business is unique.
And Stripe can’t ever really know your business like you do. And really that’s the point of this talk. Famously, it was said back in 2010 that you could add online payments to your applications with just seven lines of code. Then over the course of the years that proceeded and in response to many of you here and many customers and developers, our product suite grew and grew to be a platform that enabled you to run your entire financial infrastructure online, managing all the complexity of the payments movement that goes along with that. And last year, we introduced Stripe Workflows as a way to help decomplexify how you can link some of these products and features and functions together.
But really no platform can model every business. Your proration logic is not the same as the person next to you. Your dining rules is not the same as that of the person behind you. And your integrations and your stack is different from everybody here. So what this talk is about what you do when the configuration runs out, when there’s no toggle or select box or input to customize Stripe in a way that you need for a specific integration.
We’re going to talk about Stripe extensibility and new ways of extending and customizing Stripe that allow you to run your logic inside the Stripe platform with our reliability and our performance. And this actually opens up a whole new way of customizing Stripe that allows you to do things that were not possible before. First, let’s have a look at the old model, and this of course still works today. So this is traditionally how you would extend a platform that’s so complex such as Stripe, where you have a Stripe account and you configure that account to send webhook events downstream to some compute layer. Now that could be an EC2 instance, a Kubernetes cluster, a Lambda function, anything where you have an endpoint open on the internet that is able to receive HTTP requests, unpack that request, authorize it, authenticate it, make sure it’s from your Stripe account, run some code in response to it in your own system, and maybe even call back to Stripe with one of the Stripe SDKs or APIs.
Now, in production, it’s more likely that you’ll have multiple of these types of integrations, possibly even hundreds and hundreds of endpoints configured to send hundreds of different actions to your servers and running code in response to those actions. Now, the problem, the challenge that happens when you’re building integrations in this way is that you’re responsible for so much of the infrastructure and the logic that’s not the custom business logic that delights your customers. Things like retries and availability and item potency, making sure you’re not accidentally doing the same thing twice with a different result. You have issues of security and scalability. The system like Stripe’s so scalable, built for scalability. You could be receiving hundreds and hundreds of events or thousands of events per second, and you have to make sure that if you’re hosting an infrastructure that’s handling that kind of input, how do you scale and throttle in order to not break essentially?
So that’s the old model. What we’re going to show you today is some new ways that our team has been looking at to allow you to bring some of that business logic inside Stripe.
So what I’m talking about here is allowing you to create extensions and customizations with no external infrastructure. So all of those problems on the previous slide of retries and item potency and observability and scalability go away because that’s actually built into the Stripe platform itself. Just to zoom out a minute, I mentioned earlier that this actually opens up a way of creating extensions that were not previously possible because when you’re building with APIs and events, you’re really only interacting with the system at the start or at the end of a process. But what we’re going to show you here, you can actually interact with some of the Stripe lifecycles and state machines inside the process. So not just at the end when an event is generated. So you can build customizations and extensions now to change how proration logic is done in Stripe. You can control balance and withdrawals and change how and when customer balance is drawn down.
You can define your collection logic and each of these maps, each of these new extensions ideas map to a brand new thing that we’re calling extension points. Now, this is not a new model. You know this. If ever you’ve built a system with hooks or plugins, things like WordPress or Drupal or GitHub Actions, then you should be familiar with this, right? You have a core platform that opens up specific, we’ll call them extension points. You could call them hooks in other systems that allow you to hook your code into that particular point, narrowly scoped, receive the context of that payload at that point. The platform calls out, runs your code in Stripe, in response to that extension point, and then your extension calls back and returns the result back and the platform continues. We’ll take a real example of billing as a state machine.
So this is a subscription workload in Stripe where you start with a create or an update action for a subscription, and you end with collecting a payment. And there’s multiple states in this state machine that this workload has to go through in a specific order. So you have to calculate the proration, you have to create the line items, compute the final bill, and send the invoice, and then finally collecting the payment. These are some of the extension points that you can now hook into, upload your custom logic, and change the way the behavior happens here. So proration is all about charging customers for the exact amount of usage that they have in a particular billing period. So if you upgrade or downgrade a plan, you need to make sure that you’re debited or crediting that customer. So here there’s an extension point that allows you to change the way this is done according to your own pricing and business logic.
So you can actually customize these midcycle subscription changes. There’s another extension point for building the line items. So you can add, edit, delete exactly what appears on the inputs to the final bill. There’s another one for computing the bill where you can adjust global totals, you can apply discounts, you can apply credits. And a fast follow after Sessions, we’ll be adding another one where you collect the payments so you can customize the payment method that was used, the routing destination, the timing conditions as well. Now, I keep saying build your own logic, customize the way you want this to run. Well, we’ve also taken some of the most common customizations that customers are asking us for, for these different parts of the state machine of the billing lifecycle, and allowed you to kind of select these in the Stripe Dashboard. So you can either select something that’s prebuilt and preconfigured for you, or you can completely create your own customization.
TANYA BOITEAU: So when a customer upgrades or downgrades their subscription, perhaps midcycle, Stripe goes ahead and calculates the money that’s owed. That’s prorations. And so it’s also often one of the top reasons for support requests because the math actually creates these small, confusing charges that customers don’t immediately recognize. And so at our business, Hypernia, we actually will have a policy to waive any proration charges that are less than $5. It’s just really not worth the support ticket or the customer confusion. Now to do this today, we’re going to create some custom logic, test it, deploy it to Stripe, and then run a subscription through it. So no webhook server, no external infrastructure. It’s just code running on Stripe. Now to start, I’m actually going to go ahead and create a Stripe app. The Stripe app is the packaging and deployment mechanism. So it’s how we get our code from our laptop onto Stripe.
I’ve already done this, but the next step would be for me to tell Stripe what I actually want to generate. In this case, I’m going to use a new CLI command called stripe generate extension. I can zoom in a little bit for you there. And it requires three things. First, the extension point. So where in Stripe’s code or logic or lifecycle do I want to plug this in? In our case, we’re going to plug this into <code class="InlineCode">billing.prorations</code>. Then I need to give it a name. Let’s call it min-threshold and specify the implementation type. In this case, it’s a script. Now what’s going to happen is the CLI is going to go ahead, take my request, and go stand up all the scaffolding required for us to actually go ahead and build this extension. Once that’s complete, we can go take a look at what it’s generated. So over on the lefthand side here, I’ll see the app, hypernia-revenue. I can see the stripe-app.yaml file. Let’s take a look at that. This is the app manifest. So it defines all the code that’s in your app, including that extension we just generated.
You’ll also see that there’s an extensions folder here and inside the extension, and there’s two files, one for running unit tests and one that’s going to contain our logic. So we’re going to start with the logic and take a look at that. You can see that there’s already some code that’s been generated for us as part of that scaffolding, and that’s specific to the extension point we’re building for, Billing.Prorations. I’m also going to draw your attention here to this ProrateItems. That’s a method that Stripe is going to call every single time it needs to calculate prorations, so it’s going to hand in those items. Now it’s our job to specify whatever custom logic we want to happen with that and then return a response. Now, I’ve gone ahead and actually created some code for us and we’ll walk through this together. So first, I want to point out that we’re still using that same extension point, billing.prorations, along with that same method that’s required every time.
The first step our custom code does is calculate the proration amount for each item that’s passed in. Then we determine whether it’s a credit or a debit. So a credit is when we owe the customer money. So for example, they downgraded, and we always at Hypernian want to provide customer owed or money owed to the customer. If it’s a debit, in which case the customer owes us money, that’s where we want to evaluate whether it’s less than $5 or over $5. And if it’s under, we’re going to waive that proration charge. If it’s over, we’re going to go collect. Now, you might have picked up on this, but nowhere in my code have I hard-coded that $5 threshold. And that’s because I’m taking advantage of this interface here called MyProrationsConfig. What this allows me to do is specify minimum proration amount of type monetary amount, which is made up of a number and a currency, and the SDK will take that and dynamically generate a Dashboard UI.
What this means is that now my finance team, when they want to go change that threshold from $5 to $2, let’s say next month, their next quarter, there’s no reason for them to go code. They can do that directly in the dashboard, and we’ll see that more shortly. All right, so we’ve taken a look at this. Let’s go ahead and actually build this. So pnpm build.
Uh-oh, we’ve got an issue. I can see right here that we’ve got a type issue, so let’s go back and look at our code, and I can see it right away. I’ve got a zero here, but our SDK expects financial precision. After all, we’re dealing with prorations. And so what I’m going to do is actually go fix this to Decimal.0. We’re going to hop back, clear this out, and we will build it again. And hopefully this time we have a successful build. Great. So we got immediate feedback. We’re able to fix that, rebuild, and we’re successful. We’re good to go. Not quite. We haven’t actually checked to see if the code is doing what we think it’s going to do. Remember how I pointed out that test file earlier? We’re going to go take a look at that now.
So the test file today just checks, is there existence of an extension? Yes, but we can do more than that based on the logic we’ve actually provided already. Again, I’ve already sort of prepped some test logic here. And what this code does is it’s going to run through a bunch of different scenarios, but first we’re going to set our threshold, at least for our unit tests. We’ll call it $5. Then we’re going to run through a number of scenarios, whether you downgrade or upgrade, whether it’s over or below the threshold or mix those together and make sure we get the results that we’re expecting. So we’ll go back. We’re going to step into the... Oh, sorry. We’re going to go step into the extensions folder.
min-threshold is what we’ve called our extension, and then we’re going to run pnpm test. Now that’s going to go run all those unit tests you just saw. Fantastic. Seven tests pass, that means the logic that I created is appropriate, and we’re just about ready to upload this to Stripe. Now, I need to upload the app. I’m not only uploading the extension, so I actually need to go back to the root level of the app, cd in here, and then I’m going to run stripe apps upload. I’m going to then have to confirm this, that I do intend to upload my app, which I do. And the CLI is going to go ahead and create the packaging necessary with all the code that we just generated together, including that extension, the minimum threshold. When this is all done, I’ll end up with a Dashboard link that allows me to check on the upload progress.
I can click enter. It’ll bring me directly into the Stripe Dashboard, so I can take a look at my app. It’s currently in review, and this contains my extension. So while this is in review, let’s go see what a normal subscription to do without this custom proration behavior. I’m going to come over to my subscriptions. I can see that Alex Miles here, click into this customer and they have a subscription for $25. Let’s update their subscription. We’re going to remove this product, and instead we’re going to add a different product, in this case, the Pro plan for $27, definitely under our minimum threshold. Again, we’re using Stripe’s default proration logic. We’ll charge immediately, and you can see immediately that customer’s going to get this $1.99 charge. Those are the reasons they call us. That’s what we want to avoid. Now, if I close this and I go over to my app, I should be able to see the app that I’ve created, Hypernia Revenue.
This should be ready momentarily. We’ll give it another second. Fantastic. Right on queue. The app is now ready to install, so I can choose to install it into the sandbox. I’ll select the version that I want to install. In this case, it’s the first version we’ve done. We just did it live together. Now this is installed. I have one more step. I need to take this extension, and I need to plug it into that extension point. To do that, you can find all the extension points for Billing > Settings > Billing customizations. Now you’ll see the three extension points that are currently available. We’re interested in this “Proration behavior” extension point. I’m going to go ahead and customize that. And you can see right here, it’s using “Prorate by second.” That’s the default. That’s what we don’t want to use. I’m going to change that and I’m going to choose min-threshold.
That’s the one we want. And you can see that “Monetary amount” UI that I had configured inside of my interface. I can now specify this as $5, save it. Great. It’s now plugged in and running. I can go back to my subscription. I can go to Alex Miles. We can then update their subscription again. They’re still in their $25 plan. We’ll remove the product. We’ll add the $27 product instead. We’re going to go down, calculate immediately after update, and you can see right there, now the amount is $0. So I’ve just uploaded my own proration logic, tested it, deployed it to Stripe, and avoiding these customer requests. Ben, back to you.
BEN SMITH: So what did we just build there? Well, actually it’s an app, right? That’s the container that Tanya packaged everything into, and that gets deployed together with a couple of other important pieces. There’s the manifest file, that’s the stripe-app.yaml file that she showed you, and that actually declares what this app contains. Now, this app contains an extension. Let’s zoom into the extension here. This consists of three components. There’s the logic, which is the script that Tanya showed. There’s the UI config, so that’s the minimum proration amount that you surfaced on the Dashboard there, which users can actually change each time they want to set this extension. And there’s also the test. So you have a place to validate locally before you deploy as well. Just a note on script. So what we just built here was an extension that runs at an extension point as a script.
And scripts are one decision. One moment in time, they’re synchronous, they’re stateless, they’re deterministic, and that business logic can now live inside Stripe. But sometimes you’ll need to fetch data. You’ll need to loop and branch and notify and create a kind of sequence of decisions, and that’s not really what scripts do. If you want to coordinate multiple steps over time with reliability and guarantees, then you’re going to need a form of orchestration. And this is what we spoke about last year. It was Stripe Workflows. It’s now generally available. And these are workflows that you can create with a drag-and-drop interface that are triggered by events all now on demand, as we’ll show you in a sec. They scale automatically so that again, there’s no infrastructure for you to manage. They’re really built to handle growth, and you can use them to orchestrate and call over 600 of the Stripe API actions, pretty much any API across the Stripe platform, and use them to sequence steps with branching logic, looping, retry, and failure capture all built in.
So the mental model here is scripts run code, one decision, one moment inside part of Stripe’s lifecycle, such as billing, Workflows coordinate actions. Think multiple steps across APIs with branching and reliability built in. And just to note on AI agents, we couldn’t leave the session. We couldn’t let you go without talking about AI at least a little bit, but they’re really becoming a real part of how businesses operate now and they handle ambiguity, they make judgment calls, and they work very well for tasks where the right answer isn’t always the same result. So you might reasonably think, “Why do I need to build a workflow? I can just use an agent to handle this.” And to that, we would say that agents can follow a process, but a workflow will guarantee it every time verifiably. And when you have an operation like moving money where there’s multiple steps involved that have to happen in a specific order where the result of one step needs to be passed into the next step, you want that in force, right?
You don’t want that improvised. You want it to run the same way every time whether it’s triggered by an agent, by a support rep, or by some other system at 3:00 a.m. in the morning. So agents are really for tasks where flexibility is a feature of the task, but workflows are for tasks where consistency is a requirement. And in many real systems, you’ll probably need both.
TANYA BOITEAU: So let’s go back to our Hypernian business, our subscription business. Now, I’m getting a customer request to cancel their account. So it’s not one subscription, it’s all of them. And this particular customer has three active subscriptions. Now, regardless if that’s handled by my support agent or an AI agent, the steps are always supposed to be the same. So I need to retrieve the subscriptions, cancel the subscriptions, apply the right proration logic, maybe add the reason, and the support agent name. That’s three subscriptions, that’s three cancellations, which means there’s three opportunities to make a mistake, forget to do something, or do something differently than the last time. This is why Workflows guarantee the same execution every single time. Now let’s build this. I’ve actually already got a headstart, and so I’ve started to build out this workflow. I’m not using an event-based trigger because there is no event that’s “cancel all the things.”
Instead, I’m using an on-demand trigger, and this on-demand trigger takes a customer ID along with a reason for the cancellation. That’s an enum, so too expensive, no longer needed, switching providers. Maybe we want to add another option. We can say “other” as a catchall. Whoops.
What about if we add another field as well? Let’s collect the agent name, and let’s make this a text field. We’re going to call them an agent name, and we’ll make it as an optional. Now my form’s updated. I can also see the API call that this would use if I wanted to invoke this programmatically. We’ll go ahead and do that. Next step, I need to list the subscriptions. To do that, I’m going to pass in the customer ID from that trigger I just created. So we’re going to dynamically pass the data, and now I’m left to add an action. We need to cancel the subscriptions. I’m going to go ahead and go to my action modal, click cancel a subscription. Again, I need to refer to data from a previous step. In this case, the subscription ID is retrieved from that list action. I’m going to go ahead and choose subscription ID and you can see immediately the workflow recognizes a loop is needed because we’re passing in an array.
It puts the loop in place. I can look at it. I also have the ability to set a limit, but in this case, we do need to cancel all the subscriptions, so we’re going to say “All.” And I can go ahead and provide additional cancellation details like context from the request. So for example, we had asked to input, not the customer ID, but instead we’ll ask to input the, let’s say agent name or the reason. Great. Now I can publish this. The workflow’s now live. I then click “Run workflow” so an agent in the Dashboard has a form to now provide the information. That customer, I believe, had three subscriptions. They said it was no longer needed, and the “agent name” is myself in this case. So we can go ahead and run that workflow. You’ll see the list below of the started runs. It’s moved to “In progress.”
I can go click in there and see exactly what’s transpired. I can also click on the loop and see each subscription that’s been canceled as part of this. And so it’s not just the Dashboard though that you can run this. You can also trigger this programmatically. So if I look at the same workflow and I hop back over here to my CLI, I can choose to invoke this directly. This time, we have a user who has five subscriptions who’s asked us to cancel. I can go ahead and click “Enter.” I see the successful status, and if I pop back to my workflow, I will be able to see that I have a new run of my workflow that’s just happened. I can... Oh, I’ll just refresh here. I’ll be able to see then that all five of those subscriptions that I’ve handed into my workflow have also been canceled. Again, if I click down, I’ll see all five subscriptions done just like that. So the same workflow, same execution, whether you’re triggering that from the Dashboard or programmatically from an API.
BEN SMITH: So everything we’ve showed you so far is running inside Stripe, but we understand that your business doesn’t run inside Stripe. You have your own services, your own third-party tools, and you need workflows that can reach beyond Stripe’s boundaries. And that’s where custom actions come in. These let you plug in other actions into your workflows, things like Slack or email or your own APIs.
TANYA BOITEAU: So let’s say my team is really focused, my growth team, on all cancellations. Let’s go edit the same workflow. I want to add an action. I can now see apps that I have installed or find new ones that are available. In this case, I’m interested in sending a Slack notification. I can choose the channel. It’s my growth team that cares about this. I can provide a message in here. I can, again, also add dynamic data. For example, the reason for the cancellation, include that. I can also include Dashboard links, so it makes it really easy for my growth team to then go ahead and find this customer. Again, leaning on that dynamic data passing. I can say, “Done,” publish the workflow. We won’t leave version notes today, and then I can go ahead and run this workflow, this time with a new customer who only has one subscription. They said it’s too expensive. Again, I’m the agent. We’re going to go ahead and run that workflow. We’ll refresh. You’ll see this list page have a new workflow execution run that’s available in here. And then I can actually hop over to Slack and I can go see that I have a new message for the cancellation of my workflow. So just like that, custom actions, third-party are now available directly for you out of the box.
BEN SMITH: Yeah. Okay. Let’s go. Thank you. You notice the pattern here that we are showing again and again, right? We started this talk by saying that Stripe products now have extension points, places where you can plug in your custom code. Billing has them, Payments have them, and Workflows have them too, because custom actions are really just extension points for workflows. It’s the same concept, safe customization at these designated points, and they’re reusable too, not one-off. So that action that Tanya just built, that’s now available for everyone in the organization. Build once, use everywhere, governed, controlled building blocks. And it’s also a Stripe app. It’s the same anatomy as we showed you before. It becomes a workflow step, not a webhook that you hope survives, but a real extension. So we foresee a point in the future where people have a Stripe app with an extension directory with multiple extensions built into that directory for different parts of their organization.
So here’s how we think about some of the logic that we’ve shown you today. If Stripe has a built-in option that covers your use case, then use that. That’s the lowest effort, that’s the most supported path. If you need to customize something with an extension that’s prebuilt, one of our most requested extensions, then you can configure that and turn that on. If you need something that’s even more bespoke, then you can reach for a script with a custom extension. If you need something to coordinate things over time—multiple steps, retries, branching, calling out to other APIs—then that would be a workflow.
So just to recap everything we’ve shown you today, we understand that your business is unique. Stripe gets you very far, but it can only get you so far before you need to start building your own custom integrations and logic. And for that reason, Tanya and team have spent the last year obsessing over multiple ways to make Stripe more programmable, not just some of the options we’ve shown you, but there’s actually more and more talks here today that will show you other ways that we’re doing this too, not least of which the main concept is that we’re allowing you to run more and more of your custom logic inside the Stripe platform, not only to take away some of that undifferentiated infrastructure that you have to stand up, but also to change the way you can customize Stripe internally now. So extensions is actually in private preview. So if you’re interested in trying out extensions, building your own script, then you can sign up for the private preview here. But for now, from Tanya and myself would say thank you very much for joining us at the session and we hope to see you next year. Thank you.
TANYA BOITEAU: Thank you.