Embedded analytics — the half that ships today

Embedded analytics without a warehouse: customers see their own numbers, in your app

Every product team eventually gets the same request: customers want their numbers inside the product — their usage, their cohort, their region — not a login to somebody else's analytics portal. This page is that story: the expensive way it usually goes, and the shorter one — an agent authors the pipeline against your databases, a person releases it, and a versioned endpoint serves each customer their own rows. The API half ships today; the dashboard half is planned, and the page keeps them labelled.

The story this page is built on

The dashboard was never the expensive part

A company buys the analytics platform. A consultancy builds beautiful dashboards over a dataset that took months of preparation flows, a warehouse nobody wanted, and a nightly job that breaks on the first schema change. The bill — licence plus engagement — lands in the six figures. Then the product team asks for the same numbers inside the application customers actually log into, and the answer comes back: the contract does not allow embedding on those terms.

So an engineer rebuilds the dashboard by hand — screen after screen, in the front-end framework of the month — against an API that does not exist yet. Because "expose the dataset as an API" is a second project: a service to write and deploy, an auth story to design, pagination, on-call. Three projects, then: the dataset, the API, the embedding. The dashboard — the thing everyone assumed was the point — was never the expensive part.

datapipelines collapses the first two. The dataset is a versioned pipeline across your databases, drafted by an agent that reads your real schemas and released by a person. The API is one step after the release: a published endpoint under /api/x, parameters bound from the request, keys scoped to the path. What remains is the embedding — the dashboard your agent creates is a planned roadmap item. It would consume the same published endpoint contract instead of needing a hand-built API.

Ships today

From data to an API in a day — the shipped half

The mechanic: release the pipeline, publish it under a path, bind a key. A GET runs the released version with the query string bound to its declared parameters and returns typed JSON — schema, rows, cursor. The response carries Cache-Control: no-store on purpose: each call is a fresh execution, so a customer never sees another customer's cached answer.

Per-viewer filtering rides on parameter binding: the customer id your backend already authenticates becomes a declared parameter of the released pipeline, and the released SQL filters on it as a bound scalar — never pasted SQL. One pipeline serves every tenant; the request decides whose rows come back.

The key you give each customer integration is bound to a path prefix: it reaches that endpoint subtree and nothing else — not your product API, not another tenant's endpoints — and revoking it is a checkbox that takes effect in about a minute.

The authoring half is where the agent earns the headline. It drafts the pipeline against your real schemas, iterates until the run succeeds, and — when your analyst changes the definition of "active customer" next quarter — redoes the walk and produces the diff. The artifact under review is always a JSON document of SQL nodes a person on your team can read end to end; the agent accelerates the drafting, and the release keeps the judgment where it belongs.

The whole integration

What the call from your app looks like

GET /api/x/usage/summary?customer_id=8841&month=2026-08
DP-API-Key: dpk_<id>.<secret>

200 OK
{
  "schema":  [ { "name": "metric", "type": "STRING" },
               { "name": "value", "type": "BIGDECIMAL", "precision": 18, "scale": 2 } ],
  "rows":    [ ["seats_active", "142.00"], ["storage_gb", "812.40"] ],
  "row_count": 2, "total_rows": 2, "has_more": false
}

That is the entire server-side integration: a URL, a key, a typed JSON body your front-end renders. No service to write, no deployment to schedule, no gateway to configure — and because the response is no-store, every render is a fresh execution of the version you released. When the definition of "active seats" changes, the fix is a new draft, a review, a release: the endpoint's contract stays put and the version pointer moves, which is the change your customers actually feel — none.

Planned

The planned dashboards

The roadmap's words: "dashboards, created by your agent, you embed in your own product, fed by released pipelines and APIs, filtered per viewer." Created by the same agent that authors the pipeline; embedded behind your login, not hosted in a portal; fed by the released pipelines that already serve the endpoints; filtered so each viewer sees their rows. Named as roadmap here and on the roadmap page — not described as if it shipped, because it has not.

For planning purposes, the useful reading is architectural: the dashboard is a consumer of the same released pipeline and endpoint you can publish today — a rendering over the contract, not a new one. Teams that publish the endpoint now are not building throwaway work; they are building the exact surface the dashboard will render from.

For the reviewer

Security your reviewer will accept

No credential reaches the customer path

Database credentials are encrypted at rest on the server and never returned — not through a tool, not through the API. The customer integration holds a path-scoped key to the endpoint, nothing more.

Keys are scoped, small and revocable

An endpoint key reaches one path subtree and expires when you say; revoking it takes about a minute, and it never unlocks the product's own API. The surface you present a customer is exactly the surface you published.

A person controls what customers see

The agent iterates drafts; releasing is a human verb and a release is immutable. The numbers customers see are a version your team named — and a change is a new release, not a surprise.

Every call is audited, values are not

Each endpoint call is audited with the key, the endpoint and the execution — never the SQL text, the rows or the parameter values, which are your customers' data and stay out of the log.

Bounded by construction

GET only — every other method is refused; a released pipeline cannot carry a write-shaped node, and the check re-runs at serve time; timeouts and concurrency ceilings bound every run. The full trust model.

What it looks like

The two screens the flow runs through

The pipeline the agent drafted against your databases — the graph, the SQL, the result dock with a real run's rows.
SOURCES STAGING · H2 IN MEMORY ANSWER sample-lake · Parquet on S3 hvfhv_zone_day · 377k rows sample-reference · SQLite zones · 265 rows calculator · trailing_periods anchor 2025-01-01 → Q4 2024 stage_company_zone 523 rows · 482 ms stage_zones 263 rows · 204 ms answer 6 rows · caller DRAFT demo/top_company_by_borough · v1 · awaiting a human release · then GET /api/x/demo/…
The API section: the published endpoint, the version it serves, and the customer key bound to its path.
workspace: demo · API
endpointserveskey reach
GET /api/x/demo/top-company-by-borough?anchor_date=2025-01-01 demo/top_company_by_borough · v1 /demo/**

from the demo workspace — released by a human, served as this version forever

Asked before

Embedded analytics questions, answered plainly

Is this embedded analytics?

The API half of embedded analytics ships today: a released pipeline becomes a versioned GET endpoint your application calls, with per-viewer values bound as parameters. The dashboard half — created by your agent, embedded in your product — is the planned roadmap item. Publishing is docs/rest-api.md §19.

How does each customer see only their own rows?

Every viewer's request carries a value — from the query string or the path — that binds to a declared parameter of the released pipeline, and the released SQL filters on it. The value is a bound scalar on a prepared statement, never pasted SQL. Binding is docs/templates.md §4.5.

What can a customer-facing key reach?

One path subtree and nothing else: endpoint keys are bound to a path prefix under /api/x, cannot call your product's own API or another tenant's endpoints, and are revoked in about a minute. Key kinds and bindings are docs/auth.md §7.7.

Who controls what customers see?

A person on your team. The agent iterates drafts, but releasing is a human verb and a release is immutable — so the numbers your customers see are a version you can name, restore and audit. The lifecycle is docs/versioning.md §3.

What does the audit log record about a customer request?

Every call is audited with the key that made it, the endpoint and the execution — and never the SQL text, the row data or the parameter values, which are your customers' data. The audit log is docs/auth.md §10.

Serve one customer number this week

Point the demo stack at one real query your customers ask for, release it, and call the endpoint from your staging front-end. That afternoon is the whole pitch — the rest is the rest of your customers.