Skip to main content
How Crow authenticates when calling your API.

Auth Types

TypeUse CaseHeader Sent
API KeyStatic server-to-server authX-API-Key: your-key
Bearer TokenOAuth/static tokenAuthorization: Bearer token
JWT ForwardPer-user actionsAuthorization: Bearer <user-jwt>

API Key

Your agent sends a static key in a custom header. OpenAPI spec:
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:
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.”
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.
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:
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:
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)
})
JWT Forward only works for authenticated widget users. Anonymous users won’t have a token to forward.

Which to Use?

ScenarioAuth Type
All requests use same credentialsAPI Key or Bearer
User-specific actions (my orders, my account)JWT Forward
Public API, no auth neededNone

Troubleshooting

IssueSolution
401 errorsCheck secret matches, verify token not expired
Tools not showing for usersJWT Forward requires authenticated users
Double path prefix (/api/api/)Put prefix in Base URL OR paths, not both

Multi-Subdomain Endpoints

Route API calls to different subdomains with separate credentials