# Authentication

The recommended mechanism is OAuth 2.0 with the `client_credentials` grant. You exchange a `client_id` + `client_secret` for a short-lived bearer token and send that token on every API call. HTTP Basic and API-key credentials are also offered for callers that cannot run a token exchange.

## OAuth 2.0 client-credentials flow

### 1. Request a token

Send a form-encoded `POST` to the token endpoint. The token endpoint is served under `/api/console/v1`; all other API calls use the `/api/v1` surface:

```bash
curl -s -X POST \
  https://ari.console.adrasis.com/api/console/v1/distribution/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'
```

Request fields (all form-encoded, `application/x-www-form-urlencoded`):

| Field           | Required | Notes                                                                                                          |
| --------------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `grant_type`    | yes      | Must be `client_credentials`. Any other value is rejected with `unsupported_grant_type`.                       |
| `client_id`     | yes      | Issued at credential creation.                                                                                 |
| `client_secret` | yes      | Issued at credential creation; shown once.                                                                     |
| `scope`         | no       | Space-delimited subset of your granted scopes to down-scope this token. Omit to receive your full granted set. |

### 2. Read the response

A successful exchange returns `200 OK`:

```json
{
  "access_token": "<ACCESS_TOKEN>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "distribution:read distribution:booking",
  "issued_at": 1748851200
}
```

| Field          | Meaning                                                    |
| -------------- | ---------------------------------------------------------- |
| `access_token` | The signed bearer token. Treat it as opaque.               |
| `token_type`   | Always `Bearer`.                                           |
| `expires_in`   | Token lifetime in seconds (3600 = one hour).               |
| `scope`        | The space-delimited scopes actually granted to this token. |
| `issued_at`    | Epoch seconds at which the token was issued.               |

A failed exchange returns a standard OAuth error envelope:

```json
{
  "error": "invalid_client",
  "error_description": "client authentication failed"
}
```

| `error`                  | HTTP | Cause                                                                                                                                                |
| ------------------------ | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unsupported_grant_type` | 400  | `grant_type` was not `client_credentials`.                                                                                                           |
| `invalid_scope`          | 400  | Requested `scope` is not a subset of your granted scopes.                                                                                            |
| `invalid_client`         | 401  | Unknown `client_id`, wrong secret, or a disabled/revoked credential. All three map to the same response so credential state cannot be fingerprinted. |

### 3. Send the bearer token

Attach the token to the `Authorization` header on every Distribution call:

```bash
curl -s \
  https://ari.console.adrasis.com/api/v1/properties \
  -H 'Authorization: Bearer <ACCESS_TOKEN>'
```

The token carries your tenant identity, connector context, and granted scopes. The service verifies it statelessly on every request — there is no session, and no tenant header to send.

### 4. Handle expiry

Tokens are valid for `expires_in` seconds (3600). **No refresh token is issued** for the client-credentials grant — when a token expires, request a new one from the token endpoint with the same `client_id` / `client_secret`.

A missing, malformed, expired, or signature-invalid token returns `401` with a `WWW-Authenticate: Bearer` challenge header and one of these codes:

```json
{ "code": "auth.invalid_bearer", "message": "Bearer token is missing, expired or invalid" }
```

| `code`                | Meaning                                                                     |
| --------------------- | --------------------------------------------------------------------------- |
| `auth.missing_bearer` | No `Authorization: Bearer` header was sent.                                 |
| `auth.invalid_bearer` | Token was present but malformed, expired, or failed signature verification. |

A practical strategy is to cache one token in memory and refresh it slightly before `expires_in` elapses (for example, at \~90% of the window) rather than requesting a token per call.

## Scopes

Two scopes gate the surface. A token must carry the scope an endpoint requires, or the request returns `403` with `code: auth.insufficient_scope`.

| Scope                  | Gates                                                                                                                                            |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `distribution:read`    | `GET /properties`, `GET /properties/{public_id}`, `POST /search`, `POST /availability/check`                                                     |
| `distribution:booking` | `POST /prebook`, `POST /book`, `GET /bookings/{public_id}`, `POST /bookings/{public_id}/cancellation-quote`, `POST /bookings/{public_id}/cancel` |

A read-only integration only needs `distribution:read`. A full booking integration needs both. Use the `scope` request parameter to down-scope a token when a worker only needs a subset (for example, mint a read-only token for a search service even though the credential is granted both).

## Alternative mechanisms

The Distribution API Credentials screen can also issue **HTTP Basic** and **API-key** credentials for callers that cannot run a token exchange. Unlike OAuth, these do not use the token endpoint — the credential material is sent directly on each call (Basic on the `Authorization: Basic` header; the API key on its dedicated header). They are gated by the same `distribution:read` and `distribution:booking` scopes. OAuth 2.0 is recommended for new integrations because tokens are short-lived and the long-term secret never travels on data requests.

## Next steps

* [Getting started](/developer/getting-started.md) — the end-to-end first call.
* [The booking flow](/developer/booking-flow.md) — the booking endpoints these scopes gate.
* [Errors](/developer/errors.md) — the full status-code reference.


---

# Agent Instructions: 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:

```
GET https://adrasis.gitbook.io/developer/authentication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
