Stripe integrations, deconstructed: Core API concepts
Developer craft
Runtime
Complete form to watch full video

Getting the most out of Stripe APIs means going beyond the defaults. This session covers strategies for reducing API calls with expansion parameters, making webhooks more reliable at scale, and using metadata to connect Stripe with internal systems. Whether you’re building for high-volume transactions or complex workflows, get practical techniques to streamline your integration.
Speakers
Srikanth Chinmay, Global Head, Technical Solutions, Stripe
Michael Glukhovsky, Product, Developer Platform, Stripe
SRIKANTH CHINMAY: Hi everyone, good afternoon. I know it’s been a half-day already with lots and lots of announcements. Michael and I are going to be walking you through some quick tips on how to manage your integrations with Stripe to be robust and easy to maintain, whether it is something that you’re familiar with or something that’s new to you. A lot of this talk is inspired by what we hear from all of you.
With more than 500 endpoints that you heard earlier this morning and the broad surface of products that Stripe offers, you can imagine we have a lot of conversations with developers and users on what they’re doing with the Stripe integration. These conversations happen on many, many channels. One of them, which is most prominent, is our Discord server, where we talk to a lot of developers and help them with their technical questions in real time.
MICHAEL GLUKHOVSKY: What we’ve learned across so many conversations is that, just like everyone’s business is unique, the technical challenges that you meet are unique. It’s a testament to the diversity of Stripe’s user base. So one moment we might be helping a US-based Connect platform working on global payouts, and then the next a SaaS business in Singapore figuring out prorations and invoices. And there’s no one-size-fits-all solution. Users will adopt any number of Stripe APIs and UIs, like building blocks, and each have their own strengths and weaknesses. But what connects all these Stripe integrations together is that they rely on the Stripe API.
By harnessing some core concepts at the heart of our API, you can level up how you use Stripe and build more powerful, resilient integrations. So today, we’re going to dive into some of these concepts and then answer some of the most common questions that we hear from users. Specifically, we’ll cover how to effectively use Stripe events, how to map Stripe’s data to your internal systems, and then how to understand versioning of the Stripe API, and then how to upgrade the API version.
SRIKANTH CHINMAY: Let’s dive in first with Stripe events and how it can help you power your integration flows with asynchronous operations. Stripe accounts are a beehive of activity. At any given point of time, there’s a lot happening around them. You could be having customers onboarding to your business, obviously payments and subscriptions and all kinds of e-commerce and commerce activities are getting activated. And it’s very, very important for your business at all times to get to know what’s happening on your Stripe account in real time.
For example, when a customer places an order, you can create a great experience by tying an order fulfillment flow on top of that, or activate and upgrade your service that you offer to them based on what they change in their subscription. These activities create Stripe events. These are notifications that we send you when anything interesting is happening on your account. Each event has a distinct type.
For example, `customer.session.completed`, which tells you what has happened, and it has details about that activity in its payload. By subscribing to these events, you can truly harness the power of Stripe’s platform as a programmable hub for your business. We get a lot of questions about how to use Stripe events, and more importantly, how to build scalable integrations with it. It starts off with setting up an event destination.
It’s where we route everything that we need to generate and inform you about your account. Most integrations use a webhook. You set up a public HTTP endpoint, and then we deliver events to it, and through that, to your application. This only happens when something important and interesting is happening in your account, so that eliminates the need for you to pull the API repeatedly. For local debugging, you could also be using the Stripe CLI to set up a local listener, which forwards these events to your machine for you doing your development.
And recently we introduced cloud destinations with support for Amazon EventBridge last year. Destinations like EventBridge help you set up a much simpler flow to manage these events, reduce the latency, and also make it possible to integrate with other cloud services like Lambda. We have a lot more happening here in the coming months. Let’s take a quick spin of how these look like in the Workbench. MG?
MICHAEL GLUKHOVSKY: I’d love to. Let’s take a look at Workbench, which is our new home for developers that lives in the dashboard. And as I mentioned, we have the developers menu at the bottom left. When I click it, I’m going to go ahead and choose webhooks. Now, here we go. We can see all the event destinations, and I’m going to go ahead and create a new webhook endpoint by clicking Add Destination. Now, first I’m going to say that I want events from my account, and then snapshot events, which is our primary event type today, include a rendered object in the payload.
So we’re going to choose the API version to use for that rendered object. Then I’m going to choose the event type that I want. In this case, `payment_intent.succeeded`. Now, we recommend choosing specific event types when you set up your endpoint. It is tempting to click select all, but in the case of a spike in account activity, Stripe may overwhelm your server with events that are incoming. Essentially, your rate limiting may kick in. Think like a flash sale as an example.
So we recommend tailoring the exact event types to your integration, and only subscribe to those. Now I’m going to click Continue, and here I’ll choose the type of destination. We’re going to choose a webhook endpoint, and then we’re going to tell it where to send the events. Now Stripe will route those events to our server. I’m going to send them to pose.com/webhooks, and I’m going to hit Create Destination. Now we see the view of that particular webhook endpoint. We can then choose the signing secret.
It’s important to check that signing secret so you can verify that Stripe is the one sending you events when you receive them on your server. We’ll talk about that in a moment. And then I’m going to use this handy Shell down here to actually test this by typing `stripe trigger payment_intent.succeeded`. And an event was just sent to that endpoint. Let’s go ahead and take a look in the Event Deliveries tab. I’m going to refresh so we get the latest event deliveries. It failed, but why? Well, if we scroll down, we can see that something’s wrong with my server.
We can see the entire request payload. We can start to debug and bring that server back online. That event will actually retry in one minute, and then Stripe will exponentially back off from there. If I’m ready to resend that event, I can also just click Resend, and Stripe will retry sending immediately to that endpoint. So it’s really easy to manage webhook endpoints right from Workbench. Srikanth?
SRIKANTH CHINMAY: Okay. Let’s get back to slides. Yeah. Cool. That’s pretty cool, MG. So let’s now dive into some best practices around managing and working with events. Let’s start with security. MG just alluded to this. We strongly recommend that you ensure that your endpoints are always set up to be secure with event verification. It’s really important to make sure that you are only processing events that are coming from Stripe. And to help with this, Stripe ensures that every delivered event has a signature on it, and you can use it to verify that it’s coming from Stripe.
Our SDKs on the server side that we provide have built-in methods to help you with this. While it may appear simple, we hear a lot of questions coming from users around this. First, many frameworks like Express may sometimes end up converting the request into a structured JSON. It’s really, really important that you maintain the unpassed raw object because that’s what our SDK helpers need and can process. So it’s really important for you to configure the web framework to allow for that.
Second, secrets are unique per endpoint. So you have to always make sure that the signing secret that you’re using is mapped to the writing specific endpoint that you’re trying to send it to. And sometimes you might have run into some confusion around this when you’re testing between live mode and test mode, or maybe having multiple webhook endpoints doing different things. Next, when processing events, it’s really important to remember that the order in which these events are delivered to you is not guaranteed. Let’s take a billing integration as an example.
When a subscription gets created, a series of events get triggered. For example, customer subscription created, customer subscription updated, invoice created, invoice paid. While these events are created in Stripe, they may arrive in any order, so it’s really important for your logic not to assume that they come in any particular order. Instead, it’s better to look for an event that matches a state change that’s tied to your API object, and then trigger your business logic based on that state change.
The other thing that happens when processing events, things could be happening behind the scenes, and the state of the object in Stripe may have already changed. For example, a customer could have updated their payment method, or they could even cancel the subscription. So it’s always important for you to fetch the latest object and the latest snapshot from Stripe when you’re looking to do something with it. And that way, you’re not depending on the snapshot that’s in the event payload.
This also makes sure that your system is always acting on the most current information in Stripe. To help with all of this, in v2 API endpoints, we started introducing a new concept called thin events. It’s a new lightweight event format. Thin events don’t rely on object snapshots, which can get out of sync. Thin events also have just the essential data so that you can then use SDK methods to pull what you need to go with it.
And with type support, they allow any type language to do better and more robust error processing. And it’s also a lot easier to do API versions than when we talk about it in the day to day. Thin events today are supported on API v2 endpoints, with more coming to v1 endpoints soon.
MICHAEL GLUKHOVSKY: Next, let’s explore how to keep what happens in Stripe in sync with your internal systems by using Stripe as a source of truth. What do I mean by a source of truth? Well, it’s hard for distributed systems to agree on a consistent view of what’s happening in the world. Managing that flow of data between Stripe and your local database is challenging because both may be updated from moment to moment. So your source of truth is the first place you’ll check in before running a sensitive operation, like moving money, and then recording those changes.
So whether customers are updating their subscription status or banks are returning error codes, Stripe will always have the most up-to-date view of that commerce activity, which lies at the heart of your business. Using it as a source of truth means you can reflect the latest details to your users, like the status and time for a payout, and then also build complete reporting analytics pipelines based on Stripe data. It can be tricky to understand how to design and relate all these sources of data and sources of truth, since this will have long-term implications for your business and your technical architecture.
So users often ask us for help in understanding how to manage their data models, synchronize their data, and then lay out this architecture. So one of the most important questions to consider is whether you’ll treat Stripe as the primary or secondary source of truth for your business. If you’re mostly mirroring API objects from Stripe in your infrastructure, then you can treat Stripe as a primary source of truth. You’ll need to query the API directly for the latest state on a regular basis, and then rely on cache data on your side if you need to, and that will have an eventually consistent view.
And this will naturally increase the number of API calls to Stripe. In contrast, if you keep your own database as a primary source of truth, then you’ll need to do a lot more data synchronization with Stripe, but you’ll also need to design your own data models and manage your own database. And naturally, the number of API calls to Stripe will decrease.
Keep in mind that while Stripe’s infrastructure will scale immensely, we still have rate limits in place to ensure all users get stable access to Stripe’s services. So your choice of which route you want to take between making Stripe the primary or secondary source of truth, it’s predicated on the volume of transactions that you anticipate and the nature of your business.
SRIKANTH CHINMAY: In either case, you’re going to have to fetch a lot of data from Stripe. So let’s look at some ways to make this efficient. Many fields in Stripe sometimes will only show an ID of a related API object. And sometimes fields that you see documented in the API reference may be missing when you see the response. The omission of these fields is an intentional design choice with API performance and reliability in mind. To support the retrieval of these fields, we recommend using expansion. It’s similar to GraphQL’s field selection, and it allows you to work on specific selected fields and have them include additional data as part of the response. API expansion is quite powerful.
It lets you fetch related objects in a single API request, avoiding the need for multiple round trips. For example, when reconciling payouts, you may be able to expand a payout to collect all the charges that are part of that payout to help with reconciling. Or if you’re looking at a subscription, you can look at the status of an underlying payment status as part of a single request. Expansion simplifies your integration code and also boosts performance as you are now getting all the data in a single structured response.
There are a few things to keep in mind. Expansion is limited to four levels of depth, so you have to plan ahead as to what you are looking to expand for. And while it’s powerful, it will increase latency because Stripe has to do a little bit more work assembling the object before we send it across to you. Sometimes we don’t include fields by default in API responses because these fields may be quite expensive to render.
A particular specific example would be, let’s say, lists of line items in the checkout session. Using expansion or inclusion in the API request is a way that users can bring across these fields when needed, when they are not available by default. In API v2 endpoints, we made this a lot clearer by a clear include field, which allows you to specify when to include a field that’s not there by default. Let’s now explore another very useful concept: metadata.
Metadata is a very powerful feature in Stripe that lets you attach custom key value pairs with almost any Stripe object. These metadata values can be set when you create an object, or they can be unset or updated when you’re updating the parent object. It’s quite useful for you to use metadata to link your business to what’s in Stripe. For example, you can map your user IDs to related customer objects, you can attach order IDs from your system to payments to help with order fulfillment, or you could track purchases coming from a marketing campaign.
Metadata is searchable through the API, so it becomes really easy for you to look up Stripe objects using your identifier versus the identifiers or the IDs that you have in Stripe. Some objects that support metadata are not fully supported by the Search API today. Another common gotcha is assuming that the metadata gets automatically copied across all related objects.
In some cases, we do do that. For example, between payment intents and charges, or payment links and checkout. Sometimes there’s a dedicated parameter in the API that lets you copy metadata across related objects. It’s really important to read the documentation. Stripe Docs, as you all know, is a really, really good standard, and it got even easier today with the AI assistant that we launched that allows you to now have conversations with always up-to-date and current content. So always refer to the docs, and that’s not just a joke.
So metadata is truly like a good spice. It should be used mindfully and minimally. By sprinkling it in the right places, you can link your system with what’s happening in Stripe. It’s really important to make sure that you’re using metadata to link only the relevant parts of your business to Stripe. We have seen some very creative uses of metadata, values sometimes that look like passwords, and in one instance, a full-fledged HTML template to render a front-end application. These are not valid uses of metadata. So let’s go back to have MG maybe walk us through how you can manage metadata in your integration through Workbench and the Dashboard.
MICHAEL GLUKHOVSKY: Thanks, Srikanth. What I love about metadata is that it’s simple but really powerful. So let’s start by looking at this subscription in the Dashboard. I’m going to look at this bar in the Workbench that has a little binoculars beside it, and that tells me that I can look at a deeper view of this object from the developer’s side in what we call the Inspector. When I open it, it’s going to show me that same subscription with the full JSON view of that object, as well as related logs and events, and then all the related API objects.
This is really useful for understanding the shape of that object under the hood. Now, if we scroll down, we can see that there is a metadata section right here, and we can see that there is already an ID here that points to the entity in my database, Propose. So you can see it has my own identifier, and then that lets me quickly look it up in my database side. But I want to store a second identifier, and that’s for the sales rep who sold the subscription. Her name is Cynthia. We want to give her credit for the sale. So in order to do that, we’re going to go ahead and edit this API object in the API Explorer.
Now here we can see we have the Shell, which is a really handy command line environment with familiar commands right from your browser, and it lets me run API requests and build them out right from this convenient environment. So on the right hand side, we have the API Explorer, which will help me build up those commands. As I scroll down, you can see that it’s explaining what each field does. I’m going to go down to metadata, and then I’m going to go ahead and click. We’ll add value for sales rep ID, and we’ll credit Cynthia. And then I’ll hit Save. And when I do that, you can see that the Shell was actually building out the command for me. I’m going to go ahead and actually first print the SDK request.
And that will actually show me the integration code that I can then copy/paste into my editor. But in this case, I’m actually going to commit this change by hitting Run. And now, as I scroll up, we can see metadata. And in fact, the value has been updated. Now, I’m going to go ahead and hide Workbench for a moment, and we’re back in the Dashboard. I’m going to go ahead and refresh to fetch the latest data from Stripe, and as I scroll down, we can see that, in fact, the metadata value was recorded here. What’s nice is that I can also click this button to edit that metadata right from the Dashboard.
And we find this is really useful for teams that have less technical team members where they want to be able to modify those values, Let’s say, for example, a support team member debugging the customer issue. This is also why it’s really important to validate and sanitize your inputs because it’s just a string. So if someone enters the wrong shape or format and our integration relies on it, it could cause a bug or break our code.
Now I want to go ahead and create a larger object to store in my database. So I’m going to go ahead and pull up that last command, but this time, instead of updating, I’m going to retrieve it. Now if I do that, I’m going to scroll up, and we have a customer here. But it’s just an ID. What I want to do is staple the entire customer object in. And for that, I’m going to use the `expand` parameter—“customer.” And now when we scroll up, we get the entire customer object that’s stapled into that subscription object. This is really useful for then creating a larger shape that I actually need in my database. So it’s really easy to use metadata and expansion with the API. Srikanth?
SRIKANTH CHINMAY: Thank you. Let’s go back to—yeah. It’s super interesting to see that you can do all this in the browser between the Workbench and the Shell without even opening up a terminal. So it’s pretty cool.
MICHAEL GLUKHOVSKY: Yeah.
SRIKANTH CHINMAY: Let’s bring some of these concepts together. Design your API calls to Stripe the same way you would design API calls to your internal system—as efficiently as possible. As we talked, event payloads are lightweight and meant to be lightweight because we don’t include expandable and includable parameters and fields in them. And so you’ll typically need another API call to bring across a related object. Or you can adjust your webhook handler to do the right expansion when you need to process that particular event. This can all be a lot of work.
You don’t have to write and make individual API calls if you’re bringing across data in bulk, especially for something like analytics or reporting. Instead of writing complex logic, you could potentially look at Stripe Data Pipeline. It’s a toolkit that we are providing that allows you to string together very easily data pipelines to bring across data in bulk, especially to data warehouses and cloud-based stores. With a few low-code steps, you can quickly set up these pipelines, and it’s a lot easier to maintain, and you can also relax knowing that the data is always consistent. Let’s talk about versioning next. MG?
MICHAEL GLUKHOVSKY: So let’s talk about how to manage change in the Stripe API. APIs are living systems, and your API integration requires gardening over time. So we’ve built some tools to help you understand what new features and breaking changes are in each Stripe version. First, let’s talk about how we version our API at a high level. Historically, we would only release a new API version when we had a breaking change without a set schedule. But developers told us two things—it made it hard to plan version upgrades, and two, hard to understand when it’s safe for them to upgrade.
So starting in September of last year, we reset our versioning model to organize the API releases into sets of releases. So twice yearly, we issue a new release that commences with a dated API version that includes breaking changes and additive changes, followed by monthly updates with only additive changes. So with this approach, it’s clear when you can and should plan for breaking changes. And as a living system, we name each release after plants. So our first release in September was Acacia, flowering trees and shrubs that sustain the ecosystems around them. And in March, we released Basil, a delicious culinary herb with 60 varieties, each with a unique flavor and aroma.
Versioning is a common area of confusion for APIs, because it’s both the communication standard, but it also directly impacts your technical integration. So users often ask us, how do I identify my API version and the requests that are making them? When should I upgrade? Then, how do I upgrade? So let’s go ahead and take a look at the tools available to us within Stripe to manage this part of your integration. So right here from the Overview tab in Workbench, we can see a list of the API versions that I have in my account that requests have been made on. When I hover over them, it will show me the number of requests in the last week.
Now, it looks like most of my integration is using Basil, our latest release, and the version from April 30. But it seems like we have a couple of releases on Acacia. So I’m going to go ahead and highlight this and go to the Logs tab. In the Logs tab, I can see all the requests made to my account. Now, I’m going to go ahead and filter by the API version. And we can see that actually most of the—actually all of the API calls seem like they’re reporting API calls. I know I have a separate service on my infrastructure that does reporting, so I’m fairly certain at this point that we just are out of date on that one service.
I can then go to that service and update it, and then all of our integration will be on Basil. But how do I know what version I should upgrade to and what risks I might face? Well, I can look at the changelog. And this changelog allows you to see the changes within Stripe for each version. I’m going to go ahead and filter to just breaking changes. And as I do so, we can see right here which products are impacted by these breaking changes. I mean, it looks pretty good.
I don’t see reporting—but oh, there’s one. Affects all products. So I’ll click on that change, and it looks like the change is related to lists and the total count property. But I don’t think that that API provides any lists that I’m using, and so I know that I can safely upgrade that in my infrastructure and get on Basil. In order to do so, I can scroll down and I get precise instructions on how to upgrade, as well as which SDK version I should use. So it’s easy to manage versions and understand how to manage that change.
Now, let’s talk about how to actually upgrade your integration. It’s important to keep your API version current. Each SDK version includes new types for every new feature that we include. And some breaking changes we’ve released will significantly reduce the latency of common API operations. So it’s worth upgrading. And historically, there have been moments when new regulatory requirements could put your team on an unexpected schedule to upgrade your API version.
So for example, as countries have historically introduced new KYC requirements for Connect platforms, we’ve steadily introduced new error types or breaking API changes to support these migrations. And those are only available reasonably in new API versions. So I know it’s hard to make time for maintenance and a busy roadmap, but staying on a recent version reduces the risk of your team unexpectedly needing to upgrade through years of API versions on a deadline. The best time to plant a tree was yesterday. Second best is today. Let’s talk about the approach towards upgrading your version.
So here’s an example. We provide a full upgrade guide in our documentation, but the steps look like this. First, you want to upgrade in one motion. And so in order to do that, you first need to ensure that all of the requests you’re sending us are specifying explicitly the API version you’re using. If you’re using an SDK from Stripe, we do this automatically for you. Don’t worry about it. But if you are making direct calls to the API, we recommend including the Stripe version header explicitly with every request.
If you don’t do that, we will use the account’s default API version, and understandably, that is a more brittle upgrade process. So start by making sure that every request you include has that explicit version. Next, you’re going to want to read the changelog, identify your target version, and read through all the versions along the way. Maybe there’s some additive changes, some new features you want to adopt. Maybe there’s some breaking changes you need to consider in your upgrade plan. Then you can create a sandbox to trial any of those changes before you roll it out to production.
Next, we’re going to actually upgrade the API version. If you’re using the official SDK, easy as upgrading the SDK version. If you’re using no SDK, just update what you’re sending in that Stripe version header. You’re then going to upgrade your webhook endpoint, if you have one configured. And what that looks like is creating a new webhook endpoint with the target version, migrating your event traffic, and when you’re confident everything’s finished, decommission the older webhook endpoint. And then finally, within Workbench, you can upgrade the default API version for your account, just in case there are any stray requests that don’t explicitly set a version. It’s a lot of steps. So we have a lot of work planned here to make this a lot easier for you.
SRIKANTH CHINMAY: We covered a lot of ground today. Let’s start wrapping things up. First, it’s really important to use and consider using events properly and making sure integration works smoothly, especially when you have asynchronous operations. Remember to secure endpoints for that. Remember to not rely on ordering in which you receive those events. And as much as possible, be aware of when you have to fetch related objects. Second, consider using Stripe effectively as your source of truth. That starts off with a decision on whether it’s a primary or secondary source of truth. Using expansion or inclusion to stitch together your data model, and then also using metadata to link your system to Stripe’s.
And lastly, stay current with the changes in the API. It’s best to specify an API version in every call and every request you make to Stripe, staying on top of—and trying as much as possible being on a recent API version helps. And knowing how to upgrade, when you need to upgrade, is something that you need to start looking into, also.
Stripe allows you to do a lot of things, but some of these foundational concepts here will help make your integration fun to upkeep, and more importantly, grow as your needs evolve and expand. If you have any questions or ideas on any of the topics we covered here, feel free to find us at the DevExpert space in the expo over the next couple of days or on our Discord server. Thank you.