The Privy API supports two authentication methods — both use bearer tokens in the Authorization header:
- 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
- Create an OAuth application in your Privy dashboard to get a client ID and client secret.
- Exchange those credentials for an access token using the OAuth 2.0 client credentials flow.
- Include the access token in the
Authorization header of every request.
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 and create a new application. You’ll choose a name and select which scopes the application should have access to. All applications are granted contacts_read by default — you can optionally enable contacts_write during creation or later from the application’s settings.
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 |
contacts_read is the default scope. If you don’t specify a scope parameter in your token request, your token is granted contacts_read only.
contacts_write must be explicitly requested by including scope in your token request.
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 (default)
If you only need to read contacts, no scope parameter is required:
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"
{
"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 contacts_write explicitly:
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"
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 7200,
"scope": "contacts_read contacts_write",
"created_at": 1712150400
}
The scope response field confirms which scopes were actually granted. Always check this to verify your token has the access you need.
| 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. |
If your client ID or secret is wrong, the API returns a 401 error with "error": "invalid_client". If you request a scope that isn’t enabled on your application, the API returns a 401 error with "error": "invalid_scope".
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 token is missing, expired, or malformed, the API returns a 401 unauthorized error:
{
"error": {
"code": "unauthorized",
"message": "Bearer token is missing or invalid"
}
}
See the Errors page for all error codes.