Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2018-303
$eventId
ID of event: wwdc2018
$eventContentId
ID of session without event part: 303
$eventShortId
Shortened ID of event: wwdc18
$year
Year of session: 2018
$extension
Extension of original filename: mp4
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2018] [Session 303] Automating ...

WWDC18 • Session 303

Automating App Store Connect

App Store and Distribution • iOS, macOS, tvOS, watchOS • 40:47

The new App Store Connect API provides a familiar and highly readable REST API designed to facilitate the automation of many tasks you would typically perform through the Apple Developer website and the App Store Connect browser interfaces. Learn how this API allows you to manage your apps and testers for TestFlight, create code signing resources, download reports, and even manage your organization. See how this API leverages JWT authentication and a JSON payload for all transactions to make this new service easy to integrate with your existing automated workflows.

Speakers: Geoff Coffey, Sehoon Shon, Julie Richards

Unlisted on Apple Developer site

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Good afternoon. I'm Geoff Coffey. I'm an engineer on App Store Connect and I'm here to talk to about automating App Store Connect. And I got to be honest with you, I'm really excited about what we're going to talk about today. So let's start by talking about where we are with automation in App Store Connect today.

As I'm sure you all know, you can use Xcode to upload your builds to App Store Connect and to download your crash reports and we have the Transporter tool that lets you automate uploading the metadata.xml and also automate your build uploads. And then we have Reporter, the command line tool for downloading your sales and financial reports.

And these tools are great. A lot of you use them but we've heard from you that you want even more. You want access to more areas in App Store Connect and you want to integrate with more diverse workflows. And so we're really happy to introduce the new App Store Connect API.

[ Applause ]

We're excited too. So this is a standards-based REST API for App Store Connect. And if you ask me, that's the best part about it, it's a stock standard REST API with JSON responses, so it's going to feel familiar to a lot of you right out of the box.

That also means you can use this API from any platform, almost any programming language, and using the tools that you're probably already using today. And of course, the API needs to be secure. So we use industry-standard JSON web tokens for authentication. And that just means you don't have to pass usernames and passwords around, and you don't have to have your code tied to any specific person on your team but you still maintain control of who can access your data and what they can do.

From an ease-of-use standpoint, we focus really closely on consistency with this API. We have a single unified REST resource model, which just means that you can take what you learn in any one part of the API and apply it to every other part. And we've also built discoverability into the API itself, in simple ways like when we return JSON data to you, it's nicely formatted and indented so if you need to, you can dump it out to the console and read it right on the screen and we include links and those responses to related information to help you figure out how the parts fit together.

And, of course, the API will be fully documented. If you haven't seen this already, the documentation platform that you already use at developer.apple.com and in Xcode was extended this year to also include Apple's REST APIs. So the same format you're familiar with your framework docs will apply to the App Store Connect API as well.

Now we started with a focus on the areas of App Store Connect that don't already have an automation story and that you have to visit over and over again and in particular we're starting with these four areas you see here. So let's look at what all is included.

First, we have comprehensive coverage of TestFlight. You can manage your testers and groups. You can submit your builds for review. And if you saw the session yesterday, the What's New in App Store Connect Session, you know we just announced a new public links feature for tester acquisition. And this works hand-in-hand with the API. You can use the API to manage your public links and you can use the two of them together to run your beta test program in whatever way works best for you.

On the Users and Roles side, you can add and remove users and keep the permissions on your users in sync with your actual organization. And as we announced yesterday, this includes your complete unified user base across developer website and App Store Connect. So you have one set of users and one set of roles.

[ Applause ]

We have provisioning APIs that let you add development devices and register bundle IDs and manage your certificates and profiles, and then we have reports APIs that let you download the sales and financial report files. And if you use the Reporter tool today, this will feel really familiar to you because the parameters you send to Reporter are very similar to the ones you send to the API. So it's really easy to make the switch.

And I said there were four things and this last part isn't really part of the API itself but I wanted to bring it up because it's an important part of our automation story. We've made a couple of important changes the Transporter to help smooth all of this out for you. First of all, Transporter will officially be supported on Linux.

And also, you can take the API tokens that you use for the new API and send those through to Transporter and it can use those for authentication as well. It just makes the process easier to manage for you. Okay, we have a whole bunch to get through. We're going to go kind of fast today. I apologize for that but we have a lot to cover. We're going to talk about getting data and changing data with the API.

We'll talk about relationships which is how the different parts of the API fit together and how you work with those. We'll talk about how to handle errors, how to get access to the API and authenticate your request, then we'll wrap up with some best practices. So, you ready to get started?

[ Applause ]

So like I said, we're going to start with getting data and that means we start right here, api.appstoreconnect.apple.com. This is the home base of the API and from here we want to construct a URL. First we will add a version. Now all API endpoints have a version and right now that's always v1.

But inevitably as App Store Connect grows and changes over time, we'll have to make changes to the API that might cause your code to stop working. So when that ever happens, we'll change this version number and the old version will keep working for a period of time so you have time to adjust to our changes.

Now after the version, we have to add something called the resource type name. This is a really important concept in the API. The resource is like the fundamental unit of the API. Conceptually, you can almost think of the API as a big collection of resources that you operate on. We have a whole bunch of resources in the API.

Most of them map to things you're familiar with in App Store Connect and we're not going to talk about all of those today. We'll focus on just a few and we'll start with Users. And so here we have a complete API URL: api.appstoreconnect .apple.com/v1/users. This URL represents all the users on your team. And, of course, you can go get this URL and you'll get back a JSON object.

And the first thing you'll notice about this subject is it has a data property. Anytime we send you a successful response with data in it, we include this data property. In this case, it's an array of user resources. Now, you can only see one user on the screen right now but if the screen were much, much taller, you'd see that all the users in your team are in this array. Now I want to focus in on some of the data we return to you with each of these resource responses. Every resource has a type just telling you what it is and an ID that uniquely identifies this resource across all of App Store Connect.

Then we have the attributes. This is probably the stuff that you're most interested in. For users, it's things like their first name, last name, and email address. The values of these attributes are usually simple types like this, strings, numbers, dates and times or Booleans but they can also be complex types sometimes, like arrays and objects.

After the attributes, we have relationships. We're going to skip those for now. We'll come back and look at them later. And then we have links and in particular what we call the resource self link. Now this is a URL that uniquely identifies this particular resource, in this case the first user in the result here. Every resource we send back to you includes this resource self link.

It always looks like this: api.appstoreconnect.apple.com and then the version, v1, and then the resource type, users, and then the identifier of that resource. And you can go get it and you get back data that looks almost exactly like what we just saw. The only difference here is we're seeing just this one user now instead of all the users on your team. So that's two ways to get data.

You get a list of resources or an individual resource, but of course you're going to want to change data as well. And to do that, we lean on common REST conventions, so this will also feel familiar to a lot of you. You've already seen how to get resources. To create a new resource, you'll use the http POST method. To change a resource, you'll use PATCH, and to delete a resource, you'll use the DELETE method.

So let's try this. Let's say we want to add a new user to your team. Now a quick aside. We can't add the user directly. Just like in App Store Connect itself, you have to invite the user to your team and then they can accept that invitation. So we're going to create a user invitation resource. It looks like this. We do a POST because we're creating.

And the URL is the user Invitations resource URL. And then we have to send in the data for this user. Now this data looks a lot like what you've seen when we fetched users before. But there's one important difference I want to point out. We have a type user Invitations but we don't have an ID.

Now Apple will assign an ID to every resource you create for you. So you don't include that in the POST. We've also left off links because they're not required for creation. And we've left off relationships because they're not relevant in this particular case. If we run this request, we get a response. In particular, this is a 201 CREATED response. That's just standard REST behavior to tell you that the resource was successfully created.

And we also include the full resource information in the response data and this is important for two reasons. First, it gives you the ID and the resource self link of the resource that was created, which is stuff you're probably going to need later so you can come back and operate on this resource. And also if you look at the attributes, you may see attributes in this response that you didn't include in the POST.

For example with user invitations, Apple will assign an expiration Date to every user invitation you create. Now you don't set that, so you don't include it in the POST. And that's why this response data is really important to you. It shows you the complete picture of this resource after it's been created and all of our rules have been applied to it.

Now if this user accepts, they'll become a user on our team. And suppose we want to come back later and modify them in some way, like for instance you can see from the attributes here this user has the developer role. So let's change that so they have the developer and the marketing role.

We're changing an existing resource. So we do a PATCH request. The URL this time is the resource self link of the user we're changing. And if you look at the attributes, we're only including the roles attribute. This request says change this user so that they have the roles of DEVELOPER and MARKETING. We didn't want to change anything else about the user, so we specifically didn't include the other attributes.

If we run this request, again we get a successful response and we get the full resource representation of the resource with our changes applied. Now the last thing you probably want to do with the resource is delete it and this is the simplest operation of them all. We just do a DELETE request to the resource self link and we get a 204 NO CONTENT response. If you're familiar with REST, you know that any status code in the 200s means success.

So this is the API telling you that the user has successfully been deleted. And we didn't need to include any additional data this time, so we use the NO CONTENT version of a success response. Now before we move on to the next segment, I'd like to invite my colleague Sehoon Shon up to show you some examples of how to use these resources in real-world scenarios. Sehoon.

[ Applause ]

Thanks, Geoff. Hi, everyone. My name is Sehoon Shon and I'm a software engineer in TestFlight team and I'm here today to show you the live demo on App Store Connect API. We'll look at one use case and see how we can apply the API on users resource. Let's say someone in our team has left our company and we'd like to find this user and remove this user from the team in App Store Connect. So let's go see a demo.

We'll begin by getting the list of all the users in our team by sending GET request to the users resource, which is GET v1/users. And this returns all the users we have access to in our team. Now let's try to find the user we're looking for and we can search by the email of the user by applying the filter parameter. So this filter parameter of email specified that we would like to find the users that matches [email protected]. So let's send this request.

And we get a response with a user that contains the email of John Appleseed. Now let's use the ID of this user to get the instance of this user, which is users/ID and this should return the instance of the user with a matching ID. And we get a response with the username John Appleseed. So we found the user that we're looking for. Let's try to remove this user by sending DELETE request to self link of this user. So we'll replace GET with DELETE.

And this should remove the user with the matching ID. And we get a 204, which means the deletion was successful and NO CONTENT since the content of user is no longer available. Finally, let's confirm that the user is indeed removed by sending GET request to the self link once again. And now we get 404 NOT FOUND and it looks like the user is indeed removed.

So in this demo, we saw how to get collections of user, how to find instance of user, how to search user by applying filter, and how to remove user by sending DELETE request. And this concludes the demo on how to use App Source Connect API with the users resource. And I'll give the stage back to Geoff. Thank you.

[ Applause ]

Thanks, Sehoon. So now you know how to create, read, update, and delete all these various resources and it and might feel like that's basically it, that's all you need to be able to do but isn't the whole story and that brings us to relationships. Sometimes it's not the individual resources that you're most interested in but the connections between them. It helps if I give you an example.

We have a resource called Beta Groups. These represent all your groups in TestFlight. We have another one called Beta Testers that represent all of the people who can test your applications. And as you know, you can put those testers into groups. So how do we model something like this in the API?

Let's start by getting Beta Groups. We go a GET v1/beta Groups and we get back an array of Groups. And if we look at the first group and in particular the relationship section, we see that this group has three relationships: app, beta testers, and builds. We're only going to talk about Beta Testers today. So we'll pop that one open. And again, we see another links section.

These are links associated with this beta testers relationship on this first group in the list. You'd have a section like this in each of the groups in the array. There're two links here, the first one we call the relationship self link. And it's a URL that represents the relationship itself.

And we use this URL to operate on this relationship. Now let me explain that. We said we want to add testers to this group, right, but I want to be clear. The testers are already in TestFlight and the group is already in TestFlight. So we want to take these existing testers and put them into this existing group and that's a operation that doesn't fit with any of the things we've learned about already, if you think about it, right, like we're not creating or editing or deleting testers and we're not really editing the group either, at least not the attributes of the group. So conceptually, you might say that we're creating new connections between this group and its related beta testers. And this is where the relationship self link comes in.

It looks like this. We do a POST request to the relationship self link. And if you look at the data we're sending, it's these type and id pairs of the two testers in this case that we want to add to this group. So this request says take these two testers and put them into this group. We don't have to include more tester information because the testers are already in TestFlight. If we run it, we get a 204 NO CONTENT response again. That's the API telling you that the testers have been added to the group.

I'm sure some of you are wondering "What if I want to take testers out of the group instead?" And the answer is you do it in exactly the same way. It's the same URL. It's the same data format. You'll just use the DELETE method instead of the POST method. Now let's take a look at Beta Groups again and look at that second link.

This is what we call the related link. This URL represents the actual related data, in this case the testers in this group. If we go and get this data, we get back an array of beta testers. The format here looks exactly like what you'd get if you did a Get of v1/beta testers. The only difference is we're getting the testers in this group rather than all the testers in your TestFlight program. And I want to pause there and make sure I'm being clear.

We're getting the testers in this group and when I say "this group," I mean the group whose identifier is in that URL. So this URL get the testers in one group. If I want to get the testers in a bunch of different groups using this mechanism, I'd have to make a bunch of different requests, which can sometimes be inconvenient. So we do have one more way to get related data. It's called the include parameter.

It looks like this. We do a GET of v1/beta Groups and then we add this query parameter: include = beta testers. And this tells the API while you're bringing me back the beta groups, also include information about the related testers in each group. Here's what it looks like.

We get our array of beta testers, of beta Groups, excuse me, and if you look at the beta testers relationship in the first group, you see this new section called data. And this has the type and id pairs of the testers in this group. Now if you could see the whole response here, you'd see a data section like this inside each beta testers relationship inside each group that's in this array, right. But this is just the type and IDs. I'm sure you're wondering where the actual tester information is. And if we scroll all the way down to the end of this response, we also have a new section called include.

And this is an array of beta testers with the full tester information. More generally the include section has a full resource representation of every resource that you have included by way of relationships. And then we can match by type and id, figure out which testers belong to which group.

Now you might be wondering why we do this. We have the data section with all of our groups and then we have the include section with all of our included testers. And we do this for an important reason. We have this test on the screen John Appleseed and that tester might be in multiple groups, right.

The way we have this structured, the data for John Appleseed is only in the response one time, no matter how many groups they appear in, in the response. Make sense? Okay. Sehoon is going to come back up now and show us some more real-world examples with relationships. Sehoon.

[ Applause ]

Thanks, Geoff. So for this part of the demo we'll look at TestFlight and see how we can create new beta groups and add some external testers and we'll see some relationship between testers and groups. So let's go see the demo. We'll start by creating a new beta group by posting to the beta groups endpoint which is v1/beta Groups.

When we're creating a resource, we also need to provide the payload that looks like this. We have the data with the type of beta Groups and a set of attributes. And since the name is the only required field when we're creating a group, let's give it a name and we'll simply call it Test Group.

And I'll send the request now and we get a 409 CONFLICT response. So let's look at the details. It says, "You must provide a value for the relationship app with this request." So in TestFlight, you cannot create a beta Group that does not belong to any app and, thus, we must create a relationship to an app while we're creating beta Group.

And how do we do this? We can include the relationship in the payload like this. So now we have the relationship in the payload of an app with a data type of apps and the id specifies which apps the beta Groups should be linked to. So this will create beta Groups with the name of Test Group and link to an app with the id. So let's resend this request.

And now we get a 201 CREATED response. In the response, we have the id that is generated. So let's copy this id. So we just created a group called Test Group. But what if you don't like the name of the group we just created? We can modify the existing group by sending PATCH request to the self link of this group.

We'll send PATCH and then beta Groups/the id and we'll also need to provide the payload that looks like this. This looks a lot like payload for posting but we also need to provide the id to make sure that we're modifying the correct data. And let's paste the id of the beta Group. And the only information we'd like to change is the name.

So let's rename it to WWDC Group. And I'm sending the request and now we get a 200 response and it looks like the name has been updated. So now we have the group that we wanted. Let's start adding some external testers. To create tester, we send POST request to the beta testers endpoint, which is v1/beta testers.

And we'll also need to provide the payload that looks like this. It has a type of beta testers and a set of attribute. So we'll create tester that's called Kate Bell and this also has a relationship to the beta Groups and this will create Beta Tester and also assign to the beta Group at the same time.

So if you paste the id of the beta Group we just created, this will assign the tester to the WWDC Group. And we get a 201 CREATED response and it looks like the tester is created and also assigned to the group at the same time. While we're here, let's add one more tester and all we need to do is simply replace this attribute portion with a different person's name and this will create John Appleseed to the same group.

And again, we get a 201 CREATED response. So we just added two testers to the group and now let's confirm that these two testers are indeed inside the group by sending GET request to the related link between beta Groups and beta testers. So this link, /the id of the group/betaTesters, this will return all the beta Tester that are assigned to the beta Groups with the specified id. And as you can see, we have John Appleseed that's added earlier.

And this response we have a lot of information including list of attribute as well as relationship. But what if you only care about maybe the email of the tester? We can trim down the response by applying the special parameter called fields and this will allow us to trim down the body and only look at what we would like to see. If you do fields of beta testers equals email, as you can see now the response only contains the email of the user and nothing else.

And we have the two testers that are added to this beta Groups. So in this demo, we saw how to create beta Groups and beta testers and so some relationship between testers and groups. And this concludes the demo on How to Use App Store Connect API with TestFlight. And I'll give the stage back to Geoff. Thank you.

[ Applause ]

Thanks again, Sehoon. That was awesome. So that's getting data, changing data, and relationships. It kind of covers the basic features of the API. But as you saw a glimpse in Sehoon's demo, sometimes you make mistakes and we can't process your request. So we're going to shift gears now and talk about how the API communicates these errors back to you. Anytime a request fails, you'll get a response that looks like this.

Now the first thing to notice is that we'll send back an appropriate HTTP response status indicating what went wrong, usually something in the 400s. And this is often all you really need. Most REST client libraries have a Did Succeed or it Is Success function and you can call that and it will correctly tell you if the request was successful or not.

But if you want to know more about what went wrong, you can look down into the response. When a request fails, instead of that data property we've been seeing all along, we'll give you an error's property. This is an array of error objects. But there can be multiple of them, each one representing some problem with the request you sent.

They have an id that uniquely identifies this particular error of this particular response. You might log this away and if you think there's a problem on our end, send it to us. It'll help us track it down. More useful for you is the title and detail. These together give you an English language explanation of what went wrong. From the title here, I can see that I have something wrong with my parameters. And from the detail, it tells me that I'm filtering by email and right away I see that I spelled email incorrectly.

So these are great values to log and use in your troubleshooting and learning the API, but you don't want to use these in your code. You don't want to try and interpret these with your code. I don't make any promises that we won't change the wording on these messages over time without warning.

For programmatic error handling, you want to use the code property instead. This is a stable machine-readable string that represents what went wrong. It's a hierarchical value with dots between levels of specificity. So in this example, I can see that I have a parameter error, generally, and more specifically one of those parameters is invalid.

Now these codes can get very long and very specific and sometimes you don't care about all that specificity. In fact, usually you don't. So we structure them this way so that you can do a prefix match on the code and be as generic or a specific as you need to be for your particular use case. But if you need to be specific, say so you can report back to your own users with clarity about what went wrong, we try to give you enough information to do that.

And we help with that also with the source parameter. And whenever possible, we track this error back to the place in the request that caused the problem. Here I can see that it's the filter bracket email parameter, specifically, that produced this error. The source can be a parameter like this or it could be a JSON pointer pinpointing the spot in the JSON data you sent us where the problem originates. And that's basically everything there is to know about the App Store Connect API except how to get to the App Store Connect API and successfully send requests. So Julie Richards is going to come up on stage now and help you with that. Julie.

[ Applause ]

Hi, everyone. I'm Julie and I'm an engineer on the App Store Connect team. I'm here today to talk to you about access and authentication. So at this point you've seen a bunch of different examples of endpoints that will be available. And when you're ready to start testing out some of these new features, you might start by sending a simple GET request like this; however, if you were to simply curl this endpoint or type it into your browser, you'll end up with a response that looks like this.

So as Geoff mentioned, we're still missing one very important step stat and that is authentication credentials. Now this step is necessary for two reasons. First, authentication credentials give context to your request. After all, you don't want all apps. You want your app. And most importantly, these credentials secure the API and ensure that no one but you will have access to your data.

So to add these credentials to your request, you'll need to first create an API Key. You'll then use that key to generate tokens and those tokens will need to be sent with every request. So let's start with API Key. Each key is actually a public key, private key pair.

The private key belongs to you and will be used to add a unique signature to your token. The public key will be used by Apple to verify that signature and ensure that it was in fact signed by the associated private key. To create one of the keys, you'll need to log in to App Store Connect and navigate to a new API Keys tab. Your admin users will be able to manage your team's API Keys. You can create new Keys and revoke them when they're no longer needed. Each key will need to be assigned a level of access and this will determine what API services the key can be used for.

Once created, new keys will appear in this list and the private key file, that's the part that belongs to you, will be available to download. Now it's important to note that each private key can only be downloaded once. That is because these keys are not stored by Apple. In fact, your key is not even generated until the moment you decide to download it.

So you can think of these keys like real keys. They belong to you and they must be managed and protected by you. Also much like real keys, these keys do not expire, so if the key is lost or stolen it will continue to have access to your data until it is revoked through App Store Connect. So for this reason, it is very important to keep your keys safe and secure.

Once you have your private key file, you'll be ready to start generating JSON web tokens. Now each of these tokens will need to contain a few pieces of information. First, you'll need to add your Issuer ID, that's your account identifier, and this ID can be found at the top of that new page.

You'll also need to find or you'll also need to add the ID of your key. This ID can also be found on this new page and it is specific to the key that you'll be using. Now each token will also need an expiration timestamp. As I mentioned before, your keys do not expire but these tokens can only be used for up to 20 minutes. And the last two pieces of information are simply constant.

By that, I mean they will remain the same across all tokens and for all App Store Connect APIs, the first is the Audience for the token and that will always be App Store Connect. And finally, you'll need to assign or you'll need to add the algorithm that was used to sign the token.

And for this, we have chosen to use ES256. Now this algorithm corresponds to a JWT-supported algorithm that we have chosen to have you use but don't worry, you don't need to implement this algorithm. Fortunately, JWT provides multiple libraries across virtually any language that makes creating and signing these tokens as easy as possible.

This example behind me is written in Ruby and, as you can see, all I need to do is pass in these few pieces of information along with my private key and this encode method returns to me a complete and signed token. That token can then be added to my request by simply placing it inside of an authorization header. So now that we know how to create these keys and how to use these keys to add tokens to our requests, let's go ahead and give it a try.

Okay, so here's that new API Keys page and I don't have any keys yet. So I'll go ahead and create one. I'll call it demo and I'll have to assign a level of access. So here, if I were to choose something like finance, I'd get a key that has access to things like financial reports or sales reports but it won't have access to things like beta testers or builds.

I can add on levels of access or I can choose admin and I'll have a key that has access to all APIs. So I'm going to stick with admin. And when I generate my key, I can see that my private key is available to download. As I mentioned earlier, my private key can only be downloaded once. If I lose my key or accidentally delete it, I can't come back here to re-download it.

So for that reason, I'm prompted to make sure that I'm ready to download this now. And I am, so let's go ahead and download. Now that I have my private key in my Downloads folder, I can pull up that script we saw earlier. And I'll need to copy over my ISSUER ID.

That's the one that's shared across your API Keys. And I'll also need to copy that ID of the private key I just downloaded. And once I have those IDs in place, my private key will be loaded in from my Downloads folder. And I'll generate a new token. So if we pull up the terminal, I can curl that apps endpoint.

And as we expected, we get that 401 response. So let's call my script. And I get a new token. So I can take that token and add it to my request by putting it in an authorization header. Make sure I spelled that right. And I get back the list of my apps.

[ Applause ]

So as you can see, with just a few easy steps I was able to get access to the API and start getting back real data. Thank you. Back to Geoff.

[ Applause ]

Alright. Thanks, Julie. Pretty cool, huh? So that's access and authentication. And now in the few minutes that we have remaining, I want to talk about some best practices when using the API. We're going to start with those keys. Now as Julie said, those keys belong to you and it's your job to protect them. Anybody who has the keys can access your data.

So ideally, you're going to put those keys in some kind of a secure key store and your code will check them out, use them in memory, and never store them anywhere, like in a database or on disc. If you do have to store the keys on disc, make sure you check your file system permissions very carefully.

And, of course, if you have any reason to think a key has been compromised in some way, immediately log in to App Store Connect and revoke that key. I also want to talk about the tokens that you create from your keys. Now there's no reason that you have to send a different token with every request. In fact, you'll get better performance in your code and on our end if you reuse those tokens over and over again. And usually this is really easy, right. You just generate a token at the top of the script, send it with every request until you're finished.

And we let you control the expiration time because you're the best person to know how long it should last, long enough to complete that process and not significantly longer than it needs to be. Of course, you might have processes that run for longer than 20 minutes or run continuously and when that's the case you'll want to structure your code a little differently.

Maybe you'll generate a 20-minute token and use it as needed and then throw it away and issue a new one before the old one expires, say every 18 minutes. That way, you're getting maximum token reuse for the best performance and you're never sending expired tokens. The next think I want to talk about is the links that we include in the responses. Now we've talked about these today as a sort of nice form of self-documentation, a way for you to look at the data and see what else is available, but that's not the only reason they're there. They're actually intended to be used by your code.

When you're doing a multistep process, whenever possible take the link we gave you out of the response and use it for the next part of the process. This will help you in two ways. One, it makes your code more generically applicable to different parts of the API. And two, when that inevitable v2 API comes along, this will reduce the amount of work you have to do to adjust to the changes. And finally, I want to talk about the documentation.

We've talked about the consistency of this API today and we really focused on that. And so any time a resource can do something, it will do it in the same way as all the other resources but of course not every resource can do everything. You already saw an example of this with users. We had to create an invitation. There's no API to create a user. So the documentation is how you figure this out. It tells you what the resource can do, what operations are available, what parameters it supports, and so forth. And that's the App Store Connect API.

It's a consistent standards-based REST API. We're really excited about it. And one thing that we love about this API is that we designed it to be really flexible so that you can take App Store Connect and you can put it into your workflow into the way that you work and using the tools that you like best. We're really excited for you to start doing that. It'll be available to all app developers this summer.

And I know you guys have questions, so come and see us in the App Store Connect Lab. It's actually happening right now. So we'll head down there as soon as I stop talking and we can answer those questions. We have a lab tomorrow as well at 1:00 p.m. and also check out the What's New in App Store Connect Session if you missed it yesterday. It had some more about the API and about App Store Connect itself. And bookmark this link because we'll post the documentation there just as soon as it's available. Thank you so much. We'll see you in the labs.

[ Applause ]