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

# Authentication

> Learn how to authenticate with Meitner's APIs using OAuth 2.0 Client Credentials

The Directory and DataExport APIs use **OAuth 2.0** with the **Client Credentials** flow. This flow is designed for server-to-server communication where no end-user context is required — your application authenticates directly using its client credentials to obtain a short-lived access token.

## Obtaining an access token

Send a `POST` request to the token endpoint with your `client_id` and `client_secret`:

<CodeGroup>
  ```http HTTP theme={null}
  POST https://api.meitner.se/oauth/token HTTP/1.1
  Content-Type: application/x-www-form-urlencoded

  grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET
  ```

  ```python Python theme={null}
  import requests

  def get_access_token(client_id, client_secret):
      response = requests.post("https://api.meitner.se/oauth/token", data={
          "grant_type": "client_credentials",
          "client_id": client_id,
          "client_secret": client_secret,
      })
      data = response.json()
      return data["access_token"], data["expires_in"]

  token, expires_in = get_access_token("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")
  ```

  ```curl cURL theme={null}
  curl -X POST https://api.meitner.se/oauth/token \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET"
  ```
</CodeGroup>

The response contains the access token and its lifetime in seconds:

```json theme={null}
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

## Using the access token

Include the token in the `Authorization` header of every API request:

<CodeGroup>
  ```http Directory API theme={null}
  GET /directory/v1/school HTTP/1.1
  Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
  ```

  ```http DataExport API theme={null}
  GET /data-export/v1/attendance HTTP/1.1
  Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
  ```
</CodeGroup>

## Handling token expiry

Tokens are valid for the duration specified in `expires_in` (seconds). The Client Credentials flow does not issue refresh tokens — when your token expires, request a new one using the same client credentials.

```python theme={null}
import requests

def get_access_token(client_id, client_secret):
    response = requests.post("https://api.meitner.se/oauth/token", data={
        "grant_type": "client_credentials",
        "client_id": client_id,
        "client_secret": client_secret,
    })
    data = response.json()
    return data["access_token"], data["expires_in"]

token, expires_in = get_access_token("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")

headers = {"Authorization": f"Bearer {token}"}
response = requests.get("https://api.meitner.se/directory/v1/school", headers=headers)
```

<Note>
  Track `expires_in` in your application and proactively refresh the token before it expires to avoid failed requests.
</Note>

<Tip>
  If you are using the **Directory API SDK**, token refresh is handled automatically — you don't need to manage token expiry yourself.
</Tip>
