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

# Authentication

> Secure your API requests with API keys

## Overview

The Junis External API uses **API Keys** for authentication. All API requests must include a valid API key in the `X-API-Key` header.

<Note>
  Junis has two kinds of API keys: **organization keys** (`jns_live_`) bound to a single
  organization, and **user keys** (`jns_user_`) that work across all organizations you
  belong to. See [Organization vs User API Keys](#organization-vs-user-api-keys) below.
</Note>

***

## API Key Authentication

### Creating an API Key

<Steps>
  <Step title="Navigate to API Keys Page">
    Go to **Dashboard** → **API Keys** in the Junis dashboard
  </Step>

  <Step title="Click Create New API Key">
    Click the **"+ Create API Key"** button

    Fill in the form:

    * **Name**: A descriptive name for the key (e.g., "Production API", "Testing")
    * **Description**: Optional notes about the key's purpose
    * **Scopes**: Select permissions (default: all external API scopes enabled)
    * **Rate Limits**: Configure requests per minute/hour (optional)
  </Step>

  <Step title="Copy the API Key">
    <Warning>
      **IMPORTANT**: Copy the API key and store it securely. Treat it like a password —
      anyone with the key can act with its permissions.
    </Warning>

    The key format is: `jns_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` (32 random characters)
  </Step>
</Steps>

### API Key Format

| Prefix      | Purpose                                            | Where to Create                            |
| ----------- | -------------------------------------------------- | ------------------------------------------ |
| `jns_live_` | **Organization key** — bound to one organization   | **Admin → API Keys** (admin/owner)         |
| `jns_user_` | **User key** — works across all your organizations | **Settings → API Keys** (up to 5 per user) |

### Organization vs User API Keys

* **Organization keys** (`jns_live_`) always act within the organization they were created in.
* **User keys** (`jns_user_`) act as *you*, across every organization you belong to. Select
  which organization a request targets with the `X-Organization-Id` header; if omitted, your
  current organization is used.

```bash cURL — user key targeting a specific organization theme={null}
curl -X POST https://api.junis.ai/api/external/v1/chat/completions \
  -H "X-API-Key: jns_user_YOUR_API_KEY_HERE" \
  -H "X-Organization-Id: YOUR_ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "Hello!"}]}'
```

<Note>
  User keys are what [Junis Magic](/guides/junis-magic) uses — one key, all your squads.
</Note>

***

## Using API Keys

Include your API key in the `X-API-Key` header:

```bash cURL theme={null}
curl -X POST https://api.junis.ai/api/external/v1/chat/completions \
  -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "Hello!"}
    ]
  }'
```

***

## API Key Permissions (Scopes)

### Available Scopes

API keys can have granular permissions. Each scope grants access to specific API operations:

| Scope                 | Description                                                 | Required For                                          |
| --------------------- | ----------------------------------------------------------- | ----------------------------------------------------- |
| `orchestrator:invoke` | Invoke orchestrator with prompts (non-streaming)            | Chat Completions (non-streaming)                      |
| `orchestrator:stream` | Stream orchestrator responses (SSE)                         | Chat Completions (streaming)                          |
| `sessions:read`       | Read chat sessions                                          | List sessions, Get session status                     |
| `sessions:messages`   | Read session messages                                       | Get session messages                                  |
| `content:read`        | Read generated content                                      | List content, Get content detail                      |
| `content:retry`       | Retry failed content generation                             | Retry content generation                              |
| `agents:list`         | List available agents in organization                       | List agents                                           |
| `agents:invoke`       | Invoke specific agents directly                             | Agent completions                                     |
| `rag:upload`          | Upload files to RAG Datastore                               | Create DataStore, Upload files                        |
| `rag:read`            | Read RAG uploads and DataStores                             | List DataStores, List uploads, Get upload status      |
| `rag:delete`          | Delete RAG uploads and documents (opt-in, never default)    | Delete RAG resources                                  |
| `admin:read`          | Read admin resources (organization-scoped)                  | Admin endpoints (if accessible)                       |
| `admin:write`         | Write admin resources (organization-scoped)                 | Admin endpoints (if accessible)                       |
| `users:provision`     | Provision (create-or-get) users and pending org memberships | [User Provisioning](/api-reference/user-provisioning) |
| `users:delegate`      | Act on behalf of another organization member                | Chat Completions `on_behalf_of`                       |

<Note>
  **Default Scopes**: Organization keys created from the dashboard include:
  `orchestrator:invoke`, `orchestrator:stream`, `sessions:read`, `sessions:messages`,
  `agents:list`, `agents:invoke`, `content:read`, `content:retry`.

  User keys include a broader default set covering management operations (used by Junis Magic).
  Additional scopes — RAG, delete, and partner scopes — are granted by updating the key
  (ask your organization admin).
</Note>

<Warning>
  **Partner scopes are opt-in only**: `users:provision` and `users:delegate` are
  **never** included in any default scope set. They must be explicitly granted when
  creating or updating an API key, and are intended for trusted partner services
  that manage end-user accounts.
</Warning>

### Scope Requirements by Endpoint

| Endpoint                                                     | Required Scopes                              |
| ------------------------------------------------------------ | -------------------------------------------- |
| `POST /api/external/v1/chat/completions` (non-streaming)     | `orchestrator:invoke`                        |
| `POST /api/external/v1/chat/completions` (streaming)         | `orchestrator:invoke`, `orchestrator:stream` |
| `GET /api/external/sessions`                                 | `sessions:read`                              |
| `GET /api/external/sessions/{id}/messages`                   | `sessions:messages`                          |
| `GET /api/external/sessions/{id}/status`                     | `sessions:read`                              |
| `GET /api/external/content`                                  | `content:read`                               |
| `GET /api/external/content/{id}`                             | `content:read`                               |
| `POST /api/external/content/{id}/retry`                      | `content:retry`                              |
| `GET /api/external/agents`                                   | `agents:list`                                |
| `POST /api/external/agents/{id}/completions` (non-streaming) | `agents:invoke`                              |
| `POST /api/external/agents/{id}/completions` (streaming)     | `agents:invoke`                              |
| `POST /api/external/rag/datastores`                          | `rag:upload`                                 |
| `GET /api/external/rag/datastores`                           | `rag:read`                                   |
| `POST /api/external/rag/upload`                              | `rag:upload`                                 |
| `POST /api/external/management/users`                        | `users:provision`                            |
| `POST /api/external/v1/chat/completions` with `on_behalf_of` | `orchestrator:invoke`, `users:delegate`      |
| `GET /api/external/rag/uploads`                              | `rag:read`                                   |
| `GET /api/external/rag/uploads/{id}`                         | `rag:read`                                   |

***

## Rate Limits

API keys are subject to rate limits to ensure fair usage and system stability.

### Default Rate Limits

| Limit Type          | Default Value | Customizable                |
| ------------------- | ------------- | --------------------------- |
| Requests per minute | 100           | Yes (when creating API key) |
| Requests per hour   | 1,000         | Yes (when creating API key) |

<Note>
  Rate limits are configurable per API key. Contact your organization admin to adjust limits if needed.
</Note>

### Rate Limit Headers

All API responses include rate limit information in headers:

```http theme={null}
X-RateLimit-Limit-Minute: 100
X-RateLimit-Remaining-Minute: 85
X-RateLimit-Reset-Minute: 1767261600
X-RateLimit-Limit-Hour: 1000
X-RateLimit-Remaining-Hour: 950
X-RateLimit-Reset-Hour: 1767265200
```

See [Rate Limits](/api-reference/rate-limits) for detailed documentation.

***

## Security Best Practices

<AccordionGroup>
  <Accordion title="Keep API Keys Secret" icon="eye-slash">
    * **Never commit API keys to Git repositories**
    * Store keys in environment variables or secret management systems (e.g., AWS Secrets Manager, HashiCorp Vault)
    * Use `.env` files for local development (add to `.gitignore`)
    * Rotate keys regularly (every 90 days recommended)
  </Accordion>

  <Accordion title="Use HTTPS Only" icon="shield-check">
    * All API requests must use HTTPS
    * Never send API keys over unencrypted HTTP
    * Our API automatically rejects non-HTTPS requests
  </Accordion>

  <Accordion title="Separate Keys for Different Environments" icon="code-branch">
    * Create separate keys for development, staging, and production
    * Use descriptive names to identify key purposes (e.g., "Prod - Web App", "Dev - Testing")
    * Revoke unused keys immediately
  </Accordion>

  <Accordion title="Monitor Key Usage" icon="chart-line">
    * Check the API Keys page for usage statistics
    * Review `last_used_at` and `request_count` regularly
    * Set up alerts for unusual activity (e.g., sudden spike in requests)
  </Accordion>

  <Accordion title="Revoke Compromised Keys" icon="ban">
    If an API key is compromised:

    1. **Immediately revoke** the key in Admin → API Keys
    2. **Create a new key** with a different name
    3. **Update all applications** to use the new key
    4. **Review logs** for suspicious activity (unusual endpoints, high request counts)
  </Accordion>

  <Accordion title="Principle of Least Privilege" icon="user-shield">
    * Only grant scopes that are actually needed
    * For read-only integrations, use `sessions:read` and `sessions:messages` only
    * Avoid granting `admin:write` unless absolutely necessary
  </Accordion>
</AccordionGroup>

***

## Error Handling

### Common Authentication Errors

<ResponseExample>
  ```json 401 Unauthorized - Missing API Key theme={null}
  {
    "detail": "API key required. Include X-API-Key header with your request."
  }
  ```

  ```json 401 Unauthorized - Invalid API Key theme={null}
  {
    "detail": "Invalid or expired API key"
  }
  ```

  ```json 403 Forbidden - Insufficient Scopes theme={null}
  {
    "error": "insufficient_scopes",
    "message": "API key does not have required permissions",
    "required_scopes": ["orchestrator:stream"],
    "missing_scopes": ["orchestrator:stream"],
    "available_scopes": ["orchestrator:invoke", "sessions:read"]
  }
  ```

  ```json 429 Too Many Requests theme={null}
  {
    "error": {
      "type": "rate_limit_error",
      "message": "Rate limit exceeded. Please try again in 60 seconds.",
      "code": "rate_limit_exceeded",
      "retry_after": 60
    }
  }
  ```
</ResponseExample>

### Handling Errors in Code

When you receive an error response, check the HTTP status code and error message to determine the cause. Common error codes:

* **401 Unauthorized**: Invalid or missing API key
* **403 Forbidden**: Insufficient scopes/permissions
* **429 Too Many Requests**: Rate limit exceeded (wait for `retry_after` seconds)

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Chat Completions API" icon="comments" href="/api-reference/chat-completions">
    Start sending messages to your agents
  </Card>

  <Card title="Sessions API" icon="list" href="/api-reference/sessions">
    Retrieve and manage chat sessions
  </Card>

  <Card title="Rate Limits" icon="gauge-high" href="/api-reference/rate-limits">
    Understand rate limiting and best practices
  </Card>

  <Card title="Error Codes" icon="triangle-exclamation" href="/api-reference/error-codes">
    Full list of error codes and solutions
  </Card>
</CardGroup>
