Skip to main content
Togul separates authentication into two distinct mechanisms depending on what you are doing:
  • Bearer tokens — Used for management operations: creating flags, managing members, configuring environments, and other admin tasks. Obtained by logging in.
  • API keys — Used by your application servers and SDKs to evaluate feature flags. Scoped to a specific environment.

Bearer tokens

Bearer tokens are JWT access tokens scoped to your current organization membership and permission set. You obtain them by logging in.

Obtaining a token

curl -X POST http://localhost:8080/api/v1/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "[email protected]",
    "password": "mypassword123"
  }'
Response:
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Onur Kacmaz",
    "email": "[email protected]",
    "role": "admin",
    "email_verified_at": "2024-03-15T10:00:00Z",
    "created_at": "2024-03-15T10:00:00Z",
    "updated_at": "2024-03-15T10:00:00Z"
  },
  "current_organization": {
    "id": "org-uuid-here",
    "slug": "acme-corp",
    "name": "Acme Corp",
    "role": "owner"
  },
  "organizations": [
    {
      "id": "org-uuid-here",
      "slug": "acme-corp",
      "name": "Acme Corp",
      "role": "owner"
    }
  ]
}
The response contains:
  • token — JWT access token with a 1-hour expiry. Use this in the Authorization header for all management requests.
  • refresh_token — JWT refresh token with a 30-day expiry. Use this to obtain new access tokens without re-entering credentials.
  • current_organization — The organization context the token is scoped to, including your role in that organization.
  • organizations — All organizations you belong to. Use these IDs to switch context.

Using the token

Include the access token in the Authorization header as a Bearer token on every management request:
curl -X GET http://localhost:8080/api/v1/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Token expiry and scoping

TokenExpiryPurpose
token (access token)1 hourAuthenticate management API requests
refresh_token30 daysObtain new access tokens
Access tokens are scoped to a specific organization. If you belong to multiple organizations, each token is valid only for the organization it was issued for. Use organization switching to change context.

Token refresh

Access tokens expire after 1 hour. Use the refresh token to get a new access token without requiring the user to log in again. The refresh token is rotated on each use — the old refresh token is invalidated and a new one is returned.
curl -X POST http://localhost:8080/api/v1/refresh-token \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'
Response:
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Onur Kacmaz",
    "email": "[email protected]",
    "role": "admin",
    "email_verified_at": "2024-03-15T10:00:00Z",
    "created_at": "2024-03-15T10:00:00Z",
    "updated_at": "2024-03-15T10:00:00Z"
  },
  "current_organization": {
    "id": "org-uuid-here",
    "slug": "acme-corp",
    "name": "Acme Corp",
    "role": "owner"
  },
  "organizations": [
    {
      "id": "org-uuid-here",
      "slug": "acme-corp",
      "name": "Acme Corp",
      "role": "owner"
    }
  ]
}
Always replace your stored refresh token with the new one returned in the response. The previous refresh token is immediately invalidated.

Organization switching

If you belong to multiple organizations, your access token is scoped to one organization at a time. To switch context, call POST /api/v1/me/switch-organization with your target organization ID and your current refresh token.
curl -X POST http://localhost:8080/api/v1/me/switch-organization \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_id": "other-org-uuid-here",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'
Response:
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Onur Kacmaz",
    "email": "[email protected]",
    "role": "admin",
    "email_verified_at": "2024-03-15T10:00:00Z",
    "created_at": "2024-03-15T10:00:00Z",
    "updated_at": "2024-03-15T10:00:00Z"
  },
  "current_organization": {
    "id": "other-org-uuid-here",
    "slug": "other-org",
    "name": "Other Org",
    "role": "developer"
  },
  "organizations": [
    {
      "id": "org-uuid-here",
      "slug": "acme-corp",
      "name": "Acme Corp",
      "role": "owner"
    },
    {
      "id": "other-org-uuid-here",
      "slug": "other-org",
      "name": "Other Org",
      "role": "developer"
    }
  ]
}
The returned token is now scoped to the new organization. The current_organization field reflects the switched context. The refresh token is rotated as part of this operation.
You must be a member of the target organization for the switch to succeed. A 403 Forbidden is returned if you are not.

Security best practices

  • Never store bearer tokens or API key secrets in source code, client-side JavaScript, or version control.
  • Use environment variables or a secrets manager (e.g., AWS Secrets Manager, HashiCorp Vault) to inject credentials at runtime.
  • For browser-based applications, avoid storing tokens in localStorage — prefer httpOnly cookies or in-memory storage.
API keys do not expire by default. Rotate them periodically or after any suspected compromise using POST /api/v1/projects/{project_id}/api-keys/{id}/rotate. The old secret is immediately invalidated when rotated.You can also set an expires_at timestamp when creating or rotating a key to enforce automatic expiry.
When creating API keys, choose the narrowest scope that meets your use case:
  • Use server or sdk for flag evaluation — not stream unless you need real-time updates.
  • Never use a bearer token in application code that evaluates flags. Bearer tokens have broad management permissions and are not intended for machine-to-machine evaluation calls.
Access tokens expire after 1 hour. Build token refresh logic into your application:
  1. Catch 401 Unauthorized responses.
  2. Call POST /api/v1/refresh-token with your stored refresh token.
  3. Update your stored tokens with the new access token and rotated refresh token.
  4. Retry the original request.
If the refresh token has also expired (after 30 days), the user must log in again.
When decommissioning a service or rotating credentials, revoke old API keys with DELETE /api/v1/projects/{project_id}/api-keys/{id}. This immediately invalidates the key and prevents further use.

Build docs developers (and LLMs) love