jane* docs
API & MCP

The HTTP API

Run one tool with a JSON request, worked end to end on a melting temperature.

EndpointPOST /api/tools/<name>
ListingGET /api/tools/
Interactive reference/api/openapi

What it does

Each tool in the workbench has its own endpoint, named after the tool. The endpoint accepts that tool's real parameters, with the same bounds, units and published options the panel offers, and returns the same result the panel renders.

Finding a tool

GET /api/tools/ returns every tool with its full input schema: each parameter's type, its description, its default, its bounds and units, and for a parameter that selects between published methods, the list of ids with what each one is.

That listing is the reference. It is generated from the tool declarations themselves, so it cannot drift from what the tools actually accept.

A worked example: a melting temperature

In the editor, a melting temperature comes from loading a sequence and opening a panel. Through the API it is one request, and it takes the full set of knobs rather than a simplified subset.

We will melt M13 forward (−20), GTAAAACGACGGCCAGT, a standard sequencing primer.

The simplest call

curl -s https://<host>/api/tools/melting_temperature \
  -H 'content-type: application/json' \
  -d '{"oligo": "GTAAAACGACGGCCAGT"}'

That returns a temperature of 57.883 °C, the model that produced it, the GC content, and the thermodynamics from the same table.

The same oligo, with the knobs set

Now name a melting model, add 5% DMSO to the buffer, and choose which published DMSO depression to apply:

curl -s https://<host>/api/tools/melting_temperature \
  -H 'content-type: application/json' \
  -d '{
        "oligo": "GTAAAACGACGGCCAGT",
        "model": "nn_unified",
        "dmso_method": "cul76",
        "conditions": { "additives": { "dmso": 5 } }
      }'

The real response:

{
  "outcome": "ok",
  "data": {
    "tm": 55.383198058369146,
    "baseTm": 57.883198058369146,
    "model": "Nearest-neighbour, unified 2004",
    "gc": 52.94117647058824,
    "steps": [
      {
        "step": "Nearest-neighbour, unified 2004",
        "tm": 57.883198058369146,
        "delta": null,
        "derivation": "SantaLucia & Hicks 2004 (DNA_NN4); the table pydna `tm_default` uses · buffer applied (salt method 7)"
      },
      {
        "step": "DMSO 5 %",
        "tm": 55.383198058369146,
        "delta": -2.5,
        "derivation": "−0.5 °C per %, Cullen and Bick (1976)"
      }
    ],
    "nn": {
      "dH": -132.7,
      "dS": -358.0999999999999,
      "dG": -21.635285000000025
    },
    "lockedBases": 0,
    "caveat": ""
  }
}

What that response is telling you

This is the shape worth understanding, because every tool follows it: you get the working, not just the number.

  • tm is the answer after every correction the buffer asked for, and baseTm is what it was before any additive. The difference is what the DMSO cost you, stated rather than left to be inferred.
  • steps is the derivation, one row per correction. Each row carries the temperature after that step, the change it made (delta), and the citation that set the coefficient. Here Cullen and Bick's −0.5 °C per % across 5% DMSO is exactly the −2.5 °C in the row. Ask for a different published model and the coefficient, the citation and the number all change together.
  • model is named, never returned as a bare id.
  • nn carries ΔH, ΔS and ΔG(37 °C) from the same table as the temperature, so a row never mixes two models. It is absent for models that have no nearest-neighbour table.
  • caveat is empty when the model can be read for an oligo of this length, and says why not when it cannot. The Wallace rule outside its published 14 to 20 nt domain is the case it exists for.

What this computes

The melting-temperature endpoint offers twelve melting models: four DNA nearest-neighbour tables (SantaLucia & Hicks 2004, Allawi & SantaLucia 1997, Sugimoto 1996, Breslauer 1986), three RNA/RNA tables (Xia 1998, Chen 2012 with GU wobble, Freier 1986), an RNA/DNA hybrid table (Sugimoto 1995), pydna's dsDNA-binding-domain model, the GC formula, the Wallace rule, and NEB. It applies the salt correction you name over Na⁺, K⁺, Tris, Mg²⁺ and dNTP concentrations, then four published DMSO depressions (Ahsen 2001, Musielski 1981, Cullen & Bick 1976, Escara & Hutton 1980) and formamide depression. Locked nucleic acid is understood: write the probe as it is ordered with an L after each locked base (GTCGLAACAGC) and McTigue, Peterson & Kahn's 2004 per-neighbour increments are added to the nearest-neighbour sum.

It returns the temperature, the pre-additive temperature, the per-step derivation with citations, GC content weighted for ambiguity codes, ΔH/ΔS/ΔG from the matching table, the locked-base count, and a caveat when the model is being read outside its published domain.

Refusals

A tool that cannot compute says so, with the reason and the values it would have accepted. Asking for a melting model that does not exist:

{
  "outcome": "error",
  "error": "unknown model “not_a_model” — one of nn_santalucia, nn_unified, nn_sugimoto, nn_breslauer, nn_rna_xia, nn_rna_chen, nn_rna_freier, nn_hybrid_sugimoto, bresluc_dbd, gc, wallace, neb"
}

An oligo too short to melt:

{ "outcome": "error", "error": "provide an `oligo` of at least 2 bases" }

An enzyme name the catalogue does not hold, which is a refusal rather than an empty result:

{
  "outcome": "error",
  "error": "unknown enzyme “NotAnEnzyme” — not in the 201-enzyme cut catalog this tool resolves names against. Names are case-sensitive and spelled as REBASE spells them (BamHI, EcoRI, BsaI). That catalog is the set whose CLEAVAGE GEOMETRY the cut engine carries, which is smaller than the REBASE dossier the editor shows records from — a real, correctly spelled enzyme can be in the second and not the first (AarI is). Define it as a custom enzyme in Enzyme Collections to cut with it. Nothing was computed: a missing enzyme and an enzyme with no sites are different answers."
}

That distinction matters more than it looks. "No sites" and "that enzyme does not exist" are opposite conclusions, and a tool that returns the first for the second will have you believe your plasmid is clean.

The refusal names the right catalogue, which is not the same set as the enzyme records the editor can show you. Cutting needs a cleavage position; showing a record does not. REBASE determines no cleavage position for most of the enzymes it lists, so the set that can be cut with is the smaller one, and a correctly spelled, entirely real enzyme can fall outside it. Define its recognition site and cut offsets once as a custom enzyme and it cuts like any other.

The status codes, which mean different things

StatusMeaning
200The tool ran. The body carries "outcome": "ok" and the result.
400The request itself was malformed, checked against the schema the endpoint publishes: a body that is not a JSON object, a value of the wrong type, or a parameter the tool does not take. A misspelled parameter is named, with the one it was probably meant to be.
404No tool has that name. The body suggests the nearest names.
409The operation needs approval before it can change anything.
413The body is larger than one request may carry (16 MiB, which admits a whole bacterial genome as _subject).
422The request was well formed and the engine declined it: an unknown model, an oligo of one base, a sequence the operation cannot apply to. The body carries "outcome": "error" and the reason.
500The tool itself failed. That is a defect on our side, not in your request.

The distinction between 400 and 422 is worth keeping: the first is your request being wrong, the second is your request being answerable and the answer being "no, and here is why".

Sending a molecule

Tools that operate on a sequence take it as _subject rather than as a loose parameter, so the body shape is the same for every one of them:

curl -s https://<host>/api/tools/find_restriction_sites \
  -H 'content-type: application/json' \
  -d '{"_subject": {"sequence": "ATGGGATCCAAA"}, "enzymes": ["BamHI"]}'
{
  "outcome": "ok",
  "data": [{ "top": 4, "bot": 8, "kind": "5'", "overhangSeq": "GATC", "enzyme": "BamHI" }]
}

A _subject carries the sequence and, where it matters, its name and whether it is circular. Circularity is not cosmetic: a site spanning the origin exists on a circular molecule and does not on a linear one.

The earlier spellings $subject and $files are still read, so older scripts keep working. The underscore is what tool-calling models require of an argument name.

Naming a record instead of pasting it

Pasting bases loses the annotations and, unless you remember to say so, the topology. A _subject can instead name the record, by exactly one of four keys, and it is read the way the workbench reads it when you start a project:

keywhat it isexample
sequencethe bases themselves{"sequence": "ATGGGATCC…", "circular": true}
accessionan archive record, fetched and parsed with its features{"accession": "U55762", "source": "ncbi"}
samplea file from the bundled sample library{"sample": "data/samples/Genbank/CVU55762.gb"}
filea file you hold: text for a text format, base64 for any file{"file": {"name": "pUC19.gb", "text": "LOCUS …"}}

source is one of ncbi, ncbi-protein, ena, uniprot, igem, addgene or dbfetch (the last also takes db and format). Two tools help you find a record to name: search_records searches the archives by free text, and list_samples browses the sample library, narrowed by a query and a kind.

A file's format is read from its name's extension, so .gb, .fasta, .dna (SnapGene), .ab1 and the rest open as they do in the workbench. A file holding several records is not silently reduced to its first: the request is refused with the records listed, and you choose one with record_index.

Every response to a named record says what it resolved to under resolved: its name, length, topology, feature count, and, for an accession, where and when it was fetched.

Sending a whole project: _files

_files is a list of the same references, standing in for the project open in the workbench. Every sequence record in them becomes a document a tool can name in its sequences, parts or document arguments (the records of a multi-record file as file · record, or all of them by the file's own name), and list_library shows what they held. A tool that reads a file itself, such as a Sanger trace, finds it there too:

curl -s https://<host>/api/tools/align_sequences \
  -H 'content-type: application/json' \
  -d '{"_files": [{"sample": "data/samples/FASTA/COI-crickets.fasta"}],
       "sequences": ["COI-crickets.fasta"], "include_subject": false}'

Uploading

Every tool also accepts multipart/form-data: files as _subject or (repeatable) _files parts, everything else as JSON in an arguments field.

curl -s https://<host>/api/tools/draw_trace \
  -F '_files=@read.ab1' -F 'arguments={"document": "read.ab1"}'

Object parameters, and the two buffer shapes

Some parameters are objects rather than single values. conditions is one: it describes the buffer a temperature is measured in. The listing spells out the inside of these, so every key, unit and bound is discoverable, and a key the object does not have is refused rather than ignored.

There are two buffer shapes, because two engines are behind them, and sending one where the other is expected is an error rather than a silent default.

Nested, for melting_temperature — ions under salt, strand concentrations under oligo, DMSO and formamide under additives:

{ "conditions": { "salt": { "Na": 1000 }, "additives": { "dmso": 5 } } }

Flat, for the primer and qPCR designers (design_primers, design_gibson_primers, design_qpcr_assay, check_scorpion_stem) — ions, strand concentrations and saltcorr at the top level, with additives in Biopython's own spelling:

{ "conditions": { "Na": 1000, "saltcorr": 7, "additives": { "DMSO": 5 } } }

Send the wrong one and you get a 400 naming the path, not a quiet answer computed in the default buffer:

{ "error": "invalid arguments: data/conditions must NOT have additional properties" }

Salt is not a rounding detail. On the M13 primer above, raising Na⁺ from the default buffer to 1000 mM moves the melting temperature from 57.883 °C to 67.383 °C.

On this page