cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

cat/module-path
012 / docs / reference / json.md
json.md 10.2K rendered
1---2title: "JSON output"3sidebar_position: 74---56# JSON output78Commands without the screen write their results as JSON with9`--format json`, or as NUON (the same values, as nushell reads them)10with `--format nuon`; `012 export` writes to a file, so its result11comes with `--report json`. The schemas below are stable: a later 01212may add fields, but never renames or removes one, or changes its type.13Errors are not JSON: they go to standard error as text, with the exit14statuses in [Command line](command-line.md#exit-status).1516## Values1718A cell's value in JSON keeps what its format means: money stays money19and a date a date, so what reads it can write it back as it was. The20same form is what [`012 get`](#get) and the MCP server's reads return21and what `012 set --value`, `write_cells`, `write_table` and22`create_workbook` take:2324| Value | Is | Shown |25|---|---|---|26| `3.5`, `"00123"`, `true`, `null` | A number in the Automatic format, text (always text, however it looks), a boolean, a blank cell | `3.5`, `00123`, `TRUE` |27| `{"currency": 3.5}` | An amount; `"symbol": "€"` for another currency than dollars | `$3.50`, `€3.50` |28| `{"percent": 0.12}` | A fraction shown as a percentage | `12%` |29| `{"date": "2026-09-29"}` | A date, or with a time `"2026-09-29T14:30:00"` on the sheet's clock (an offset, `Z` or `-06:00`, converts it to the local time zone) | `9/29/2026` |30| `{"time": "14:30:00"}` | A time of day | `2:30:00 PM` |31| `{"duration": "90min"}` | Elapsed time, in [nushell's units](https://www.nushell.sh/book/types_of_data.html#durations) (`ns` to `wk`), or written as a number of seconds | `1:30:00` |32| `{"size": 1500}` | A number of bytes, or written as nushell's `"1.5kb"` | `1.5 kB` |33| `{"number": 1234.5, "decimals": 2}` | A number in another format than Automatic | `1,234.50` |34| `{"text": "00123"}` | Text, written this way or as a string | `00123` |3536Any of the objects may add `"decimals"` (0 to 15) or `"format"`, a37[number format](../sheets/formatting.md#number-formats) code shown in38the type's place (`{"date": "2026-09-29", "format": "yyyy-mm-dd"}`).39A value read back carries them when its cell's format isn't the one its40type gives by itself, so writing it again gives the same value and41format. An error reads as its text (`"#DIV/0!"`). NUON has its own42[types](../nushell/types.md) for sizes, durations and dates, and none43for currency or percentages.4445## get4647`012 get file ref --format json` writes a cell's [value](#values) alone,48or a range as a list of records named by its first row49(`--no-header`: by column letters), each field a value, as50[Scripts](../files/scripts.md#get) describes. In NUON dates, file sizes51and durations are nushell's, and currency and percentages numbers.5253## set5455```json56{57 "file": "book.012",58 "saved": true,59 "changes": [60 {"kind": "cell", "sheet": "Sheet1", "item": "B7", "field": "input", "old": "12", "new": "=SUM(B1:B6)"}61 ],62 "warnings": ["Sheet1!C2: Enter a number between 1 and 10"]63}64```6566| Field | Is |67|---|---|68| `file` | The workbook's path, as given |69| `saved` | Whether the file was written: false with `--dry-run` |70| `changes` | What changed, as [`diff`](#diff) lists it |71| `warnings` | Entries a validation rule marks invalid but lets in |7273## recalc7475```json76{77 "file": "book.012",78 "errors": [79 {"cell": "Q3!B7", "sheet": "Q3", "addr": "B7", "value": "#DIV/0!", "why": "Division by zero in B6/C6", "jev": false}80 ],81 "circular": false,82 "notebook_failures": []83}84```8586`errors` are the formulas showing errors, sheet by sheet and row by87row; `why` is what the screen's context line says, or `""`; `jev` is88set for a JEV function's error that `--jev` would answer. `circular` is89set when a formula reads its own cell. `notebook_failures` are the90lines `--notebooks` reported for cells that failed. The exit status is911 when there are errors, as without `--format`.9293## describe9495```json96{97 "sheets": [98 {99 "name": "Sales", "kind": "sheet", "shown": true, "hidden": false,100 "used": "A1:F9", "rows": 9, "cols": 6, "cells": 54, "formulas": 9, "errors": 0,101 "header_row": 1, "columns": ["Date", "Region", "Units", "Price", "Total", "Share"],102 "frozen": [1, 0], "filter": "",103 "tables": [{"name": "Orders", "range": "A1:F9", "columns": ["Date", "Region", "Units", "Price", "Total", "Share"]}],104 "regions": [{"name": "files", "kind": "output", "range": "H1:J12"}],105 "charts": [{"number": 1, "title": "Units by date", "type": "column", "data": "A1:C9", "at": "H2"}]106 }107 ],108 "names": [{"name": "Rate", "range": "Sales!B12"}]109}110```111112| Field | Is |113|---|---|114| `kind` | `sheet`, `notebook` or `pivot` |115| `shown`, `hidden` | The sheet shown when the file was saved; a hidden tab |116| `used` | A1 to the last cell with contents, `""` for an empty sheet; `rows` and `cols` its size |117| `cells`, `formulas`, `errors` | Cells with contents, formulas, and formulas showing errors; `formulas` and `errors` are -1 on sheets of over a million cells, which aren't counted |118| `header_row`, `columns` | The row guessed to name the columns (1 for the first; 0 and `[]` when none) and its names, `""` for a blank cell |119| `frozen` | Frozen rows and columns |120| `filter` | The filtered range, or `""` |121| `tables` | Named tables, header row included in `range` |122| `regions` | Notebook outputs (`kind` `output`) and linked files (`linked`, with `file`) sent to the sheet, with the range their rows fill |123| `charts` | Each chart's `number` (for `012 export --chart`), title (its type's name when it has none), `type`, `data` range and `at`, the cell under its top-left corner |124| `pivot` | On a pivot table, `{"source": "Sales!A1:F9"}` |125| `notebook_cells` | On a notebook, each cell: `number`, `kind` (`code` or `note`), `name` (its output's), `source`, and for code `state` (`ran`, `failed`, `not run`) and `error` |126| `names` | Named ranges, `range` `#REF!` when its cells were deleted |127128## diff129130`012 diff a b --format json` writes a list of changes:131132| Field | Is |133|---|---|134| `kind` | `cell`, `sheet`, `region`, `table`, `layout` (another field of a sheet: widths, rules, charts), `name`, `macro`, `notebook` or `workbook` (its locale, its arithmetic) |135| `sheet` | The sheet, by its name in the second workbook where it has one |136| `item` | The cell, region, name, macro or field that changed |137| `field` | What about it: for a cell `input`, `value`, `format` or `note`; for a sheet `added`, `removed`, `renamed` or `moved` |138| `old`, `new` | Its values, `null` where it wasn't or isn't there |139140See [Diff and merge in git](../files/git.md#012-diff).141142## export143144`012 export file out --report json`:145146```json147{"file": "out.csv", "format": "csv", "rows": 9, "notes": ["1 formula saved as values"]}148```149150## merge-driver151152`012 merge-driver base ours theirs --format json` writes153`{"conflicts": [...]}`, each with `where`, `field`, and the `base`,154`ours` and `theirs` versions as text (`""` where it isn't there), and155`what` for a conflict about a whole sheet. The merge and exit status156are as without it.157158## MCP tools159160The [MCP server](../agents/mcp.md)'s tools return these, as structured161content with the same JSON as text beside it. `describe` with a path returns `path` and162[describe's](#describe) schema, and so does `create_workbook`, of the163workbook it made; without a path, `describe` returns the folders open164to the server and the workbooks in them, `more` set when there were165more than it lists:166167```json168{169 "roots": ["/Users/me/Documents/budgets"],170 "workbooks": [171 {"path": "2026.012", "format": "012", "size": 18422, "modified": "2026-09-29T14:02:11Z"},172 {"path": "bank/september.csv", "format": "CSV", "size": 5120, "modified": "2026-09-28T09:30:00Z"}173 ]174}175```176177`list_errors` returns recalc's `errors` and `circular`.178179`read_range`, and the range resources:180181```json182{183 "range": "Sales!A1:C3", "rows": 3, "cols": 3,184 "values": [["Region", "Units", "Price"], ["North", 3, {"currency": 12.5}], ["South", 5, null]],185 "text": [["Region", "Units", "Price"], ["North", "3", "$12.50"], ["South", "5", ""]],186 "formulas": {"C3": "=IF(B3>4, \"\", 9.75)"},187 "truncated": false188}189```190191`values` are [values](#values) (blank is `null`, an error its text);192`text` is each cell as shown, left out with `"text": false`;193`truncated` is set when the range held more cells than `max_cells`, and194`rows` says how many came back. The result's `_meta["o12/view"]` holds what195[the view](../agents/mcp.md#views-in-the-chat) draws: `title` (the196workbook's name), `where` and `html`.197198`write_cells` takes each entry's `input` as a person types it, or its199`value` as a [value](#values), and `format`, a number format code for200the cell or, alone, for every cell of a range:201202```json203{"entries": [204 {"ref": "B2", "value": {"currency": 3.5}},205 {"ref": "C2", "input": "12%"},206 {"ref": "D2", "value": {"date": "2026-09-29"}, "format": "yyyy-mm-dd"},207 {"ref": "E2:E9", "format": "#,##0.00"}208]}209```210211`write_table` (and `apply_operations`' `write_table`, and212`create_workbook`'s `data`) writes rows under a header naming their213columns, in `columns`' order (a JSON object's keys can reach the tool214in any order), each row a record of values or a list of them in that215order, and formats each column as its first typed value, or as216`formats` says:217218```json219{"at": "A1", "columns": ["Item", "Price", "Bought"],220 "rows": [{"Item": "Tea", "Price": {"currency": 3.5}, "Bought": {"date": "2026-09-29"}},221 ["Cake", 2, {"date": "2026-09-28"}]],222 "formats": {"Price": "$#,##0.00"}}223```224225Writes (`write_cells`, `write_table`, `apply_operations`, `sort`, `filter`) return226`{"saved", "changes", "warnings"}` as [`set`](#set) does, `saved` false227for a dry run or a change that changed nothing; `create_chart` adds228`"chart": {"sheet", "number", "title"}`, its view in `_meta` as229`read_range`'s (with `name`, the chart's data), and `create_pivot`230`"sheet"`, the pivot's.231232| Tool | Returns |233|---|---|234| `evaluate` | `cell` where it was computed, `value`, `text` as shown, `error` explaining an error value, and `spill`, a `read_range` result, when the formula spilled |235| `find` | `matches`, each `cell`, `text` and `input`, the first `limit` of them, and `total` |236| `run_notebook_cell` | `notebook`, `cell`, `name`, `state` (`ran` or `failed`), `error`, `output` as NUON and `cut` when the output was longer than 64 KB |237238## version239240```json241{"version": "v0.9.0", "go": "go1.27.1", "os": "darwin", "arch": "arm64"}242```