No credential reaches the customer path
Database credentials are encrypted at rest on the server and never returned, not through a tool and not through the API. The customer integration holds a path-scoped key to the endpoint, nothing more.
Embedded analytics: the API and the dashboard
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. Customer facing analytics, in other words, and self-hosted analytics at that: the data never leaves your infrastructure to be served. This page is that story: the expensive way it usually goes, and the shorter one, where an agent authors the pipeline against your databases, a person releases it, and a versioned endpoint serves each customer their own rows. The API ships today, and so does a dashboard your product can embed; the page says what each one does, and where its limits are.
The story this page is built on
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 your own /api namespace, parameters bound from the request, keys scoped to the
path. What remains is the embedding, and the same agent can draft that too: a dashboard that pins the
released pipeline, renders its charts inside your product, and is relayed by your backend so the key never
reaches a customer's browser. Or your front end renders the endpoint's JSON itself.
Ships today
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 and not another tenant's endpoints. 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
GET /api/usage/v1/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 a change your customers actually feel none of.
Ships today
The same agent that authors the pipeline drafts the visualizations and the dashboard: charts, tables and KPI cards bound to pinned versions of released pipelines, with filters from a reusable parameter set. A person on your team reviews the draft and releases it, so the numbers a customer sees came from a version someone signed off.
Your backend embeds it. It holds a dashboard key, relays the dashboard's runtime routes and
attaches the key server-side; the customer's browser talks only to you. Every viewer behind one key shares
its identity and budget, so the key does not tell your tenants apart: your backend signs the customer in
and decides which filter values that customer may submit, and the source pipeline filters on them.
What it does not do yet: no drag-and-drop editor, no live subscriptions, no scheduled refresh and no report delivery. The SQL dashboards page walks through the build, the review, the refresh and the proxy contract in full; if your own front end should draw every chart, the endpoint above is the whole integration.
For the reviewer
Database credentials are encrypted at rest on the server and never returned, not through a tool and not through the API. The customer integration holds a path-scoped key to the endpoint, nothing more.
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. What you present a customer is exactly the API you published.
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.
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.
GET only, and 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
| endpoint | serves | key reach |
|---|---|---|
GET /api/demo/v1/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
Both halves ship. A released pipeline becomes a versioned GET endpoint your application calls, with per-viewer values bound as parameters, and a dashboard over it, drafted by your agent and released by a person, embeds in your product through your own backend. The contracts are docs/rest-api.md §19 and docs/dashboards.md §6.5.
Row-level authorisation stays your backend's job: it authorises the viewer and derives or limits the customer value that viewer may submit. The published endpoint accepts declared parameters from its path or query string (docs/rest-api.md §19.3), and the released SQL filters rows using that value bound as a scalar on a prepared statement (docs/templates.md §4.5), never pasted SQL. An endpoint key restricts published paths, not result rows (docs/auth.md §7.7).
One path subtree and nothing else: endpoint keys are bound to a path prefix of the published tree, 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.
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.
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.
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.