> ## Documentation Index
> Fetch the complete documentation index at: https://docs.papermap.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Basic Embedding

## 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.

<Info>
  Although this guide demonstrates iframe embedding, the same backend pattern of
  generating a signed token server-side also applies when embedding Papermap
  through `@papermap/papermap`. If you are embedding `PaperChat`, `PaperCard`,
  or `PaperBoard`, see [React
  Components](/documentation/embedding/react-components).
</Info>

<Warning>
  **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.
</Warning>

<Info>
  To generate tokens on your backend, see the [Backend: Generating Embed
  Tokens](#backend%3A-generating-embed-tokens) section below for implementation
  examples in Python, TypeScript, PHP, and Go.
</Info>

<Info>
  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`
</Info>

<Info>
  The same `dashboardId` can also be used by the React components package when
  rendering `PaperChat`, `PaperCard`, and `PaperBoard`.
</Info>

### React Example

```tsx theme={null}
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
    />
  );
}
```

<Info>
  **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
</Info>

## Frontend Token Generation (Testing Only)

<Warning>
  **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.
</Warning>

For quick testing or prototyping, you can generate tokens directly in the frontend:

```javascript theme={null}
// 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();
```

```html theme={null}
<!-- Your HTML -->
<div class="dashboard-container">
  <iframe
    id="dashboard-iframe"
    width="100%"
    height="600"
    frameborder="0"
    allowfullscreen
  >
  </iframe>
</div>
```

<Info>
  **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
</Info>

## Simple iframe Embedding

For testing purposes, you can also use a static iframe with a pre-generated token:

```html theme={null}
<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:

```html theme={null}
<!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:

```css theme={null}
.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

<CodeGroup>
  ```python Python theme={null}
  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
  ```

  ```typescript TypeScript/Node.js theme={null}
  import crypto from "crypto";

  function generateEmbedToken(
    workspaceId: string,
    dashboardId: string,
    apiKeyId: string,
    secretKey: string
  ): string {
    // Token valid for 1 hour
    const validUntil = Math.floor(Date.now() / 1000) + 3600;

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

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

    // Encode token
    return Buffer.from(JSON.stringify(tokenData)).toString("base64url");
  }
  ```

  ```php PHP theme={null}
  <?php
  function generateEmbedToken($workspaceId, $dashboardId, $apiKeyId, $secretKey) {
      // Token valid for 1 hour
      $validUntil = time() + 3600;

      // Create HMAC signature
      $payload = $workspaceId . $validUntil;
      $signature = hash_hmac('sha256', $payload, $secretKey);

      // Create token data
      $tokenData = [
          'api_key_id' => $apiKeyId,
          'workspace_id' => $workspaceId,
          'dashboard_id' => $dashboardId,
          'valid_until' => $validUntil,
          'signature' => $signature
      ];

      // Encode token
      $token = base64_encode(json_encode($tokenData));
      return str_replace(['+', '/', '='], ['-', '_', ''], $token);
  }
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "crypto/hmac"
      "crypto/sha256"
      "encoding/base64"
      "encoding/hex"
      "encoding/json"
      "time"
  )

  func generateEmbedToken(workspaceID, dashboardID, apiKeyID, secretKey string) string {
      // Token valid for 1 hour
      validUntil := time.Now().Unix() + 3600

      // Create HMAC signature
      payload := workspaceID + fmt.Sprintf("%d", validUntil)
      h := hmac.New(sha256.New, []byte(secretKey))
      h.Write([]byte(payload))
      signature := hex.EncodeToString(h.Sum(nil))

      // Create token data
      tokenData := map[string]interface{}{
          "api_key_id":   apiKeyID,
          "workspace_id": workspaceID,
          "dashboard_id": dashboardID,
          "valid_until":  validUntil,
          "signature":    signature,
      }

      // Encode token
      jsonData, _ := json.Marshal(tokenData)
      encoded := base64.URLEncoding.EncodeToString(jsonData)
      return strings.TrimRight(encoded, "=")
  }
  ```
</CodeGroup>

### Usage Example

<CodeGroup>
  ```python Python (FastAPI) theme={null}
  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}
  ```

  ```typescript TypeScript (Express) theme={null}
  import express from "express";

  const router = express.Router();

  router.get("/embed-token", (req, res) => {
    const { workspaceId, dashboardId } = req.query;

    const token = generateEmbedToken(
      workspaceId as string,
      dashboardId as string,
      "your-api-key-id",
      "your-secret-key"
    );

    res.json({ token });
  });
  ```
</CodeGroup>

<Warning>
  **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.
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="Configuration" icon="gear" href="/documentation/embedding/configuration">
    Learn about advanced configuration options and customization parameters for your embedded dashboards
  </Card>

  <Card title="Parameter Usecases" icon="list-check" href="/documentation/embedding/parameter-usecases">
    Explore common parameter combinations and use cases for different embedding scenarios
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/documentation/embedding/troubleshooting">
    Common issues and solutions for embedding Papermap dashboards in your application
  </Card>
</CardGroup>
