# Edit

> Upload a Word document, read its text, send edits, and download the edited document and a redline of what changed.

Upload a `.docx`, read its paragraphs and their numbers, then send edits that name the
paragraphs to change by those numbers. A fourth call fetches the result when you don't wait
for it. Every edit is a real Word tracked change, so the output is a document a reviewer can
accept or reject, plus a redline of exactly what the request changed.

| | |
| --- | --- |
| Base URL | `https://api.versionstory.com` |
| Auth | `Authorization: Bearer vs_...`. See [Authentication](/developers/authentication) |

Non-2xx responses use the same envelope as [Compare](/developers/rest/compare), and
`X-Request-Id` works the same way.

```json
{ "error": { "code": "INVALID_EDIT", "message": "...", "request_id": "..." } }
```

## The flow

An edit says which paragraph to change by its number, such as "replace `[Company Name]` in
paragraph 7". So before you can edit, you need the document stored with us and its paragraph
numbers:

1. **Upload it.** `POST /v1/files` with the `.docx` returns a `file_id`, which every later
   call uses to name the document.
2. **Read it.** `GET /v1/files/{file_id}/content` returns the document's text with each
   paragraph's number, and the numbers of its headers, footers, notes, comments and tables.
   Find what you want to change and note its number.
3. **Edit it.** `POST /v1/edit` with the `file_id` and a list of `edits` that use those
   numbers. Alternatively, send an `instruction` in plain English and skip reading the numbers
   yourself. Returns an `edit_id` and, with `wait`, the finished result.
4. **Get the result.** `GET /v1/edit/{edit_id}` until the result is ready, if you did not
   wait. Download the edited document and the redline.

## POST /v1/files

`multipart/form-data` with one file field, `document`.

```bash
curl -sS https://api.versionstory.com/v1/files \
  -H "Authorization: Bearer vs_live_..." \
  -F "document=@nda.docx"
```

```json
{ "file_id": "3f2a9c1e-...", "status": "processing" }
```

A `.docx` answers at once while processing finishes in the background. `processing` here
needs no polling, because reading the document and editing it both wait for it. A `.doc` or `.pdf` is
converted first: the request waits up to 20 seconds and answers `ready`, `failed` with an
`error`, or `processing` if conversion is still running.

A `file_id` names one immutable upload, so its content never changes. An edit produces a new
file with its own `file_id`; to edit the result, read that file's content. A user's updated
copy is a new upload.

## GET /v1/files/&#123;file_id&#125;/content

Returns the document's text and structure.

| Parameter | In | Required | Default | Meaning |
| --- | --- | --- | --- | --- |
| `file_id` | path | Yes | — | The id from `POST /v1/files` |
| `format` | query | No | `md` | `json` for the full structure with every address an edit uses, `md` for the same text as Markdown. Use `json` for editing |

```bash
curl -sS "https://api.versionstory.com/v1/files/3f2a9c1e-.../content?format=json" \
  -H "Authorization: Bearer vs_live_..."
```

The call waits up to 5 seconds for processing to finish and answers as soon as it finishes. While the
document is still processing, the response carries `Retry-After: 1`; call again.

The JSON carries `document.schema_version` (currently `1`), then each body paragraph
with its `index`, style, list numbering, tables, sections, fields, bookmarks and inline
formatting. Beside the body it lists the document's `headers`, `footers`, `notes`,
`comments`, `revisions`, `list_styles` and `settings`. Tabs and line breaks appear as
segments, which selectors match as `\t` and `\n`. The document is read with
content controls and smart tags unwrapped.

This is different from the redline JSON described on
[JSON format](/developers/reference/json-format), which describes the changes between two
documents. The two have separate `schema_version`s.

What the JSON contains, and how to read it:

- **Indexes.** A body paragraph has an `index`, counted from 0. The paragraphs of a header,
  footer or note carry an `index` counted from 0 within that part. Table cells' paragraphs
  are included in the count.
- **Parts.** Each header and footer prints a `part` locator (`kind`, `type` and the first
  `section` that shows it) and `sections`, every section that shows it. Each note prints a
  `part` (`kind` and `number`) and a `label`, the mark the reader sees.
- **Counting from 1.** Sections, list levels, tables, rows, columns, comment threads, notes
  and revisions all count from 1. `index` is the only value counted from 0.
- **Revisions.** Existing tracked changes are listed one entry per accept/reject decision, each with a `number`,
  a `kind` (`insertion`, `deletion`, `move` or `property`), `author`, `date` and `text`. A
  replacement is two units: a deletion and an insertion.
- **Comments.** Listed as threads with their `replies`, numbered in document order.

## POST /v1/edit

A JSON body. A request that uploads a style template is `multipart/form-data` instead, with
the same fields as form fields and `edits` and `outputs` as JSON strings.

| Field | Required | Meaning |
| --- | --- | --- |
| `file_id` | Yes | The upload to edit |
| `schema_version` | Yes | The `document.schema_version` from `GET /v1/files/{file_id}/content`, `1`. A mismatch is `400 INVALID_EDIT`, so a change to how that content counts paragraphs cannot silently shift your addresses |
| `edits` | One of `edits` or `instruction` | 1 to 200 edit objects, applied in order |
| `instruction` | One of `edits` or `instruction` | A plain-English request, up to 8,000 characters. See [Editing with an instruction](#editing-with-an-instruction) |
| `replacement_text` | No | With `instruction` only: exact replacement or new paragraph text |
| `author` | No | Name on every tracked change and comment the request makes. Default `Version Story AI` |
| `date` | No | ISO 8601 date and time for those changes. Default: the time of the request |
| `wait` | No | `true` answers with the finished result instead of an `edit_id` to poll |
| `outputs` | No | With `wait`: which files to return, `["docx"]` (the default), `["redline"]`, or both |
| `inline` | No | With `wait`: `true` adds the file's bytes as `content_base64` to each ready download |
| `style_template` | No | Multipart only: a `.docx`, `.dotx` or `.dotm` whose styles and numbering are imported before the first edit |

The body is at most 208 KB, 200 KB of it edits. Unknown fields are refused. That includes
`document`: a document is staged with `POST /v1/files`, never sent with the edit.

```bash
curl -sS https://api.versionstory.com/v1/edit \
  -H "Authorization: Bearer vs_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "file_id": "3f2a9c1e-...",
    "schema_version": 1,
    "author": "Dana Reyes",
    "wait": true,
    "outputs": ["docx", "redline"],
    "edits": [
      {
        "type": "replace",
        "paragraphs": { "index": 7 },
        "text": { "match": "[Company Name]" },
        "content": "Acme, Inc."
      }
    ]
  }'
```

### Attributing the changes

`author` is the name Word shows on every tracked change in the result, and on any comment that
does not name its own author. It is one line of at most 255 characters with no control
characters; surrounding spaces are trimmed. A comment edit can carry its own `author`, which
takes precedence for that comment.

`date` takes an ISO 8601 timestamp such as `2026-10-05T14:30:00Z`. Send the author's UTC
offset (`2026-10-05T10:30:00-04:00`) if you want Word versions that read only the local
clock time to show local time; without an offset, the value is UTC. It is kept to the second.

### Waiting for the result

By default, `POST /v1/edit` answers `202` with the edit's ids and you poll. With
`"wait": true`, the response is the same body `GET /v1/edit/{edit_id}` returns: `200` when the
edit finished, `202` if it is still processing, in which case you continue by polling.
`outputs` and `inline` apply only with `wait`.

## How edits address things

This is the short version; [Edit operations](/developers/rest/edit-operations#locators) has every form.

An edit never uses a bare number. It names what it acts on with a locator object, an address
such as `{"index": 7}`, and the document's content prints each locator so you can copy it.

### Locating paragraphs

Every operation that acts on paragraphs names them in one required field, `paragraphs`:

| Form | Names |
| --- | --- |
| `{"index": 12}` | Body paragraph 12 |
| `{"part": {"kind": "footnote", "number": 3}, "index": 0}` | The first paragraph of footnote 3 |
| `{"part": {"kind": "footer", "type": "default", "section": {"number": 1}}, "index": 0}` | The first paragraph of section 1's default footer |
| `{"from": {"index": 9}, "through": {"index": 11}}` | The paragraphs between two locators, inclusive, within one part |
| `[{"index": 3}, {"from": ..., "through": ...}]` | An array of locators and ranges |
| `"all"` | Every paragraph of every part |
| `{"ref": "p1"}` | A paragraph this same request created |

There is no implicit scope: a whole-document replacement says `"paragraphs": "all"`. Other
things have locators of the same style: a section is `{"number": 2}`, a table is
`{"number": 2}`, a row or column is `{"table": {"number": 2}, "number": 4}`, a comment thread
is `{"number": 3}`, a note is `{"kind": "footnote", "number": 3}`, and an existing tracked
change is `{"number": 5}`, `"all"`, or a filter by `author` and `kind`.

### Selecting text

`text` selects text inside the named paragraphs. `at` names an insertion point.

```json
"text": { "match": "Series A Preferred Stock", "prefix": "the ", "suffix": " issued", "occurrence": 2 }
"at": "start"
"at": { "after": { "match": "Name:\t" } }
```

- **`match`** is the text to find. It is case-sensitive, and a match never crosses a paragraph
  boundary. Curly and straight quotes, en and em dashes and hyphens, and non-breaking and
  ordinary spaces are read alike.
- **`prefix` and `suffix`** are optional context that must sit immediately before and after
  the match. They narrow the candidates; the edit never acts on them.
- **`occurrence`** picks among several matches, counted across every paragraph the edit
  names: `N` for the Nth, or `"all"` for every one. Omitted, the match must be unique.
- **`at`** is `"start"`, `"end"`, or `{"after": selector}` or `{"before": selector}`.

A selector that matches nothing, or more than once without `occurrence`, is refused, naming
the count and the paragraphs that hold the text:

```
edit at index 0 (replace): "Preferred" matches 3 times in paragraph 8; give prefix or suffix
so it matches once, or occurrence (1 to 3, or "all")
```

### Writing content

New text is always `content`: a string, or an array of runs. In a string, `\t` becomes a tab
and `\n` a line break. Runs add formatting and richer content.

| Run | Inserts |
| --- | --- |
| `{"text": "..."}` | Text |
| `{"type": "tab"}` | A tab |
| `{"type": "break", "kind": "line" \| "page" \| "column"}` | A break |
| `{"type": "field", "code": "PAGE"}` | A Word field |
| `{"type": "cross_reference", "to": locator, "show": "number"}` | A reference to another paragraph |
| `{"type": "footnote" \| "endnote", "content": ...}` | A note |
| `{"type": "hyperlink", "url": "...", "content": ...}` | Linked text |

Any run can carry `format`, for example `{"text": "Name and title", "format": {"bold": true}}`.
Plain new text takes the formatting of the text before it, as typing does in Word.

For property objects such as `format`, a value sets the property, `false` turns it off even
where the style turns it on, `null` removes the direct setting so the style decides, and an
omitted key leaves it alone. To remove bold everywhere, send `false`; `null` would leave a
heading that is bold through its style bold.

## How a request is applied

Three rules govern the list of edits in one request:

1. **Addresses mean the uploaded document.** Every locator and selector refers to the
   document as you read it, whatever earlier edits in the request have done. If edit 1
   inserts a paragraph after paragraph 5, edit 2's `{"index": 10}` still means the paragraph
   that was number 10 when you read it.
2. **Edits apply in request order.** When two edits set the same property of the same text,
   the later one wins. Content inserted at the same point appears in request order.
3. **An edit whose target an earlier edit removed or rewrote is refused**, and so is the whole
   request. For example, replacing text that an earlier edit already deleted.

The result is the same as sending each edit as its own request in turn, except that every
address comes from the one read of the document. Three consequences:

- **All or nothing.** If any edit is refused, or fails while being applied, nothing is
  applied and no file is produced. Every refusal in the request is reported together.
- **Undefined means refused.** A combination whose result the reference does not define is
  refused with an error that names it. Nothing is guessed.
- **Repeating an edit repeats its effect.** Sending an insertion twice inserts the content
  twice.

To refer to something an earlier edit created, give that edit a `ref` and use
`{"ref": "name"}` where a locator is expected. A ref must be defined before it is used. The
text of a paragraph the request creates cannot be selected, because it was not in the content you read;
give it its formatting in the edit that creates it.

Decisions on existing tracked changes (`accept_revision`, `reject_revision`) go alone. A
request that contains one contains only decisions. To accept changes and then edit, send two
requests, the second against the first's result.

## Operations

Every edit has a `type` and the fields that type defines. Unknown fields are refused. Each
operation's fields, accepted values, examples and refusals are in the
[Edit operations](/developers/rest/edit-operations) reference, along with the full rules for
locators, text selectors, content runs and property values. The machine-readable definition of
each is in the API's OpenAPI specification under `components.schemas.EditRequest`.

| Operation | Does |
| --- | --- |
| `insert` | Insert text, breaks, fields, cross-references, notes or links at a point |
| `replace` | Replace selected text |
| `delete` | Delete selected text, or a page break |
| `format` | Change character formatting of selected text or whole paragraphs |
| `insert_paragraphs` | Add new paragraphs at a destination, with styles and lists |
| `delete_paragraphs` | Delete whole paragraphs, and tables a range covers |
| `move_paragraphs` | Move paragraphs and tables as a tracked move |
| `split_paragraph` | Split one paragraph in two at a point |
| `join_paragraphs` | Merge a range of paragraphs into one |
| `set_paragraph_style` | Apply a paragraph style, optionally clearing direct formatting |
| `set_paragraph_format` | Set alignment, indents, spacing and pagination |
| `set_numbering` | Apply, restart, continue, remove or reformat list numbering |
| `insert_section_break`, `remove_section_break` | Add or remove a section break |
| `set_page_setup` | Set orientation, paper, margins, first page and page numbering |
| `add_header_footer` | Give a section a header or footer of its own |
| `insert_table`, `delete_table` | Add or remove a table |
| `insert_table_row`, `delete_table_row` | Add or remove a table row |
| `insert_table_column`, `delete_table_column` | Add or remove a table column |
| `add_hyperlink`, `set_hyperlink`, `remove_hyperlink` | Link existing text, change a link's address, or unlink |
| `insert_table_of_contents` | Insert a table of contents |
| `refresh_fields` | Mark every field for Word to update on open |
| `delete_note` | Delete a footnote or endnote |
| `add_comment` | Add a comment on text, a paragraph or a range |
| `reply_comment`, `resolve_comment` | Reply to a comment thread, or resolve it |
| `accept_revision`, `reject_revision` | Decide existing tracked changes |
| `set_document_settings` | Set Track Changes, editing restriction and personal-information removal |

Every change to document content is a tracked change carrying the request's `author` and
`date`. The things Word cannot track are applied directly and stay when revisions are accepted
or rejected: comments and replies, document settings, field update flags, and supporting
parts such as new list definitions, a notes part or a header part.

### Examples

Fill in a signature block. Each value goes after its label's tab, so the underlined tab that
draws the line is untouched:

```json
[
  { "type": "replace", "paragraphs": { "index": 302 }, "text": { "match": "[Insert Company Name]" }, "content": "Version Story" },
  { "type": "insert", "paragraphs": { "index": 304 }, "at": { "after": { "match": "Name:\t" } }, "content": "Jordan Bryan" },
  { "type": "insert", "paragraphs": { "index": 305 }, "at": { "after": { "match": "Title:\t" } }, "content": "CTO" }
]
```

Replace a defined term everywhere, and un-bold a whole document:

```json
[
  { "type": "replace", "paragraphs": "all", "text": { "match": "Acme Ltd", "occurrence": "all" }, "content": "Acme Holdings Ltd" },
  { "type": "format", "paragraphs": "all", "format": { "bold": false } }
]
```

Add a comment, then reply to it. The `ref` names the new thread:

```json
[
  { "type": "add_comment", "paragraphs": { "index": 25 }, "content": "double check on this", "ref": "c1" },
  { "type": "reply_comment", "comment": { "ref": "c1" }, "content": "will do" }
]
```

Add a row to a table after an existing row, with one value per column:

```json
{
  "type": "insert_table_row",
  "destination": { "row": { "index": 311 }, "position": "after" },
  "cells": ["Google LLC", "Google Cloud Support", "AI-Enhanced PDF Conversion"]
}
```

Turn a page break into a section break that restarts page numbering. Three edits, with the
last addressing the section the second one creates:

```json
[
  { "type": "delete", "paragraphs": { "index": 129 }, "text": { "match": [{ "type": "break", "kind": "page" }] } },
  { "type": "insert_section_break", "paragraphs": { "index": 129 }, "section_type": "next_page", "ref": "schedule" },
  { "type": "set_page_setup", "section": { "ref": "schedule" }, "page_setup": { "page_numbering": { "start": 1 } } }
]
```

Move a block of paragraphs to a new place. Accepting the change leaves them at the
destination only; rejecting restores them:

```json
{
  "type": "move_paragraphs",
  "paragraphs": { "from": { "index": 9 }, "through": { "index": 11 } },
  "destination": { "paragraph": { "index": 19 }, "position": "after" }
}
```

## Editing with an instruction

`instruction` takes the place of `edits`. Send the `file_id`, the `schema_version` and a
sentence; the server reads the document and builds the edits, so you supply no locators.

```bash
curl -sS https://api.versionstory.com/v1/edit \
  -H "Authorization: Bearer vs_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "file_id": "3f2a9c1e-...",
    "schema_version": 1,
    "instruction": "Replace every \"Acme Ltd\" with \"Acme Holdings Ltd\"."
  }'
```

The edits made from your instruction are checked against the document exactly as written edits are, and the
request is applied the same way, with the same response. `replacement_text` supplies exact
text separately, for example newline-separated paragraphs to insert.

The instruction path covers existing paragraph styles and direct-format cleanup, decisions on
tracked changes, deleting existing text, literal replacements, inserting supplied paragraphs,
and turning one explicit page break into a section break with a page-number restart. Drafting
prose, defining styles, setting fonts, adding table rows and removing paragraph elements are
outside it; use `edits` for those.

An instruction that is unclear, unsupported, conflicting or incomplete is refused with
`400 INVALID_EDIT` before anything is created, as is one that selects no changes. A
temporarily unavailable model is `503 EDIT_MODEL_UNAVAILABLE`.

## Response

`POST /v1/edit` and `GET /v1/edit/{edit_id}` return the same shape. `files` holds three
entries, and their `file_id`s are allocated when the edit is created, so they are present
while processing and after a failure.

| Entry | Contents |
| --- | --- |
| `files.original` | The document you uploaded |
| `files.edited` | The edited document, with every edit as a tracked change |
| `files.redline` | A redline: the uploaded document compared with the edited one |

`comparison_id` opens the same redline through [`GET /v1/compare/{comparison_id}`](/developers/rest/compare),
if you want it as `pdf`, `md` or `json` as well.

### GET /v1/edit/&#123;edit_id&#125;

Safe to call as often as you like: it creates nothing. It waits up to 5 seconds for the apply
job and answers as soon as it finishes.

| Parameter | In | Required | Default | Meaning |
| --- | --- | --- | --- | --- |
| `edit_id` | path | Yes | — | The id from `POST /v1/edit` |
| `format` | query | No | `docx` | `docx` selects downloads for `files.edited`, `redline` for `files.redline`. Repeatable or comma-separated: `docx,redline` |

```bash
curl -sS "https://api.versionstory.com/v1/edit/edt_...?format=docx,redline" \
  -H "Authorization: Bearer vs_live_..."
```

### Response — ready

Downloads are nested under the file they belong to. Both report `format: "docx"`.

```json
{
  "edit_id": "edt_MTp2cy0xOnZlLTE",
  "status": "ready",
  "comparison_id": "cmp_MTp2cy0xOnZtLTE",
  "files": {
    "original": { "file_id": "3f2a9c1e-...", "file_name": "nda.docx" },
    "edited": {
      "file_id": "8b41d7a0-...",
      "file_name": "nda (edited).docx",
      "downloads": [
        {
          "format": "docx",
          "url": "https://documents.versionstory.com/...",
          "file_name": "nda (edited).docx",
          "expires_at": "2026-10-05T22:00:00+00:00"
        }
      ]
    },
    "redline": {
      "file_id": "d90c5e12-...",
      "file_name": "Redline.docx",
      "downloads": [
        {
          "format": "docx",
          "url": "https://documents.versionstory.com/...",
          "file_name": "Redline.docx",
          "expires_at": "2026-10-05T22:00:00+00:00"
        }
      ]
    }
  }
}
```

Each `url` is signed, needs no `Authorization` header, and stops working at `expires_at`, one
hour after it was issued. Poll again for fresh ones. With `"inline": true` on a waiting
request, each ready download also carries `content_base64`, the file's bytes, so no second
request is needed.

```bash
curl -sS --fail -o nda-edited.docx "<files.edited.downloads[0].url>"
```

### Response — still processing

Returned with `Retry-After: 1`. A selected file that is not ready yet carries
`pending_formats: ["docx"]` in place of `downloads`. There are no top-level `downloads` or
`pending_formats` fields on an edit.

### Response — failed

Terminal: `"status": "failed"` with an `error`. Submit a corrected request to retry. There are two codes.

| `error.code` | Meaning |
| --- | --- |
| `INVALID_EDIT` | The worker could not apply an edit as written, for instance one whose target an earlier edit removed. `error.message` names it as `edit at index N (type): ...`, so you can correct it and send the request again |
| `EDIT_FAILED` | Anything else, without a message. Also returned if a requested output is missing after the worker succeeded; any downloads that did get produced stay in the response |

Nothing is applied when an edit fails.

### Editing the result again

Any `file_id` in the response can be downloaded as `docx` or `pdf` through
`GET /v1/files/{file_id}`. To edit further, read the `edited` file's content and send a new
request naming its `file_id`. The earlier result stays untouched.

## Errors

| Status | Code | Cause |
| --- | --- | --- |
| 400 | `INVALID_REQUEST` | The request itself is malformed: a missing `file_id`, a field of the wrong type, `outputs` or `inline` without `wait`, or an `author` or `date` that does not meet the rules above |
| 400 | `INVALID_EDIT` | An edit was refused before anything was created: a stale `schema_version`, a selector that matched nothing or too much, a combination that is undefined, or an `instruction` that could not be turned into edits. Every refusal in the request is reported together, each as `edit at index N (type): ...` |
| 400 | `UNSUPPORTED_FILE_TYPE` | The upload to `POST /v1/files` is not a `.docx`, `.doc` or `.pdf` |
| 400 | `DOCUMENT_UNREADABLE` | The upload is not a readable Word file: corrupt, or encrypted |
| 400 | `INVALID_EDIT_ID` | The `edit_id` is malformed |
| 401 | `INVALID_API_KEY` | Missing, malformed, unknown, revoked, or expired key |
| 404 | `EDIT_NOT_FOUND` | No such edit or file, or it belongs to another organization. `GET /v1/files/{file_id}` answers `FILE_NOT_FOUND` |
| 409 | `CONFLICT` | The document is still processing after the wait; retry after `Retry-After` |
| 410 | `CONTENT_DELETED` | The file's content was removed by the organization's retention setting |
| 503 | `EDIT_MODEL_UNAVAILABLE` | An `instruction` request could not reach the model; try again later |

Validation runs before anything is created, so a refused request costs nothing.

## The whole loop, in Python

```python
import base64
import requests

BASE = "https://api.versionstory.com"
headers = {"Authorization": "Bearer vs_live_..."}

with open("nda.docx", "rb") as document:
    uploaded = requests.post(f"{BASE}/v1/files", headers=headers, files={"document": document})
uploaded.raise_for_status()
file_id = uploaded.json()["file_id"]

content = requests.get(f"{BASE}/v1/files/{file_id}/content", headers=headers, params={"format": "json"})
content.raise_for_status()
# Read content.json() to find the paragraph index for "[Company Name]".

edit = requests.post(
    f"{BASE}/v1/edit",
    headers=headers,
    json={
        "file_id": file_id,
        "schema_version": content.json()["document"]["schema_version"],
        "author": "Dana Reyes",
        "wait": True,
        "inline": True,
        "edits": [
            {
                "type": "replace",
                "paragraphs": {"index": 7},
                "text": {"match": "[Company Name]"},
                "content": "Acme, Inc.",
            }
        ],
    },
)
edit.raise_for_status()
body = edit.json()
if body["status"] == "failed":
    raise RuntimeError(f"Edit failed: {body['error']['code']} {body['error'].get('message', '')}")
if body["status"] != "ready":
    raise RuntimeError("Still processing: poll GET /v1/edit/{edit_id}")

download = body["files"]["edited"]["downloads"][0]
with open(download["file_name"], "wb") as out:
    out.write(base64.b64decode(download["content_base64"]))
```

## Related

- [Compare](/developers/rest/compare): two documents, one redline
- [JSON format](/developers/reference/json-format): the redline JSON behind `format=json`
- [Errors](/developers/reference/errors): every error code and what to do about it
