> For the complete documentation index, see [llms.txt](https://docs.millpont.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.millpont.com/meti-api-documents/authentication.md).

# Authentication

## Authentication

To interact with the METI API, you must receive a client id and client secret for your Custodian account from MillPont administrators.\
\
This API uses **Auth0 JWT Bearer tokens** for authentication. Each client gets dedicated credentials with account-specific access.

### 1. Get an Access Token (Auth0)

#### Request

**URL:** https\://\<AUTH0\_DOMAIN>/oauth/token\
**Method:** POST\
**Headers:** Content-Type: application/json\
**Body**\
`{ "client_id": "<YOUR_CLIENT_ID>", "client_secret": "<YOUR_CLIENT_SECRET>", "audience": "https://api.meti.millpont.com", "grant_type": "client_credentials", "scope": "read:sources write:sources delete:sources" }`

#### Example (cURL)

```
curl -X POST "https://<AUTH0_DOMAIN>/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "<YOUR_CLIENT_ID>",
    "client_secret": "<YOUR_CLIENT_SECRET>",
    "audience": "https://api.meti.millpont.com",
    "grant_type": "client_credentials",
    "scope": "read:sources write:sources delete:sources"
  }'

```

#### **Response**

`{`\
`"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",`\
`"token_type": "Bearer",`\
`"expires_in": 3600,`\
`"scope": "read:sources write:sources delete:sources"`\
`}`

Key fields:

* `access_token` (string): The JWT used to authenticate with the METI API.
* `token_type` (string): Always `Bearer`.
* `expires_in` (integer): Lifetime of the token in seconds.
* `scope` (string): Space-separated API permissions granted to this token.

Depending on your Auth0 configuration, the token may also include custom METI claims such as:

* `https://api.meti.millpont.com/account_id`
* `https://api.meti.millpont.com/role`
* `https://api.meti.millpont.com/client_name`
* `https://api.meti.millpont.com/v1_client_id`

These are used by the API to associate requests with accounts and roles.

### 2. Using the Access Token with METI API

Once you have an `access_token`, include it in the `Authorization` header for all METI API requests.

**Format**

```
Authorization: Bearer <access_token>
```

#### Example: List Sources

```bash
curl -X GET "https://api.meti.millpont.com/sources" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```

#### Example: Filtered by Methodology

```bash
curl -X GET "https://api.meti.millpont.com/sources?methodology=Agriculture" \
  -H "Authorization: Bearer <access_token>"
```

***

### 3. Token Expiration & Automated Workflows

Access tokens are valid for the duration specified in `expires_in`.\
For long-running or automated workflows, you should:

1. **Store the token and its expiry time**.
2. **Check validity before each request**.
3. **Request a new token automatically when needed**.

#### Steps for Token Renewal

1. **Track Token Expiration**
   * When you receive `expires_in`, compute an expiry timestamp, e.g.:

     ```python
     expiry_timestamp = time.time() + response_data["expires_in"]
     ```
2. **Check Before Each API Call**
   * If `time.time() >= expiry_timestamp`, request a new token from Auth0.
3. **Automate Token Requests**
   * Implement a helper that handles token fetching and refreshing, and reuse it across your application.

***

### 4. Minimal Python Example (Client Credentials + Auth Header)

Below is a simplified example inspired by your test script that:

* Requests an access token from Auth0.
* Automatically refreshes it when expired.
* Calls the METI API `/sources` endpoint.

```python
import time
import os
import requests

AUTH0_DOMAIN = os.getenv("AUTH0_DOMAIN")
AUTH0_AUDIENCE = os.getenv("AUTH0_AUDIENCE", "https://api.meti.millpont.com")
CLIENT_ID = os.getenv("ARVA_CLIENT_ID")       # or generic CLIENT_ID
CLIENT_SECRET = os.getenv("ARVA_CLIENT_SECRET")
BASE_URL = "https://api.meti.millpont.com"

token = None
token_expiry = 0  # epoch seconds

def get_access_token():
    global token, token_expiry

    token_url = f"https://{AUTH0_DOMAIN}/oauth/token"
    payload = {
        "client_id": CLIENT_ID,
        "client_secret": CLIENT_SECRET,
        "audience": AUTH0_AUDIENCE,
        "grant_type": "client_credentials",
        "scope": "read:sources write:sources delete:sources"
    }

    response = requests.post(token_url, json=payload, timeout=10)
    response.raise_for_status()
    data = response.json()

    token = data["access_token"]
    # Subtract a small buffer so we refresh slightly before actual expiration
    token_expiry = time.time() + data["expires_in"] - 30

def get_auth_headers():
    global token, token_expiry

    if token is None or time.time() >= token_expiry:
        get_access_token()

    return {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }

def get_sources():
    headers = get_auth_headers()
    response = requests.get(f"{BASE_URL}/sources", headers=headers, timeout=10)
    response.raise_for_status()
    return response.json()

if __name__ == "__main__":
    sources = get_sources()
    print(f"Found {len(sources)} sources")
```

**Benefits of this pattern**

* Prevents downtime due to expired tokens.
* Keeps API access continuous for long-running or large-scale jobs.
* Centralizes authentication logic in one place.

***

### 5. Scopes & Permissions

Certain endpoints require specific scopes. For example:

* `read:sources` – read access to sources
* `write:sources` – create/update sources
* `delete:sources` – delete sources

If your token does not include the necessary scopes, calls may fail with `403 Forbidden`.\
Your Auth0 administrator must grant the appropriate scopes to your application in the Auth0 dashboard.

***

### 6. Common Errors

* **401 Unauthorized**
  * Missing `Authorization` header.
  * Malformed token.
  * Expired token.
  * Using the wrong Auth0 domain or audience when requesting the token.
* **403 Forbidden**
  * Token is valid, but does not have the required scopes (permissions) for the endpoint.
  * Ask your Auth0 administrator to update the application’s API permissions (e.g., `read:sources`, `write:sources`, `delete:sources`).

***

By integrating Auth0 client-credentials authentication and automated token management as shown above, you can maintain secure, uninterrupted access to the METI API—even for complex, long-running workflows.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.millpont.com/meti-api-documents/authentication.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
