Currently in alpha. Sign up free. Expect a few rough edges.
Docs

The warehouse

Every table, every file the business gave it, and a query editor for whoever wants one.

Warehouse, the first row under Data on the left side, is three panes, the way a database tool lays them out. Files is the first row on the left, above your datasets: every file you gave Overscore, in one list (see Files, below). Under it is every table, by dataset: each dataset folds open and shut, says how many tables and views it holds (6 tables, 3 views), and lists them in name order with their rows, a dataset's views among its tables, each marked with a small icon the way BigQuery marks them: a grid for a table, an eye in a frame for a view, and a grid with a pencil for a table people fill in (store visits, a call log: Tables you fill in yourself), whose columns say what each one holds. Find a table, at the top, narrows the list as you type, and what you fold stays folded next time. The list keeps up by itself while it is on screen: a table loaded from the terminal, a model built or something deleted shows within seconds, and Refresh, beside Find a table, reads it again at once (Models and Sources keep up the same way). The tables Overscore keeps for itself are under a line at the bottom, folded. A table or a dataset you may change has a ··· at the right of its row, with Delete the table or Delete the dataset: it asks once, then the table (or the dataset and all its tables) leaves Warehouse and waits in Trash for 7 days, after which it is gone for good. While something waits, Trash is a line at the foot of the list on the left; it lists each one with who deleted it, when, and the day it goes, and Restore puts it back as it was. In Files, a row's ··· has the same Delete the table for the table a file made, beside Delete the file.

In the middle is the table you pick, rows first: its name, what it is, and under that who stands behind it (a green dot, Confirmed by Sarah on Oct 13, and what one row is; an amber dot when the AI guessed the card and nobody has said yes); About this table, Query and Download; how many rows and columns it has, when it was last updated where that is known, what reads it (Read by 3 metrics and 2 pages, which opens About this table) and how many queries read it in the last 30 days; then three tabs. Rows is its rows, filling the page, each column's type under its name, with how many there are and Download the rows under them. Columns lists each column on one line: its type, how much of it is empty as a short bar, how many different values it holds, a small drawing of what it holds (the spread of a number or a date between its smallest and largest, the most frequent values of text, one value, or every row its own), and what it means with who wrote it. Click a column and its own panel opens at the right: the drawing large, its figures (empty, distinct, smallest, largest, median, mean, sum, and when it was read), the values it holds most often with their counts, Query this column (a count by its values, in a new query) and Add a field beside it. What a column holds is read from the table for its first sixty columns, on a table over a million rows from a sample of 200,000 of them (the panel says so), and never shown to someone who sees the dataset masked. History is the runs that touched it and what changed since a day you pick. The tab you were on is kept for each table while the app is open.

Words a chat filed and no person has said yet end in Not confirmed yet: what it is under the table's name, and a column's meaning in Columns. About this table opens the pane at the right: its card (one row is, use it for, and what it is, with Edit; saving it makes it yours), what reads it (the metrics, dashboards, saved queries and models built on it, and the destinations it is sent to, each a link where it has a page of its own), the notes people left, your fields, and what it comes from, with Open the whole graph. The × closes it; it stays open or closed as you left it. Open a view and its SQL is where a table's rows would be, with its Columns beside it: nothing runs until you press Query, and Edit SQL opens its model.

Above the middle are your open queries and Lineage; + opens a query. A query has an editor. To have one written, ask the chat beside the page (Ask, at the top left, or ⌘J): it puts the query in your editor, in the query you have open when its editor is empty, else in a new query tab, never over SQL you typed, and says which (Wrote the query into Query 2); running it is yours. A query the app writes (Query on a table, one the chat writes for you, a saved query you open) arrives one clause per line, and Enter starts the next line where the line above starts. Under the editor are Run, Save query (it asks for a name and keeps the query for the whole team), Download and Save as model. Beside the editor are the columns of the table the query names (or the last table you opened), each with its type; click one to put it in the query, or the table's name to put that in. The pane under the editor has its results (each column's type under its name, and the count and time beside Run), Columns (what each column of the result holds, drawn the way a table's are from the rows on screen), the Preview of the last table you opened, History (what you ran here, newest first) and Saved (the team's saved queries, each with who saved it and when, and Delete in its ···); click a query in History or Saved to put it in the editor. On a phone the table's columns are a Schema tab in the pane instead. Drag the line between the editor and the pane to give either more room: the editor takes all the room above the line, with its buttons under it, down to three lines, and each query keeps where you left its line. A link to a table opens that table and lights it on the left, even if you last looked at another one. Opening Warehouse without a link opens what you had open last. Lineage draws the map of a dataset with one table lit: the last one you opened, or the one you pick at the top. Beside the map, Fed by is what the table comes from, nearest first, and Feeds is everything built on it, nearest first: tables, models, metrics, pages, saved queries and the destinations it is sent to, with a line that says how far a change to it reaches. Click a table on the map or in the list to light it instead, double-click one to open it, and Open the table opens the lit one; a source on the map opens Sources, and a metric or a page opens its own page. On a phone the list stands alone. Before there is any table to trace, it says so and offers Connect a source. On a phone, Warehouse opens on the list of tables; a table is a screen of its own with a back arrow, its rows under its head and About this table opening over it, and a query is a screen of its own with its results under it.

A table's rows and a query's results are one grid. Each value is shown as it is stored: an order id reads 11658155295083, with no commas put in, and numbers sit at the right of their column. The one exception is money the app was told about: a column whose words on its Columns tab say dollars, written or confirmed by someone who writes the table, or that a metric shown in dollars adds up, reads to the cent (7477.60 where 7477.5999999999985 is stored), and pointing at it shows the value as stored. A column's name alone never makes it money. Every other row is shaded and a thin line runs between columns, so a wide table is easy to read across. To make a column wider or narrower, drag the edge of a column's heading; the others stay as they are. A cell too long for its column ends in dots: rest the pointer on it to read all of it, or click it to open it in place.

Columns lists every column a table has, however wide the table: a metric, a question or a chat can use any of them. What a column holds (how much of it is empty, a sample) is read for a table's first sixty columns; the ones after are listed by name and type.

A query's results and a table's rows come in pages of 50. At the foot of the grid is the pager, as BigQuery has it: Rows per page (50, 100 or 500), which rows you are looking at (1–50 of 2,520), and the first, previous, next and last page. The line beside Run says how many rows there are in all and how long the query took. Click a number column's heading for largest first, again for smallest first, and a third time to restore the original order; a sort runs over every row, not only the page you are on, and starts again from the first page. A query whose rows come in no order of their own (a total by month, a sort with ties) is run once and its rows kept for a day after you last page through them, so every page is from the same result and no row shows twice or goes missing between pages; through the API that is keep: true on POST /query, and the next page names the result its answer gave.

The sample

A first company on Self-serve or Free starts with a sample store in its warehouse, to learn the app on: a made-up leather goods shop with its orders in a dataset called sample, its cost sheet in files, two models, its own net sales and gross margin, and a Sales page marked From the sample. None of it is your business's data, and nothing of it pretends to be a connected system. Remove the sample takes all of it out in one go, with anything you made on it, after saying what goes: ask the chat to remove it and press Remove on its card, or run ovs sample remove.

Download every row

Download, beside Run on a query and beside Query on a table, saves every row, not just the ones on the screen: CSV for any spreadsheet, Excel (up to 1,048,576 rows, Excel's own limit), Parquet for data tools, or JSON. Every number keeps its digits: money is a number in Excel, and an id longer than 15 digits is written as text on the screen, in Excel and in JSON, which would otherwise round its last digits. JSON needs a different name for each column, so a result with two columns of one name (two ids from a join) asks you to rename one in the query, or to download CSV. Sorted results download sorted, in the order on the screen, whatever their size; text sorts the way the warehouse sorts it, capital letters first. A result of up to 100,000 rows downloads straight away. A bigger one is written in the background: you can leave the page, and if you turned on notifications your phone tells you when it's ready, and the notice opens it.

Only you can see your download. The file is kept for a day, then deleted. When you come back to the query editor, your last download is waiting on its card until you close it. Download on its card gets a new link each time, and a link passed to someone else stops working after ten minutes. If someone leaves your company, their files are deleted within about ten minutes; if an admin moves them out of Everything, a link they already have stops working at once and their files go within a minute. A download reads only what you may read, masked where you see things masked, and it shows in Activity and Runs like any query. Its size counts toward your plan's daily allowance for data sent out, once each time the file is fetched; a fetch the allowance has no room for is refused and counts nothing. When today's allowance has no room left for the file, its card says so where Download was, and the file is still kept for its day: download it once the allowance starts again (midnight UTC).

Files

Files, the first row on the left above your datasets, is the files you gave Overscore, kept as they came, beside the tables they made, in one list; synced and built tables keep no file here. An email source (Sources, A file someone emails) keeps each email whole here, of the kind email, beside the file it made: the email's panel names the version it made, and the file's panel the email it came in. Upload a file here, or with the Upload a file card at the top of Sources, and it's kept as it came: a document, meeting notes, a price list, the model a data scientist fitted. A spreadsheet (CSV, Excel, JSON or Parquet) can also become a table in the same step, and the file stays behind the table, so a number can be traced back to the file it came from: the file's row links to its table, and the table's own page says From and the file's name, which opens the file here. Once a spreadsheet is in, you are taken to its table. That file is the original, not where the table's rows are: every table's rows, whatever made it, are kept as Parquet files in your company's own storage, with a list in your company's own database of which files make which table, and a view is only its SQL. A source's sync and a model built as a table make no file in Files. What Files keeps counts toward your plan's data, so deleting a file frees its room. A Google Sheet becomes a table that refreshes on its own, with every pull that changed it kept. It is added on Sources, with the Add a Google Sheet card: connect Google and pick the sheet (it stays private, and Overscore reads only the sheets you pick), or paste a link to a sheet anyone can open. Click a sheet to see who it is read as. Each pull downloads the sheet. When its rows are the ones the table already holds, nothing is written: the pull's run says Unchanged, and so does the sheet's panel here. Otherwise it loads the table. A pull that fails is in its run history, with a card in the Inbox. Pull now always loads, even when nothing changed and even while paused; Manage refresh opens its run history. Neither changes who reads it or restarts a paused schedule.

A file lives in a dataset, like a folder, and the list is grouped that way, as the tables are: finance's tables and finance's files sit in the same dataset. Whoever can read that dataset can open it; a file kept for the whole account is for admins only. Opening or downloading a file counts toward your plan's daily allowance for data sent out, as a download does. Someone who sees a dataset masked sees that its files exist, but never opens them. Choose a short name for a kept file: up to 63 lowercase letters, digits or underscores, starting with a letter. This is separate from its original filename, and the upload form checks it before sending. Upload a file under a name it already has and it's a new version; the old ones stay, and you pick which one is live. A new version runs the Python models that read the file (the same bytes again run nothing). Two uploads arriving together keep separate versions, each with its own contents. A Python model reads a file by its name, when whoever saves the model may open the file, so nothing needs labeling for that.

Click a file for what it is (its kind, size, pages and the note it was kept with, on one line), where it lives, who added it and when, and its fingerprint. Open never shows the raw file in a browser tab: on a spreadsheet that became a table it is Open the table, and on a document Open shows the document's pages in the middle of the Warehouse, where a table's rows are, as Overscore read them. Download saves the file itself. Delete is behind the ··· beside them, for an admin or whoever added the file; it asks once, and says there what goes. When deletion finishes, the original, its saved versions and the text read from it are removed from your current storage. The originals' backup copies go at the next successful nightly backup. Earlier nightly backups keep the document's text. The table it became, approved notes and the record of who deleted it and when stay. If deletion cannot finish, it says so and you can try again. Every open, download and delete is recorded.

Upload a file asks for the file first, and nothing else until you've chosen one. Choose a spreadsheet and Overscore looks at it there and then, before anything is kept: its first rows, under the column names. Under each column's name is what Overscore read it as: text, number, date, date and time, or yes/no. Each is a choice. A zip code read as a number loses its leading zero, so say it's text; a column of amounts written "$1,284.50" is a number if you say so. The rows are read again as they would land, and if some values don't fit what you chose ("n/a" in a number column), the line under the rows says how many: those land empty, and everything else lands as you said. Then say where it lands: Dataset is the dataset it goes in, like quickbooks, and a new name makes a new one; the table takes the file's name unless you change it. What you typed stays if you choose another file. The button says what it does: Make the table.

For an Excel file the tabs are listed with their rows, and the one that reads as a table is already picked; a tab with nothing to read (a chart) can't be. The row the column names are on is found and shown, a title above it passed over, months come in as dates, and you can change the header row or say there is none. A JSON file's nested values land nested, as they are; a model flattens them. A PDF, a Word file or notes has no rows to look at: it is kept as it is, and its text is read here, page by page. A spreadsheet you only want kept, not made a table: Keep it as a file only, under the buttons.

Uploading onto a table that already has rows asks first: Add to it puts the file's rows in beside the ones there (next week's export after this week's; the columns must be the same), and Replace what is there makes the table the file's rows. The question shows the rows and dates on each side, and what the file changes against the table as it is now: the columns it removes, the ones it adds, and any column whose values change kind (a number column that now reads as text, because some of the file's values aren't numbers). The same columns in another order are the same: nothing reads a column by its place. Under that, what reads the table (models, saved queries, metrics and the dashboards they feed), so a renamed column is caught before it breaks them. The button then says what it does, Replace the table or Add the rows; Cancel leaves the table exactly as it was.

A table's current original is the file used for its latest load. If you delete that original, the pane says "The original this table came from was deleted." The table stays, and any older originals are still older. Search inside a table picks up changed contents at its next background update, including when a replacement spreadsheet has the same number of rows.

What's inside a document

When you keep a document in Files (a PDF, a Word file, notes, a scan or a photo of a page), its text is read inside Overscore, on your account's own machine, page by page, within a minute of the upload. Nothing is sent to another company to read it. Scanned pages and photos are read too, by a reader for pictures that runs on the same machine, in English.

Find a file searches the words inside documents as well as the names, and shows the page and the sentence it found under the file. Click a file and its text is in the pane, page by page. If it is still waiting or being read, the open pane checks again and shows the text when it is ready. Open at this page opens the document's pages right where the words are. A PDF shows itself there too, in your browser's own viewer, beside its pages as Overscore read them and at the same page; on a phone the pages alone, and Download is the file.

The chat reads them too. Ask "what did the Q3 board deck say about churn?" and it finds the page, reads it, and answers. Under the answer, a link such as "From q3-board-deck.pdf, page 2" opens that version of the file at the page it read. These links appear even when the answer has no numbers. They come from the pages returned by successful file reads or searches during that answer, not from words the AI writes. It only reads files you're allowed to open, and every search and every page it reads is recorded, the same as opening the file.

When a document is read, it also suggests up to five facts from it, to the Inbox, as one row named for the document, each fact saying the page it came from. For that, the first pages' text (up to 16,000 characters) goes to the AI, the way a chat's question does; on Free, where the AI only sorts what you add, no facts are suggested. Only people who can open that file see them. Approve one and it becomes a fact about the company, with the file and the page as its source; whatever a document says, it can't change anything by itself.

The file's rules hold for its words: someone who sees its dataset masked never gets them, in a search or in the chat. A file over 50 MB or 2,000 pages isn't read, and its pane says so. A new version is read again; a completed delete removes the text from search and the file's pane. Earlier backups keep it, and approved notes stay. A kept note's source says when its original file was deleted.

What did the Q3 board deck say about churn?
Which contract mentions a 60-day notice period?
Summarise the pricing memo Nina sent in September.

Signing off a dataset

A dataset can be signed off: these are the final tables. The AI uses a signed-off dataset's tables first, and a number worked out by hand from one says so. It's the dataset you sign off, never one table or one file: every table in it counts, and so does each one added later. Anyone who can change the dataset does it in Warehouse, from the dataset's menu (the three dots beside its name): Sign off the dataset asks for one line on what it is, like "The tables every report reads", and the dataset then says signed off beside its count. Take the sign-off back, in the same menu, undoes it, and each table goes back to what it was. From a terminal it's ovs signoff analytics "The tables every report reads". A metric has its own confirmation, on its own page.

The dialect

DuckDB's SQL: Postgres-like, fast, with JSON operators. Rows that arrived as JSON keep the whole object in a payload column; wrap extracts in parentheses when comparing them.

select payload->>'status' as status, sum((payload->>'total')::double) as revenue
from sales.orders group by 1

A connected app's tables (shopify.orders) carry _synced_at, when each row landed, and leave out what the app deleted; its copy behind the scenes (_shopify.orders) keeps the deleted ones too, with when they went (_deleted_at), and Connect a source says how each source does it.

Time travel

Every version of every table is kept for 30 days, and of a model's table for 3: a model is built again from what it reads, and its page keeps every version of its SQL. Ask for a table as it was on a date in that time and you get that version; a table nobody has changed keeps what it holds, however old. Dropped means hidden for a week, not gone. Anything older than 30 days is in the nightly backup, which we can bring back.

From the command line

ovs tables
ovs describe sales orders
ovs query "select count(*) from shopify.orders where created_at > '2026-01-01'"
ovs query "select * from shopify.orders" --out orders.csv      every row into a file: .csv .xlsx .parquet .json

All docs · Something wrong on this page? Tell us.