Skip to main content

Secure Embedding with JavaScript

The recommended way to embed a dashboard is to fetch a secure token from your backend and dynamically create the iframe. This keeps your API credentials safe.
Security Best Practice: Never expose your API credentials (API Key ID and Secret Key) in your frontend code. Always generate tokens on your backend server and fetch them from your frontend.
To generate tokens on your backend, see the Backend: Generating Embed Tokens section below for implementation examples in Python, TypeScript, PHP, and Go.
To get the dashboardId for your dashboard, navigate to your Papermap workspace and click the top-left dropdown menu to view all dashboards. Select the “Show All Dashboards” section to view information about all dashboards including the dashboardId

React Example

import { useEffect, useState } from "react";

function DashboardEmbed({ workspaceId, dashboardId }) {
  const [iframeUrl, setIframeUrl] = useState("");
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchToken() {
      try {
        const response = await fetch(
          `/api/embed-token?workspaceId=${workspaceId}&dashboardId=${dashboardId}`
        );
        const data = await response.json();

        const url = `https://papermap.ai/embedded/dashboard?embedded-token=${data.token}&workspace-id=${workspaceId}&dashboard-id=${dashboardId}`;
        setIframeUrl(url);
        setLoading(false);
      } catch (err) {
        setError(err.message);
        setLoading(false);
      }
    }

    fetchToken();
  }, [workspaceId, dashboardId]);

  if (loading) return <div>Loading dashboard...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <iframe
      src={iframeUrl}
      width="100%"
      height="600"
      frameBorder="0"
      allowFullScreen
    />
  );
}
Why This Approach is Secure: - Your API credentials stay on the server, never exposed to the browser - Tokens are time-limited. Each user gets a fresh token when they load the page - You can add authentication checks before generating tokens

Frontend Token Generation (Testing Only)

For Development/Testing Only: This approach exposes your API credentials in the frontend code. Only use this for local development or testing. Never use this in production applications.
For quick testing or prototyping, you can generate tokens directly in the frontend:
// Generate token directly in frontend (TESTING ONLY)
import crypto from 'crypto';

export function generateTokenFrontend(
  workspaceId: string,
  dashboardId: string,
  apiKeyId: string,
  secretKey: string,
): string {
  const oneHour = 3600;
  const validUntil = Math.floor(Date.now() / 1000) + oneHour;

  const payload = `${workspaceId}${validUntil}`;
  const signature = crypto
    .createHmac('sha256', secretKey)
    .update(payload)
    .digest('hex');

  const tokenData = {
    api_key_id: apiKeyId,
    workspace_id: workspaceId,
    dashboard_id: dashboardId,
    valid_until: validUntil,
    signature: signature,
  };

  return Buffer.from(JSON.stringify(tokenData)).toString('base64url');
}

// Usage
async function embedDashboardWithFrontendToken() {
  const workspaceId = "YOUR_WORKSPACE_ID";
  const dashboardId = "YOUR_DASHBOARD_ID";
  const apiKeyId = "YOUR_API_KEY_ID"; // ⚠️ EXPOSED IN FRONTEND
  const secretKey = "YOUR_SECRET_KEY"; // ⚠️ EXPOSED IN FRONTEND

  const token = await generateTokenFrontend(
    workspaceId,
    dashboardId,
    apiKeyId,
    secretKey
  );

  const iframeUrl = `https://papermap.ai/embedded/dashboard?embedded-token=${token}&workspace-id=${workspaceId}&dashboard-id=${dashboardId}`;

  const iframe = document.getElementById("dashboard-iframe");
  iframe.src = iframeUrl;
}

// Call when page loads
embedDashboardWithFrontendToken();
<!-- Your HTML -->
<div class="dashboard-container">
  <iframe
    id="dashboard-iframe"
    width="100%"
    height="600"
    frameborder="0"
    allowfullscreen
  >
  </iframe>
</div>
When to Use Frontend Generation: - ✅ Local development and testing - ✅ Internal tools with restricted access - ✅ Quick prototypes and demos - ❌ Never in production (credentials exposed to users) - ❌ Public-facing applications - ❌ Applications requiring authentication

Simple iframe Embedding

For testing purposes, you can also use a static iframe with a pre-generated token:
<iframe
  src="https://papermap.ai/embedded/dashboard?embedded-token=YOUR_EMBEDDED_TOKEN&workspace-id=YOUR_WORKSPACE_ID"
  width="100%"
  height="600"
  frameborder="0"
>
</iframe>

Complete Example

Here’s a full HTML example using kebab-case naming:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Dashboard</title>
    <style>
      .dashboard-container {
        width: 100%;
        height: 600px;
        border: 1px solid #ddd;
        border-radius: 8px;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <h1>Sales Dashboard</h1>
    <div class="dashboard-container">
      <iframe
        src="https://papermap.ai/embedded/dashboard?embedded-token=YOUR_EMBEDDED_TOKEN&workspace-id=dIwIBPYdGmWS&dashboard-id=specific-dashboard-id&show-toolbar=true&show-header=false&show-nav=false&show-dashboard-select=true&chat-composer=false&show-screenshot=true&theme=dark"
        width="100%"
        height="100%"
        frameborder="0"
        allowfullscreen
      >
      </iframe>
    </div>
  </body>
</html>

Responsive Design

CSS Styling

Make your embedded dashboards responsive:
.dashboard-container {
  width: 100%;
  height: 600px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

@media (max-width: 768px) {
  .dashboard-container {
    height: 400px;
  }
}

@media (max-width: 480px) {
  .dashboard-container {
    height: 300px;
    border-radius: 4px;
  }
}

Backend: Generating Embed Tokens

To securely embed dashboards, you need to generate a signed token from your backend. Here’s how to create the token:

Token Generation

import hmac
import hashlib
import json
import base64
import time

def generate_embed_token(workspace_id: str, dashboard_id: str, api_key_id: str, secret_key: str):
    # Token valid for 1 hour
    valid_until = int(time.time()) + 3600

    # Create HMAC signature
    payload = f"{workspace_id}{valid_until}"
    signature = hmac.new(
        secret_key.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    # Create token data
    token_data = {
        "api_key_id": api_key_id,
        "workspace_id": workspace_id,
        "dashboard_id": dashboard_id,
        "valid_until": valid_until,
        "signature": signature
    }

    # Encode token
    token_json = json.dumps(token_data)
    encoded_token = base64.urlsafe_b64encode(token_json.encode()).decode()

    return encoded_token

Usage Example

from fastapi import APIRouter

router = APIRouter()

@router.get("/embed-token")
async def get_embed_token(workspace_id: str, dashboard_id: str):
    token = generate_embed_token(
        workspace_id=workspace_id,
        dashboard_id=dashboard_id,
        api_key_id="your-api-key-id",
        secret_key="your-secret-key"
    )
    return {"token": token}
Security Note: Always store your API credentials in environment variables, never hardcode them in your source code. The token is valid for how long you set it for but it is recommended to be 1 hour for security sake.

Next Steps