Parity builds the dashboard from your data, shows each of your customers only their own numbers, and answers their questions in plain words. You add one snippet and sign a token.
Every software product ends up needing analytics: what happened, for this customer, and why. Building it well is a product of its own.
Upload an export, connect a Google Sheet your systems keep up to date, or a tool like Stripe or HubSpot. Parity builds the dashboard and checks every number against the data.
Pick the column that says which customer a row belongs to. Parity shows you, customer by customer, exactly what each one will see.
Your server signs a short token naming the customer. Put it in the iframe, and embed.js sizes the frame to the dashboard.
// npm install jsonwebtoken
import jwt from "jsonwebtoken";
// On your server: a ten-minute token for the person signed in.
const token = jwt.sign(
{ embed: "YOUR_EMBED_ID", tenant: customer.account_id, user: user.id },
process.env.PARITY_EMBED_SECRET,
{ algorithm: "HS256", expiresIn: "10m" },
);<iframe
src="https://parityai.pro/embed/YOUR_EMBED_ID#token=TOKEN_FROM_YOUR_SERVER"
data-parity-embed
title="Dashboard"
style="width:100%;height:600px;border:0"
referrerpolicy="strict-origin-when-cross-origin"
></iframe>
<script src="https://parityai.pro/embed.js" async></script>Build the dashboard once. Parity runs its queries again for each customer, on that customer's rows. Here is the same dashboard, signed in as three clinics of a booking app.
| Clinic | Bookings | Revenue | Booked online | Bookings in September |
|---|---|---|---|---|
| Elm Street Physio | 1,147 | $78.2K | 57% | 128 |
| Northside Sports Clinic | 708 | $48.3K | 88% | 89 |
| Riverside Dental | 393 | $55.8K | 54% | 31 |
Every table is cut down to the customer in the token before anything is worked out, so no count, total or top value includes anyone else's rows.
A table without the customer column stops the page from showing, rather than showing it whole.
The headline, summary and insights were written about all your data, so they're left off. Your customers ask instead.
Above the dashboard, a customer can ask anything about their numbers. Parity works the answer out with queries on that customer's rows, the same way it built the dashboard.
The same question, asked by three clinics
How many bookings did we have last month, and how does that compare with the month before?
Elm Street Physioacct_1001
In September 2026, you had 128 total bookings (120 completed, 4 cancellations, and 4 no-shows). That is an increase of 23 bookings (up 21.9%) compared to August 2026, which had 105 bookings (97 completed). Completed appointments alone grew by 23.7% month-over-month.
Northside Sports Clinicacct_1003
In September 2026 (the most recent month recorded), there were 89 total bookings, up from 73 in August 2026—an increase of 16 bookings, or 21.9%. Completed appointments rose by 30.2% from 63 to 82, while cancellations fell from 9 to 4.
Riverside Dentalacct_1006
In September 2026, you had 31 total bookings (27 completed, 2 cancellations, and 2 no-shows), generating $4,051 in revenue. In the month before (August 2026), there were 37 total bookings (33 completed) with $4,775 in revenue. That represents a decrease of 6 bookings (-16.2%) month-over-month.
Real answers from Parity, each worked out from that clinic's rows of the same sample data.
Your palette and logo from Branding settings, five designs, and the “Made with Parity” link off if you like.
List the sites it may appear on. Anywhere else, the page shows nothing.
embed.js sets the frame's height to the dashboard's, so there's no scrollbar inside your page.
Dashboards on connected tools can refresh every day, or when their sheet changes.
Views and questions are counted for each embed, in the report's Embed settings.
Every number is a query result, checked against your data when the dashboard is built.
Sign the token on your server with the embed's secret (HS256, any JWT library) and put it after #token= in the frame's address. The fragment never reaches a server log or another site.
| embed | Required | The embed's id, from Embed settings. |
|---|---|---|
| tenant | With a customer column | The customer's value of that column, exactly as your data holds it (send ids past 2^53 as strings). Refused when there's no customer column. |
| user | Optional | Your user's id: questions are limited per user. |
| exp | Required | Unix seconds, within 24 hours. Ten minutes is plenty. |
parity:readyThe dashboard has loaded.parity:heightThe page's height changed (embed.js resizes the frame).parity:errorIt can't be shown: detail.code says why.parity:token-expiredSend a new token: embed.js calls window.parityToken(embedId, iframe) if you define it.Error codes: token_required, token_invalid, token_expired, origin_refused, tenant_required, tenant_refused, owner_left, column_missing, page_not_safe, data_unavailable.
// npm install jsonwebtoken
import jwt from "jsonwebtoken";
// On your server: a ten-minute token for the person signed in.
const token = jwt.sign(
{ embed: "YOUR_EMBED_ID", tenant: customer.account_id, user: user.id },
process.env.PARITY_EMBED_SECRET,
{ algorithm: "HS256", expiresIn: "10m" },
);<iframe
src="https://parityai.pro/embed/YOUR_EMBED_ID#token=TOKEN_FROM_YOUR_SERVER"
data-parity-embed
title="Dashboard"
style="width:100%;height:600px;border:0"
referrerpolicy="strict-origin-when-cross-origin"
></iframe>
<script src="https://parityai.pro/embed.js" async></script>Your server signs a token that names the customer, with a secret only you and Parity know. Parity narrows every table to that customer's rows before anything is worked out, then runs the dashboard's own queries on what's left. A table without the customer column is refused rather than shown whole, and text written about all your data is left out of the page. What's left is checked for anything that belongs to one customer, such as a name or an id in a title, and the page isn't shown until it's reworded.
Anything about the numbers in front of them. With a customer column, answers are worked out from that customer's rows, in the tables and columns the dashboard uses, so other columns, such as notes or emails, stay out of reach. When everyone sees the same dashboard, answers come from its own numbers only.
CSV and Excel files, Google Sheets, Google Drive and Excel on OneDrive, and connected tools such as Stripe, Shopify, HubSpot and QuickBooks. Most software companies start with a scheduled export or a Google Sheet their systems keep up to date. Direct database connections, such as Postgres, BigQuery or Snowflake, aren't available yet.
Yes. New dashboards are built with your palette and logo from Branding settings, and come in five designs, from classic to dark. You can turn off the small "Made with Parity" link, so your customers only see your product.
Viewing an embedded dashboard doesn't use credits. Each question a customer asks uses credits from your plan, usually a few, and you can turn questions off at any time. Embedding is in early access: for larger volumes, talk to us.
No. Parity counts views and questions for each embed so you can see how it's used. The questions and answers aren't kept once the answer is sent.
The page asks your page for a new one, and embed.js calls the function you give it (window.parityToken). The dashboard stays on screen meanwhile, and a question that was waiting is asked again with the new token. Tokens can last up to a day; ten minutes is what we suggest.
Building something specific?Talk to us at hello@parityai.pro