The document format

A vibe-sketch document is one JSON object that holds a drawing and everything the compute side needs from it. It is saved as QVF, the vibe family’s file format, or as plain JSON in a .vsk file. This page describes schema version 1, identified by "schema": "vibe-sketch/1".

In a QVF file

A vibe-sketch QVF (the QVF specification, release 0.3, lives in the qvf repository) holds:

Section

Kind

Contents

vibesketch_document

x_vibesketch.document

document: this JSON object. species: each species’ id, compound number, name, SMILES, InChI, InChIKey, formula, charge, multiplicity and its structure section, those without a 3D structure included. sdf: the species as an SD file.

preview

preview

thumbnail: a PNG of the species, 256 to 1024 pixels on its longest side, the picture a file manager shows. image: the scheme as SVG.

structure_<species id>

structure

structure: the species’ first conformer, atoms in angstrom; bonds: its bonds with orders. On the section: the species’ charge, multiplicity, SMILES, InChI, InChIKey, its name when you gave it, and its compound number as label.

reaction_scheme

reaction.scheme

scheme: the reaction steps whose reactants and products all have a 3D structure, each species named by its structure section.

job_<job id>

job.spec

spec: a calculation on a species with a 3D structure, one section per job.

The x_vibesketch namespace is declared in the manifest’s extensions, version 1, not critical: a QVF reader that does not know vibe-sketch skips the document and still knows every molecule, the scheme and the calculations. source.program is vibe-sketch, and the media type is application/vnd.vibe-qc.qvf+zip.

The scheme records each species by its chemical role, as QVF does: a retrosynthetic arrow points from the target to its precursors, so the species drawn at its tail are the step’s products. A reagent without a 3D structure is named in the step’s conditions. A yield printed as 87% is also given as a number.

The calculations are a QVF job set. Each job names its structure and states the species’ charge and multiplicity, the method and functional in vibe-qc’s names (rks with b3lyp, uhf, ccsd(t), r2scan-3c), the basis, dispersion, solvation and resources. What only some programs can do goes into the job’s options: aux_basis, approximation (such as rijcosx), states for TDDFT, constraints. A runner that does not know an option refuses the job rather than drop it, so a job fails closed as the input writers do. A geometry job computes nothing and has no section.

The machine-readable contract is the JSON schema in schema/vibe-sketch-1.schema.json (draft 2020-12), which vibe-sketch schema prints and the server serves at GET /api/schema/document. It is generated from the Python model and a test fails when the two drift.

Two layers

The canvas is the drawing exactly as the canvas engine stores it. Today that is KET, Ketcher’s JSON format, embedded verbatim: whatever the engine writes comes back unchanged, including fields vibe-sketch does not know about. That is what makes a round trip through the browser lossless.

The normalized layer is what the compute side reads: species with their identifiers, charge, multiplicity and 3D conformers; reactions with roles, arrows and conditions; program-neutral job specifications; the figure style; and provenance. It is derived from the canvas by the server and can be written directly through the API by an agent.

A minimal document

{
  "schema": "vibe-sketch/1",
  "provenance": {"app": "vibe-sketch 0.1.0", "created": "2026-10-13T00:00:00Z"}
}

Every other field has a default: revision 0, an empty KET canvas, no species, reactions or jobs, and the acs-1996 style.

Fields

Field

Type

Meaning

schema

"vibe-sketch/1"

The format and its major version.

revision

integer, at least 0

Incremented by the server on every accepted edit; an edit names the revision it read.

canvas

object

format ("ket") and data, the KET document or {}.

species

list

The molecules and ions in the drawing.

reactions

list

The steps of the scheme.

jobs

list

Calculations to run.

style

object

preset, a journal style such as acs-1996.

provenance

object

app, created (a timezone-aware timestamp) and edits.

A species has an id and optionally a label (its compound number in the scheme), smiles, inchikey, name, and conformers. charge is the sum of the formal charges (default 0). multiplicity is the spin multiplicity 2S+1 (default 1); it defaults from the radical count when the server derives it and is always editable, because a drawing cannot know the spin state of a metal centre. A name records its value, its source (for example vibeqc_naming or user) and a confidence of high, medium or low.

A conformer is one 3D structure: method (how it was made: etkdg+mmff94, etkdg+uff or etkdg from vibe-sketch’s embedding, atom for a lone atom, imported), symbols (one element per atom) and xyz_angstrom (one [x, y, z] per atom). Every conformer of a species has the same atoms in the same order, the drawing’s atoms first and then the hydrogens; the first conformer, the lowest in energy, is the one writers use.

A reaction has an id, reactants and products (at least one each), reagents (species drawn at the arrow), an arrow (forward, equilibrium, retrosynthetic or resonance), conditions (text lines at the arrow) and a yield as printed, such as "87%".

A job has an id, the species it computes, the program that writes its input (a writer name; see input writers) and a spec, the program-neutral specification:

spec field

Example

Meaning

task

opt+freq

geometry (no calculation), sp, opt, freq, opt+freq, tddft or nmr.

method

B3LYP

Required for every task but geometry.

basis, aux_basis

def2-TZVP, def2/J

Basis sets.

approximation

RIJCOSX

Integral approximation.

dispersion

D4

Dispersion correction.

solvent

{"model": "CPCM", "name": "water"}

Implicit solvation, CPCM or SMD.

states

10

Excited states; required for tddft, and only for it.

constraints

[{"kind": "distance", "atoms": [0, 1], "value": 1.21}]

Coordinates an optimisation holds fixed; opt and opt+freq only.

resources

{"nprocs": 8, "maxcore_mb": 3000}

Processes and memory per core.

A job may also carry runs, its submissions to a queue, oldest first: each has the queue (vibe-queue), the host (localhost), the queue’s id for the run and when it was submitted.

A constraint has a kind (distance, angle, dihedral or position), the 0-based atoms it names in the species’ conformer (2, 3, 4 or 1 of them: the drawing’s atoms first, then the hydrogens) and an optional value in angstrom or degrees; without one, the starting geometry’s value is kept. A position fixes an atom where it is.

A geometry task sets none of the calculation fields. Nothing in the specification names a program: each writer translates it, and refuses what it cannot express.

An edit in provenance.edits records the revision it produced, when (at), the actor (human, agent or system), the client it came through (browser, cli, rest, mcp) and a one-line summary.

What validation checks

The model is strict, so a mistake is reported instead of silently dropped:

  • Unknown fields are refused, at every level except inside the canvas data.

  • Identifiers start with a letter and are unique across species, reactions and jobs. Every species a reaction or a job names exists.

  • No species appears twice in one role of a reaction.

  • A conformer has one position per atom, three finite coordinates each, and known element symbols.

  • Charge and multiplicity agree with the conformer’s atoms: the charge leaves a non-negative electron count, and the number of unpaired electrons (multiplicity minus one) has the parity of the electron count. A methyl radical drawn as a singlet is refused.

The JSON schema expresses the structural rules. The cross-references and the charge and spin rule are checks it cannot express, so a client that has only the schema should still validate through vibe-sketch validate or POST /api/documents/validate before trusting a document.

Reading and writing

vibe-sketch validate scheme.vsk
from vibesketch.document import load, save, new_document

document = load("scheme.vsk")
document.species[0].multiplicity = 3  # validated on assignment
save(document, "scheme.vsk")  # written atomically

Files are written with two-space indentation, unescaped Unicode and a trailing newline, and loading then saving an unchanged document reproduces it byte for byte, so .vsk files diff cleanly under version control.

Versioning

The major version is part of the schema value. A change that would make an existing document invalid, or change the meaning of a field, is a new major version with a migration; that decision is the maintainer’s (AGENTS.md). Because unknown fields are refused, adding a field is also a schema change, and the committed JSON schema changes with it in the same commit.