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

# Authenticating API requests with Bearer tokens

> Learn how to obtain a Bearer token via POST /api/auth/login, pass it in the Authorization header, and refresh it when it expires.

All protected endpoints in the Shipfastai API require a Bearer token passed in the `Authorization` request header. You obtain a token by logging in, and you keep access alive by refreshing it before it expires. This page walks you through the full token lifecycle.

## Obtaining a token

Call `POST /api/auth/login` with your email and password. On success, the response includes an `access_token` and a `refresh_token`.

<CodeGroup>
  ```bash curl theme={null}
  curl --request POST \
    --url http://localhost:8000/api/auth/login \
    --header "Content-Type: application/json" \
    --data '{
      "email": "user@example.com",
      "password": "your-password"
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "http://localhost:8000/api/auth/login",
      json={
          "email": "user@example.com",
          "password": "your-password",
      },
  )
  data = response.json()
  access_token = data["access_token"]
  refresh_token = data["refresh_token"]
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "user": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "email": "user@example.com",
    "full_name": "Jane Smith",
    "is_verified": true,
    "subscription_status": "active",
    "subscription_tier": "pro",
    "created_at": "2024-01-15T10:30:00Z"
  }
}
```

## Passing the token

Include the access token in the `Authorization` header of every request to a protected endpoint:

```http theme={null}
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

<CodeGroup>
  ```bash curl theme={null}
  curl --request GET \
    --url http://localhost:8000/api/auth/me \
    --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  ```

  ```python Python theme={null}
  import requests

  headers = {"Authorization": f"Bearer {access_token}"}

  response = requests.get(
      "http://localhost:8000/api/auth/me",
      headers=headers,
  )
  print(response.json())
  ```
</CodeGroup>

## Token expiry and refresh

Access tokens expire after **30 minutes**. Before making a request after expiry, call `POST /api/auth/refresh` with your refresh token to receive a new token pair.

```bash theme={null}
curl --request POST \
  --url http://localhost:8000/api/auth/refresh \
  --header "Content-Type: application/json" \
  --data '{"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'
```

The response has the same shape as the login response: a new `access_token` and a new `refresh_token`. Replace both stored tokens with the new values.

<Tip>
  Store your refresh token in a secure, persistent location such as an `httpOnly` cookie or an encrypted local store. Never expose it in JavaScript accessible to the page or in localStorage without additional protections, as a stolen refresh token grants long-lived access.
</Tip>

## Error responses

### 401 Unauthorized

Returned when the token is missing, malformed, or expired.

```json theme={null}
{
  "detail": "Could not validate credentials"
}
```

### 403 Forbidden

Returned when the token is valid but the account is inactive.

```json theme={null}
{
  "detail": "Account is inactive"
}
```

When you receive a `401`, attempt to refresh the access token. If the refresh also fails with a `401`, the user's session has expired and they must log in again.
