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

# API Authentication: OAuth 2.0 Client Credentials

> Authenticate with Slate using the OAuth 2.0 client credentials grant. Exchange your client ID and secret for a Bearer token, then send it on every request.

Slate authenticates API requests using the OAuth 2.0 `client_credentials` grant. Exchange your client ID and secret at the token endpoint for a short-lived Bearer token, then include that token in the `Authorization` header on every API call. Tokens are scoped to an owner organization and carry permissions configured at client provisioning time.

## Token Endpoints

Use the endpoint that matches your target environment:

| Environment | Token URL                                        |
| ----------- | ------------------------------------------------ |
| UAT         | `https://api.uat.slate.inc/auth/v1/oauth2/token` |
| Production  | `https://api.slate.inc/auth/v1/oauth2/token`     |

## Requesting a Token

Send a `POST` request to the token endpoint with your client credentials and the `client_credentials` grant type. Credentials are passed via HTTP Basic auth (client ID as the username, client secret as the password).

```bash theme={null}
curl --request POST \
  --url "https://api.slate.inc/auth/v1/oauth2/token" \
  --user "<client-id>:<client-secret>" \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data "grant_type=client_credentials" \
  --data "scope=https://api.slate.inc/files/write https://api.slate.inc/accounts/write"
```

A successful response returns a JSON object containing the access token and its lifetime:

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

Cache the token for its `expires_in` window (minus a small buffer) and request a new one before it expires. Do not request a new token on every API call.

## Scopes

Request one or more of the following scopes in the `scope` parameter, separated by spaces:

| Scope                                    | Grants                        |
| ---------------------------------------- | ----------------------------- |
| `https://api.slate.inc/files/write`      | Read and write file resources |
| `https://api.slate.inc/signatures/write` | Read and write signatures     |
| `https://api.slate.inc/accounts/write`   | Read and write accounts       |
| `https://api.slate.inc/users/write`      | Read and write users          |
| `https://api.slate.inc/qc/write`         | Read and write QC records     |

<Note>
  Scopes are not yet enforced at the API layer, but they will be. Request only the scopes your integration needs today so it keeps working once enforcement is enabled. Actual access is currently governed by the permissions attached to your client at provisioning time.
</Note>

## Sending Your Token

Include the access token in the `Authorization` header with the `Bearer` scheme:

```bash theme={null}
Authorization: Bearer <access-token>
```

Here is a complete example using `curl`:

```bash theme={null}
curl --request GET \
  --url "https://api.slate.inc/accounts/v1/accounts" \
  --header "Authorization: Bearer <access-token>"
```

Each API is served under its own service prefix on `api.slate.inc` — for example `/accounts`, `/files`, `/qc`, `/signatures`, and `/users`. The same Bearer token works across every service. All requests, including `GET`, `POST`, `PATCH`, and `DELETE`, require this header.

<Note>
  Treat your client secret and access tokens like passwords. Never commit them to source control, log them, or expose them in client-side code. Store them in a secrets manager or environment variable.
</Note>

## Owner Scoping

Your client is bound to a single **owner**, the organization Slate uses to isolate your data. Slate delivers your `owner` identifier alongside your client credentials. Every account, file, signature, and QC record you create or retrieve is scoped to that owner automatically, and you do not need to pass the owner identifier on standard requests.

### List endpoints

List endpoints require you to specify the owner explicitly to keep them context-free. Pass your `owner` identifier as a query parameter, and it must be an owner your token is authorized to read:

```bash theme={null}
curl --request GET \
  --url "https://api.slate.inc/accounts/v1/accounts?owner=<owner-id>" \
  --header "Authorization: Bearer <access-token>"
```

Requests to list endpoints without an `owner` parameter will be rejected.

## Getting Client Credentials

Slate does not provide self-serve client registration. To obtain a client ID and secret:

1. Contact your **Slate account manager** and provide the name or identifier of the service you are building, along with the scopes and permissions it needs.
2. Slate provisions a client scoped to your owner with the requested permissions and delivers the client ID, client secret, and `owner` identifier through a secure channel.
3. Store the client ID and secret in your secrets manager and reference them at runtime via environment variables (for example, `SLATE_CLIENT_ID` and `SLATE_CLIENT_SECRET`).

If your client secret is compromised or needs to be rotated, contact your account manager immediately.

## Error Responses

### Token endpoint errors

The token endpoint returns standard OAuth 2.0 error responses when a token request fails:

```json theme={null}
{
  "error": "invalid_client"
}
```

Common causes:

| Error            | What to do                                                                            |
| ---------------- | ------------------------------------------------------------------------------------- |
| `invalid_client` | Verify your client ID and secret are correct and sent via HTTP Basic auth             |
| `invalid_grant`  | Confirm `grant_type=client_credentials` is set                                        |
| `invalid_scope`  | Check that every requested scope is spelled correctly and provisioned for your client |

### API errors

When authentication fails on an API request, Slate returns `401 Unauthorized` with a JSON body:

```json theme={null}
{
  "message": "Invalid or missing authorization credentials."
}
```

Common causes:

| Cause                                    | What to do                                                 |
| ---------------------------------------- | ---------------------------------------------------------- |
| Token is missing from the request        | Add the `Authorization: Bearer <access-token>` header      |
| Token is expired                         | Request a new token from the OAuth endpoint                |
| Token is malformed or truncated          | Verify the full token value was copied correctly           |
| Token has been revoked                   | Contact your Slate account manager for a replacement       |
| Client lacks permission for the endpoint | Confirm the correct client is being used for this resource |

## Account identifier (crid) uniqueness

When you upsert accounts, you supply a `crid` (your identifier for the account in your own system), and that value must be **unique within your owner**. Two different owners may use the same `crid` without conflict, but within a single owner, each `crid` maps to exactly one Slate account. Because `crid` is the natural key the upsert matches on, re-sending the same `crid` updates the existing record rather than creating a new one.
