This guide explains how to build third-party applications that integrate with the Understory API on behalf of Understory customers. Third-party integrations use the OAuth 2.0 Authorization Code Grant flow, allowing your application to request access to a customer's Understory data with their explicit consent.
This is useful if you provide services that can use the data from Understory. Common examples are:
- Marketplaces
- Marketing tools
- Accounting systems
Third-party integrations are currently in public beta. We are actively accepting new integrations and would love to work with you. However, some aspects of the integration process may change over the coming months as we learn and improve. We will communicate any changes in advance and work with you to ensure a smooth transition.
To build a third-party integration, you'll need OAuth 2.0 client credentials issued by Understory.
Contact us at integrations@understory.io with the following information.
| Field | Description |
|---|---|
| Name | The name of your application as it will appear to users during authorization |
| Description | A one or two sentence description of what your integration provides to the Understory customer |
| Scopes | The API scopes your application requires (e.g., booking.read, marketing.read). See Available Scopes for the complete list. |
| Redirect URIs | The callback URLs in your application where users will be redirected after they authorize access. This is a URL on your server that will receive the authorization code. For example: https://example.com/oauth/callback. You can register multiple URIs for different environments (development, staging, production). |
| Logo URI | URL to your application's logo (displayed during authorization) |
| Policy URI | URL to your privacy policy |
| Terms URI | URL to your terms of service |
| App Owner | The name of your company or organization |
| Contact Email | A contact email for integration-related communications |
After review, you will receive your client ID and secret key via a secure link and you can start building your integration.
Your credentials, both client ID and secret key, must be kept confidential. Never expose them in client-side code, version control, or public repositories.
If your credentials have been compromised, reach out to integrations@understory.io and we will rotate them immediately.
Third-party integrations use the OAuth 2.0 Authorization Code Grant flow. This is the industry-standard protocol for secure delegated authorization, used by platforms like Linear, Google, and Okta.
The flow consists of four main steps:
- Redirect the user to Understory's authorization endpoint
- Receive the authorization code via callback to your redirect URI
- Exchange the code for access and refresh tokens
- Use the access token to make API requests
Redirect the user to Understory's authorization endpoint to request access to their resources.
Include the following query parameters and remember to URL encode audience, redirect_uri, and scope.
| Field | Value |
|---|---|
response_type | Literal string code |
audience | Literal string https://api.understory.io |
client_id | Client ID |
redirect_uri | One of your registered redirect URIs |
scope | Applicable space-separated scopes |
state | A unique, random string to prevent CSRF attacks. Store this value and verify it in the callback. |
https://api.auth.understory.io/oauth2/auth
?response_type=code
&audience=https%3A%2F%2Fapi.understory.io
&client_id=client-id
&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
&scope=offline+marketing.read
&state=random-stringThis will show a consent screen with your application name, logo, and the permissions being requested. If they approve, they will be redirected to your specified redirect URI.
After the user authorizes your application, Understory redirects them to your redirect_uri with an authorization code:
https://example.com/callback
?code=AUTHORIZATION_CODE
&state=random-stringAlways verify that the state parameter matches the value you sent in Step 1. This protects against cross-site request forgery (CSRF) attacks.
If the user denies authorization or an error occurs, the callback will include error parameters instead. Read more in Error Handling.
https://example.com/callback
?error=access_denied
&error_description=The+user+denied+the+request
&state=random-stringExchange the authorization code for access and refresh tokens by making a POST request to the token endpoint https://api.auth.understory.io/oauth2/token. Use the following parameters provided in the body of the request.
| Field | Value |
|---|---|
grant_type | Literal string authorization_code |
client_id | Client ID |
client_secret | Secret Key |
redirect_uri | The registered redirect URI used to get the code |
code | The authorization code received in the callback |
curl -X POST https://api.auth.understory.io/oauth2/token \
-H "User-Agent: My-Company/Marketing-Automation-App" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=<client-id>" \
-d "client_secret=<secret-key>" \
-d "redirect_uri=https://example.com/oauth/callback" \
-d "code=<authorization-code>"If all parameters are valid you will receive the following response body with your token.
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjI0ZTUzZDkyLTY4ZmUt...",
"expires_in": 3599,
"refresh_token": "ory_rt_4dt-0JWm-9H2V4U1jfjUTCyJRbCZPSsvniMZQS18...",
"scope": "offline marketing.read",
"token_type": "bearer"
}Authorization codes can only be used once and expire shortly after being issued. Exchange them for tokens immediately.
After receiving the tokens, store them securely:
- Access tokens are short-lived (1 hour) and used to make API requests
- Refresh tokens are long-lived (30 days) and used to obtain new access tokens without requiring user re-authorization
- Store tokens encrypted at rest
- Never log tokens or include them in error messages
- Use secure, server-side storage (never browser localStorage for sensitive tokens)
- Implement proper token rotation when using refresh tokens
Include the access token in the Authorization header of your API requests.
- https://api.understory.io/v1/marketing-consents
curl -i -X GET \
'https://api.understory.io/v1/marketing-consents?cursor=string&limit=100' \
-H 'Authorization: Bearer <YOUR_TOKEN_HERE>'When an access token expires, use the refresh token to obtain a new one without requiring user interaction:
curl -X POST https://api.auth.understory.io/oauth2/token \
-H "User-Agent: My-Company/Marketing-Automation-App" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=<refresh-token>" \
-d "client_id=<client-id>" \
-d "client_secret=<secret-key>"Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjI0ZTUzZDkyLTY4ZmUt...",
"expires_in": 3599,
"refresh_token": "ory_rt_XOnJ6xdZlbAAPBSSJ91uoGSYwJ3DVBsKFL-GEM5O...",
"scope": "offline marketing.read",
"token_type": "bearer"
}A new refresh token may be issued with each refresh request. Always store the latest refresh token received.
OAuth 2.0 errors follow the standard format defined in RFC 6749 Section 5.2:
{
"error": "invalid_grant",
"error_description": "The authorization code has expired"
}Common errors include:
| Error | Description |
|---|---|
invalid_request | Missing or invalid parameters |
invalid_client | Client authentication failed |
invalid_grant | Authorization code or refresh token is invalid or expired |
unauthorized_client | Client is not authorized to use this grant type |
invalid_scope | Requested scope is invalid or exceeds granted permissions |
To test your third-party integration during development:
Register development redirect URIs - When requesting credentials, include both production and development callback URLs (e.g.,
https://localhost:3000/oauth/callbackfor local testing).Use a test Understory account - Create a separate Understory account specifically for testing. This allows you to authorize your app and test the OAuth flow without affecting real customer data.
Test the complete flow - Verify that your application can:
- Redirect users to the authorization endpoint
- Handle the callback with the authorization code
- Exchange the code for tokens
- Make API requests with the access token
- Refresh tokens when they expire
Handle edge cases - Test scenarios like:
- User denies authorization
- Invalid or expired authorization codes
- Expired access tokens
- Network failures during token exchange
Understory does not provide a separate sandbox environment. Use a dedicated test account and free experiences to safely test your integration without affecting production data or processing real payments.
See Testing to learn more.
- Understory API Authentication - General authentication overview
- Understory API Reference
- OAuth 2.0 RFC 6749 - The OAuth 2.0 Authorization Framework
- OAuth 2.0 Security Best Practices - Current security recommendations
- What is the OAuth 2.0 Authorization Code Grant Type? - Introduction to the Authorization Code grant type