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

# Authentication

> How to authenticate with the Sticker API

## Overview

Sticker uses **API Key authentication** to secure all partner API requests. Your API key identifies your partner account and authorizes access to your organizations and users.

***

## API Key Authentication

### Getting Your API Key

Contact the Sticker team to receive your API credentials:

* **Partner ID** (UUID) - Your unique partner identifier
* **API Key** (string) - Starts with `sk_live_` or `sk_test_`

```bash theme={null}
# Store securely in your environment
STICKER_PARTNER_ID=550e8400-e29b-41d4-a716-446655440000
STICKER_API_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

<Warning>
  **Keep your API key secret!** Never expose it in client-side code, public repositories, or browser network requests.
</Warning>

***

## Authentication Headers

### Organization Setup Endpoint

Use the `Authorization: Bearer` header:

```javascript theme={null}
const response = await fetch('https://api.usesticker.com/v1/organizations/setup', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.STICKER_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ /* ... */ })
});
```

### Partner Handshake Endpoint

Use the `X-API-Key` header:

```javascript theme={null}
const response = await fetch('https://api.usesticker.com/v1/partner/handshake', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.STICKER_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ /* ... */ })
});
```

***

## API Environments

| Environment         | Base URL                                | Key Prefix |
| ------------------- | --------------------------------------- | ---------- |
| **Production**      | `https://api.usesticker.com/v1`         | `sk_live_` |
| **Staging/Sandbox** | `https://api.staging.usesticker.com/v1` | `sk_test_` |

<Info>
  Use sandbox credentials for development and testing. Sandbox data is isolated from production.
</Info>

***

## Security Best Practices

<AccordionGroup>
  <Accordion title="Server-Side Only" icon="server">
    **Never expose your API key in client-side code.**

    Your backend should:

    1. Receive requests from your frontend
    2. Make authenticated requests to Sticker API
    3. Return results to your frontend

    ```javascript theme={null}
    // ❌ BAD - API key exposed in browser
    const response = await fetch('https://api.usesticker.com/v1/partner/handshake', {
      headers: { 'X-API-Key': 'sk_live_xxx' }
    });

    // ✅ GOOD - API call goes through your backend
    const response = await fetch('/api/supplies/auth', {
      method: 'POST',
      body: JSON.stringify({ userId: user.id })
    });
    ```
  </Accordion>

  <Accordion title="Environment Variables" icon="key">
    Store API keys in environment variables, never in code:

    ```bash theme={null}
    # .env (never commit this file)
    STICKER_API_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    ```

    ```javascript theme={null}
    // Read from environment
    const apiKey = process.env.STICKER_API_KEY;
    ```
  </Accordion>

  <Accordion title="Key Rotation" icon="arrows-rotate">
    If you suspect your API key has been compromised:

    1. Contact Sticker support immediately
    2. We'll issue a new key
    3. Update your environment variables
    4. Redeploy your application

    Old keys are invalidated immediately upon rotation.
  </Accordion>

  <Accordion title="Audit Logging" icon="list-check">
    Sticker logs all API requests with:

    * Timestamp
    * Partner ID
    * Endpoint called
    * Response status
    * IP address

    Contact support to review your API activity.
  </Accordion>
</AccordionGroup>

***

## Error Responses

### 401 Unauthorized

Returned when authentication fails:

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

**Common causes:**

* Missing `Authorization` or `X-API-Key` header
* Invalid or expired API key
* Using production key in sandbox or vice versa

### 403 Forbidden

Returned when authenticated but not authorized:

```json theme={null}
{
  "error": "Forbidden",
  "message": "API key does not have required scope",
  "code": "FORBIDDEN"
}
```

**Common causes:**

* API key lacks required permissions/scope
* Trying to access resources belonging to another partner

***

## Session Tokens

When users access the embedded iframe, they use **session tokens** instead of API keys.

Session tokens are:

| Property        | Description                                       |
| --------------- | ------------------------------------------------- |
| **Short-lived** | Expire after 5 minutes                            |
| **Single-use**  | Invalidated after first use                       |
| **User-bound**  | Tied to a specific user profile                   |
| **Secure**      | 64-character cryptographically random hex strings |

```javascript theme={null}
// Generate session token via handshake
const { session_key, iframe_embed_url } = await handshake(userId);

// Session token is embedded in iframe URL
// https://shop.usesticker.com/embedded/{partner_id}?session_key={token}
```

<Warning>
  **Never reuse session tokens.** Generate a fresh token every time a user opens the supplies module.
</Warning>

***

## Rate Limits

| Endpoint                  | Rate Limit          |
| ------------------------- | ------------------- |
| `/v1/organizations/setup` | 100 requests/minute |
| `/v1/partner/handshake`   | 300 requests/minute |

When rate limited, you'll receive a `429 Too Many Requests` response:

```json theme={null}
{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "retry_after": 60
}
```

Implement exponential backoff for retries:

```javascript theme={null}
async function withRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status === 429 && i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000;
        await new Promise(r => setTimeout(r, delay));
        continue;
      }
      throw error;
    }
  }
}
```

***

## Testing Authentication

Verify your authentication is working:

```bash theme={null}
# Test with curl
curl -X POST https://api.usesticker.com/v1/partner/handshake \
  -H "X-API-Key: sk_test_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"internal_user_id": "test-user-123"}'
```

Expected response (if user exists):

```json theme={null}
{
  "success": true,
  "session_key": "abc123...",
  "iframe_embed_url": "https://shop.usesticker.com/embedded/...",
  "expires_at": "2024-01-15T10:35:00.000Z",
  "profile": {
    "id": "550e8400-...",
    "first_name": "Test",
    "last_name": "User",
    "email": "test@example.com"
  }
}
```

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Organization Setup" icon="building" href="/api-reference/organization-setup">
    Learn how to provision organizations
  </Card>

  <Card title="User Handshake" icon="handshake" href="/api-reference/handshake">
    Learn how to authenticate user sessions
  </Card>
</CardGroup>
