---
id: "projects/oauth-setup"
title: "Create an OAuth client"
description: "This article shows you how to register an OAuth client and implement RevenueCat's authorization flow. You'll register the client, send developers through authorization, and call the REST API with the tokens you receive. By the end, your client can access RevenueCat on behalf of developers who approve it."
permalink: "/docs/projects/oauth-setup"
slug: "oauth-setup"
version: "current"
original_source: "docs/projects/oauth-setup.mdx"
---

> **AI agents:** This is the Markdown version of a RevenueCat documentation page. For the complete documentation index, see [llms.txt](https://www.revenuecat.com/docs/llms.txt).

This article shows you how to register an OAuth client and implement RevenueCat's authorization flow. You'll register the client, send developers through authorization, and call the REST API with the tokens you receive. By the end, your client can access RevenueCat on behalf of developers who approve it.

:::info\[Who this page is for]
This page is for teams building an OAuth client that other RevenueCat developers will connect to. If you want to connect to an existing client, or revoke one you already authorized, see [OAuth](https://www.revenuecat.com/docs/projects/oauth-overview). If you're calling the REST API for your own project, create a [secret API key](https://www.revenuecat.com/docs/projects/authentication) instead.
:::

## How client authorization works

RevenueCat supports the OAuth 2.0 Authorization Code flow. Public clients must use Proof Key for Code Exchange (PKCE) (`S256`). Confidential clients can omit it. If a client sends a `code_challenge`, RevenueCat validates it. A RevenueCat developer approves your client on a consent screen, then your client receives an authorization code and exchanges it for an access token (`atk_`) and refresh token (`rtk_`).

OAuth tokens are developer-level: they can access the projects that developer owns or collaborates on, limited by the scopes they granted. Access tokens expire after 1 hour. Refresh tokens expire after 30 days. Refreshing rotates both tokens.

## Client creation steps

1. [Register your client](#client-registration) with RevenueCat Support. You'll get a client ID, and a client secret if the client is confidential.
2. [Send the developer through authorization](#authorization-flow). Direct them to the authorize endpoint, handle the redirect, and exchange the code for tokens.
3. [Call the REST API](#making-api-requests) with the access token, and [refresh](#token-management) when it expires.

Public clients must implement [PKCE](#pkce-implementation). Confidential clients can send it too. The rest of this page covers each of those in detail.

## Client Registration

To integrate with RevenueCat's OAuth server, you'll need to register your application as an OAuth client. Contact our [support team](mailto:support@revenuecat.com) to register your client with the following information:

- **Client Name**: Display name for your application
- **Client URI**: Your application's homepage URL
- **Redirect URIs**: Valid callback URLs for your application
- **Client Type**: Public (for native/desktop apps) or Confidential (for server-side apps)
- **Token auth method**: Confidential clients use HTTP Basic authentication by default. Public clients use `none`.
  If your integration requires sending credentials in the token request body, request `client_secret_post` when you register your client.
- **Scopes**: [Available scopes](#available-scopes) your application would like to request

## Authorization Flow

### Step 1: Initiate Authorization

Direct users to the authorization endpoint:

```
GET https://api.revenuecat.com/oauth2/authorize
```

**Required Parameters:**

- `client_id`: Your client identifier
- `response_type`: Must be `code`
- `redirect_uri`: Must match a registered redirect URI
- `scope`: Space-separated list of requested permissions
- `state`: Opaque value you generate and later compare to the redirect (required; omitting it returns `invalid_request`)
- `code_challenge`: PKCE code challenge (required for public clients, optional for confidential clients)
- `code_challenge_method`: Must be `S256` (required when `code_challenge` is present)

**Example Authorization URL:**

```url
https://api.revenuecat.com/oauth2/authorize?
  client_id=your_client_id&
  response_type=code&
  redirect_uri=https://yourapp.com/callback&
  scope=project_configuration:apps:read&
  state=random_state_string&
  code_challenge=your_code_challenge&
  code_challenge_method=S256
```

### Step 2: Handle Authorization Response

After the user grants permission, they'll be redirected to your `redirect_uri`. Confirm that `state` matches the value you sent in Step 1.

**Success Response:**

```
https://yourapp.com/callback?code=authorization_code&state=random_state_string
```

**Error Response:**

```
https://yourapp.com/callback?error=access_denied&error_description=description&state=random_state_string
```

### Step 3: Exchange Code for Tokens

Exchange the authorization code for access and refresh tokens:

```bash
curl -X POST https://api.revenuecat.com/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "your_client_id:your_client_secret" \
  -d "grant_type=authorization_code&code=your_auth_code&redirect_uri=https://yourapp.com/callback"
```

**Parameters:**

- `grant_type`: Must be `authorization_code`
- `code`: The authorization code from Step 2
- `redirect_uri`: Must match the redirect URI from Step 1
- `code_verifier`: PKCE code verifier (required when a `code_challenge` was sent)

Confidential clients authenticate with the client ID and client secret using HTTP Basic authentication by default.
If your client is registered with `client_secret_post`, send `client_id` and `client_secret` in the form body
instead of using the `Authorization` header.

**Success Response:**

```json
{
  "access_token": "atk_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rtk_...",
  "scope": "project_configuration:apps:read"
}
```

## Token Management

### Access Tokens

- **Lifetime**: 1 hour
- **Usage**: Include in API requests via `Authorization: Bearer {access_token}` header
- **Prefix**: `atk_`

### Refresh Tokens

- **Lifetime**: 30 days
- **Usage**: Exchange for new access tokens when they expire
- **Prefix**: `rtk_`

### Refreshing Tokens

When your access token expires, use the refresh token to get a new pair:

```bash
curl -X POST https://api.revenuecat.com/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "your_client_id:your_client_secret" \
  -d "grant_type=refresh_token&refresh_token=your_refresh_token"
```

**Parameters:**

- `grant_type`: Must be `refresh_token`
- `refresh_token`: Your current refresh token

:::warning\[Token Rotation]
When tokens are refreshed, both the old access and refresh tokens are revoked, and new ones are issued. Make sure to update your stored tokens.
:::

## Available Scopes

Request only the scopes your application needs:

### Project Configuration

- `project_configuration:projects:read` - List projects
- `project_configuration:projects:read_write` - Create projects
- `project_configuration:apps:read` - Read apps and app config
- `project_configuration:apps:read_write` - Create, update, and delete apps
- `project_configuration:entitlements:read` - Read entitlements and attached products
- `project_configuration:entitlements:read_write` - Create, update, and delete entitlements
- `project_configuration:offerings:read` - Read offerings and paywalls
- `project_configuration:offerings:read_write` - Create, update, and delete offerings and paywalls
- `project_configuration:packages:read` - Read packages and attached products
- `project_configuration:packages:read_write` - Create, update, and delete packages
- `project_configuration:products:read` - Read products
- `project_configuration:products:read_write` - Create, update, delete, and push products to stores
- `project_configuration:integrations:read` - List webhook integrations
- `project_configuration:integrations:read_write` - Create, update, and delete webhook integrations
- `project_configuration:virtual_currencies:read` - Read virtual currencies
- `project_configuration:virtual_currencies:read_write` - Create, update, and delete virtual currencies

### Customer Information

- `customer_information:customers:read` - Read customers, aliases, attributes, and active entitlements
- `customer_information:customers:read_write` - Manage customers and customer-level actions
- `customer_information:subscriptions:read` - Read subscriptions and related entitlements or transactions
- `customer_information:subscriptions:read_write` - Manage subscriptions, including cancellations and refunds
- `customer_information:purchases:read` - Read purchases and purchase entitlements
- `customer_information:purchases:read_write` - Manage purchases and virtual currency balance operations
- `customer_information:invoices:read` - Read customer invoices

### Charts & Metrics

- `charts_metrics:overview:read` - Read overview metrics for a project
- `charts_metrics:charts:read` - Read chart data and options for a chart

## Making API Requests

OAuth access tokens authenticate [REST API v2](https://www.revenuecat.com/api-v2) requests. Don't configure the RevenueCat SDK with an OAuth token. For your own app or backend, use [API keys](https://www.revenuecat.com/docs/projects/authentication) instead.

Include the access token in the `Authorization` header, using the same `Bearer` format as a secret API key:

```bash
curl -H "Authorization: Bearer atk_your_access_token" \
  https://api.revenuecat.com/v2/projects
```

## PKCE Implementation

Proof Key for Code Exchange (PKCE) is required for public clients. Confidential clients can omit it. If a client sends a `code_challenge`, it must use `S256` and send the matching `code_verifier` when exchanging the code.

### 1. Generate Code Verifier and Challenge

```javascript
// Generate a random code verifier (43-128 characters)
function generateCodeVerifier() {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return base64URLEncode(array);
}

// Create code challenge from verifier
async function generateCodeChallenge(verifier) {
  const encoder = new TextEncoder();
  const data = encoder.encode(verifier);
  const digest = await crypto.subtle.digest("SHA-256", data);
  return base64URLEncode(new Uint8Array(digest));
}

// Base64 URL encoding helper
function base64URLEncode(str) {
  return btoa(String.fromCharCode.apply(null, str))
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=/g, "");
}
```

### 2. Use in Authorization Request

Include `code_challenge` and `code_challenge_method=S256` in your authorization URL.

### 3. Include in Token Exchange

Send the original `code_verifier` when exchanging the authorization code for tokens.

## Error Handling

### Authorization Errors

- `invalid_request` - Missing or invalid parameters
- `unauthorized_client` - Client not authorized for this grant type
- `access_denied` - User denied authorization
- `unsupported_response_type` - Invalid response type
- `invalid_scope` - Requested scope is invalid or unknown
- `server_error` - Internal server error

### Token Errors

- `invalid_request` - Missing or invalid parameters
- `invalid_client` - Client authentication failed
- `invalid_grant` - Authorization code/refresh token is invalid or expired
- `unauthorized_client` - Client not authorized for this grant type
- `unsupported_grant_type` - Grant type not supported

## Best Practices

1. **Store tokens securely** - Never expose tokens in client-side code
2. **Implement proper error handling** - Handle token expiration gracefully
3. **Use HTTPS only** - All OAuth flows must use secure connections
4. **Validate state parameter** - Prevent CSRF attacks
5. **Request minimal scopes** - Only request permissions you actually need
6. **Implement token refresh** - Handle access token expiration automatically

## Rate Limits

OAuth tokens are subject to the same rate limits as API keys. Monitor your usage and implement appropriate backoff strategies.

## Next steps

- [OAuth](https://www.revenuecat.com/docs/projects/oauth-overview) — how RevenueCat developers connect to your client and revoke access
- [REST API v2](https://www.revenuecat.com/api-v2) — the endpoints your access token can call

To register a client or ask about the integration, contact [support@revenuecat.com](mailto:support@revenuecat.com).
