API Overview
Base URL, authentication, rate limits, error format, and versioning for the BroomVA API.
API Overview
The BroomVA API provides programmatic access to the platform's chat, organization management, billing, and agent services. All endpoints are served from the primary application at broomva.tech.
Base URL
https://broomva.tech/apiAll API paths in this documentation are relative to this base URL. For example, the chat endpoint is POST /api/chat.
For local development, the chat application runs on port 3001:
http://localhost:3001/apiAuthentication
The API uses Bearer token authentication. Include your token in the Authorization header of every request:
curl -H "Authorization: Bearer $TOKEN" https://broomva.tech/api/...Obtaining a token
There are three ways to get an API token:
-
Device-code flow (RFC 8628) -- use
broomva auth loginfrom the CLI. The flow issuesPOST /api/auth/device/codeto get a user code, then pollsPOST /api/auth/device/tokenuntil the user approves. The token is stored in~/.broomva/config.jsonand can be printed withbroomva auth token. See CLI Authentication for the full protocol. -
API token endpoint -- sign in to broomva.tech and visit
/api/auth/api-tokento generate a long-lived token. This is the simplest method for scripts. -
Organization API keys -- create organization-scoped API keys from the console. These do not expire unless explicitly revoked. They provide admin-level access scoped to the creating organization.
Token format
Tokens are JWTs signed by the platform's auth system (Better Auth) using signLifeJWT(). They encode:
- User ID -- the authenticated user
- Email -- for identity resolution
- Organization memberships -- resolved at auth time
- Token expiration -- configurable, default matches
JWT_ACCESS_EXPIRY_MS - Scopes -- for API keys, the permitted operations
Refresh tokens
When using the device-code flow, the server may return a refresh_token alongside the access_token. Use the refresh token to obtain a new access token without re-authenticating:
curl -X POST https://broomva.tech/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "eyJ..."}'Authentication errors
| Status | Error | Description |
|---|---|---|
401 | unauthorized | Missing or invalid token |
403 | forbidden | Valid token but insufficient permissions (RBAC check failed) |
The RBAC system uses a 4-tier role hierarchy (owner > admin > member > viewer). When a 403 is returned, it means the user's role in the target organization does not meet the minimum required role for the operation. See Organizations for the full permission matrix.
Rate limits
Rate limits are applied per-organization (or per-user for personal workspaces):
| Plan | Requests/minute | Requests/day |
|---|---|---|
| Free | 10 | 100 |
| Pro | 60 | 5,000 |
| Team | 120 | 20,000 |
| Enterprise | Custom | Custom |
When rate-limited, the API returns a 429 Too Many Requests response with the following headers:
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1711234567The device-code endpoints have separate rate limits: 10 requests/minute per IP for code generation, and 5 requests/minute per device code + IP for token polling.
Error format
All errors follow a consistent JSON structure:
{
"error": {
"code": "validation_error",
"message": "The 'model' field is required.",
"details": {
"field": "model",
"constraint": "required"
}
}
}Standard error codes
| Code | HTTP Status | Description |
|---|---|---|
unauthorized | 401 | Authentication required |
forbidden | 403 | Insufficient permissions |
not_found | 404 | Resource does not exist |
conflict | 409 | Resource already exists (e.g., duplicate slug) |
validation_error | 422 | Request body validation failed |
rate_limited | 429 | Too many requests |
credits_exhausted | 402 | Credit limit reached |
internal_error | 500 | Unexpected server error |
The credits_exhausted error (HTTP 402) is returned when the organization's credit balance is insufficient for the requested operation and the plan's overage policy blocks the request. See Billing for overage policy details per plan.
Content type
All request bodies must be JSON with Content-Type: application/json. Responses are JSON unless the endpoint specifies streaming (in which case text/event-stream is used).
Streaming
Chat endpoints support Server-Sent Events (SSE) for streaming responses. Set the Accept header to text/event-stream or include "stream": true in the request body:
curl -N -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
https://broomva.tech/api/chat \
-d '{"model": "claude-sonnet-4-20250514", "messages": [{"role": "user", "content": "Hello"}]}'The SSE stream uses the Vercel AI SDK v6 data stream format, compatible with the ai package's useChat and streamText functions.
Stream event types
The stream emits UiPart objects:
| Event type | Description |
|---|---|
text-delta | Incremental text token from the model |
tool-call | Tool invocation request (name, arguments) |
tool-result | Tool execution result |
finish | Completion signal with usage data and finish reason |
error | Error during generation |
Versioning
The API is currently unversioned -- all endpoints are served at /api/. When breaking changes are introduced, the API will adopt path-based versioning (/api/v2/) with a deprecation period for the previous version.