Reporting for many clients, without thirty spreadsheets

A client reporting API per client: one pipeline each, one key each, a release you can invoice

An agency's reporting is a map of other people's databases: a Postgres here, a client's SQL Server there, a lake for the third. The work is the same shape every time: join their sources, produce their numbers, put those numbers in front of them, prove where the number came from. datapipelines is less a client reporting tool than the governed data layer under one, with SQL dashboards when the client wants charts. Each client gets its own workspace, its own key, an agent that drafts the pipeline, and a release your team signs, which becomes the API or the dashboard the client's portal shows.

Isolation by construction

One workspace per client, one key per client

Every client gets a workspace, and a key is pinned to one workspace at issuance: the analyst, or the agent, working client A sees nothing of client B. The other workspace's pipelines, datasources and executions are absent, not merely hidden. Cross-client leakage by a mistyped id resolves as not-found, which is the property you want when the clients are each other's competitors.

The client-facing half is a second kind of key: bound to a path prefix of the published tree under /api, it can call that client's endpoints and nothing else. Handing client A their key never touches client B's, and revoking one is a checkbox. There is no shared password to rotate across the roster.

Isolation is also the answer to the question agencies ask first, which is not technical: what happens when one client's data must never influence another's numbers? Here the answer is structural. The workspaces do not share datasources unless you deliberately bind a global one, the agent's key cannot see across them, and the endpoint keys cannot reach another prefix. There is no "careful not to touch the other tab" step in the runbook, because the isolation is not a convention the team follows; it is the shape of the system they are working in.

The first draft, automated

The agent does the first draft of every client's pipeline

The repetitive half of agency work is walking a new client's schema: what tables exist, what the columns hold, how their "orders" differs from the last client's. The agent does that walk with the same tools on every engagement, from schemas and catalogue statistics to a bounded probe, and drafts the pipeline: one node per source, the join in the per-run scratch database, the client's definition of "active customer" as the filter your reviewer reads before anything ships. The draft is the deliverable your team reviews; the variance between clients becomes reading, not re-typing. Engagement number four costs a fraction of engagement number one, because the ritual is identical and only the schemas are new.

The agent's session on a new client's sources: schemas read, the draft iterated, the run checked. It is the same ritual for every engagement.
workspace: demo · agent session

you › Which rideshare company carried the most trips in each borough last quarter?

  • read the datasource facts, columns and stats. hvfhv_zone_day is a census at zone × day × company
  • resolved "last quarter" from the data's last day, which gave Q4 2024
  • rendered 3 templates, ran the draft: 4 nodes · 763 ms
  • draft demo/top_company_by_borough v1, left for a human to release
boroughtop companytripsborough tripsshare
Manhattan Uber 17,660,839 23,669,163
Brooklyn Uber 11,946,362 16,057,699
Queens Uber 9,786,602 12,847,231
Bronx Uber 6,078,750 7,572,899
Staten Island Uber 715,593 918,360

GET /api/demo/v1/top-company-by-borough?anchor_date=2025-01-01 · after release · key scoped to /demo

from the demo workspace. A real result, not a promise

The engagement, end to end

Onboarding a new client, as a ritual

  1. Open the workspace

    One workspace per client, created in minutes. Everything that follows, from datasources and pipelines to releases and keys, lives inside it, invisible to every other workspace.

  2. Register the client's sources, read-only

    Each database the client's reporting reads becomes a datasource, marked read-only unless the engagement explicitly includes writing. Credentials are encrypted on the server; the agent never sees one.

  3. Get each person their key

    Everyone who works the engagement creates their own key in the client's workspace, on the Keys page: the dialog offers each creator exactly the roles whose permissions are a subset of theirs, and each key is revocable on its own without touching any other client's access.

  4. Let the agent draft, run, and compare

    Describe the client's reporting in business terms; the agent drafts the pipeline, runs it against the client's sources, and you compare the numbers with the report it replaces.

  5. Release, publish, hand over the key

    A person releases the version the engagement agrees on; the endpoint is published; the client-facing key is bound to its path. The invoice can name the version.

Sign-off with a version number

A release per client, with a version you can name in the invoice itself

The report a client disputes is the report you can defend: the pipeline that produced it was released, by a person on your team, as an immutable version, and the endpoint serves the version you point it at. "The numbers changed" becomes "version 4 was the deliverable; here is its diff", which is a different conversation. Agents cannot release; the sign-off is structurally yours.

It also changes the retainer conversation. "Maintenance" stops being a euphemism for re-running things by hand: a schema change upstream is a new draft your agent produces, a diff your reviewer reads, and a release your client's invoice can reference. The engagement's artifacts are versioned all the way down, which is what "professional services" was always supposed to mean.

What ships today

White-label reporting in the client's portal: the API and the dashboard

Ships today The endpoint in their portal

  • The client's white-label page calls GET /api/… with their key; the query string binds the report's parameters, such as month, region and account.
  • Cache-Control: no-store: every call is a fresh run of the released version, so the portal never shows a stale cached copy.
  • The key reaches that path and nothing else; the contract is enforced by the router, not by a promise.

Ships today The dashboard beside it

  • Your agent drafts the client's dashboard over their released pipelines, and a person on your team releases it.
  • The client's portal embeds it through its own backend, which holds a dashboard key bound to that client's dashboards; the key never reaches the browser.
  • Viewers behind one key share it, so the portal's backend decides which values each of its users may submit.
  • Isolation holds either way: the dashboard, like the key, belongs to one client's workspace and stops at its boundary.

A month-end pipeline can also run on a schedule. Scheduled report mail is not built; the extracts, alerts and dashboards page labels what is later.

What it looks like

The client's endpoint, the key, the version

The API section: one published endpoint per client, the version each serves, and the key bound to each path. It is the roster, read at a glance. The panel here is the demo workspace's own endpoint, rendered from the same recorded run the home page shows, so what a client receives is never a mock-up.
workspace: demo · API
endpointserveskey 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

One more property matters at review season: the audit log. Every call a client's key made, every release a teammate signed, every agent action on every workspace, recorded with the actor and the tool, and never the data itself. When a client asks "who looked at our numbers and when", the answer is a query over your own deployment's log, not a search through inboxes. The same log answers the earlier question too, which version of the report was live on which date, because releases and calls land in one record your agency owns outright.

Asked before

Agency questions, answered plainly

Can each client be kept separate from the others?

Yes, one workspace per client. A key is pinned to one workspace at issuance, so an agent working for client A cannot switch that key to client B. A colleague without the super admin role can switch their session only to workspaces they belong to; other workspaces' pipelines, datasources and executions are absent, not hidden. Workspace resolution is docs/auth.md §5.6.

Can a client's key reach only that client's numbers?

Yes. Endpoint keys are bound to a path prefix of the published tree, so the key you hand a client calls its endpoints and nothing else, and you revoke it without touching anyone else's access. Key kinds and bindings are docs/auth.md §7.7.

Who does the work, and who signs it off?

Your agent drafts each client's pipeline over the same authoring surface every user gets, and a person on your team releases it. Agents cannot release, and a released version is immutable. That is the version you can name in the invoice. The lifecycle is docs/versioning.md §3.

What do we put in the client's portal today?

The API: a released pipeline published as a GET endpoint returns JSON with the query string bound to its declared parameters, which is what a portal or a white-label page calls. A dashboard can sit beside it, embedded through your backend with a dashboard key bound to that client's dashboard folder. The contracts are docs/rest-api.md §19, docs/auth.md §7.7 and docs/dashboards.md §6.5.

Pilot it on one real client this week

One workspace, one datasource, one pipeline, one release, one endpoint: the smallest engagement-shaped slice of the demo proves the flow before you point it at a real client's database. The same ritual scales from the pilot client to the roster without changing shape. Only the workspace names change.