---
title: "Authentication"
description: "All calls to AgentSync APIs require a valid access token obtained through the OAuth 2.0 Client Credentials flow."
url: "https://developer.agentsync.io/api-authentication"
image: "https://developer.agentsync.io/_og/d/c_Ocean.takumi,title_Authentication,description_All+calls+to+AgentSync+APIs+require+a+valid+access+token+obtained+through+the+OAuth+2.0+Client+Credentials+flow.,props_eyJ0aGVtZSI6eyJtb2RlIjoibGlnaHQiLCJjb2xvcnMiOnsicHJpbWFyeSI6IiMxODdFRkYifX19,p_Ii9hcGktYXV0aGVudGljYXRpb24i,s_hDby7eMp2d8mv5MN.png"
---

# Authentication

All calls to AgentSync APIs require a valid access token obtained through the OAuth 2.0 Client Credentials flow.

## [Overview](#overview)

As part of the Client Credentials flow:

1.  Your client authenticates with AgentSync's authorization server using securely provided credentials
2.  If valid, the server issues an access token
3.  This token is then used to authenticate all subsequent API requests
4.  The issued token will remain valid for a limited time and should be reused until expiration

> **Security Notice**: We hold the highest standards for data privacy and security. We expect that developers using our APIs follow secure practices when integrating with us including, but not limited to, (i) accessing APIs through secure communication channels and (ii) following security best practices when storing, sharing and accessing API credentials and access tokens. You must promptly notify us about any known or suspected security incidents. We reserve the right to suspend or deactivate clients whose credentials we suspect have been breached.

## [Requesting API Access](#requesting-api-access)

API access is not self-service at this time. Please contact [support@agentsync.io](mailto:support@agentsync.io) to:

-   Request new API keys
-   Rotate or revoke existing credentials

Upon approval, you will securely receive your:

-   `client_id`
-   `client_secret`
-   `scope` (if applicable)

These credentials are used to retrieve an access token.

## [Scopes](#scopes)

Some AgentSync APIs require explicit OAuth 2.0 scopes. If your request fails with `"access_denied"` and `"Policy evaluation failed"`, you need to include the required scope in your token request.

Scopes are granted per-API when your credentials are provisioned; contact [support@agentsync.io](mailto:support@agentsync.io) to add one. Pass required scopes in the `scope` parameter of your token request, space-separated — see [Token Retrieval](#token-retrieval) below for the full request format.

| Scope                                | API / Surface                  | Operations Permitted                                                             |
| :----------------------------------- | :----------------------------- | :------------------------------------------------------------------------------- |
| contracting.producers.read           | Contracting & Hierarchies (v2) | Read producers (GET /v2/producers...)                                            |
| contracting.contracts.read           | Contracting & Hierarchies (v2) | Read contracts (GET /v2/contracts...)                                            |
| contracting.contractassignments.read | Contracting & Hierarchies (v2) | Read contract assignments, assignment changes, assigned carriers/products/commission levels, available uplines, and hierarchies |
| identity.profiles.read               | Identity (v2)                  | Read persons, firms, addresses, phones, bank accounts, and E&O policies          |
| identity.profiles.write              | Identity (v2)                  | Create, update, and delete those resources                                       |

## [Token Retrieval](#token-retrieval)

Send a POST request to the token endpoint for your environment:

| Environment | Token URL                                      |
| :---------- | :--------------------------------------------- |
| Sandbox     | https://auth.sandbox.agentsync.io/oauth2/token |
| Production  | https://auth.agentsync.io/oauth2/token         |

After obtaining your access token, all subsequent API requests must use the appropriate API base URLs for your environment — do not send API calls to the `auth.*` domain. See [API Base URLs](https://developer.agentsync.io/api-base-urls).

### [Required Parameters](#required-parameters)

| Parameter     | Description                                                       |
| :------------ | :---------------------------------------------------------------- |
| Content-Type  | Must be application/x-www-form-urlencoded                         |
| grant_type    | Must be client_credentials                                        |
| client_id     | Provided by AgentSync Support                                     |
| client_secret | Provided by AgentSync Support                                     |
| scope         | Required for some APIs (e.g., Contracting API). See Scopes above. |

### [Sample Token Request (cURL)](#sample-token-request-curl)

```bash
curl --request POST \
  --url https://auth.sandbox.agentsync.io/oauth2/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data grant_type=client_credentials \
  --data client_id=YOUR_CLIENT_ID \
  --data client_secret=YOUR_CLIENT_SECRET \
  --data 'scope=contracting.producers.read contracting.contracts.read'
```

Omit the `scope` parameter if your API does not require it.

### [Sample Token Responses](#sample-token-responses)

**200 Success**

```json
{
  "token_type": "Bearer",
  "expires_in": 3600,
  "access_token": "xyz123",
  "scope": "a_valid_scope"
}
```

| Field        | Description                                           |
| :----------- | :---------------------------------------------------- |
| token_type   | Always Bearer                                         |
| expires_in   | Token lifespan in seconds (3600 seconds = 60 minutes) |
| access_token | The token used to authenticate API calls              |
| scope        | The level of access granted                           |

**401 Unauthorized** - invalid (or no) credentials provided

```json
{
    "errorCode": "invalid_client",
    "errorSummary": "Invalid value for 'client_id' parameter.",
    "errorLink": "invalid_client",
    "errorId": "oaeKUn1hFVRReCw-7SeCP3j7g",
    "errorCauses": []
}
```

See [API Status Codes](https://developer.agentsync.io/api-status-codes) for a comprehensive list of expected errors.

## [Authenticating API Requests](#authenticating-api-requests)

Use the retrieved token to call AgentSync APIs. **Most APIs sit behind a path prefix** — omitting it is the most common cause of an unexpected `404` immediately after a successful token request:

```bash
# Contracting & Hierarchies — note the /contracting prefix
curl --header "Authorization: Bearer $ACCESS_TOKEN" \
  "https://api.sandbox.agentsync.io/contracting/v2/producers"

# Identity — note the /identity prefix
curl --header "Authorization: Bearer $ACCESS_TOKEN" \
  "https://api.sandbox.agentsync.io/identity/v2/persons"

# ProducerSync — no prefix
curl --header "Authorization: Bearer $ACCESS_TOKEN" \
  "https://api.sandbox.agentsync.io/v2/licenses"
```

See [API Base URLs](https://developer.agentsync.io/api-base-urls) for every host and prefix.

## [Rate Limiting](#rate-limiting)

The token endpoint is rate-limited to **200 requests per minute**.

See [Rate Limits](https://developer.agentsync.io/api-rate-limits) to learn more.

## [Token Expiration & Reuse](#token-expiration-reuse)

Tokens are valid for **60 minutes**. You should reuse the token during this window.

### [Recommended Strategies](#recommended-strategies)

-   **Reuse tokens** — don't request a new token per API call
-   **Preemptive refresh** — refresh 5 minutes before expiry to avoid mid-request failures
-   **Handle 401s gracefully** — catch `401 Unauthorized` responses, re-authenticate, and retry once

### [Token Reuse Example (Python)](#token-reuse-example-python)

```python
import threading
import time
import requests

class TokenClient:
    """Caches a client-credentials token and refreshes it just before expiry.
    Safe to share across threads."""

    TOKEN_BUFFER_SECONDS = 300  # refresh 5 minutes before expiry

    def __init__(self, client_id, client_secret, token_url, scope=None):
        self.client_id = client_id
        self.client_secret = client_secret
        self.token_url = token_url
        self.scope = scope
        self._token = None
        self._token_expiry = 0.0
        self._lock = threading.Lock()
        self._session = requests.Session()  # reuse the connection across refreshes

    def get_token(self):
        """Return a valid token, refreshing if expired or not yet set."""
        # Fast path: no lock needed while the cached token is still valid.
        if self._token and time.time() < self._token_expiry:
            return self._token
        with self._lock:
            # Re-check inside the lock so concurrent callers refresh only once.
            if not self._token or time.time() >= self._token_expiry:
                self._refresh()
            return self._token

    def _refresh(self):
        data = {
            "grant_type": "client_credentials",
            "client_id": self.client_id,
            "client_secret": self.client_secret,
        }
        if self.scope:
            data["scope"] = self.scope
        response = self._session.post(self.token_url, data=data)
        response.raise_for_status()
        token_data = response.json()
        self._token = token_data["access_token"]
        self._token_expiry = time.time() + token_data["expires_in"] - self.TOKEN_BUFFER_SECONDS


# Usage — scope is optional, include only if required by the API
client = TokenClient(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    token_url="https://auth.sandbox.agentsync.io/oauth2/token",
    scope="contracting.producers.read contracting.contracts.read",
)

headers = {"Authorization": f"Bearer {client.get_token()}"}
```