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

CLI reference

Everything the app does, from a terminal. For a script, a cron job, or an agent on your laptop.

uv tool install overscore-cli      # or: pip install overscore-cli
ovs login                          # approve this computer in the browser; it makes ~/Overscore/<company>
cd ~/Overscore/<company>
ovs pull                           # everything, as files
ovs guide

ovs login ends by saying what the folder is: your company's models, metrics, saved queries, dashboards and data alerts as files, which you change and push back, and which you can delete and pull again whenever you like. The README.md it writes in the folder says the same in one screen, with what lives only in Overscore and the command that shows each. Then run ovs pull there and open the folder in Claude Code (or your own agent). The key stays in the computer's keychain, never in the folder.

When the ovs on your computer is older than the one your Overscore knows, it says so once a day, on its own line: ovs 0.1.14 is behind 0.1.24; run uv tool upgrade overscore-cli.

Every command speaks HTTP to your address (https://<you>.overscore.tech, which ovs login remembers) with this computer's key. Output is text on a terminal and JSON otherwise; add -o json to force it. Failures exit 1. JSON output is {"success": false, "error": "what to fix", "status": null} (status is the HTTP code for a server refusal); terminal output starts with error:. A missing file names the path to check; bad JSON names the file or stdin and the place to fix before retrying. A machine runs four queries at once; when all four are taken for two seconds the answer is busy, and nothing ran: ovs prints busy: four queries are already running on this machine, try again in 2 seconds on stderr, asks once more after those 2 seconds, and fails with that line (status 503) only if it is still busy. Busy is not down: wait a moment and run it again. If the connection drops while a command waits (a laptop's lid closed, a network that changed), ovs notices within about a minute however long the answer may take, says so, and sends a read or a save once more, since saving the same thing again changes nothing; anything that starts work, such as ovs run, it leaves for you to run again, and says it may have started.

No browser or keychain (a server, a scheduled job)? Make a key on Settings, Agents and set OVERSCORE_KEY=ovs_… and OVERSCORE_URL=https://<you>.overscore.tech instead.

For a person in a group with Proof off, ovs api POST /dashboards/<slug>/data returns the page’s numbers and allowed rows without the calculation’s SQL or source details.

A project folder: models, saved queries, metrics, dashboards, analyses, files, syncs

A dashboard push saves a version; it does not publish it. Each version keeps its own theme settings, and omitting the theme keeps the latest draft's settings. Pushing a dashboard under a deleted name restores it with its earlier versions. If a changed saved query is used by a published page, the push is refused and names the pages: save the changed SQL under a new query name, update the draft to read that name, then review and publish. For an intentional change to the shared query, PUT /saved-queries/<name> accepts replace: true with the existing ownership checks; this changes live pages immediately, so use it only when that shared change is intended. A laptop has no browser to draw a page in, so a pushed dashboard's card in the app gets its picture the first time someone who can build opens that version there.

A pushed metric that works out the very number one on file does (the same thing added up, on the same table, with the same filter and date) is refused, and the refusal names that one: use it, or, if yours is a different number, add a differs line to its .toml saying how in one sentence, and push again. A push of a model or a dashboard prints, under it, what is already on file about what it reads: the metrics defined on its tables or named on its page, and the facts and notes its name and description match. Use a metric by name rather than adding up the same thing again.

A metric's .toml carries other_names = ["GMV", "sales"], the words people say for the number, so a question in their words lands on it. No two metrics may share one, and a push that would is refused, naming the other. Spaces, underscores and punctuation count as the same spelling, so top-line and top line cannot name different metrics. If older names conflict, ask which metric is meant before working out a figure.

Pushing a metric with an agent's key, even one that can write, saves a guess (heard), not Trusted. The push answer says so and tells you how to confirm it. An admin or nominated confirmer uses ovs login, then ovs push, to confirm as themselves; Overscore records who and when. A nominated confirmer can also choose Looks right on an answer review in Inbox. Admins choose confirmers in Settings, Company. The comment at the top of each pulled metric file repeats the rule. Confirmation names or dates typed into that file are ignored.

ovs push saves a dashboard as a draft. ovs dashboard publish <slug> makes its latest version the one everyone sees, at once, with nobody having to open it first; --version 4 publishes that one instead, which is also how you go back. It is refused only when there is no such version, or the version waits for a person to press Load in the builder (a chat read something outside the warehouse). The push prints the link to see the page first, if you want to.

ovs init [folder] [--dataset <dataset>] [--for claude|codex|cursor|gemini|agents|all]      a folder of your own, as ovs login makes the company's; --dataset is only a default for pull, run, test and models
ovs pull        the warehouse's models, saved queries, metrics, dashboards, analyses and data alerts as files: models/<name>.sql with a .toml beside it (its tests inside), models/<name>/model.py, saved_queries/<name>.sql, metrics/<name>.toml, dashboards/<slug>/, analyses/<name>/, alerts/<name>.toml (each naming the dashboard it is on), and the live version of each file a Python model reads into files/
ovs status      what changed here since the pull, what changed in the warehouse, what is new on either side
ovs push [path] [--note "what changed"]        every changed file back, each push a version in the app; a view or a table is made as it is pushed (after any build already running in its dataset: it waits its turn, two minutes at most), and one that cannot be made says DuckDB's reason and the push exits with an error; a model's tests are that model's own, and go with it, so a model just pushed is not built a second time when the next sync lands; a push that is stopped part way keeps what it sent, so running it again goes on with the rest; a thing that changed in the warehouse since you pulled it is refused: "pull first" (never one that is the same there as in your folder), and ovs pull then merges the warehouse's change into yours (where both changed the same lines, both are written between markers to settle by hand: ovs status lists them, and ovs push waits until the markers are gone); a dashboard's new version prints the link to open it once before publishing
ovs pull <dataset.table>        ovs model run <name> --local        ovs model push <name> [--note "what changed"]        ovs model log <name>        ovs model take <name> [--why "..."]      a version its checks held, taken by you
ovs run <dataset> --local [--only <model>]        the folder's SQL models built on this computer, on the pulled samples, each after the ones it reads
ovs dashboard preview <slug> [--phone]        ovs dashboard look <slug> [--phone]        a page in the folder, before it is pushed
ovs dashboard publish <slug> [--version N]        the latest pushed version (or N) live at once; nobody has to open it first
ovs dashboard start <connector> [--source <dataset>]        the starting page a source ships (Pipedrive's Pipeline), as a draft only builders see, with the saved queries it reads
ovs uploads        ovs upload put <name> <file> [--dataset <dataset>]        ovs upload live <name> <version>
ovs destinations        ovs destination add webhook "<name>" https://…        ovs destination add hubspot "<name>" --source <dataset.source>
ovs syncs        ovs sync add <name> --from <table> --key <column> --to "<destination>"        ovs sync run <name>        ovs sync show <name>
ovs sync preview <name>      what the next send would be: how many rows wait, and one as it would leave        ovs sync sent <name> [--find <id>]        ovs sync pause|resume|rm <name>
ovs destination check <name>      one small request to it, nothing sent        ovs destination rm <name>      it and its syncs; what went is kept
ovs destination secret <name>      a webhook's signing secret, copied to your clipboard (never printed)
ovs destination add meta "<name>" --dataset <id> --token - --test-code <code>      sales and leads to Meta as conversions; paste the token when it waits
ovs destination set <name> --live      a Meta destination's test events off: the next run counts for real
ovs destination add bigquery "<name>" --project <id> --bq-dataset <dataset> [--table <name>] --key-file <service account .json>      tables into your own BigQuery; a send with --no-key replaces the whole table
ovs destination add gcs "<name>" --bucket <bucket> [--prefix <path>] --key-file <service account .json>      a kept file's versions into your bucket
ovs destination add s3 "<name>" --bucket <bucket> [--prefix <path>] [--region <region>] [--endpoint <https://…>] --key-id <id> --secret -      the same on S3; the secret on stdin
ovs sync add <name> --from <file name> --to "<bucket place>"      a file's versions, copied once each as they land
ovs destination set "<bucket place>" --copy-everything | --no-copy-everything      the whole warehouse into that bucket every night, under <prefix>/overscore/ (an admin, on their own laptop; off deletes nothing): see Your data, in your own bucket

Try things on your computer before you push them. ovs pull sales.orders keeps a sample of a table in the folder; ovs run sales --local --only clean_orders then builds the folder's SQL models on those samples, each after the models it reads, and prints each one's rows, columns and first rows. It says plainly that it ran on samples, so its sums are a sample's, not your totals. It needs no key and sends nothing. A model that reads a table with no sample says which one to pull.

ovs dashboard preview weekly-sales shows dashboards/weekly-sales/ in your browser before you push it, in the same frame the app uses, with its numbers asked of your warehouse as you; reload to see an edit. ovs dashboard look weekly-sales photographs it the way the builder checks its own pages, in a headless Chrome, and prints what is wrong with it: the pictures land in .overscore/looks/weekly-sales/, for an agent to read; add --phone for the phone's width. Nothing is saved: no version, nothing on the page's list of numbers. A page in preview only reads; marking a row or filling in a table waits for the app. Your key stays in the command line, never in the browser.

A SQL model's .toml says its kind: view, table or incremental. With no kind, a push keeps the model's own, and a new one takes the plan's. On Free that is a view, the only kind Free takes: a push of another kind is refused with the reason and changes nothing, and the push says so when it saved a view you didn't name. ovs model is for a Python model, which runs on Self-serve or Done for you.

A model's .toml (model.toml for a Python model) holds each test as [[tests]] with a name, its SQL and a description: one plain sentence saying what it checks, which the model's Tests tab shows. ovs push sends the description with the test, and ovs pull writes back one changed in the app, so a push never loses it. The file's note says what the version in use changed; change it, or pass --note, to say what your push changes, and it goes on the version the push saves. A note left as it was at the last pull or push is not sent again.

A Python model's model.toml has hosts = ["api.geocod.io"] when it may reach a service outside: a model reaches nothing outside unless an admin lists the hosts it may reach, and then only those, through Overscore's proxy, with every run's log saying what went where. ovs pull writes the line and ovs push sends it; a push that changes it from anyone but an admin's own laptop is refused with the warehouse's sentence, and the script is not saved either. hosts = [] reaches nothing; a file with no hosts line keeps what the model has. Keys for those services are pasted once in the app and never shown again: keys = [...] names them, never their values, and nothing pulls or pushes a key. ovs model run --local of a model with hosts runs with this computer's own network and says so once (local runs are not sandboxed), and takes each key keys names from this computer's environment, when it is set there.

Uploads use the project dataset unless you pass --dataset. Without either, the file is kept for the whole account and only an organization admin may upload it. The short name for a kept file (--name with ovs files put, or <name> with ovs upload put) is at most 63 lowercase letters, digits or underscores, starting with a letter. Sending the same file name again creates a new version; simultaneous uploads keep separate versions with their own contents. Use ovs uploads to see the versions and which one is live.

ovs how <connector> prints its setup steps followed by More detail: terminal examples, table reference and upkeep. The app folds that reference below the setup form.

Anything else

ovs api GET /metrics        ovs api PUT /metrics/<name> --data '{...}'        ovs api GET /dashboards

Every screen in the app has a door behind it; ovs api reaches any of them with this computer's key, for the jobs that have no command of their own. A request's body may be 32 MB at most (413 past it); a file or an upload goes through its own door, which takes 512 MB. A source address includes its dataset: /sources/connector/shopify_uk/shopify. List GET /sources first; use the returned kind, dataset and name for its page and actions. An old address without a dataset is refused when more than one connection matches.

Logging in and out

ovs login        ovs logout        ovs whoami

The approval page is a page of its own: type the code, then it shows which computer asked, from where and how long ago, and the company the key will be for, named large. If you belong to more than one company, check it is the one you mean: Switch picks another of yours and keeps the code you typed. Approve, and the page says to close the tab: your terminal has the key. If a browser approval expires or is cancelled, run ovs login again in the terminal and choose Enter another code on the approval page. Login checks that the keychain kept the actual key before it reports success. A failed save says whether its new key was turned off; if cleanup cannot be confirmed, remove that computer on Settings, Agents before retrying. If you approved and the terminal says the key could not reach this computer, that key is turned off for you. Run ovs login again. The setup Copy block on Settings, Agents runs installation, login and ovs tables; choose a dataset from that list before initializing a project.

Look around

ovs whoami        ovs tables        ovs index        ovs describe <dataset> <table>
ovs search <words>        ovs notes <dataset>        ovs context <words>

ovs whoami says who your key acts for, your company's plan (the app takes the same when you open it with a key), and the datasets the key reads.

Ask and query

ovs query "select …"        ovs query "select …" --out rows.csv   (every row: .csv .xlsx .parquet .json; rows.csv.part until it is whole)
ovs research <website>        ovs plan
ovs research "<question>" [--host <site>]      the researcher searches the web and reads pages (it sees none of your data), then prints its note

A table or a chart in a chat answer is how the app's chat shows rows. Here, and for an agent over MCP, the rows themselves are already yours: ovs query, or ovs api POST /metrics/<name>/run for a metric, with the same receipt. Draw them however you like.

ovs plan prints today's meters and your company's AI limit as Settings shows it: the AI used this month against the limit, who sets it, who to ask to raise it and when it starts again (sorting what you add to the library is on us and never counted). Then AI on its own, the AI that acts without being asked: whether it is on, and its spend this month against its own ceiling. Your machine sleeps when nobody is there, on every plan, and it also says when the machine woke and how long it had slept, and when its own work next wakes it. On Free its awake hours are on the line for this month, against its ten. On Free and Self-serve it says this month's background time too, the time your company's workers were awake against the plan's ten or fifty hours, and once it is used up, that syncs run once a day until the 1st. ovs plan --who (an admin) prints AI this month by who asked for it: each person with their chats and what they cost, then the AI on its own apart, by what ran it; --month 2026-09 for an earlier month.

Get data in

ovs connectors        ovs how shopify        ovs connect stripe key=rk_live_… --test      ovs connect stripe key=rk_live_… [--name stripe_eu | --dataset d] [--wait]
QuickBooks: add sandbox=true for an Intuit sandbox; the choice is saved
ovs api POST /sources/sheet/sales/sales.targets/sync      pull once, leave a paused schedule paused
ovs google status        ovs google disconnect        ovs connect sheet --file-id <id> --tab Targets --into sales.targets        ovs connect sheet --url <link> --into sales.targets
ovs link app "host=… user=… dbname=…" [--copy --tables a b]        ovs links        ovs copy app        ovs unlink app
ovs drop <dataset> [<table>]        ovs trash        ovs restore <dataset> [<table>]      a table, or a whole dataset, to the trash for 7 days; what waits; put it back
ovs sample remove [--yes]      the sample store your company started with: what removing it takes, the things you made on it named, then all of it with --yes
ovs load <dataset> <table> file.csv [--replace | --add | --preview] [--types zip:text,total:number]        ovs insert <dataset> <table> rows.json
ovs files        ovs files put notes.pdf --dataset <dataset>        ovs files put prices.csv --as-table <dataset>.<table>        ovs files get <id> [--out path]        ovs files rm <id> --yes
ovs files search "<words>" [--dataset <dataset>]        ovs files text <id> [--pages 1-3]      what documents say: the pages that match, a page's text
ovs hook <dataset> <table> [--provider stripe --secret …] [--every 6h]        ovs hooks        ovs hook-log <token>        ovs unhook <token>      --every 15m|1h|6h|1d|1w: how often rows are expected, so a quiet address is late, then behind, like a source
ovs sources mail add <name> --from <sender> --dataset <dataset> [--expect csv] [--pattern 'Weekly*.xlsx'] [--expected weekly|monthly|none]      a file someone emails: prints its address; a day past the period with no file, --tell hears; every email is kept as <name>_emails, for a model's emails()
ovs sources mail list        ovs sources mail pause|resume <name>        ovs sources mail remove <name> --yes

A load names its dataset every time: ovs load quickbooks invoices invoices.csv, or quickbooks.invoices invoices.csv, or the table with --dataset quickbooks. Name it after what the data is; the first load makes it. Without one the load is refused, whatever the folder's default says.

ovs load sales orders orders.csv --preview loads nothing and prints what the file holds: its rows and columns, what each column was read as (text, number, date, date and time, yes/no), an Excel file's tabs with their rows, and, against a table that is already there, what the file would change. Where a guess is wrong, say what the column holds: ovs load sales orders orders.csv --types zip:text keeps a zip code's leading zero, and several go together, --types zip:text,total:number. A value that doesn't fit lands empty, and the load says how many did; --preview with the same --types counts them first. Sending the same file again with --replace, read the same way, writes nothing and says unchanged: a script that sends its export every hour costs nothing when the export has not changed.

Connecting is two steps here too. ovs connect stripe key=… --test tries the key and prints what it found and the tables it will pull, saving nothing; without --test it saves the source and starts its first pull, which runs on its own: the command answers at once, ovs runs shows the pull, and --wait stays until it has landed and prints what landed or what went wrong. ovs link … --copy and ovs connect sheet take --test and --wait the same way. ovs copy copies a linked database now on one of your company's workers, waits for it, and prints where it ran, pulled on worker-2.

Before a waiting pull fetches rows, Overscore checks the person or key that started it again. An expired or revoked key or a removed person leaves a failed run without fetching rows. Someone with access can start it again.

On Free, ovs connect takes three apps, each pulled once a day at your company's own time (ovs plan says when), and a sync by hand once an hour; a fourth, a link or a webhook is refused in plain words.

ovs link takes a login that can only read. One that can write is refused, and the refusal prints the lines that make a read-only login; a link whose login is later given a right to write is paused until it only reads (see Connect a source).

For a replacement source key or Postgres password, open that source’s Settings in the app. Google Ads repair uses Reconnect Google and its account picker. Source API addresses include kind, dataset and name; Postgres mode is an explicit PUT to its /mode address with {"mode":"copy"}.

ovs files rm <id> --yes and ovs upload rm <name> use the same deletion. When it finishes, originals, saved versions and active document text are removed now; the originals' backup copies go at the next successful backup. Earlier nightly backups keep the document's text. Tables, approved notes and the deletion record stay. A failed deletion says it could not finish; try again to finish it.

Analyses

ovs analysis start "<title>"      open one: every ovs call from this folder is kept in it until you save it
ovs analysis keep "<its title>" --metric <name> [--by …] [--period …] [--filter …] | --saved-query <name> | --sql "<sql>" | --query <step> [--kind table|bar|line|kpi]
ovs analysis save --conclusion-file conclusion.md   (or --conclusion "…"; [[exhibit:N]] puts that table or chart where it sits)
ovs analysis list        ovs analysis open <id>
ovs analysis run-again <id>      the next version: every table and step asked again now, the words as they were
ovs analysis pick-up <id>        carry one on here: its record, and its next version open in this folder

ovs analysis start keeps the analysis's id in the folder's .overscore/analysis, and every ovs call from the folder sends it, so ovs query, a metric asked with ovs api POST /metrics/<name>/run and a saved query run the same way land in it as steps, each with what it asked, as an agent's do. If the file names one that was saved or is not yours, nothing is kept in it and ovs says so once. ovs pull writes every analysis you may open into analyses/<name>/; a changed conclusion pushed is its next version, and a push over an older version says to pull first.

Later

ovs later [--done|--all]        ovs later add "<what>" [--why …] [--about table:shopify.orders]        ovs later take|done|reopen <id> [--note …]

ovs later take <id> keeps a working chat with the item, creating one if you have none yet, before marking it in a session. It does not call a model. Open the item in Inbox, Later and choose Resume the chat to continue; Start a new chat is the separate fresh-start choice. The JSON list includes your saved chat id.

Conversation records

ovs record        ovs record <conversation> [--before <cursor>]

With a conversation id, ovs record prints the saved questions, answers and what they used, and each table or chart a chat drew with what it cost. If an answer has a number-check notice, it prints on a separate line below the answer. JSON output keeps the notice in the turn's number_warning field.

What was done in your company's name

ovs actions [--since 2026-09-01] [--of queries|steps] [--out actions.csv]

An admin's: their laptop, or the owner's key. With no --of, the admin actions: keys made and ended, people added, removed and put in groups, pages shared with a group, plan changes, sources connected and removed, tables deleted, a deletion asked or cancelled, every company setting changed in Settings (web search, the AI switch, house rules, the time zone, Slack, live links and the rest), a live link made or revoked, everything taken as one zip, stand-ins unmasked (how many, never which), a page published or unpublished, and a page's Real time turned on or off. --of queries is every query and insert, --of steps every step of every chat and agent. Each row says who did it and the key it came in with, by how the key starts and its name. Without --out it prints the newest; with it, every row since that day goes into a .csv or .xlsx file, written as actions.csv.part until it is whole. The admin actions are kept 90 days; the queries and the steps are kept for now. Without a laptop, an admin presses Export on Settings, Activity for the same CSV, from the day they pick.

Models and tests

ovs models <dataset>        ovs run <dataset>        ovs test <dataset>        ovs signoff <dataset> "what it is"        ovs runs        ovs run-log <id>
ovs pull        ovs push        ovs status        ovs export <dataset> <name> "select …" --format csv

A build you start from here runs on one of your company's workers where it has them, ahead of the scheduled work. ovs run waits for it, says on stderr where it is as it moves (waiting for a worker, building on worker-2, or behind another build of the same dataset), then prints each model as before with a line saying where it was built, built on worker-2. With -o json the same is in where.

ovs take-everything --out <folder> packs the whole account as your key reads it and unpacks it into the folder (or keeps the zip whole when the path ends .zip): every table as Parquet (and CSV when small) with a manifest, the models as a dbt project, every kept file and version, each dashboard with its code, a snapshot of its numbers and the runtime, so it opens from the folder, read-only. It is built on the warehouse while the command waits, however big the account. See Take everything with you.

ovs export makes a live link: an address that always returns the query's current result as a file, with no sign-in, for a spreadsheet to read. Anyone who has the address has that data, so a live link answers only while an admin has live links on (Settings, Company), and stops when the key that made it is revoked.

Context, cards, facts

ovs interview        ovs map        ovs cards        ovs approve <id> [--ticked <id>,<id>]        ovs ignore <id> [--ticked <id>,<id>]        ovs keep <id>        ovs facts <table>        ovs propose "<something heard>"
ovs note <dataset> <name> "<what you learned>" [--on table:<dataset.table>|dashboard:<slug>]        a note on a dataset, on one of its tables (a note named for a table lands on it), or on a dashboard, a model, a metric, a person or a source; a rewrite stays where the note was
ovs accuracy        ovs accuracy add "<a question>" "<the answer you know>" [--within 2%]        ovs accuracy check        ovs accuracy try <card id>

ovs cards lists what waits on a person, one row per thing, as the Inbox does. The things heard in one interview or chat, or read from one document, are one row whose id starts with g, with its lines under it: [x] marks the ones a plain yes takes. ovs approve g… saves every marked line, each exactly as approving it on its own would, and ovs ignore g… leaves them out; name the lines yourself with --ticked. The rest stay waiting on the same row, and a line's own id still answers that line alone. A card that would replace what is on file takes ovs keep <id> too: what is on file stays, and the card's words go on that fact's record beside it, never used; the card's order says which of the two the fixed order would keep. ovs propose prints each card that waits with its id and who says yes to it, so it can be approved from the terminal. A card about a metric that gives none of its calculation is kept, when approved, as a company rule in its own words, and the metric is as it was. A fact card that carries only its sentence is kept, when approved, as that sentence, about whom or what its name says (a team, a person, the company), true from the date it starts from; nothing has to be filled in to agree with it. The metrics nobody has confirmed are one row, metrics, with each metric under it: they are confirmed on their own pages, or with ovs api POST /metrics/<name>/confirm by an admin or a named confirmer. A metric a source's first sync proposed is a card of its own. Stripe's money starters give no dollar figure when the selected rows contain another currency or no currency. Choose one currency or a conversion rule first. ovs approve <id> defines it as you, confirmed, when you are an admin or a named confirmer, and ovs ignore <id> leaves it out for good for that source.

For the whole Inbox, including feedback, use the paged door; add &search=refund or &section=feedback to narrow it down. Increase offset by limit for the next page. What closed itself this month, the Inbox's Handled for you tab, is /context/handled.

ovs api GET '/context/cards?paged=true&feedback=1&limit=25&offset=0'
ovs api GET /context/handled
ovs api GET '/agent/chats?paged=true&limit=25&offset=0&search=August'

Tables you fill in

ovs table make entered store_visits --entered --columns "visit_date:date,store:link:quickbooks.customers,rep:one_of:Nina Okafor|Sarah Bell,notes:text" --one-per store,visit_date --about "One visit to one store"
ovs rows add entered store_visits visit_date=2026-10-08 store="Ember & Oak" rep="Nina Okafor"     a store by its id or its name
ovs rows add entered store_visits --file visits.csv      many at once: a CSV with the columns on its first line, or a JSON list
ovs rows edit entered store_visits 12 notes="called back"     an admin, or whoever added it
ovs rows delete entered store_visits 12
ovs rows list entered store_visits

Your fields on a table

ovs fields <dataset> <table>        ovs field <dataset> <table> <name> <type> "<what it means>"        ovs mark <dataset> <table> <keys> <field>=<value>        ovs marks <dataset> <table>

When something's off

ovs inbox [--section issues|feedback|checks|facts|all] [-n 25]        ovs runs --log <id>        ovs issues        ovs fix <id>

ovs inbox lists the cards waiting in your Inbox as you see them there. Each says what broke, went quiet or crossed its line, what was found, and the fix the card offers, which ovs fix <id> presses. A fix that pulls a source again runs on one of your company's workers, and ovs fix waits for it and prints where it ran, pulled on worker-2. An alert's card also says what it checked before it told you. A claimed fall or rise must match that part's change, direction and comparison period. A current total cannot stand for a fall. It checks whether the data under the number is fresh, whether a sync landed far fewer rows than usual, which part of the number moved, and whether the definition changed. ovs runs --log <id> prints a run's steps and the end of its log, with the source's own keys taken out, so you can see why it failed. Claude Code reads the same over MCP, and none of it spends the app's AI.

ovs --help and ovs <command> --help are complete and current.

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