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

# Authentication

> API authentication with Clerk JWT tokens

The Soundcheck API uses Clerk JWT tokens for authentication.

## Overview

All API requests (except public endpoints) require a valid JWT token in the `Authorization` header:

```bash theme={null}
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Imluc18y...
```

***

## Token flow

```
┌─────────────────┐
│   User Login    │
│  (via Clerk)    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Clerk Session   │
│   Established   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Get JWT Token  │
│  via getToken() │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  API Request    │
│  with Bearer    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  API Verifies   │
│  Clerk Token    │
└─────────────────┘
```

***

## Getting a token

### Mobile (React Native)

```typescript theme={null}
import { useAuth } from '@clerk/clerk-expo';

function MyComponent() {
  const { getToken } = useAuth();

  const fetchData = async () => {
    const token = await getToken();

    const response = await fetch('https://api.soundchecklive.io/api/v1/users/me', {
      headers: {
        'Authorization': `Bearer ${token}`,
      },
    });
  };
}
```

### Web (Next.js)

```typescript theme={null}
import { useAuth } from '@clerk/nextjs';

function MyComponent() {
  const { getToken } = useAuth();

  const fetchData = async () => {
    const token = await getToken();

    const response = await fetch('https://api.soundchecklive.io/api/v1/users/me', {
      headers: {
        'Authorization': `Bearer ${token}`,
      },
    });
  };
}
```

***

## Token lifecycle

| Stage | Description |
| - | - |
| **Generation** | Created when user signs in |
| **Caching** | Stored in SecureStore (mobile) or cookies (web) |
| **Retrieval** | `getToken()` returns current valid token |
| **Refresh** | Clerk auto-refreshes expired tokens |
| **Expiration** | Tokens expire after 1 hour (configurable) |

***

## Backend verification

The API verifies tokens using Clerk's secret key:

```go theme={null}
// Go example
import "github.com/clerk/clerk-sdk-go"

func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        claims, err := clerk.VerifyToken(token)
        if err != nil {
            http.Error(w, "Unauthorized", 401)
            return
        }

        ctx := context.WithValue(r.Context(), "userID", claims.Subject)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}
```

***

## Error responses

### 401 Unauthorized

Missing or invalid token:

```json theme={null}
{
  "status": 401,
  "code": "UNAUTHORIZED",
  "message": "Authentication is required to access this resource",
  "details": ""
}
```

### 403 Forbidden

Valid token but insufficient permissions:

```json theme={null}
{
  "status": 403,
  "code": "FORBIDDEN",
  "message": "You do not have permission to access this resource",
  "details": ""
}
```

***

## Public endpoints

These endpoints don't require authentication:

| Endpoint | Description |
| - | - |
| `GET /health` | Health check |
| `POST /api/v1/lead-requests` | Public booking form submissions |
| `POST /webhooks/*` | Webhook endpoints (Clerk, Stripe, Cleo Pay) |

***

## Testing authentication

### Get a token

```bash theme={null}
# Use the Clerk dashboard to get a test token
# Or log in via the mobile/web app and extract from requests
```

### Test API request

```bash theme={null}
TOKEN="eyJhbGciOiJSUzI1NiIs..."

curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:4000/api/v1/users/me
```

***

## Security best practices

1. **Never expose tokens** - Don't log or store tokens insecurely
2. **Use HTTPS** - Always use HTTPS in production
3. **Short expiration** - Tokens expire after 1 hour
4. **Secure storage** - Use SecureStore (mobile) or HTTP-only cookies (web)
5. **Revocable sessions** - Sessions can be revoked from Clerk Dashboard

***

## Troubleshooting

> [accordion] Token not being sent
>
> Check that your HTTP client is including the Authorization header:
>
> ```typescript theme={null}
> const token = await getToken();
> console.log('Token:', token);
> ```

> [accordion] 401 on every request
>
> 1. Verify Clerk secret key matches between frontend and backend
> 2. Check token hasn't expired
> 3. Ensure user is signed in

> [accordion] Token refresh failing
>
> 1. Check network connectivity
> 2. Verify Clerk session is valid
> 3. Try signing out and back in


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.