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: Management actions (CRUD on dashboards, visuals, etc.)
  • Required fields:
    • type (must be 'project')
    • projectId
    • projectSecret
  • Optional fields: tokenExpiry
  • Default expiry: 30 minutes (1800 seconds)
  • Note: Project tokens do not support params, config, cls, or rcls.

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)
tenantIdstringNodashboardTenant identifier (self-service only)
endUserIdstringNodashboardEnd user identifier (self-service only)
endUserEmailstringNodashboardEnd user email (self-service only)
orgUserIdstringNodashboardOrg user identifier (advanced)
orgUserEmailstringNodashboardOrg user email (advanced)
displayNamestringNodashboardDisplay name (self-service only)
allowEditbooleanNodashboardEnable editing (self-service only)
clsobject/arrayNodashboardConnection-level security policies
rclsobject/arrayNodashboardRow-level security policies
paramsobjectNodashboardUser parameters and preferences
configobjectNodashboardUI 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 Dashboard Token (Self-Service)

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",
    "tenantId": "tenant_123",
    "endUserId": "user_123",
    "endUserEmail": "user@example.com",
    "displayName": "Jane Doe",
    "allowEdit": true,
    "params": {
      "currencyFormat": {
        "locale": "en-US",
        "currency": "USD"
      }
    },
    "config": {
      "showAdvancedMode": true,
      "showDashboardAssistant": true
    }
  }'

3. Generate Project Token (Management)

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

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 or project.
 */
export type TokenRequest = {
  /**
   * Type of the token request. Default is 'dashboard'.
   */
  type?: 'dashboard' | 'project';
  /**
   * Identifier of the dashboard to be accessed.
   */
  dashboardId?: string;
  /**
   * Secret key used for validating access to the dashboard.
   */
  dashboardSecret?: string;
  /**
   * 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;
  /**
   * 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;
};

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