# Issuing Session Tokens
URL: https://www.quiltt.dev/authentication/issuing-session-tokens
Description: Learn how to authenticate Profiles for Connector and GraphQL using Session Tokens.
Navigation: authentication → issuing-session-tokens
Tags: Authentication
Content Length: 8k characters

REST API Note:

Session tokens authenticate Profile-specific requests to the Connector and GraphQL API. Issue them server-side, use them client-side.

API Endpoint - POST https://auth.quiltt.io/v1/users/sessions:

**Required headers:**

```yaml
Authorization: Bearer <API_SECRET_KEY>
Content-Type: application/json
```

Destructive:
**Rate Limits: 10/hour, 20/day per Profile**

Each issuance creates a NEW token counting toward limits. To avoid 429 errors:

1. Cache tokens client-side
2. Check expiration before issuing
3. Revoke on logout (freed tokens don't count)

Server-to-server? Use [Basic Auth](/authentication#basic-auth) instead—no rate limits.

## Existing Profile

Provide the Profile ID in the request body:

Code Examples:

  ```sh
    curl --request POST \
      --url 'https://auth.quiltt.io/v1/users/sessions' \
      --header 'Authorization: Bearer <API_SECRET_KEY>' \
      --header 'Content-Type: application/json' \
      --data-raw '["userId": "p_11ewrVkEnd7LIvSVAmt8XL5"]'
    ```
  ```json
    ["token": "eyJhbGciOiJIUzUxMiJ9.eyJuYmYiOjE2NzE4MjI5MTIsImlhdCI6MTY3MTgyMjkxMiwianRpIjoiNDU1MWNhNDktYzAwMi00ZDliLTkyZWMtNDY1MDE4ZTI4ZmRjIiwiaXNzIjoiYXV0aC5xdWlsdHQuaW8iLCJhdWQiOiJhcGkucXVpbHR0LmlvIiwiZXhwIjoxNjcxOTA5MzEyLCJ2ZXIiOjIsImRpZCI6ImFwaV8xN05PRXdWR2N2eU9xcGxuUWREMjdnWSIsInVpZCI6InBfMTFld3JWa0VuZDdMSXZTVkFtdDhYTDUifQ.5tYTjr_k0GKG6LsaAEt3V0RAiJe9UU59USUAASJTXf5e1923njb4UqYUozAVm34fARXT-SRvlE1-_J4wdiVNwg",
      "userId": "p_11ewrVkEnd7LIvSVAmt8XL5",
      "environmentId": "env_1Bx4omNvZgKao2OqGshImD",
      "expiration": 1718944186,
      "expiresAt": "2024-06-21T04:29:46Z"]
    ```

## New Profile

Create a Profile and issue a token in one request. Optionally provide Profile attributes or your own UUID:

Code Examples:

  ```sh
    curl --request POST \
      --url 'https://auth.quiltt.io/v1/users/sessions' \
      --header 'Authorization: Bearer <API_SECRET_KEY>' \
      --header 'Content-Type: application/json' \
      --data-raw '{
        "metadata": ["firebaseId": "Xk3D12aB4zO7QW5z8s9Y"]
      }'
    ```
  ```json
    ["token": "eyJhbGciOiJIUzUxMiJ9.eyJuYmYiOjE2NzE4MjI5MTIsImlhdCI6MTY3MTgyMjkxMiwianRpIjoiNDU1MWNhNDktYzAwMi00ZDliLTkyZWMtNDY1MDE4ZTI4ZmRjIiwiaXNzIjoiYXV0aC5xdWlsdHQuaW8iLCJhdWQiOiJhcGkucXVpbHR0LmlvIiwiZXhwIjoxNjcxOTA5MzEyLCJ2ZXIiOjIsImRpZCI6ImFwaV8xN05PRXdWR2N2eU9xcGxuUWREMjdnWSIsInVpZCI6InBfMTFld3JWa0VuZDdMSXZTVkFtdDhYTDUifQ.5tYTjr_k0GKG6LsaAEt3V0RAiJe9UU59USUAASJTXf5e1923njb4UqYUozAVm34fARXT-SRvlE1-_J4wdiVNwg",
      "userId": "p_11ewrVkEnd7LIvSVAmt8XL5",
      "environmentId": "env_1Bx4omNvZgKao2OqGshImD",
      "expiration": 1718944186,
      "expiresAt": "2024-06-21T04:29:46Z"]
    ```

---

Info - Save the `userId`:
Store the returned `userId` to re-authenticate this Profile later (unless providing your own UUIDs).

## Best Practices

### Cache Tokens

Session tokens last 24 hours. Cache client-side to avoid rate limits:

Code Examples:

  ```typescript
    function useQuilttSessionToken(profileId: string) {
      const [token, setToken] = useState<string | null>(null)
      const [loading, setLoading] = useState(true)

      useEffect(() => {
        async function loadSession() {
          // Check cache first
          const cached = localStorage.getItem(`quiltt_session_${profileId}`)

          if (cached) {
            const [token, expiresAt] = JSON.parse(cached)

            if (new Date(expiresAt) > new Date()) [setToken(token)
              setLoading(false)
              return]
          }

          // Fetch new token
          const response = await fetch('/api/quiltt-session', {
            method: 'POST',
            headers: ['Content-Type': 'application/json'],
            body: JSON.stringify([userId: profileId])
          })

          const session = await response.json()

          localStorage.setItem(`quiltt_session_${profileId}`, JSON.stringify([token: session.token,
            expiresAt: session.expiresAt]))

          setToken(session.token)
          setLoading(false)
        }

        loadSession()
      }, [profileId])

      return [token, loading]
    }
    ```
  ```typescript
    async function getOrCreateSession(profileId: string): Promise<string> {
      // Check for cached token
      const cached = localStorage.getItem(`quiltt_session_${profileId}`)

      if (cached) {
        const [token, expiresAt] = JSON.parse(cached)

        // Verify token hasn't expired
        if (new Date(expiresAt) > new Date()) [return token]
      }

      // Issue new token if no valid cached token
      const response = await fetch('https://auth.quiltt.io/v1/users/sessions', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${API_SECRET_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify([userId: profileId])
      })

      const session = await response.json()

      // Cache the token
      localStorage.setItem(`quiltt_session_${profileId}`, JSON.stringify([token: session.token,
        expiresAt: session.expiresAt]))

      return session.token
    }
    ```

### Revoke on Logout

Revoke tokens at logout to free rate limit quota:

Code Examples:

  ```tsx
    import {useCallback} from 'react'
    import {useQuilttSession} from '@quiltt/react'

    function LogoutButton() {
      const {revokeSession} = useQuilttSession()

      const handleLogout = useCallback(async () => [// Revokes token on server and clears from local storage
        await revokeSession()

        // Redirect to login
        window.location.href = '/login'], [revokeSession])

      return [Log Out]
    }
    ```
  ```tsx
    import {useCallback} from 'react'
    import {useQuilttSession} from '@quiltt/react-native'

    function LogoutButton() {
      const {revokeSession} = useQuilttSession()

      const handleLogout = useCallback(async () => [// Revokes token on server and clears from storage
        await revokeSession()

        // Navigate to login
        navigation.navigate('Login')], [revokeSession])

      return [Button]
    }
    ```
  ```typescript
    import {AuthAPI} from '@quiltt/core'

    async function logout(token: string, profileId: string) {
      const auth = new AuthAPI()

      // Revoke the token
      await auth.revoke(token)

      // Clear from cache
      localStorage.removeItem(`quiltt_session_${profileId}`)
    }
    ```

## Advanced

### Update Profile Attributes

Provide optional attributes when issuing tokens for existing or new Profiles:

Code Examples:

  ```sh
    curl --request POST \
      --url 'https://auth.quiltt.io/v1/users/sessions' \
      --header 'Authorization: Bearer <API_SECRET_KEY>' \
      --header 'Content-Type: application/json' \
      --data-raw '{
        "email": "quiltty@quiltt.io",
        "metadata": ["favoriteColor": "Purple"]
      }'
    ```
  ```json
    ["token": "eyJhbGciOiJIUzUxMiJ9.eyJuYmYiOjE2NzE4MjI5MTIsImlhdCI6MTY3MTgyMjkxMiwianRpIjoiNDU1MWNhNDktYzAwMi00ZDliLTkyZWMtNDY1MDE4ZTI4ZmRjIiwiaXNzIjoiYXV0aC5xdWlsdHQuaW8iLCJhdWQiOiJhcGkucXVpbHR0LmlvIiwiZXhwIjoxNjcxOTA5MzEyLCJ2ZXIiOjIsImRpZCI6ImFwaV8xN05PRXdWR2N2eU9xcGxuUWREMjdnWSIsInVpZCI6InBfMTFld3JWa0VuZDdMSXZTVkFtdDhYTDUifQ.5tYTjr_k0GKG6LsaAEt3V0RAiJe9UU59USUAASJTXf5e1923njb4UqYUozAVm34fARXT-SRvlE1-_J4wdiVNwg",
      "userId": "p_11ewrVkEnd7LIvSVAmt8XL5",
      "environmentId": "env_1Bx4omNvZgKao2OqGshImD",
      "expiration": 1718944186,
      "expiresAt": "2024-06-21T04:29:46Z"]
    ```

The supplied `email` and `metadata` will be persisted to the Profile for future use.

See the [API reference](/api-reference/rest) for all supported attributes.

### Custom Profile IDs

Provide your own UUID for new Profiles to maintain referential integrity with external systems:

Code Examples:

  ```sh
    curl --request POST \
      --url 'https://auth.quiltt.io/v1/users/sessions' \
      --header 'Authorization: Bearer <API_SECRET_KEY>' \
      --header 'Content-Type: application/json' \
      --data-raw '["uuid": "885d9f06-7e1a-49f2-bc94-6b9e6a2c1c96"]'
    ```
  ```json
    ["token": "eyJhbGciOiJIUzUxMiJ9.eyJuYmYiOjE2NzE4MjI5MTIsImlhdCI6MTY3MTgyMjkxMiwianRpIjoiNDU1MWNhNDktYzAwMi00ZDliLTkyZWMtNDY1MDE4ZTI4ZmRjIiwiaXNzIjoiYXV0aC5xdWlsdHQuaW8iLCJhdWQiOiJhcGkucXVpbHR0LmlvIiwiZXhwIjoxNjcxOTA5MzEyLCJ2ZXIiOjIsImRpZCI6ImFwaV8xN05PRXdWR2N2eU9xcGxuUWREMjdnWSIsInVpZCI6InBfMTFld3JWa0VuZDdMSXZTVkFtdDhYTDUifQ.5tYTjr_k0GKG6LsaAEt3V0RAiJe9UU59USUAASJTXf5e1923njb4UqYUozAVm34fARXT-SRvlE1-_J4wdiVNwg",
      "userId": "p_11ewrVkEnd7LIvSVAmt8XL5",
      "environmentId": "env_1Bx4omNvZgKao2OqGshImD",
      "expiration": 1718944186,
      "expiresAt": "2024-06-21T04:29:46Z"]
    ```

Or create Profiles directly via the [REST Profiles API](/api-reference/rest#tag/Profiles/operation/createProfile).

### Self-Signed Tokens

Issue Session tokens on your server without calling the Auth API. [Contact us](mailto:support@quiltt.io?subject=Signing%20Secret) to request a signing secret.

## Troubleshooting

### 429 "Too Many Requests"

Hit the rate limit (10/hour or 20/day per Profile). Common causes:

1. Not caching tokens (issuing on every page load)
2. Not checking expiration (issuing when cache valid)
3. Not revoking on logout (old tokens count)

**Fix:**

- Cache tokens as shown in [Best Practices](#best-practices)
- Check expiration before issuing
- [Revoke tokens](/authentication/managing-session-tokens#revoke-tokens) on logout
- Switch to [Basic Auth](/authentication#basic-auth) for server-side use