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

# OpenAPI Authentication

> Configure API authentication for your agent

How Crow authenticates when calling your API.

## Auth Types

| Type             | Use Case                     | Header Sent                        |
| ---------------- | ---------------------------- | ---------------------------------- |
| **API Key**      | Static server-to-server auth | `X-API-Key: your-key`              |
| **Bearer Token** | OAuth/static token           | `Authorization: Bearer token`      |
| **JWT Forward**  | Per-user actions             | `Authorization: Bearer <user-jwt>` |

***

## API Key

Your agent sends a static key in a custom header.

**OpenAPI spec:**

```yaml theme={null}
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

security:
  - ApiKeyAuth: []
```

**Dashboard config:**

1. Select **API Key**
2. Enter header name (`X-API-Key`)
3. Enter your key

***

## Bearer Token

Your agent sends a static token in the Authorization header.

**OpenAPI spec:**

```yaml theme={null}
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer

security:
  - BearerAuth: []
```

**Dashboard config:**

1. Select **Bearer Token**
2. Enter your token

***

## JWT Forward

Your agent forwards the user's Crow identity token to your API. This enables user-specific actions like "check my orders" or "cancel my subscription."

<Note>
  This is **not** your app's session JWT. It's a Crow-scoped identity token your backend mints with `CROW_VERIFICATION_SECRET`. See [Identity Verification](/identity-verification).
</Note>

**How it works:**

```
1. Your backend mints a Crow identity token (JWT signed with Crow secret)
2. Frontend passes it to widget: window.crow('identify', { token })
3. When calling your API, Crow forwards that same token
4. Your API verifies it (same secret) and identifies the user
```

**OpenAPI spec:**

```yaml theme={null}
components:
  securitySchemes:
    UserJWT:
      type: http
      scheme: bearer
      bearerFormat: JWT

security:
  - UserJWT: []

paths:
  /my/orders:
    get:
      operationId: getMyOrders
      summary: Get user's orders
```

**Dashboard config:**

1. Select **JWT Forward**
2. No additional config—token comes from widget user

**Your API must verify the token:**

<CodeGroup>
  ```javascript Node.js theme={null}
  const jwt = require('jsonwebtoken')

  function authMiddleware(req, res, next) {
    const token = req.headers.authorization?.split(' ')[1]
    
    try {
      const payload = jwt.verify(token, process.env.CROW_VERIFICATION_SECRET)
      req.user = { id: payload.user_id, email: payload.email }
      next()
    } catch {
      res.status(401).json({ error: 'Invalid token' })
    }
  }

  app.get('/my/orders', authMiddleware, async (req, res) => {
    const orders = await db.orders.findMany({ where: { userId: req.user.id } })
    res.json(orders)
  })
  ```

  ```python Python theme={null}
  from fastapi import Depends, HTTPException
  from fastapi.security import HTTPBearer
  import jwt, os

  security = HTTPBearer()

  async def get_current_user(credentials = Depends(security)):
      try:
          payload = jwt.decode(
              credentials.credentials,
              os.environ["CROW_VERIFICATION_SECRET"],
              algorithms=["HS256"]
          )
          return {"id": payload["user_id"], "email": payload.get("email")}
      except:
          raise HTTPException(401, "Invalid token")

  @app.get("/my/orders")
  async def get_my_orders(user = Depends(get_current_user)):
      return await db.get_orders(user["id"])
  ```
</CodeGroup>

<Note>
  JWT Forward only works for authenticated widget users. Anonymous users won't have a token to forward.
</Note>

***

## Which to Use?

| Scenario                                      | Auth Type         |
| --------------------------------------------- | ----------------- |
| All requests use same credentials             | API Key or Bearer |
| User-specific actions (my orders, my account) | JWT Forward       |
| Public API, no auth needed                    | None              |

***

## Troubleshooting

| Issue                            | Solution                                       |
| -------------------------------- | ---------------------------------------------- |
| 401 errors                       | Check secret matches, verify token not expired |
| Tools not showing for users      | JWT Forward requires authenticated users       |
| Double path prefix (`/api/api/`) | Put prefix in Base URL OR paths, not both      |

***

<Card title="Multi-Subdomain Endpoints" icon="globe" href="/multi-subdomain-endpoints">
  Route API calls to different subdomains with separate credentials
</Card>
