logoSemaphor

Tokens API

Generate and manage authentication tokens

Overview

The Tokens API allows you to generate secure authentication tokens for embedding Semaphor dashboards or for performing management actions (CRUD) on dashboards and visuals.

There are two types of tokens:

  • Dashboard Token: Scoped to a specific dashboard, used for embedding (read-only or self-service).
  • Project Token: Scoped to a project, used for management actions (CRUD on dashboards, visuals, etc.).

Token Types

Dashboard Token

  • Purpose: Embed a dashboard (read-only or self-service)
  • Required fields: dashboardId, dashboardSecret
  • Optional fields:
    • For read-only: cls, rcls, params, config
    • For self-service: tenantId, endUserId, endUserEmail, displayName (plus above)
  • Default expiry: 30 minutes (1800 seconds)

Project Token

  • Purpose: Access to multiple dashboards within a project with flexible user management
  • Required fields:
    • type (must be 'project')
    • projectId
    • projectSecret
  • User identification (one required):
    • endUserId - Direct user ID lookup
    • endUserEmail + (tenantId or tenantName) - Email-based lookup
    • orgUserId - Organization user lookup
  • Optional fields:
    • tokenExpiry - Token expiration in seconds
    • autoCreateEndUser - Enable JIT user provisioning
    • role - User role ('VIEWER' or 'POWER_USER')
    • displayName - Display name for users
    • initialDashboardId - Initial dashboard to load
    • allowedSemanticDomains - Array of domain IDs or names
    • allowEdit - Enable self-service editing
    • cls, rcls, sls - Security policies
    • params, config - UI customization
  • Default expiry: 30 minutes (1800 seconds)

Request Body Reference

FieldTypeRequiredFor Token TypeDescription
typestringYes*projectRequired for project token: must be 'project'. For dashboard token, omit or set to 'dashboard'.
dashboardIdstringYes*dashboardDashboard ID
dashboardSecretstringYes*dashboardDashboard secret
projectIdstringYes*projectProject ID
projectSecretstringYes*projectProject secret
tokenExpiryintegerNobothToken expiry in seconds (default: 1800)
tenantIdstringNobothTenant identifier
tenantNamestringNoprojectTenant name (alternative to tenantId)
endUserIdstringNobothEnd user identifier
endUserEmailstringNobothEnd user email
orgUserIdstringNobothOrg user identifier
orgUserEmailstringNodashboardOrg user email
displayNamestringNobothDisplay name for user
autoCreateEndUserbooleanNoprojectAuto-create users if they don't exist
rolestringNoprojectRole for auto-created users ('VIEWER' or 'POWER_USER')
initialDashboardIdstringNoprojectInitial dashboard to load
allowedSemanticDomainsarrayNoprojectArray of semantic domain IDs or names
allowEditbooleanNobothEnable self-service editing
clsobject/arrayNobothConnection-level security policies
rclsobject/arrayNobothRow-level security policies
slsstringNoprojectSchema-level security policy
paramsobjectNobothUser parameters and preferences
configobjectNobothUI configuration options

*Required for the respective token type.


Example Requests

1. Generate Dashboard Token (Read-Only)

curl -X POST https://semaphor.cloud/api/v1/token \
  -H "Content-Type: application/json" \
  -d '{
    "dashboardId": "d_cf007a8b-19bc-46ad-8787-2915445b7b86",
    "dashboardSecret": "ds_f32f0b30-b7e1-40f9-ba6a-9804a5b9d635",
    "cls": { "name": "store_sales_primary", "params": { "tenant": "tenant_abc_123" } },
    "rcls": { "name": "region_filter", "params": { "state": ["California", "Nevada"] } }
  }'

2. Generate Project Token (Basic)

curl -X POST https://semaphor.cloud/api/v1/token \
  -H "Content-Type: application/json" \
  -d '{
    "type": "project",
    "projectId": "p_1234567890abcdef",
    "projectSecret": "ps_abcdef1234567890",
    "endUserId": "user_123",
    "tokenExpiry": 1800
  }'

3. Generate Project Token with JIT User Provisioning

curl -X POST https://semaphor.cloud/api/v1/token \
  -H "Content-Type: application/json" \
  -d '{
    "type": "project",
    "projectId": "p_1234567890abcdef",
    "projectSecret": "ps_abcdef1234567890",
    "endUserEmail": "newuser@example.com",
    "tenantName": "Acme Corp",
    "autoCreateEndUser": true,
    "role": "VIEWER",
    "displayName": "John Doe",
    "tokenExpiry": 3600
  }'

4. Generate Project Token with Semantic Domains

curl -X POST https://semaphor.cloud/api/v1/token \
  -H "Content-Type: application/json" \
  -d '{
    "type": "project",
    "projectId": "p_1234567890abcdef",
    "projectSecret": "ps_abcdef1234567890",
    "endUserId": "user_123",
    "allowedSemanticDomains": ["sales_data", "marketing"],
    "initialDashboardId": "dashboard_main"
  }'

TypeScript Types

/**
 * Parameters for customizing token behavior and formatting.
 */
export type TokenParams = {
  currencyFormat: {
    /**
     * Locale identifier (e.g., 'en-US', 'fr-FR').
     */
    locale: string;
    /**
     * Currency code compliant with ISO 4217 (e.g., 'USD', 'EUR').
     */
    currency: string;
  };
};
 
/**
 * Defines a security policy that can be applied at connection or row level.
 */
export type TokenSecurityPolicy = {
  /**
   * Name of the policy (e.g., 'store_sales_primary', 'region_filter').
   */
  name: string;
  /**
   * Arbitrary parameters associated with the policy.
   * Examples:
   * // Single value
   * { tenant: 'tenant_abc_123' }
   * // Multiple values
   * { state: ['California', 'Nevada', 'Washington'] }
   */
  params: {
    [key: string]: string | number | string[] | number[];
  };
};
 
/**
 * UI and behavior configuration for the embedded dashboard.
 */
export type UIConfig = {
  /**
   * Enables self-service editing and lens creation by the end user. Legacy field. Use config.allowEdit instead.
   */
  allowEdit?: boolean;
  /**
   * Enables advanced mode features in the dashboard.
   * Defaults to true unless explicitly disabled.
   */
  showAdvancedMode?: boolean;
  /**
   * Enables the AI-powered dashboard assistant.
   * Defaults to true unless explicitly disabled.
   */
  showDashboardAssistant?: boolean;
};
 
/**
 * Main payload for generating a secure access token for a dashboard.
 */
export type DashboardTokenRequest = {
  /**
   * Identifier of the dashboard to be accessed.
   */
  dashboardId: string;
  /**
   * Secret key used for validating access to the dashboard.
   */
  dashboardSecret: string;
  /**
   * Token expiry duration in seconds.
   */
  tokenExpiry?: number;
  /**
   * Unique identifier of the tenant.
   */
  tenantId?: string;
  /**
   * Unique identifier of the tenant user accessing the dashboard.
   */
  endUserId?: string;
  /**
   * Email of the tenant user (for personalization, audit, or identification).
   */
  endUserEmail?: string;
  /**
   * Unique identifier of the org user accessing the dashboard.
   */
  orgUserId?: string;
  /**
   * Email of the org user (for personalization, audit, or identification).
   */
  orgUserEmail?: string;
  /**
   * Display name of the end user.
   */
  displayName?: string;
  /**
   * Enables self-service editing and lens creation by the end user.
   */
  allowEdit?: boolean;
  /**
   * Connection-level security policies.
   * Can be a single policy or an array of policies.
   */
  cls?: TokenSecurityPolicy[] | TokenSecurityPolicy;
  /**
   * Row-level security policies.
   * Can be a single policy or an array of policies.
   */
  rcls?: TokenSecurityPolicy[] | TokenSecurityPolicy;
  /**
   * Parameter overrides and preferences (e.g., formatting).
   */
  params?: TokenParams;
  /**
   * UI behavior and feature flags.
   */
  config?: UIConfig;
};
 
/**
 * Main payload for generating a project-level access token.
 */
export type ProjectTokenRequest = {
  /**
   * Must be 'project' for project tokens.
   */
  type: 'project';
  /**
   * Unique identifier of the project.
   */
  projectId: string;
  /**
   * Secret key used for validating access to the project.
   */
  projectSecret: string;
  /**
   * Token expiry duration in seconds.
   */
  tokenExpiry?: number;
  /**
   * Unique identifier of the tenant.
   */
  tenantId?: string;
  /**
   * Name of the tenant (alternative to tenantId).
   */
  tenantName?: string;
  /**
   * Unique identifier of the end user.
   */
  endUserId?: string;
  /**
   * Email of the end user.
   */
  endUserEmail?: string;
  /**
   * Unique identifier of the organization user.
   */
  orgUserId?: string;
  /**
   * Display name for the user.
   */
  displayName?: string;
  /**
   * Automatically create user if they don't exist.
   */
  autoCreateEndUser?: boolean;
  /**
   * Role for auto-created users ('VIEWER' or 'POWER_USER').
   */
  role?: 'VIEWER' | 'POWER_USER';
  /**
   * Initial dashboard to load.
   */
  initialDashboardId?: string;
  /**
   * Array of semantic domain IDs or names to restrict access.
   */
  allowedSemanticDomains?: string[];
  /**
   * Enables self-service editing.
   */
  allowEdit?: boolean;
  /**
   * Connection-level security policies.
   */
  cls?: TokenSecurityPolicy[] | TokenSecurityPolicy;
  /**
   * Row-level security policies.
   */
  rcls?: TokenSecurityPolicy[] | TokenSecurityPolicy;
  /**
   * Schema-level security policy.
   */
  sls?: string;
  /**
   * Parameter overrides and preferences.
   */
  params?: TokenParams;
  /**
   * UI behavior and feature flags.
   */
  config?: UIConfig;
};
 
/**
 * Combined token request type.
 */
export type TokenRequest = DashboardTokenRequest | ProjectTokenRequest;

Security Policies & Params

Connection-Level Security (CLS)

{
  "cls": {
    "name": "store_sales_primary",
    "params": {
      "tenant": "tenant_abc_123"
    }
  }
}

Row-Level Security (RCLS)

{
  "rcls": {
    "name": "region_filter",
    "params": {
      "state": ["California", "Nevada", "Washington"]
    }
  }
}

Multiple Policies

{
  "cls": [
    {
      "name": "store_sales_primary",
      "params": { "tenant": "tenant_abc_123" }
    },
    {
      "name": "data_center_filter",
      "params": { "region": "us-west" }
    }
  ],
  "rcls": [
    {
      "name": "region_filter",
      "params": { "state": ["California", "Nevada"] }
    },
    {
      "name": "department_filter",
      "params": { "department": ["Sales", "Marketing"] }
    }
  ]
}

Error Handling & Best Practices

Common token API errors:

Error CodeDescriptionSolution
INVALID_CREDENTIALSDashboard ID or secret is incorrectVerify credentials
TOKEN_EXPIREDToken has expiredGenerate new token
INVALID_TOKENToken format is invalidCheck token format
INVALID_SECURITY_POLICYSecurity policy configuration is invalidReview policy syntax
RATE_LIMIT_EXCEEDEDToo many token requestsWait before retrying

Best Practices

1. Token Lifecycle Management

Implement proper token lifecycle management:

class TokenManager {
  constructor() {
    this.token = null;
    this.expiry = null;
  }
 
  async getValidToken() {
    if (this.isTokenValid()) {
      return this.token;
    }
 
    return await this.generateNewToken();
  }
 
  isTokenValid() {
    return this.token && this.expiry && Date.now() < this.expiry;
  }
 
  async generateNewToken() {
    const response = await fetch('https://semaphor.cloud/api/v1/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        dashboardId: process.env.SEMAPHOR_DASHBOARD_ID,
        dashboardSecret: process.env.SEMAPHOR_DASHBOARD_SECRET,
        tokenExpiry: 3600,
      }),
    });
 
    const { data } = await response.json();
    this.token = data.accessToken;
    this.expiry = new Date(data.expiresAt).getTime();
 
    return this.token;
  }
 
  async invalidateToken() {
    if (this.token) {
      await fetch('https://semaphor.cloud/api/v1/invalidate-token', {
        method: 'POST',
        headers: { 'Authorization': `Bearer ${this.token}` },
      });
      this.token = null;
      this.expiry = null;
    }
  }
}

2. Multi-Tenant Token Generation

Handle multiple tenants with different configurations:

async function generateTenantToken(tenantConfig, userInfo) {
  const tokenRequest = {
    dashboardId: tenantConfig.dashboardId,
    dashboardSecret: tenantConfig.dashboardSecret,
    tenantId: tenantConfig.tenantId,
    endUserId: userInfo.id,
    endUserEmail: userInfo.email,
    allowEdit: tenantConfig.allowEdit,
    tokenExpiry: 7200,
  };
 
  // Add security policies if configured
  if (tenantConfig.securityPolicies) {
    tokenRequest.cls = tenantConfig.securityPolicies.cls;
    tokenRequest.rcls = tenantConfig.securityPolicies.rcls;
  }
 
  // Add user preferences
  if (userInfo.preferences) {
    tokenRequest.params = {
      currencyFormat: userInfo.preferences.currency,
      timezone: userInfo.preferences.timezone,
      locale: userInfo.preferences.locale,
    };
  }
 
  const response = await fetch('https://semaphor.cloud/api/v1/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(tokenRequest),
  });
 
  return response.json();
}

3. Token Validation

Regularly validate tokens to ensure they're still active:

async function validateTokenPeriodically(token, interval = 300000) {
  // 5 minutes
  setInterval(async () => {
    try {
      const response = await fetch(
        'https://semaphor.cloud/api/v1/token/validate',
        {
          headers: { 'Authorization': `Bearer ${token}` },
        }
      );
 
      const { data } = await response.json();
 
      if (!data.valid) {
        console.warn('Token is no longer valid');
        // Handle token invalidation
      }
    } catch (error) {
      console.error('Token validation failed:', error);
    }
  }, interval);
}

4. Error Recovery

Implement robust error handling and recovery:

async function generateTokenWithRetry(maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch('https://semaphor.cloud/api/v1/token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          dashboardId: process.env.SEMAPHOR_DASHBOARD_ID,
          dashboardSecret: process.env.SEMAPHOR_DASHBOARD_SECRET,
        }),
      });
 
      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.error.message);
      }
 
      return await response.json();
    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }
 
      // Wait before retrying (exponential backoff)
      await new Promise((resolve) =>
        setTimeout(resolve, Math.pow(2, attempt) * 1000)
      );
    }
  }
}

Rate Limits

  • Token Generation: 100 requests per minute per dashboard
  • Token Validation: 1000 requests per minute per token
  • Token Invalidation: 100 requests per minute per token