Per-customer attributes
How embed JWT attributes flow into your dataset SQL.
Every embed token carries an attributes object — free-form JSON your backend sets at mint time. Values referenced from your dataset SQL as {{trend.attributes.<key>}} are resolved from that object at query time and bound as parameters.
Defining attributes
Attributes are a flat JSON object your backend sends when calling POST /api/embed/token:
{
"externalId": "acme-corp",
"attributes": {
"company_id": "acme-corp",
"region": "us-east",
"plan": "enterprise"
}
}
Keys must look like SQL identifiers (letters, digits, underscores; no leading digit). Values can be strings, numbers, booleans, arrays, or nested objects.
Referencing them in dataset SQL
Use the trend.attributes. prefix. trend parameter-binds these — they never get concatenated into your query text:
SELECT date_trunc('day', created_at) AS day,
sum(amount) AS revenue
FROM orders
WHERE company_id = {{trend.attributes.company_id}}
AND created_at >= now() - interval '30 days'
GROUP BY 1
ORDER BY 1
If a customer’s token supplies company_id: "acme-corp", the query runs as WHERE company_id = $1 with acme-corp as the bound parameter.
Optional clauses
Wrap conditional filters in [[...]] — the block gets dropped when its variables are missing. Useful for chart-level filters that come from dashboard controls:
SELECT * FROM events
WHERE company_id = {{trend.attributes.company_id}}
[[ AND event_type = {{event_type}} ]]
Warning
Don’t wrap {{trend.attributes.*}} references in [[...]]. If the token doesn’t supply the attribute, trend hard-rejects the query with a 400 — but if you wrap it, the block silently drops and the WHERE disappears, returning every customer’s rows. The extractor catches wrapped references at the API boundary and still rejects, but writing the SQL correctly (attribute filters outside [[...]], control filters inside) makes the intent obvious.
The safety guarantee
Every embed chart query goes through this check at the API boundary:
- Extract every
{{trend.attributes.<key>}}reference from the dataset SQL (including references inside[[...]]blocks). - Diff against the keys the token supplied.
- If any reference is missing an attribute in the token, return
400 Missing required attributes: <list>.
Then the same three-layer SQL safety as any internal chart query applies: SELECT-only validator, session-level default_transaction_read_only, and the DB-level read-only role you set up for the connection.
Observed customers
Every mint auto-populates a row in Embed → Customers with the external ID and a "last seen" timestamp. No manual registration required — the page is an audit view showing which external IDs you’ve issued tokens for.
What to read next
- iframe mode — how the two-step mint + iframe flow looks concretely.
- Web component mode — the
<trend-dashboard>custom element for SPAs.