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

# Authentication

> Learn how to authenticate with the Smartbills API using JWT Bearer tokens and OAuth2 flows

## Overview

The Smartbills API uses JWT Bearer token authentication. Every API request must include a valid token in the `Authorization` header. Tokens can be obtained through API keys or OAuth2 flows.

<Note>
  **Before you begin**: You need a Smartbills account and API keys to authenticate. Visit [developers.smartbills.io](https://developers.smartbills.io) to get started.
</Note>

## Authentication Header

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

```http theme={null}
GET /v1/expenses HTTP/1.1
Host: api.smartbills.io
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
x-tenant-id: 123
```

The header format is:

```
Authorization: Bearer {token}
```

Where `{token}` is either:

* An API key (e.g., `sk_live_1234567890abcdef`)
* An OAuth2 access token obtained through the token endpoint

## Multi-Tenant Header

Smartbills is a multi-tenant platform. You must include the `x-tenant-id` header to specify the business context:

```http theme={null}
x-tenant-id: 123
```

This header tells the API which business's data you want to access. Your token must have permission to access the specified business.

## Locale Header

Use the `Accept-Language` header to receive localized responses:

```http theme={null}
Accept-Language: en-CA
```

Supported locales: `en-CA`, `fr-CA`, `en-US`. See [Localizations](/api-reference/localizations) for details.

## Complete Request Headers

A complete authenticated request includes these headers:

```http theme={null}
GET /v1/expenses HTTP/1.1
Host: api.smartbills.io
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Accept: application/json
x-tenant-id: 123
Accept-Language: en-CA
```

## OAuth2 Authentication

### Client Credentials Flow

Use this flow for server-to-server communication where no user interaction is required.

**Endpoint:** `POST https://api.smartbills.io/auth/connect/token`

**Request:**

```http theme={null}
POST /auth/connect/token HTTP/1.1
Host: api.smartbills.io
Content-Type: application/x-www-form-urlencoded

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

**Response:**

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

#### Code Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.smartbills.io/auth/connect/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'
  ```

  ```javascript JavaScript theme={null}
  import { SmartbillsClient } from '@smartbills/sdk';

  const client = new SmartbillsClient({
    accessToken: 'YOUR_API_KEY',
    businessId: 123
  });

  // The SDK handles token management automatically.
  // For manual OAuth2 flows:
  const tokenResponse = await fetch('https://api.smartbills.io/auth/connect/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: 'YOUR_CLIENT_ID',
      client_secret: 'YOUR_CLIENT_SECRET'
    })
  });

  const { access_token, expires_in } = await tokenResponse.json();
  ```

  ```python Python theme={null}
  from smartbills import SmartbillsClient
  import requests

  client = SmartbillsClient(access_token="YOUR_API_KEY", business_id=123)

  # The SDK handles token management automatically.
  # For manual OAuth2 flows:
  token_response = requests.post(
      'https://api.smartbills.io/auth/connect/token',
      data={
          'grant_type': 'client_credentials',
          'client_id': 'YOUR_CLIENT_ID',
          'client_secret': 'YOUR_CLIENT_SECRET'
      }
  )

  token_data = token_response.json()
  access_token = token_data['access_token']
  expires_in = token_data['expires_in']
  ```
</CodeGroup>

### Authorization Code Flow

Use this flow for user-facing applications where you need to act on behalf of a user.

**Step 1: Redirect to Authorization Endpoint**

```http theme={null}
GET /auth/connect/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=expenses.read expenses.write
Host: api.smartbills.io
```

**Step 2: Exchange Authorization Code for Access Token**

```http theme={null}
POST /auth/connect/token HTTP/1.1
Host: api.smartbills.io
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=YOUR_REDIRECT_URI&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET
```

**Response:**

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

#### Code Examples

<CodeGroup>
  ```bash cURL theme={null}
  # Step 2: Exchange authorization code for token
  curl --request POST \
    --url https://api.smartbills.io/auth/connect/token \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data 'grant_type=authorization_code' \
    --data 'code=AUTHORIZATION_CODE' \
    --data 'redirect_uri=YOUR_REDIRECT_URI' \
    --data 'client_id=YOUR_CLIENT_ID' \
    --data 'client_secret=YOUR_CLIENT_SECRET'
  ```

  ```javascript JavaScript theme={null}
  // Step 1: Redirect user to authorization page
  const authUrl = new URL('https://api.smartbills.io/auth/connect/authorize');
  authUrl.searchParams.set('response_type', 'code');
  authUrl.searchParams.set('client_id', 'YOUR_CLIENT_ID');
  authUrl.searchParams.set('redirect_uri', 'YOUR_REDIRECT_URI');
  authUrl.searchParams.set('scope', 'expenses.read expenses.write');
  // Redirect: window.location.href = authUrl.toString();

  // Step 2: Exchange code for token (in your callback handler)
  const tokenResponse = await fetch('https://api.smartbills.io/auth/connect/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code: 'AUTHORIZATION_CODE',
      redirect_uri: 'YOUR_REDIRECT_URI',
      client_id: 'YOUR_CLIENT_ID',
      client_secret: 'YOUR_CLIENT_SECRET'
    })
  });

  const { access_token, refresh_token, expires_in } = await tokenResponse.json();
  ```

  ```python Python theme={null}
  import requests
  from urllib.parse import urlencode

  # Step 1: Build authorization URL
  auth_params = {
      'response_type': 'code',
      'client_id': 'YOUR_CLIENT_ID',
      'redirect_uri': 'YOUR_REDIRECT_URI',
      'scope': 'expenses.read expenses.write'
  }
  auth_url = f"https://api.smartbills.io/auth/connect/authorize?{urlencode(auth_params)}"
  # Redirect user to auth_url

  # Step 2: Exchange code for token (in your callback handler)
  token_response = requests.post(
      'https://api.smartbills.io/auth/connect/token',
      data={
          'grant_type': 'authorization_code',
          'code': 'AUTHORIZATION_CODE',
          'redirect_uri': 'YOUR_REDIRECT_URI',
          'client_id': 'YOUR_CLIENT_ID',
          'client_secret': 'YOUR_CLIENT_SECRET'
      }
  )

  token_data = token_response.json()
  access_token = token_data['access_token']
  refresh_token = token_data['refresh_token']
  ```
</CodeGroup>

## Token Expiration and Refresh

Access tokens expire after a set period (typically 3600 seconds / 1 hour). Use the `expires_in` field to know when a token will expire.

### Refreshing Tokens

When your access token expires, use the refresh token to obtain a new one without requiring user interaction:

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.smartbills.io/auth/connect/token \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data 'grant_type=refresh_token' \
    --data 'refresh_token=YOUR_REFRESH_TOKEN' \
    --data 'client_id=YOUR_CLIENT_ID' \
    --data 'client_secret=YOUR_CLIENT_SECRET'
  ```

  ```javascript JavaScript theme={null}
  async function refreshAccessToken(refreshToken) {
    const response = await fetch('https://api.smartbills.io/auth/connect/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'refresh_token',
        refresh_token: refreshToken,
        client_id: 'YOUR_CLIENT_ID',
        client_secret: 'YOUR_CLIENT_SECRET'
      })
    });

    const { access_token, refresh_token, expires_in } = await response.json();

    // Store the new tokens securely
    return { access_token, refresh_token, expires_in };
  }
  ```

  ```python Python theme={null}
  def refresh_access_token(refresh_token):
      response = requests.post(
          'https://api.smartbills.io/auth/connect/token',
          data={
              'grant_type': 'refresh_token',
              'refresh_token': refresh_token,
              'client_id': 'YOUR_CLIENT_ID',
              'client_secret': 'YOUR_CLIENT_SECRET'
          }
      )

      token_data = response.json()
      # Store the new tokens securely
      return token_data
  ```
</CodeGroup>

**Refresh Token Response:**

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

<Warning>
  **Refresh token rotation**: Each time you use a refresh token, a new refresh token is returned. The old refresh token is invalidated. Always store the latest refresh token.
</Warning>

## Using the SDKs

The official SDKs handle authentication, token refresh, and header management automatically:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { SmartbillsClient } from '@smartbills/sdk';

  const client = new SmartbillsClient({
    accessToken: 'YOUR_API_KEY',
    businessId: 123
  });

  // The SDK automatically includes Authorization and x-tenant-id headers
  const expenses = await client.expenses.list();
  ```

  ```python Python theme={null}
  from smartbills import SmartbillsClient

  client = SmartbillsClient(access_token="YOUR_API_KEY", business_id=123)

  # The SDK automatically includes Authorization and x-tenant-id headers
  expenses = client.expenses.list()
  ```
</CodeGroup>

## Authentication Errors

| HTTP Status | Error Code               | Description                              |
| ----------- | ------------------------ | ---------------------------------------- |
| 401         | `UNAUTHORIZED`           | Missing or invalid API key               |
| 401         | `API_KEY_EXPIRED`        | API key has expired                      |
| 401         | `API_KEY_REVOKED`        | API key has been revoked                 |
| 401         | `INVALID_TOKEN`          | Invalid or malformed JWT token           |
| 403         | `FORBIDDEN`              | Token does not have required permissions |
| 403         | `BUSINESS_ACCESS_DENIED` | No access to the specified business      |

### Example Error Response

```json theme={null}
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key",
    "requestId": "req_1234567890abcdef"
  }
}
```

## Security Best Practices

<AccordionGroup>
  <Accordion title="Protect Your Credentials" icon="shield">
    * Store API keys and secrets in environment variables
    * Never commit credentials to version control
    * Never expose tokens in client-side code
    * Use HTTPS for all API requests
  </Accordion>

  <Accordion title="Use Appropriate Scopes" icon="lock">
    * Request only the scopes your application needs
    * Use read-only scopes when you do not need write access
    * Review and audit scopes regularly
  </Accordion>

  <Accordion title="Handle Token Expiration" icon="clock">
    * Check the `expires_in` value after obtaining tokens
    * Implement automatic token refresh before expiration
    * Handle 401 errors by refreshing and retrying
  </Accordion>

  <Accordion title="Rotate Keys Regularly" icon="rotate">
    * Rotate API keys every 90 days
    * Revoke compromised keys immediately
    * Use separate keys for each environment
  </Accordion>
</AccordionGroup>

## Related Resources

<CardGroup cols={2}>
  <Card title="API Keys" icon="key" href="/api-reference/api-keys">
    Create and manage API keys
  </Card>

  <Card title="Environments" icon="server" href="/api-reference/environments">
    Sandbox and production environments
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/api-reference/errors">
    Handle authentication errors
  </Card>

  <Card title="Rate Limits" icon="gauge" href="/api-reference/rate-limits">
    Understand rate limiting
  </Card>
</CardGroup>
