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

# Server Tokens

> Authenticate server-to-server requests using OAuth client credentials.

This guide walks you through obtaining access tokens for server-to-server authentication using the OAuth 2.0 client credentials flow. This is useful when your backend application needs to call other services or APIs on behalf of itself, without involving a user.

## Prerequisites

Before you start, make sure you have:

* A Prelude account with access to Prelude Auth
* An **Application ID** (`appID`) — see [Applications](/auth/documentation/applications)
* Your **Management API key** for backend calls

## How it works

The client credentials flow allows your backend application to:

1. Register an OAuth client (a "service account") with Prelude Auth
2. Exchange the client credentials (ID and secret) for an access token
3. Use the token to authenticate requests to your backend or other services

This flow is ideal for:

* Service-to-service communication
* Scheduled jobs and background tasks
* Microservices that need to call each other
* API integrations that don't involve a user

## Set up server tokens

<Steps>
  <Step title="Register an OAuth client">
    Create a new OAuth client registered for the `client_credentials` grant type:

    ```bash theme={null}
    curl -X POST https://api.prelude.dev/v2/session/apps/${APP_ID}/oauth/clients \
      -H "Authorization: Bearer ${MANAGEMENT_API_KEY}" \
      -H "Content-Type: application/json" \
      -d '{
        "client_name": "backend-service",
        "redirect_uris": [],
        "provider_url": "https://api.example.com",
        "scopes": ["read", "write"],
        "grant_types": ["client_credentials"]
      }'
    ```

    The response includes your `client_id` and `client_secret`. Store the secret securely — you'll need it to request tokens. **The secret is only returned once** — if you lose it, you'll need to rotate the client.

    | Field           | Required | Description                                                                                                                     |
    | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
    | `client_name`   | Yes      | A human-readable name for this client (e.g., `"backend-service"`, `"background-jobs"`).                                         |
    | `provider_url`  | Yes      | The URL where your application operates. Used for security validation.                                                          |
    | `redirect_uris` | No       | Can be an empty array for client credentials clients.                                                                           |
    | `scopes`        | No       | Scopes this client is allowed to request (e.g., `["read", "write"]`). Tokens can only request scopes registered for the client. |
    | `grant_types`   | Yes      | For server tokens, use `["client_credentials"]`.                                                                                |
  </Step>

  <Step title="Request an access token">
    Exchange your client credentials for an access token:

    ```bash theme={null}
    curl -X POST https://api.prelude.dev/v1/session/oauth/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d 'grant_type=client_credentials' \
      -d 'client_id=${CLIENT_ID}' \
      -d 'client_secret=${CLIENT_SECRET}' \
      -d 'scope=read write'
    ```

    Alternatively, use HTTP Basic authentication:

    ```bash theme={null}
    curl -X POST https://api.prelude.dev/v1/session/oauth/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -H "Authorization: Basic $(echo -n '${CLIENT_ID}:${CLIENT_SECRET}' | base64)" \
      -d 'grant_type=client_credentials' \
      -d 'scope=read write'
    ```

    The response contains:

    ```json theme={null}
    {
      "access_token": "eyJhbGc...",
      "token_type": "Bearer",
      "expires_in": 3600,
      "scope": "read write"
    }
    ```

    | Parameter       | Description                                                                                                                 |
    | --------------- | --------------------------------------------------------------------------------------------------------------------------- |
    | `grant_type`    | Must be `"client_credentials"`.                                                                                             |
    | `client_id`     | Your registered client ID.                                                                                                  |
    | `client_secret` | Your registered client secret. Keep this secure.                                                                            |
    | `scope`         | (Optional) Scopes to request. Must be a subset of the scopes registered for your client. If omitted, no scopes are granted. |

    Note: Client credentials tokens do not include a `refresh_token`. When the token expires, request a new one.
  </Step>

  <Step title="Use the access token">
    Include the token in the `Authorization` header of your requests:

    ```bash theme={null}
    curl https://your-backend.example.com/api/resource \
      -H "Authorization: Bearer ${ACCESS_TOKEN}"
    ```

    On your backend, verify the token signature using the [JWKS endpoint](/auth/documentation/jwks). The token's subject (`sub` claim) will be the client ID.
  </Step>
</Steps>

## Token claims

Tokens issued via client credentials include the following claims:

```json theme={null}
{
  "sub": "client_id_...",
  "scope": "read write",
  "app_id": "app_...",
  "iat": 1234567890,
  "exp": 1234571490,
  "iss": "https://auth.prelude.dev",
  "aud": "your-app-id"
}
```

Key differences from user tokens:

* `sub` is the client ID (not a user ID)
* `session_id` is absent — there is no session
* Tokens are not revocable via session invalidation
* Tokens expire after 1 hour (configurable)

## Client rotation

If you suspect your client secret has been compromised:

1. Create a new client with the same scopes and grant types
2. Update your application to use the new credentials
3. Delete the compromised client using the Management API

```bash theme={null}
curl -X DELETE https://api.prelude.dev/v2/session/apps/${APP_ID}/oauth/clients/${CLIENT_ID} \
  -H "Authorization: Bearer ${MANAGEMENT_API_KEY}"
```

## What's next?

* Review the [token verification](/auth/documentation/jwks) guide to validate tokens on your backend
* Set up [webhooks](/auth/documentation/webhooks/introduction) if you need to be notified of token-related events
* Explore the [Management API](/auth/api-reference/management/users/list-users) for advanced token management
