Skip to main content
Zero Data Retention lets you render charts without persisting your data on Tako servers. You send the visualization payload at render time, either via our lightweight widget or by posting directly to the embed endpoint.

How it works

  • You generate a card with your data via Tako’s Visualize API as usual and receive a card_id and a visualization_data object.
  • To render: you have two options:
    • Option A: use the TakoZDREmbed widget to create an iframe and POST the visualization_data for you, or
    • Option B: POST the visualization_data directly to the embed URL with an HTML <form> that targets an <iframe>.
  • Data is not stored by Tako; it exists only in your request and the user’s browser.

Option A: Use the ZDR Embed widget

  1. Include the widget in your app (serve the file from your app or a CDN):
<script src="zdr_embed_widget.js"></script>
  1. Create a container where the chart will render:
<div id="my-zdr-chart"></div>
  1. Render using your card_id and visualization_data:
const chart = window.TakoZDREmbed.render({
  containerId: "my-zdr-chart",
  takoUrl: "https://trytako.com", // or your self-hosted Tako URL
  cardId: "YOUR_CARD_ID",
  visualizationData: {
    viz_config: { /* chart config */ },
    data: { /* chart data */ }
  },
  width: "100%",
  height: "600px"
});
/**
 * Tako ZDR Chart Embed Widget
 *
 * A standalone JavaScript snippet for embedding Tako charts with Zero Data Retention (ZDR).
 * This widget allows you to embed charts without storing sensitive data on Tako's servers.
 *
 * Usage:
 *
 * <div id="my-chart"></div>
 * <script>
 *   TakoZDREmbed.render({
 *     containerId: 'my-chart',
 *     takoUrl: 'https://trytako.com',
 *     cardId: 'ABC123xyz',
 *     visualizationData: {
 *       viz_config: { ... },
 *       data: { ... }
 *     }
 *   });
 * </script>
 */

(function (window) {
  "use strict";

  const TakoZDREmbed = {
    /**
     * Render a ZDR chart embed
     *
     * @param {Object} options - Configuration options
     * @param {string} options.containerId - DOM element ID where the chart will be embedded
     * @param {string} options.takoUrl - Base URL of your Tako instance (e.g., 'https://trytako.com')
     * @param {string} options.cardId - The chart card ID returned from the visualize API
     * @param {Object} options.visualizationData - The visualization_data object containing viz_config and data
     * @param {Object} options.visualizationData.viz_config - The chart configuration
     * @param {Object} options.visualizationData.data - The chart data
     * @param {number} [options.width] - Width of the iframe (default: '100%')
     * @param {number} [options.height] - Height of the iframe (default: '600px')
     * @param {Function} [options.onLoad] - Callback function called when chart loads
     * @param {Function} [options.onError] - Callback function called if there's an error
     *
     * @returns {Object} - Object with iframe element and destroy method
     */
    render: function (options) {
      // Validate required options
      if (!options.containerId) {
        throw new Error("TakoZDREmbed: containerId is required");
      }
      if (!options.takoUrl) {
        throw new Error("TakoZDREmbed: takoUrl is required");
      }
      if (!options.cardId) {
        throw new Error("TakoZDREmbed: cardId is required");
      }
      if (!options.visualizationData) {
        throw new Error("TakoZDREmbed: visualizationData is required");
      }
      if (!options.visualizationData.viz_config) {
        throw new Error(
          "TakoZDREmbed: visualizationData.viz_config is required"
        );
      }
      if (!options.visualizationData.data) {
        throw new Error("TakoZDREmbed: visualizationData.data is required");
      }

      // Get container element
      const container = document.getElementById(options.containerId);
      if (!container) {
        throw new Error(
          "TakoZDREmbed: Container element not found: " + options.containerId
        );
      }

      // Set default dimensions
      const width = options.width || "100%";
      const height = options.height || "600px";

      // Create embed URL
      const embedUrl =
        options.takoUrl.replace(/\/$/, "") + "/embed/" + options.cardId + "/";

      // Create iframe
      const iframeName = "tako-zdr-embed-" + options.cardId + "-" + Date.now();
      const iframe = document.createElement("iframe");
      iframe.name = iframeName;
      iframe.id = iframeName;
      iframe.style.width = width;
      iframe.style.height = height;
      iframe.style.border = "none";
      iframe.setAttribute("scrolling", "no");
      iframe.setAttribute("frameborder", "0");

      // Add load handler
      if (options.onLoad) {
        iframe.addEventListener("load", function () {
          options.onLoad(iframe);
        });
      }

      // Add iframe to container
      container.innerHTML = "";
      container.appendChild(iframe);

      // Create and submit form to iframe
      try {
        const form = document.createElement("form");
        form.method = "POST";
        form.action = embedUrl;
        form.target = iframeName;
        form.style.display = "none";

        // Create hidden input with visualization_data
        const input = document.createElement("input");
        input.type = "hidden";
        input.name = "visualization_data";
        input.value = JSON.stringify(options.visualizationData);

        form.appendChild(input);
        document.body.appendChild(form);

        // Submit form
        form.submit();

        // Clean up form after a short delay
        setTimeout(function () {
          if (form.parentNode) {
            document.body.removeChild(form);
          }
        }, 100);
      } catch (error) {
        if (options.onError) {
          options.onError(error);
        } else {
          console.error("TakoZDREmbed: Error creating embed:", error);
        }
      }

      // Return object with iframe reference and destroy method
      return {
        iframe: iframe,
        destroy: function () {
          if (iframe.parentNode) {
            iframe.parentNode.removeChild(iframe);
          }
        },
        update: function (newVisualizationData) {
          // Re-render with new data
          TakoZDREmbed.render({
            containerId: options.containerId,
            takoUrl: options.takoUrl,
            cardId: options.cardId,
            visualizationData: newVisualizationData,
            width: width,
            height: height,
            onLoad: options.onLoad,
            onError: options.onError,
          });
        },
      };
    },

    /**
     * Render multiple charts at once
     *
     * @param {Array} charts - Array of chart configuration objects
     * @returns {Array} - Array of chart instances
     */
    renderMultiple: function (charts) {
      return charts.map(function (chartConfig) {
        return TakoZDREmbed.render(chartConfig);
      });
    },
  };

  // Expose to global scope
  window.TakoZDREmbed = TakoZDREmbed;
})(window);

Option B: Direct POST to the embed (no widget)

Use this minimal helper to POST visualization_data into the Tako embed iframe.
// ============================================
// Tako Embed - Minimal Integration Snippet
// ============================================

// CONFIGURATION
const TAKO_EMBED_ID = 'YOUR_EMBED_ID';  // Replace with your Tako embed ID
const TAKO_URL = 'https://staging.trytako.com';  // Or production URL

// Your visualization data
const VISUALIZATION_DATA = {
    viz_config: {
        // Your viz_config object here
    }
};

// ============================================

/**
 * Load Tako embed into an iframe with POST data
 * @param {string} iframeId - The ID of your iframe element
 * @param {string} embedId - Your Tako embed ID
 * @param {object} visualizationData - Your visualization data object
 * @param {string} takoUrl - Tako base URL (default: staging)
 */
function loadTakoEmbed(iframeId, embedId, visualizationData, takoUrl = 'https://staging.trytako.com') {
    const iframe = document.getElementById(iframeId);
    if (!iframe) {
        console.error(`Iframe with id "${iframeId}" not found`);
        return;
    }
    
    // Create form for POST submission
    const form = document.createElement('form');
    form.method = 'POST';
    form.action = `${takoUrl}/embed/${embedId}/`;
    form.target = iframe.name || iframeId;
    form.style.display = 'none';
    
    // Add visualization data as hidden input
    const input = document.createElement('input');
    input.type = 'hidden';
    input.name = 'visualization_data';
    input.value = JSON.stringify(visualizationData);
    form.appendChild(input);
    
    // Submit form to iframe
    document.body.appendChild(form);
    form.submit();
    document.body.removeChild(form);
}

// USAGE EXAMPLE:
// 1. Add an iframe to your page: <iframe id="tako-embed" name="tako-embed"></iframe>
// 2. Call: loadTakoEmbed('tako-embed', TAKO_EMBED_ID, VISUALIZATION_DATA, TAKO_URL);

End-to-end flow

  1. Call POST /v1/beta/visualize with your data and query.
  2. Extract card_id and visualization_data from the response.
  3. Render with either the widget or direct form-post approach shown above.
I