Developer Tooling & API

Passing Parameters to Embedded Dashboards Dynamically

In Metabase embedded analytics, parameters are values passed from your host application into an embedded dashboard to control what data is displayed....

📅
📖9 min read

Passing Parameters to Embedded Dashboards Dynamically

In Metabase embedded analytics, parameters are values passed from your host application into an embedded dashboard to control what data is displayed. There are two types: locked parameters, which are set server-side in the JWT token and cannot be changed by the user, and enabled parameters, which can be pre-populated but adjusted by the user via the dashboard's filter UI. Understanding which type to use — and how to pass values dynamically — is central to building a flexible, secure embedded analytics experience.

---

How Parameters Map to Dashboard Filters

Before passing parameters, you need to create filters in your Metabase dashboard. Each filter has a name (slug) that becomes the parameter key in your JWT or URL.

Creating Dashboard Filters

  • Open the dashboard in edit mode
  • Click Add a filter (the filter icon)
  • Choose a filter type:
  • - ID — for numeric foreign keys (e.g., organization_id, user_id) - Text — for string-based filters (e.g., region, plan_type) - Number — for numeric ranges - Date — for date range and relative date filters - Category — for dropdown selection from a list of values

  • Give the filter a name (this becomes the parameter slug)
  • Connect the filter to columns in your questions
  • The filter name, lowercased and with spaces replaced by underscores, is the parameter key. A filter named "Organization ID" has the parameter key organization_id.

    Connecting Filters to Questions

    Each filter must be connected to a column in each question on the dashboard. Metabase adds a WHERE clause for each connected question when the filter has a value.

    If a filter is not connected to a question, that question ignores the filter value.

    ---

    Locked Parameters (Server-Side, Immutable)

    Locked parameters are passed in the JWT payload. They are applied as immutable WHERE clauses — users cannot see or change them in the dashboard UI.

    Setting Parameters as Locked

    In the dashboard's embedding configuration (Sharing → Embed this dashboard), set each sensitive filter to Locked.

    Passing Locked Parameters in the JWT

    javascript
    

    const payload = { resource: { dashboard: 42 }, params: { // Keys must match the filter slugs exactly organization_id: user.organizationId, user_id: user.id, }, exp: Math.round(Date.now() / 1000) + 600, };

    Rules for Locked Parameters

  • All locked parameters must be provided. If a parameter is marked Locked in the embedding config but not included in the JWT payload, Metabase will return an error.
  • Values must match the expected type. An ID filter expects a number or numeric string. A text filter expects a string.
  • Null values are not the same as omitting the parameter. Passing organization_id: null may return an empty dashboard rather than an error.
  • Dynamic Locked Parameters

    Locked parameters change based on the currently authenticated user. The pattern is always the same: read the user's identity from your session/auth system, and include the relevant attributes in the JWT:

    javascript
    

    // Express.js route app.get("/api/analytics/embed-url", requireAuth, (req, res) => { const { user } = req;

    // Different dashboards may need different locked params const lockedParams = { organization_id: user.organizationId, };

    // Optionally add role-based params if (user.role === "team_member") { lockedParams.team_id = user.teamId; }

    const payload = { resource: { dashboard: parseInt(req.query.dashboardId) }, params: lockedParams, exp: Math.round(Date.now() / 1000) + 600, };

    const token = jwt.sign(payload, process.env.METABASE_SECRET_KEY); const embedUrl = ${process.env.METABASE_SITE_URL}/embed/dashboard/${token};

    res.json({ embedUrl }); });

    ---

    Enabled Parameters (User-Adjustable, Optional Defaults)

    Enabled parameters are visible to users in the dashboard filter UI. Users can change them freely. You can pre-populate them with default values from your application.

    Pre-Populating Enabled Parameters via URL Hash

    Enabled parameter defaults are passed as URL hash parameters (after the #), not in the JWT:

    javascript
    

    function buildEmbedUrl(token, enabledParams = {}) { const base = ${METABASE_SITE_URL}/embed/dashboard/${token};

    // UI options and enabled parameter defaults go in the URL hash const hashParts = [ "bordered=false", "titled=false", ];

    // Add enabled parameter defaults for (const [key, value] of Object.entries(enabledParams)) { if (value !== null && value !== undefined) { hashParts.push(${key}=${encodeURIComponent(value)}); } }

    return ${base}#${hashParts.join("&")}; }

    // Usage: pre-populate the date filter to "last 30 days" const embedUrl = buildEmbedUrl(token, { date_range: "past30days", region: "us-west", });

    Passing Date Parameters

    Metabase date filters accept several formats:

    FormatExampleMeaning
    Relative (past)past30daysLast 30 days
    Relative (this)thismonthThis calendar month
    Absolute range2024-01-01~2024-03-31Specific date range
    Single date2024-06-15On a specific date
    This/last unitthisquarter, lastweekCurrent or previous period

    javascript
    

    // Pre-set to the current quarter const embedUrl = buildEmbedUrl(token, { date_range: "thisquarter", });

    // Pre-set to last month const embedUrl = buildEmbedUrl(token, { date_range: "lastmonth", });

    // Pre-set to a specific range from your application state const embedUrl = buildEmbedUrl(token, { date_range: ${startDate}~${endDate}, // "2024-01-01~2024-06-30" });

    ---

    Passing Multiple Values for a Single Parameter

    Some filter types support multiple values. Pass them as arrays in the JWT (for locked params) or as comma-separated values in the URL hash (for enabled params):

    javascript
    

    // Locked: multiple organization IDs (if a user belongs to multiple orgs) const payload = { resource: { dashboard: 42 }, params: { organization_id: [101, 102, 103], // array of IDs }, exp: Math.round(Date.now() / 1000) + 600, };

    javascript
    

    // Enabled: multiple regions as URL hash param const embedUrl = buildEmbedUrl(token, { region: "us-west,us-east,eu-central", // comma-separated });

    ---

    Dynamic Parameters Based on Application Context

    A common pattern: the same dashboard is embedded on multiple pages in your application, each showing a different subset of data based on the current page context.

    Example: Order Detail Page

    You have an orders dashboard. When embedded on a customer's account page, it shows all their orders. When embedded on a specific order's page, it filters to that single order.

    javascript
    

    // Customer account page: filter to all orders for this customer const embedUrl = await getEmbedUrl({ dashboardId: ORDERS_DASHBOARD_ID, lockedParams: { organization_id: user.organizationId, }, enabledParams: { date_range: "past90days", // default, user can change }, });

    // Specific order page: filter to one order const embedUrl = await getEmbedUrl({ dashboardId: ORDERS_DASHBOARD_ID, lockedParams: { organization_id: user.organizationId, order_id: currentOrderId, // further restrict to this order }, enabledParams: {}, });

    Example: Admin vs. Customer View

    Admin users see an unfiltered dashboard. Customer users see data filtered to their organization.

    javascript
    

    function getAnalyticsDashboardUrl(user) { const lockedParams = {};

    // Admins see everything; customers see only their org if (user.role !== "admin") { lockedParams.organization_id = user.organizationId; }

    return buildSignedEmbedUrl(DASHBOARD_ID, lockedParams); }

    ---

    Hiding Parameters From the UI

    Sometimes you want a parameter to be filterable (enabled) but you don't want to show the filter widget in the dashboard UI — for example, when your host application provides its own filtering controls that update the iframe URL.

    Use the hide_parameters URL option:

    javascript
    

    const hashParts = [ "bordered=false", "titled=false", hide_parameters=date_range,region, // comma-separated parameter names date_range=past30days, // still pre-populated, just hidden ];

    This hides the filter widget from the dashboard but still applies the filter value.

    ---

    Updating Parameters Without Re-Loading the Iframe

    When your host application has its own filter controls that should update the embedded dashboard, you have two options:

    Option 1: Re-generate the URL (Recommended)

    Generate a new embed URL with the updated parameters and update the iframe src. This is the simplest approach and doesn't require any special Metabase configuration.

    react
    

    function EmbeddedDashboard({ filters }) { const [embedUrl, setEmbedUrl] = useState(null);

    useEffect(() => { // Re-fetch embed URL whenever filters change fetch(/api/analytics/embed-url? + new URLSearchParams({ dashboardId: DASHBOARD_ID, ...filters, })) .then(res => res.json()) .then(data => setEmbedUrl(data.embedUrl)); }, [JSON.stringify(filters)]); // re-run when filters change

    return embedUrl ? ( <iframe src={embedUrl} style={{ width: "100%", height: 600, border: "none" }} /> ) : null; }

    Option 2: postMessage to Update Filter Values

    Metabase supports receiving postMessage events from the parent frame to update enabled filter values without reloading the iframe:

    javascript
    

    // Send a message to the Metabase iframe function updateDashboardFilter(iframeEl, paramName, value) { iframeEl.contentWindow.postMessage( { metabase: { type: "filter", parameterSlug: paramName, value: value, }, }, process.env.NEXT_PUBLIC_METABASE_SITE_URL ); }

    // Usage const iframe = document.getElementById("analytics-iframe"); updateDashboardFilter(iframe, "date_range", "past7days");

    This only works for enabled (non-locked) parameters. You cannot update locked parameters via postMessage.

    ---

    Debugging Parameter Issues

    Dashboard shows data for all organizations despite locked parameter Verify the filter is connected to the organization_id column in each question on the dashboard. Open each question in edit mode and check the filter connection.

    "Parameter X not found" The parameter key in the JWT doesn't match any filter slug in the dashboard. Check the filter name in the dashboard and ensure the slug matches (lowercased, underscores for spaces).

    Date filter not working as expected Date filter format is case-sensitive. Use lowercase (past30days, not Past30Days). Test date formats in the Metabase UI before passing them programmatically.

    Enabled parameter default is not appearing URL hash parameters are decoded by the browser. Ensure special characters (spaces, slashes) are URL-encoded in your buildEmbedUrl function.

    ---

    Summary

    Dashboard parameters in Metabase come in two forms: locked (server-side JWT, immutable by users) and enabled (URL hash, user-adjustable with optional defaults). Locked parameters are the security mechanism for multi-tenant data isolation. Enabled parameters create interactive dashboards where users can explore different dimensions. The slugs for parameters must exactly match the filter names configured in the dashboard. Dynamic parameter passing — adjusting locked params per user, or updating enabled params from host application controls — follows the same pattern: generate a new signed URL with the updated params and update the iframe source.