Skip to main content

Authentication

The platform supports two authentication methods: API Keys and JWT Tokens.

API Keys

API keys are the recommended method for programmatic access.

Format

sk-proj-{random_string}

Usage

Include your API key in the Authorization header:

Authorization: Bearer sk-proj-your-api-key

Creating API Keys

  1. Log in to the Portal
  2. Navigate to API Keys
  3. Click Create Key
  4. Choose a key type:
    • Inference for OpenAI-compatible model calls
    • Management for public portal automation such as usage, billing, and limits
  5. Select the API scopes the key should have
  6. Copy the key immediately - it won't be shown again

Key Management

  • Multiple Keys: Create separate keys for different applications
  • Key Names: Use descriptive names for easy identification
  • Rotation: Regularly rotate keys for security
  • Revocation: Delete compromised keys immediately

JWT Tokens

JWT tokens are used for user session authentication (Portal access).

Registration Unavailable

Self-service registration is currently unavailable and returns a 503 Service Unavailable response. New users are onboarded via invitation links sent by organization admins or platform administrators.

Accepting an Invitation

An invitation email carries a one-time token. Accept it -- which also sets the account password -- with:

curl -X POST https://api.bulutistan.ai/api/v1/auth/accept-invite \
-H "Content-Type: application/json" \
-d '{
"token": "<invitation-token>",
"password": "your-new-password"
}'
FieldRequiredNotes
tokenYesThe invitation token from the email
passwordYesMinimum 8 characters, with an upper-case letter, a lower-case letter, a digit and a special character
first_name, last_name, phoneNoOptional profile details
organization_nameNoRequired when the invitation creates a new organization

email is not required and is no longer published as a parameter. The invitation token already identifies who was invited, so an address in the body was never the one used. A request that still sends email is accepted and behaves identically, so existing integrations keep working without a change.

Then log in as normal with the address the invitation was sent to.

Obtaining Tokens

curl -X POST https://api.bulutistan.ai/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "your-password"
}'

Response

{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 1800,
"user": {
"id": "uuid",
"email": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"tenant_id": "uuid",
"is_active": true,
"created_at": "2024-01-01T00:00:00Z",
"email_verified": true,
"onboarding_completed": true
}
}

Token Lifetimes

TokenLifetime
Access token30 minutes
Refresh token7 days

The access token expires after 30 minutes. Use the refresh token to obtain a new access token without requiring the user to log in again.

Usage

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Endpoint Path Prefixes

The platform exposes endpoints under two prefixes. Both route to the same handlers for inference; management endpoints are only under /api/v1/*.

  • /v1/*: use this when configuring the OpenAI SDK base_url. Example: OpenAI(base_url="https://api.bulutistan.ai/v1"). The SDK appends paths like /chat/completions itself, so the final URL becomes https://api.bulutistan.ai/v1/chat/completions.
  • /api/v1/*: use this for direct HTTP requests (curl, raw fetch) and for all platform management endpoints (profile, api-keys, billing, usage, jobs).

API Key Types and Scopes

This platform separates API keys by purpose.

Inference Keys

Use an Inference key with OpenAI-compatible SDKs and direct inference HTTP calls. Inference endpoints are available under both /v1/* and /api/v1/*.

API ScopeEndpoints
inference:chatPOST /v1/chat/completions, POST /api/v1/chat/completions
inference:completionsPOST /v1/completions, POST /api/v1/completions
inference:embeddingsPOST /v1/embeddings, POST /api/v1/embeddings
inference:imagesPOST /v1/images/generations, POST /api/v1/images/generations
inference:audioPOST /v1/audio/transcriptions, POST /v1/audio/translations, POST /v1/audio/speech (and the /api/v1/* equivalents), plus the realtime transcription WebSocket
models:listGET /v1/models, GET /api/v1/models
models:retrieveGET /v1/models/{model_id}, GET /api/v1/models/{model_id}
jobs:stats_ownGET /api/v1/jobs/stats
jobs:read_ownGET /api/v1/jobs, GET /api/v1/jobs/{job_id}
jobs:cancel_ownPOST /api/v1/jobs/{job_id}/cancel

Submit async jobs through the inference endpoints with the background/async request option; POST /api/v1/jobs and DELETE /api/v1/jobs/{job_id} are not public API-key operations.

Management Keys

Use a Management key for public portal automation. Management keys cannot call inference endpoints.

API ScopeEndpoints
usage:read_currentGET /api/v1/usage, GET /api/v1/usage/current
usage:read_historyGET /api/v1/usage/history
usage:read_summaryGET /api/v1/usage/summary
usage:read_reportGET /api/v1/usage/report
usage:read_breakdownGET /api/v1/usage/tenant-breakdown (tenant admin/owner only)
billing:read_currentGET /api/v1/billing/wallet, GET /api/v1/billing/commitment (both tenant admin/owner only), GET /api/v1/plan/current (any user in the tenant)
billing:read_cyclesRetired name, kept working. It now grants exactly what billing:read_invoices grants (tenant admin/owner only)
billing:read_invoicesGET /api/v1/billing/documents/invoices, GET /api/v1/billing/documents/invoices/{invoice_id}, GET /api/v1/billing/documents/invoices/{invoice_id}/transactions, GET /api/v1/billing/documents/transactions (tenant admin/owner only)
billing:read_summaryRetired name, kept working. It now grants exactly what billing:read_current grants (tenant admin/owner only)
profile:readGET /api/v1/profile, GET /api/v1/auth/profile
rate_limits:readGET /api/v1/rate-limits, GET /api/v1/profile/rate-limits
budgets:read_ownGET /api/v1/profile/cost-limit, GET /api/v1/billing/payg-spend-cap
budgets:update_ownPUT /api/v1/profile/cost-limit, DELETE /api/v1/profile/cost-limit, PUT /api/v1/billing/payg-spend-cap, DELETE /api/v1/billing/payg-spend-cap (the two spend-cap writes are tenant admin/owner only)

usage:read_current, usage:read_history, usage:read_summary, and usage:read_report return the key owner's personal usage by default. Admin and owner users can request tenant-level usage where the endpoint supports a scope=tenant query parameter. usage:read_breakdown is hidden from other users in the portal. GET /api/v1/usage is an alias of GET /api/v1/usage/current and takes the same scope. It reports the open period and accepts no date-range parameters -- it used to advertise start_date and end_date and silently ignore them, so a caller could believe a custom window had been applied when it had not. Use the history and report endpoints when you need a window.

Most billing scopes are only useful to a tenant admin or owner. GET /api/v1/plan/current is the exception: it is covered by billing:read_current but any user in the tenant can read it.

The scope and the role are two separate checks, and they fail differently. The scope is checked at the gateway, before the request is forwarded; the role is checked by the service that answers. A key held by an ordinary member that carries billing:read_current therefore passes the gateway and is still refused by the service, and the same is true of budgets:update_own against the PAYG spend cap. Both refusals are a 403, so grant a key a scope its owner's role cannot actually use only if you intend that key to stop working.

billing:read_cycles and billing:read_summary are the names of two withdrawn endpoints. Keys already issued with them keep working, so nothing has to be reissued, but a new key gains nothing by selecting them on top of billing:read_invoices and billing:read_current.

POST /api/v1/billing/coupons/redeem is not on this table on purpose. Redeeming a coupon moves money into your wallet, and no API-key scope grants a write of that kind: use a browser/JWT session, or the portal's Billing page.

API Key Limitations

Public API keys do not manage organizations, members, API key creation, or admin portal resources. Use a browser/JWT session for those workflows.

If a key calls an endpoint outside the public API-key surface, you will receive a 403 Forbidden error:

{
"error": {
"message": "API keys cannot access this endpoint.",
"type": "authentication_error"
}
}

If the endpoint is available to API keys but the key is missing the required capability, the error message names the missing scope.

When the organization is suspended

If the organization behind a credential is suspended, inference is refused with 403 and error.code of tenant_suspended. The credential itself is fine -- rotating or reissuing the key changes nothing, and an owner or admin has to bring the account back into good standing.

This used to be reported as "API key expired or revoked", which pointed at the wrong remedy. API keys and portal sessions now receive the same code, so a single branch on error.code covers both. See Error Handling.

Security Best Practices

  1. Never share API keys in public repositories
  2. Use environment variables for key storage
  3. Rotate keys periodically
  4. Use separate keys for development and production
  5. Monitor usage for unexpected activity

Rate Limits

Authentication affects rate limits:

Auth TypeRate Limit
API KeyPer-key limits
JWT TokenPer-user limits

See Rate Limiting for details.