> ## Documentation Index
> Fetch the complete documentation index at: https://help.privy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> How to authenticate with the Privy API using OAuth 2.0 bearer tokens or API tokens.

The Privy API supports two authentication methods. Both methods produce a bearer token that you send in the `Authorization` header:

* **[API tokens](/docs/api-reference/api-tokens)** — Generate a long-lived token from the dashboard and use it directly. Best for scripts and quick integrations.
* **OAuth applications** — Exchange client credentials for a short-lived access token. Best for third-party integrations with automated rotation.

## OAuth: How it works

1. Create an OAuth application in your Privy dashboard to get a **client ID** and **client secret**.
2. Exchange those credentials for an access token using the OAuth 2.0 client credentials flow.
3. Include the access token in the `Authorization` header of every request.

```bash theme={null}
curl -X GET "https://api.privy.com/v1/contacts" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

## Creating an OAuth application

Navigate to **Settings > Apps** in your [Privy dashboard](https://dashboard.privy.com) and create a new application. You'll choose a name and select which scopes the application should have access to. By default, no scopes are assigned.

When the application is created, the dashboard displays the **client secret once**. Copy it immediately — you won't be able to view it again. If you lose it, you can regenerate a new secret from the application's settings.

## Scopes

Scopes control what your token can access. Your OAuth application must have a scope enabled before you can request it — configure scopes in **Settings > Apps** in your dashboard.

| Scope            | Description                                      | Default |
| ---------------- | ------------------------------------------------ | ------- |
| `contacts_read`  | List and filter contacts                         | Yes     |
| `contacts_write` | Create, update, unsubscribe, and delete contacts | No      |
| `events_write`   | Ingest custom events                             | No      |

## Getting an access token

Exchange your client ID and client secret for an access token by making a `POST` request to the token endpoint.

### Read-only access

To read contacts, request the `contacts_read` scope explicitly:

```bash theme={null}
curl -X POST "https://api.privy.com/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scopes=contacts_read"
```

```json theme={null}
{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 7200,
  "scope": "contacts_read",
  "created_at": 1712150400
}
```

### Read and write access

To create, update, or delete contacts, request the `contacts_write` scope explicitly:

```bash theme={null}
curl -X POST "https://api.privy.com/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=contacts_read contacts_write"
```

```json theme={null}
{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 7200,
  "scope": "contacts_read contacts_write",
  "created_at": 1712150400
}
```

<Note>
  The `scope` response field confirms which scopes were actually granted. Always check this to verify your token has the access you need.
</Note>

| Field          | Description                                                                       |
| -------------- | --------------------------------------------------------------------------------- |
| `access_token` | The bearer token to include in API requests.                                      |
| `token_type`   | Always `Bearer`.                                                                  |
| `expires_in`   | Token lifetime in seconds. Access tokens expire after **2 hours** (7200 seconds). |
| `scope`        | The scopes granted to this token.                                                 |

<Warning>
  If your client ID or secret is wrong, the token endpoint returns a `401` error with `invalid_client`. If you request a scope that isn't enabled on your application, it returns `invalid_scope`.
</Warning>

### Refreshing tokens

When your access token expires, request a new one by repeating the client credentials exchange above. There is no refresh token in the client credentials flow — simply request a new access token.

## Security tips

* **Keep credentials secret.** Never share your client secret or access tokens in client-side code, public repositories, or URLs.
* **Rotate secrets regularly.** Regenerate your client secret periodically from the dashboard, especially if a team member leaves.
* **Use one application per integration.** This makes it easy to revoke access for a single integration without affecting others.

## Invalid tokens

If your bearer token is missing, expired, revoked, or malformed, API endpoints return a `401 unauthorized` error:

```json theme={null}
{
  "error": {
    "code": "unauthorized",
    "message": "Bearer token is missing or invalid"
  }
}
```

See the [Errors](/docs/api-reference/errors) page for all error codes.
