Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Spec format

ScaiScribe's source of truth is a JSON document — the spec. Every other artefact (the rendered .docx, the PDF, the per-page screenshots) is derived from it. Edit the spec, re-render, you get a new artefact. Ingest a document, you get a spec back. The spec is what the system is; the binaries are what it produces.

The full schema lives at schema/scaiscribe.schema.json. This page describes the parts you'll touch when authoring or editing.

Top-level shape#

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "schema_version": "0.1",
  "doc_id": "doc_01HXYZ...",
  "type": "docx",
  "theme": "office",
  "metadata": {
    "title": "Q4 Review",
    "author": "Marketing"
  },
  "body": [
    { "type": "heading", "level": 1, "text": "Quarterly review" },
    { "type": "paragraph", "markdown": "Revenue **up 12%** YoY." }
  ],
  "fidelity": { "score": 1.0, "warnings": [] }
}

Four fields drive everything:

  • typedocx | pptx | xlsx. Drives which renderer runs and which elements are legal in body/slides/sheets.
  • theme — name of a registered theme. Controls typography, colours, page geometry. See Themes.
  • body (DOCX) / slides (PPTX) / sheets (XLSX) — the actual content tree.
  • fidelity — set by the ingester when the spec came from a binary. score ∈ [0, 1]; warnings is a list of structured records describing structural loss.

Element model — DOCX body#

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{ "type": "heading", "level": 2, "text": "Section" }
{ "type": "paragraph", "markdown": "Inline **markdown** is supported." }
{ "type": "list", "ordered": false, "items": [{ "text": "alpha" }, { "text": "beta" }] }
{ "type": "table", "headers": ["Q", "Rev"], "rows": [["Q1", "12"], ["Q2", "15"]] }
{ "type": "image", "asset_id": "ast_01HXY...", "alt_text": "Logo" }
{ "type": "callout", "style": "info", "markdown": "Heads up." }
{ "type": "code_block", "language": "python", "content": "def hello():\n    return 'hi'" }
{ "type": "chart", "kind": "column", "title": "Revenue", "data": { "categories": ["Q1","Q2"], "series": [{"name": "EMEA", "values": [10, 12]}] } }
{ "type": "page_break" }
{ "type": "toc", "title": "Contents", "depth": 3 }

Every element carries an elem_id once it lives in the database (the API mints it). When you submit elements via PATCH add_element, you omit the id — the server assigns one and returns the materialised spec.

Element model — PPTX slides#

json
1
2
3
4
5
6
7
8
{
  "layout": "title",
  "elements": [
    { "type": "slide_title", "text": "Q4 Review" },
    { "type": "slide_subtitle", "text": "FY26 close" }
  ],
  "notes": "Cover slide; 30 seconds."
}

Slide-element types: slide_title, slide_subtitle, bullet_list, image, chart, table. The layout matches a theme master id (see Themes) — title, content, two_column, image_feature, data_feature, section_divider, closing in the default theme.

Element model — XLSX sheets#

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "name": "Data",
  "freeze": { "cols": 1, "rows": 1 },
  "cells": [
    { "address": "A1", "value": "Region", "style_id": "header_row" },
    { "address": "B1", "value": "Revenue", "style_id": "header_row" },
    { "address": "A2", "value": "EMEA" },
    { "address": "B2", "value": 1450, "number_format": "currency_eur" }
  ],
  "charts": [
    { "anchor": "D2", "kind": "column", "title": "By region", "data": { ... } }
  ],
  "named_ranges": [
    { "name": "RevenueData", "range": "A1:B5" }
  ]
}

Cell style_id references a theme-defined style block (header_row, alternating_row, totals_row). number_format references the theme's format catalogue (currency_eur, percent, thousands).

Editing — PATCH operations#

Don't rewrite the whole spec to make a change. PATCH applies a list of operations atomically:

json
1
2
3
4
5
6
7
8
{
  "operations": [
    { "op": "add_element", "element": { "type": "paragraph", "markdown": "New text" } },
    { "op": "edit_element", "elem_id": "elem_01HX...", "element": { "type": "heading", "level": 2, "text": "Updated title" } },
    { "op": "delete_element", "elem_id": "elem_01HY..." },
    { "op": "move_element", "elem_id": "elem_01HZ...", "after": "elem_01HA..." }
  ]
}

For slides and sheets, the matching ops are add_slide / add_slide_element / add_sheet / add_sheet_chart. Every op returns the materialised spec with the change applied, so the client always sees what the server now considers canonical.

Versioning#

Each PATCH creates a new spec version internally; the document's current_version advances. Ingest replaces the version (the spec becomes whatever the ingester reconstructed). Finalize renders a specific version — usually current_version. Old versions stay queryable for 30 days as a recovery aid.

The schema_version field is a stability contract. v0.1.x specs are forward-compatible within the v0.1 line; a breaking schema change would bump to 0.2 and ship with a migration policy.

Why JSON?#

  • Diffable. You can git diff two versions of a spec.
  • Validatable. JSON Schema catches structural mistakes at the boundary.
  • Language-agnostic. Python, TypeScript, .NET SDKs all generate types from the same schema.
  • Editable by AI. The MCP tool surface is structured around making targeted edits to this exact tree.
  • Cheap to store. Specs are small (10–500 KB typical); binaries are ~10× larger and derivable.

Where to next#

  • Themes — how typography and colours apply across formats.
  • Charts — the dual-emission strategy.
  • Ingestion — what survives when you read a binary back in.
  • Reference → API — the REST and MCP endpoints that produce / mutate specs.
Updated 2026-07-03 13:00:09 View source (.md) rev 10