# Third-Party Integrations 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 Public Beta 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. ## Getting Started 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](/docs/usage/authentication#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. Keep your credentials secure 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. ## OAuth 2.0 Authorization Code Grant Third-party integrations use the [OAuth 2.0 Authorization Code Grant](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1) 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: 1. **Redirect the user** to Understory's authorization endpoint 2. **Receive the authorization code** via callback to your redirect URI 3. **Exchange the code** for access and refresh tokens 4. **Use the access token** to make API requests ### Step 1: Initiate Authorization 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-string ``` This 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. ### Step 2: Handle the Callback 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-string ``` Verify the state parameter Always 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](#error-handling). ``` https://example.com/callback ?error=access_denied &error_description=The+user+denied+the+request &state=random-string ``` ### Step 3: Exchange Code for Tokens Exchange 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 | ```sh 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=" \ -d "client_secret=" \ -d "redirect_uri=https://example.com/oauth/callback" \ -d "code=" ``` If all parameters are valid you will receive the following response body with your token. ```json { "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjI0ZTUzZDkyLTY4ZmUt...", "expires_in": 3599, "refresh_token": "ory_rt_4dt-0JWm-9H2V4U1jfjUTCyJRbCZPSsvniMZQS18...", "scope": "offline marketing.read", "token_type": "bearer" } ``` Authorization codes are single-use Authorization codes can only be used once and expire shortly after being issued. Exchange them for tokens immediately. ### Step 4: Store Credentials Securely 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 Security requirements - 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 ## Making API Requests Include the access token in the `Authorization` header of your API requests. ## Refreshing Access Tokens When an access token expires, use the refresh token to obtain a new one without requiring user interaction: ```sh 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=" \ -d "client_id=" \ -d "client_secret=" ``` **Response:** ```json { "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjI0ZTUzZDkyLTY4ZmUt...", "expires_in": 3599, "refresh_token": "ory_rt_XOnJ6xdZlbAAPBSSJ91uoGSYwJ3DVBsKFL-GEM5O...", "scope": "offline marketing.read", "token_type": "bearer" } ``` Refresh token rotation A new refresh token may be issued with each refresh request. Always store the latest refresh token received. ## Error Handling OAuth 2.0 errors follow the standard format defined in [RFC 6749 Section 5.2](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2): ```json { "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 | ## Testing Your Integration To test your third-party integration during development: 1. **Register development redirect URIs** - When requesting credentials, include both production and development callback URLs (e.g., `https://localhost:3000/oauth/callback` for local testing). 2. **Use a test Understory account** - Create a separate [Understory account](https://backoffice.understory.io/signup) specifically for testing. This allows you to authorize your app and test the OAuth flow without affecting real customer data. 3. **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 4. **Handle edge cases** - Test scenarios like: - User denies authorization - Invalid or expired authorization codes - Expired access tokens - Network failures during token exchange No sandbox environment 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](/docs/usage/testing) to learn more. ## Further Reading - [Understory API Authentication](/docs/usage/authentication) - General authentication overview - [Understory API Reference](/apis) - [OAuth 2.0 RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749) - The OAuth 2.0 Authorization Framework - [OAuth 2.0 Security Best Practices](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics) - Current security recommendations - [What is the OAuth 2.0 Authorization Code Grant Type?](https://developer.okta.com/blog/2018/04/10/oauth-authorization-code-grant-type) - Introduction to the Authorization Code grant type