Skip to content

Guides & reference

The REST API

Authenticate with a glyp_ key and call all ten /api/v1 endpoints: request and response JSON, allowances, AI points, errors and CI recipes.
Sculptural study of connected forms and structured ideas

The flowss REST API lets a script, a CI job, a docs build or your own back end do what the studios do, without a browser. Ten endpoints live under /api/v1/: one generates a diagram from a sentence with AI, and the other nine are deterministic tools that parse, compare, convert, critique and verify diagram source, assemble evidence graphs, and run the Science studio's checkers. Every endpoint authenticates with an account API key that starts with glyp_, answers in JSON, and describes itself when you send it a plain GET. This page is the complete reference: how to get and use a key, the daily allowances and AI points each call spends, every endpoint's request and response, every error you can receive and what to do about it, and ready-to-use recipes.

At a glance

FactValue
Base URLhttps://www.flowss.ai
EndpointsPOST /api/v1/diagram, generate, diff, convert, detect, critique, verify, synthesize, analyze, science
Self-describing docsGET on any endpoint returns its own documentation as JSON, with no key needed
AuthenticationAuthorization: Bearer glyp_… (or X-Api-Key: glyp_…; when both are sent, Authorization is the one read)
Where keys come fromYour account page, section API keys, button Create key
FormatJSON request bodies (Content-Type: application/json), JSON responses
Deterministic endpointsgenerate, diff, critique, verify, synthesize, analyze, science: no AI, same input gives the same output
AI endpointsdiagram always uses AI; convert and detect use AI only when no built-in method applies
Daily allowance (deterministic)60 calls per account per day on every plan, shared with the headless render API and some in-app actions
Daily allowance (diagram)60 a day on Free and Starter, 200 on Plus, 400 on Ultra, 500 on Enterprise
AI cost1 AI point per AI call on Plus, Ultra and Enterprise; your own provider key on Starter and up
Day boundaryMidnight UTC
Called fromServers, scripts and CI. The API sends no CORS headers, so a browser page on another site cannot call it directly

Which plans can use it

Every signed-in account can create API keys. What a key can do depends on the plan of the account that owns it.

EndpointFreeStarterPlusUltraEnterprise
generate, diff, critique, verify, synthesize, analyze, scienceYesYesYesYesYes
detect (built-in recogniser)YesYesYesYesYes
convert (built-in converters)YesYesYesYesYes
diagram, and the AI path of convert and detectNo AIWith your own AI keyAI points, or your own keyAI points, or your own keyAI points, or your own key

Free includes no AI at all: no hosted points and no own-key use. Starter includes no hosted AI points, but you may send your own provider key with each request (see Using your own AI key). Plus, Ultra and Enterprise spend the account's monthly AI points, and may also bring their own key. Plan prices and the full comparison are on Plans and what they include and the pricing page.

Getting an API key

API keys belong to your account, not to a project or a team. All of an account's keys share that account's allowances, so creating more keys does not create more allowance.

  1. Sign in and open your account page at /account.
  2. Scroll down to the API keys card. On a wide screen it sits to the right of the Bring-your-own key card; on a narrow one, below it.
  3. Type a name in the field whose placeholder reads Key name (e.g. CI pipeline). The name is for you; anything past 80 characters is cut off. If you leave it empty the key is called Untitled.
  4. Click Create key.
  5. A highlighted box appears: New key — copy now, won't show again. Click Copy (you see "Copied to clipboard") and store the key somewhere safe, such as your CI system's secret store or a password manager.
  6. Click Dismiss to hide the box.
Warning: The full key is shown exactly once. flowss stores only a one-way fingerprint of it, so nobody, including support, can show it to you again. If you lose it, create a new key and revoke the old one.

What the key list shows

Each key appears as a row under the create field, newest first:

Part of the rowMeaning
NameThe name you gave the key
Prefix, for example glyp_AbC12…The first ten characters of the key, so you can tell keys apart without seeing them in full
last used date, or never usedThe last day a request authenticated with this key
created dateWhen you created it
RevokeRevokes the key

If you have no keys yet, the list reads No API keys yet. Create one to call /api/render and the /api/v1 endpoints from CI — the render API needs a key. The same keys work for every /api/v1/ endpoint, the headless render API and the flowss MCP server. There is no limit on how many keys you can create.

Revoking a key

  1. Click Revoke on the key's row.
  2. Confirm the prompt: Revoke this key? Existing integrations using it will stop working immediately.
  3. The row disappears and you see Key revoked.

Revocation takes effect on the next request: a revoked key is refused with HTTP 401 from that moment. Keys do not expire on their own; revoke a key when you no longer need it or when it may have been exposed.

Note: At the moment a revoked key can reappear in the list the next time you load your account page, still showing a Revoke button. It stays revoked and every request made with it is refused. Clicking Revoke on it again shows Key not found or already revoked.

Authentication

Send your key on every POST. Any of these forms is accepted by the /api/v1/ endpoints:

Authorization: Bearer glyp_YOUR_KEY
Authorization: glyp_YOUR_KEY
X-Api-Key: glyp_YOUR_KEY

The word Bearer is optional and is not case-sensitive. When a request carries both an Authorization header and X-Api-Key, only Authorization is read, so do not let a proxy add its own Authorization header in front of your key. A value that does not start with glyp_, a key that was revoked, or a key that does not exist are all answered the same way:

{ "error": "Invalid or missing API key. Send 'Authorization: Bearer glyp_<key>'." }

with HTTP status 401.

Note: The headless render API (POST /api/render) reads the key from either header too, and needs one (or a signed-in session): without a key it answers 401 sign-in-required, and a key it does not recognise gets 401 bad-api-key. On the Free plan it refuses the Lab's engine (glyphscript) with 402 engine-plan. See Embedding and the render API.

Keep the key on the server

A key spends your account's allowances and, on the AI endpoint, your AI points. Treat it like a password:

  • Store it in your CI system's secrets (for example a repository secret), never in source control.
  • Call the API from a server, a script or a CI job. The API sends no cross-origin (CORS) headers, so a web page on another site cannot call it from the browser, and you should never ship a key inside front-end code anyway.
  • Use one key per integration, named after it, so you can revoke one without breaking the others and so last used tells you which integrations are alive.

Self-describing endpoints

Every endpoint answers a plain GET with its own documentation as JSON: a description, the body it accepts, options, limits and an example. No key is needed and nothing is counted.

curl -sS https://www.flowss.ai/api/v1/diff | jq .

GET /api/v1/science also lists every operation and every calculation method its results may cite; GET /api/v1/critique and GET /api/v1/verify include a ready-made GitHub Actions workflow in their action field.

Conventions

Requests and responses

  • Send Content-Type: application/json and a JSON object as the body. Malformed JSON, or a body that is null, a number or a string, is refused with HTTP 400 and "Invalid JSON body" (or "Invalid JSON body." on critique and verify). A top-level array is refused the same way by convert; elsewhere it fails the endpoint's own field checks instead (for example Missing 'prompt').
  • Every response is JSON. An error response always carries a human-readable error, and usually a machine-readable code.
  • Engine ids are the short ids listed on the engine reference, for example mermaid, d2, graphviz, plantuml, dbml, sql. The Weave canvas's id is lucidflow.

Pass or fail endpoints

critique and verify exist to gate pull requests. They always answer HTTP 200 with pass: true or pass: false when the request was valid, so your pipeline can tell "the diagram did not meet the bar" (a 200 with pass: false) apart from "the request itself failed" (any 4xx or 5xx).

The science ok field

science answers every operation with an ok field. ok: false with HTTP 200 means the request was well formed and the answer is "no" or "declined": a figure with a failing claim, an unbalanceable equation, or a model operation that refused (with a named refusal). HTTP 400 with ok: false and an error means an argument was missing or could not be read, for example a unit the converter does not know, a formula it cannot parse, an unknown journal or guideline id, or an impossible randomisation. One operation answers 404: hazards for a chemical that is not in its reference table.

Findings

critique, verify and analyze return a findings array in one shared shape, the same objects the Studio underlines in its editor and the flowss Studio Agent reports at the end of a run:

FieldMeaning
idA short identifier for the finding
sourceWhich checker produced it, for example verify, critique, drift or evidence
levelerror, warning or info
messageOne sentence a person can read
codeOptional rule code, for example critique/fidelity or drift/missing-entity
whereOptional location: line, column, range, elementId or subject
fixOptional suggested correction, as an object with a label
detailOptional list of extra sentences
evidenceOptional list of provenance-ledger entry ids the finding rests on (science findings only)

Allowances, rate limits and AI points

The daily allowances

AllowanceWhat spends itFreeStarterPlusUltraEnterprise
Deterministic calls per daygenerate, diff, critique, verify, synthesize, analyze, science, the headless render API with a key, and some in-app actions6060606060
AI generation requests per dayEvery diagram call, whether it runs on points or on your own key6060200400500
Hosted AI points per monthEvery AI call made without your own key00200600600
Own-key AI requests per dayEvery AI request carrying your own provider key, across the whole platformNot available1,0001,0001,0001,000

The deterministic allowance is the same on every plan: upgrading does not raise it. What a key gives you is an allowance of your own, counted per account, instead of one shared with everybody behind the same internet address.

The deterministic allowance is one counter per account, and several things draw on it:

  • every call to the seven deterministic /api/v1/ endpoints above;
  • every call to the headless render API (POST /api/render) made with your key;
  • in the app, each drift Check on a living diagram, and some Evidence actions (rendering, exporting, causal analysis and building a review from a diagram).

detect and convert do not spend the deterministic allowance. When they need AI they spend AI points instead (see below).

Allowances reset at midnight UTC.

When a call is counted

  • diagram and critique validate your request first, so a malformed request is refused without spending anything.
  • generate, diff, verify, synthesize, analyze and science count any request that carries a valid key, before reading the body. A request refused for a missing field or a malformed body has still been counted. Validate locally before you loop over many files.
  • generate and diff accept up to 100 files in one request, and a batch counts once. Batch whenever you can.
  • On diagram, the daily request ceiling is counted before the AI points check, so a request later refused for lack of points has still used one of the day's requests.

What an AI call costs

CallHosted AI costWith your own key
diagram1 pointNo points; counts toward your 1,000 own-key requests a day, and still toward the diagram daily ceiling (60 on Starter)
convert when no built-in converter applies1 pointNo points; counts toward your own-key requests
detect when the built-in recogniser is unsure1 pointNo points; counts toward your own-key requests

A request that fails before or during the AI provider call costs no points. On diagram and detect, the point is spent once the provider answers, even if its answer turns out to be unusable; convert charges only for a conversion it actually delivers. Monthly points and top-up packs are explained on AI points and limits.

Using your own AI key

On Starter and up you can pay your AI provider directly instead of spending points. Send your provider key in a second header:

X-User-Api-Key: sk-ant-…
X-User-Ai-Provider: anthropic
HeaderMeaning
X-User-Api-KeyYour key from a supported AI provider. Used for this request only
X-User-Ai-ProviderOptional. One of anthropic, openai, google, deepseek, qwen, glm, moonshot, mistral

If you leave out X-User-Ai-Provider, flowss guesses the provider from the key's shape (Anthropic, Google, Zhipu GLM, OpenAI and Mistral keys are recognisable; a key that matches none is sent to Anthropic). DeepSeek, Qwen and Moonshot keys look like OpenAI keys, so always send the provider header with those. A key whose shape matches no supported provider is refused with bad-key-shape before anything is sent.

Note: A key saved to your account on the Bring-your-own key card of your account page is used by the web app, where you are signed in. API calls carry no sign-in, so a saved key is not used by them: send X-User-Api-Key on every request that should run on your own key. More on Bring your own AI key.

Calls on your own key spend no points but are not unlimited: every own-key AI request on the platform counts toward a ceiling of 1,000 a day per account.

Rate-limit responses

StatusBodyReturned byWhat to do
429{"error":"Rate limit reached (60/day). Try later.","code":"rate-limited"} with header X-Quota-Remainingdiagram, generate, diff, synthesize, analyze, scienceWait for midnight UTC, batch more work per call, or spread runs over days
429{"error":"Daily critique quota reached (60/day).","code":"user-cap"}critiqueAs above
429{"error":"Daily verify quota reached (60/day).","code":"user-cap"}verifyAs above
429Daily own-key limit: "code":"rate-limit", "bucket":"byok", with headers X-RateLimit-Bucket and X-RateLimit-Capdiagram and convert on your own key (detect answers with its low-confidence guess instead)Wait for midnight UTC
429"code":"hosted-ai-account-daily-limit", with resetsAt and a Retry-After headerHosted AI calls (diagram, the AI path of convert) once your account reaches its daily share of hosted AINo points were taken. Wait until resetsAt (midnight UTC), or send your own key
503{"code":"limiter-unavailable", …} with Retry-After: 30diagramNothing was run or charged. Retry after the number of seconds in Retry-After

The number in a 429 message is your actual cap for that allowance, so on Plus the diagram message reads (200/day). A code of ip-cap on critique or verify means the call was counted against your internet address instead of your account, which happens only when account counting is briefly unavailable.

Endpoint reference

EndpointIn one lineAISpends
POST /api/v1/diagramA sentence in, a complete diagram and an embed link outAlways1 point and one AI request
POST /api/v1/generateA schema or spec in, a stable Mermaid overview outNeverOne deterministic call
POST /api/v1/diffTwo versions in, what changed structurally outNeverOne deterministic call
POST /api/v1/convertA diagram in one engine in, the same diagram in another outOnly without a built-in converterNothing, or 1 point
POST /api/v1/detectA snippet in, its engine outOnly when unsureNothing, or 1 point
POST /api/v1/critiqueA diagram in, readiness, quality scores and fixes outNeverOne deterministic call
POST /api/v1/verifyA diagram and its source of truth in, pass or fail outNeverOne deterministic call
POST /api/v1/synthesizeClaims in, an evidence graph outNeverOne deterministic call
POST /api/v1/analyzeClaims in, the full evidence analysis outNeverOne deterministic call
POST /api/v1/scienceOne of 42 scientific checks and calculationsNeverOne deterministic call

POST /api/v1/diagram

Generates a complete diagram from a natural-language prompt using AI, and returns its source plus a ready-to-embed link.

Uses AIYes, always
Cost1 AI point, or one own-key request
AllowanceAI generation requests per day
PlansStarter (own key only), Plus, Ultra, Enterprise
Time limit30 seconds
Body limit250 KB

Request body:

FieldTypeRequiredMeaning
promptstringYesWhat to draw. Up to 8,000 characters
enginestringNoPreferred engine id. If it is not a valid engine id it is ignored and the AI picks
curl -sS https://www.flowss.ai/api/v1/diagram \
  -H "Authorization: Bearer $FLOWSS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"A C4 container diagram for a food-delivery app","engine":"d2"}'

Response (200):

{
  "engine": "d2",
  "title": "Food delivery — containers",
  "code": "…complete d2 source…",
  "embed_url": "https://www.flowss.ai/embed?engine=d2&code=…&theme=dark",
  "links": {
    "embed": "https://www.flowss.ai/embed?engine=d2&code=…&theme=dark",
    "render": "https://www.flowss.ai/api/render"
  }
}
FieldMeaning
engineThe engine the diagram is written for. If the AI names an engine that does not exist, you get your requested engine, or mermaid
titleA short title, or Diagram if the AI gave none
codeThe complete diagram source
embed_urlA link to the /embed page that draws this diagram, in the dark theme. Drop it into an <iframe>. Change theme=dark to theme=light if you prefer
links.renderThe headless render API, which turns code into an SVG or PNG file

Errors specific to this endpoint:

StatusMessage or codeMeaning
400Missing 'prompt'The prompt is missing or blank
400Prompt too long (>8000 chars)Shorten the prompt
402points-exhaustedYour plan has no AI, Starter has no hosted points, or your points are used up. The message names which, and the body includes your balance, tier, allowance, whether your plan can bring its own key (byok) and the top-up packs (topUp)
402byok-upgrade-requiredYou sent X-User-Api-Key on a Free account. Own keys start on Starter
400bad-key-shapeX-User-Api-Key does not look like any supported provider's key. Nothing was sent
429rate-limitedThe day's diagram requests are used up (Rate limit reached (…/day). Try later.)
429rate-limit (bucket byok)The day's 1,000 own-key AI requests are used up
429hosted-ai-account-daily-limitYour account reached its daily share of hosted AI. No points were taken; wait for midnight UTC or use your own key
502The model returned malformed output.The AI answered with something that was not a diagram, or its answer was cut off. The point is still spent. Try again or rephrase
502Generation failed., or Generation failed — followed by a short reasonThe AI provider failed or timed out. When the reason is the upstream credentials were rejected and you sent X-User-Api-Key, your provider refused your key: check it and X-User-Ai-Provider
503plan-unavailableYour plan could not be read for a moment. Nothing was charged; retry
503hosted-ai-daily-limit or hosted-ai-budget-unavailableHosted AI is paused platform-wide for safety. Nothing was charged. Your own key still works
503limiter-unavailableThe limits could not be checked. Nothing was run; retry after Retry-After

POST /api/v1/generate

Turns a structural source (a schema, a spec, a graph) into a stable Mermaid overview. No AI and no network, so the same input always produces byte-identical output: ideal for regenerating a committed diagram in CI on every push.

Uses AINo
AllowanceDeterministic calls (one per request, batch or single)
Time limit15 seconds
Body limit8 MB declared; a single source up to 256 KB (262,144 characters); a batch up to 4 MB of source in total

Engines it can read: mermaid (flowcharts only), d2, graphviz, dbml, structurizr, sql, erd, openapi, and Weave boards (lucidflow, the board's JSON). The endpoint's own GET lists the first eight; Weave boards are read too. A SQL or DBML schema with columns becomes a Mermaid erDiagram with column types and PK/FK markers (direction does not apply); everything else becomes a Mermaid flowchart whose nodes are numbered n0, n1, … in sorted order, so the output never changes unless the source does.

Single request body:

FieldTypeRequiredMeaning
enginestringYesThe source's engine id
codestringYesThe source text, up to 256 KB
directionstringNoFlowcharts only: TD, TB, LR, RL or BT. Default LR. Anything else is treated as LR
titlestringNoWritten as a %% comment line at the top of the output
curl -sS https://www.flowss.ai/api/v1/generate \
  -H "Authorization: Bearer $FLOWSS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"engine":"sql","code":"CREATE TABLE users (id INT PRIMARY KEY, email TEXT);"}'

Response (200):

{
  "engine": "sql",
  "ok": true,
  "mermaid": "erDiagram\n  users {\n    INT id PK\n    TEXT email\n  }\n",
  "nodes": 1,
  "edges": 0
}

A flowchart example: {"engine":"d2","code":"web -> api\napi -> db","direction":"TD","title":"Overview"} returns a mermaid value of %% Overview, then flowchart TD, then one line per node and per edge.

If the source cannot be parsed, the answer is HTTP 422 with ok: false:

{ "engine": "sql", "parseable_engine": true, "ok": false, "error": "Could not parse this source structurally." }

For an engine with no structural reader, parseable_engine is false and error reads Engine 'pikchr' has no structural parser. (with your engine's id). A Mermaid source that is not a flowchart (a sequence or class diagram, for example) cannot be parsed structurally either.

Batch mode

Send { "files": [ … ] } instead, with up to 100 entries of { path?, engine, code, direction?, title? }. The whole batch counts as one call. In a batch only the 4 MB total is enforced; there is no separate per-file limit.

{
  "files": [
    { "path": "db/schema.sql", "engine": "sql", "code": "CREATE TABLE …" },
    { "path": "infra/arch.d2", "engine": "d2", "code": "web -> api", "direction": "TD" }
  ]
}

Response (200): { "results": [ { "path": "db/schema.sql", "engine": "sql", "ok": true, "mermaid": "…", "nodes": 2, "edges": 1 }, … ], "total": 2, "generated": 2 }. Each result has the same shape as a single response, plus the path you sent. A file that fails to parse is a result with ok: false; it does not fail the batch.

StatusMessageMeaning
400Missing 'engine'Single mode without an engine
400'code' must be a string.Single mode without source
400'files' is empty.Batch with no files
400Each file needs { engine, code }.A batch entry is incomplete
413Source too large (>256 KB).Single source too large
413Too many files (>100).Split the batch
413Batch too large (>4 MB total).Split the batch

POST /api/v1/diff

Compares two versions of a diagram by meaning, not by text: which nodes and edges were added, removed, renamed or relabelled, and for schemas which columns changed. Formatting and whitespace changes are ignored. Built for pull-request comments and merge gates.

Uses AINo
AllowanceDeterministic calls (one per request, batch or single)
Time limit15 seconds
Body limit8 MB declared; 256 KB (262,144 characters) per side in single mode; 4 MB of source per batch

Structural comparison covers mermaid (flowcharts), d2, graphviz, dbml, structurizr, sql, erd, openapi and Weave boards (lucidflow). Any other engine, a side that does not parse, an empty side, or a pair whose two engines differ falls back to a plain text comparison that ignores only leading and trailing whitespace: you learn whether it changed, but not what.

Single request body:

FieldTypeRequiredMeaning
enginestringYesEngine id of the before source
beforestringYesThe old source (send an empty string for a new file)
afterstringYesThe new source (send an empty string for a deleted file)
afterEnginestringNoEngine of the after source, if it changed. Defaults to engine
formatstringNomarkdown adds a ready-to-paste Markdown summary. You can also add ?format=markdown to the URL
curl -sS https://www.flowss.ai/api/v1/diff \
  -H "Authorization: Bearer $FLOWSS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"engine":"d2","before":"a -> b","after":"a -> b\nb -> c"}'

Response (200):

{
  "engine": "d2",
  "structured": true,
  "parseable_engine": true,
  "changed": true,
  "textChanged": true,
  "summary": "Added 1 node (c); added 1 edge (b→c).",
  "diff": {
    "addedNodes": [{ "id": "c" }],
    "removedNodes": [],
    "renamedNodes": [],
    "addedEdges": [{ "from": "b", "to": "c" }],
    "removedEdges": [],
    "relabeledEdges": [],
    "changedAttributes": [],
    "changed": true
  }
}
FieldMeaning
structuredtrue when both sides were compared structurally. false means a text comparison was used and diff is null
parseable_engineWhether engine is one of the structural engines at all
changedWhether anything material changed. For a text comparison, whether the text changed at all once leading and trailing whitespace is trimmed
textChangedWhether the source text changed, even if only cosmetically (leading and trailing whitespace ignored)
summaryA one-line description. No structural change. when nothing material changed; The source changed. or No change. for a text comparison
diff.addedNodes, removedNodesNodes as { id, label? } (schemas also carry attributes)
diff.renamedNodesSame id, new label: { id, from, to }
diff.addedEdges, removedEdgesEdges as { from, to, label? }
diff.relabeledEdgesSame endpoints, new label: { from, to, fromLabel, toLabel }
diff.changedAttributesSchemas only: per table, the columns added, removed and changed (same name, new type or key)

For a schema the summary reads like a column changelog. Changing users (id INT PRIMARY KEY, age INT) to users (id BIGINT PRIMARY KEY, phone TEXT) summarises as Columns users: +phone, −age, ~id. Long lists in a summary are shortened with +N more (after four nodes or edges, six columns of a table, or three tables); the full lists are always in diff.

With format: "markdown", a single comparison adds a markdown heading (Diagram changed or No structural change) followed by the summary; a batch adds a heading such as 2 of 5 diagrams changed and a table with one row per file (file, engine, change).

Batch mode

Send { "files": [ { path?, engine, before, after, afterEngine? }, … ] } with up to 100 entries. The response is { "results": [ … ], "changed": <number of files that changed>, "total": <number of files> }, each result carrying the path you sent. Add "format": "markdown" to get one markdown table summarising every file, ready to post as a pull-request comment.

StatusMessageMeaning
400Missing 'engine'Single mode without an engine (and without a files array)
400Both 'before' and 'after' must be strings.Send empty strings for added or deleted files
400'files' is empty. / Each file needs { engine, before, after }.Fix the batch
413Source too large (>256 KB).One side is too large
413Too many files (>100). / Batch too large (>4 MB total).Split the batch

POST /api/v1/convert

Converts a diagram from one engine to another. Where flowss has a built-in converter for the pair, the result is instant, free and deterministic; otherwise AI rewrites the diagram.

Uses AIOnly when no built-in converter applies
CostBuilt-in: nothing. AI: 1 point, or one own-key request
AllowanceDoes not spend the deterministic allowance
Time limit60 seconds
Body limit512,000 bytes (about 500 KB), checked as the body streams in; source up to 100,000 characters

Built-in converters:

FromTo
Mermaid flowchartD2, PlantUML
D2Mermaid
Graphviz (DOT)D2
DBMLMermaid ER, ERD
MarkmapWeave mind map (lucidflow)
Weave (lucidflow)Mermaid, Graphviz, D2, PlantUML
Any structural engine (mermaid flowcharts, d2, graphviz, dbml, structurizr, sql, erd, openapi)Weave (lucidflow), laid out automatically; notes reads Mapped N nodes and M edges from … onto the Weave canvas.

A built-in converter only runs on a source it can read: a Mermaid sequence diagram, for example, is not a flowchart, so converting it goes to AI.

Request body:

FieldTypeRequiredMeaning
codestringYesThe diagram source, up to 100,000 characters
targetEnginestringYesEngine id to convert to
sourceEnginestringNo, but recommendedEngine id of code. Without it no built-in converter can run and the conversion always uses AI. Up to 64 characters
curl -sS https://www.flowss.ai/api/v1/convert \
  -H "Authorization: Bearer $FLOWSS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sourceEngine":"mermaid","targetEngine":"d2","code":"graph LR\n  A[API] --> B[(DB)]"}'

Response (200):

{
  "engine": "d2",
  "code": "direction: right\n\nA: \"API\"\nB: \"DB\" { shape: cylinder }\n\nA -> B",
  "notes": "Converted with the built-in mermaid → d2 transpiler (lossless: every node, connection and label carried over).",
  "method": "transpiler"
}
methodMeaning
transpilerA built-in converter produced it with nothing lost. Also returned, unchanged, when sourceEngine equals targetEngine
transpiler-lossyA built-in converter produced it, but something did not carry over. notes names what
aiAI rewrote the diagram. notes is the AI's one-sentence account of what carried over and anything dropped

When the built-in converter would lose structure (a node, a connection, a label), flowss tries AI first, since AI is asked to keep everything. If AI is not available to you (no points, Free plan, a refused or malformed own key, a daily limit, a provider failure), you get the lossy built-in result instead of an error, with method: "transpiler-lossy". When the built-in converter loses only styling, its result is returned straight away as transpiler-lossy without trying AI; notes names what was dropped and suggests converting with AI instead only when structure was lost.

The errors below therefore reach you only when there is no built-in result to fall back on:

StatusMessage or codeMeaning
400Invalid JSON bodyThe body is not a JSON object
400Missing 'code' fieldSend the source
400Missing or unknown 'targetEngine'Use an id from the engine reference
400Code is too long (>100 KB)More than 100,000 characters. Convert a smaller diagram
400Source engine name is too long.sourceEngine over 64 characters
413Conversion request is too large.Body over 512,000 bytes
400bad-key-shapeX-User-Api-Key does not look like a supported provider's key
401bad-keyYour provider rejected the key you sent in X-User-Api-Key (Your API key was rejected: …)
402points-exhausted or byok-upgrade-requiredThe pair needs AI and your plan or balance cannot pay for it
422output-truncatedThe converted diagram was too large for one AI reply. Convert a smaller diagram
422refusalThe AI declined. Rephrase rather than resending as is
429rate-limit (bucket byok) or hosted-ai-account-daily-limitA daily AI limit was reached
502The converter didn't return a usable diagram — try a different target engine.The AI answer was unusable. No point is charged
503plan-unavailable, hosted-ai-daily-limit or hosted-ai-budget-unavailableA temporary condition. Nothing was charged; retry later
503no-key: Converting between these engines needs AI, and this deployment has no AI configured.AI is not available on this service

POST /api/v1/detect

Identifies which engine a snippet of diagram source is written for.

Uses AIOnly when the built-in recogniser is not confident
CostBuilt-in: nothing. AI: 1 point, or one own-key request
AllowanceDoes not spend the deterministic allowance
Time limit30 seconds
Source limit100,000 characters; only the first 4,000 are sent to AI

Request body: { "code": "…" }.

curl -sS https://www.flowss.ai/api/v1/detect \
  -H "Authorization: Bearer $FLOWSS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code":"graph LR\n  A --> B"}'

Response (200):

{ "engine": "mermaid", "confidence": "high", "source": "heuristic" }
FieldValuesMeaning
engineAn engine idThe best match. Always a real engine id
confidencehigh, medium, lowHow sure the answer is
sourceheuristic, aiWhether the built-in recogniser or AI answered

detect is designed never to block you. If AI would be needed but is not available (no points, the daily limit reached, a provider failure, or an unusable AI answer), it returns its best low-confidence guess, source: "heuristic" and confidence: "low", falling back to mermaid if it has no guess at all. Treat any low answer as a suggestion to confirm.

It returns an error only in these cases: Invalid JSON body (400), Missing 'code' field (400), Code is too long (>100 KB) (400, more than 100,000 characters), and problems with your own AI key that only you can fix: byok-upgrade-required (402, a Free account sent X-User-Api-Key), bad-key-shape (400), bad-key (401, the provider rejected your key) and plan-unavailable (503, your plan could not be read for a moment while you sent your own key; retry). Reaching the daily own-key limit does not produce an error here: you get the low-confidence guess.

POST /api/v1/critique

The full quality read the Studio shows in its status bar, as an API: is the diagram broken, and how good is it across seven dimensions, with the concrete edit that would improve each. No AI, nothing stored, nothing fetched, so the answer is reproducible and costs only one deterministic call.

Uses AINo
AllowanceDeterministic calls; malformed requests are not counted
Time limit30 seconds
Body limit2 MB declared; diagram up to 512 KB

Request body:

FieldTypeRequiredMeaning
enginestringYesAny engine id
codestringYesThe diagram source, non-empty
requeststringNoThe words that asked for this diagram. Supplying them unlocks the fidelity read: does the figure state the numbers the request named, and is it drawn in the notation the request implies? Without it, fidelity is reported as not assessed. Only the first 2,000 characters are used
gatestringNoThe readiness level pass requires: draft, sound, reviewed or publishable. Default sound

The readiness levels, lowest to highest:

LevelMeaning
emptyNothing a person would miss
draftIt exists, and something in it is broken
soundEvery mechanical check passes
reviewedA quality read was taken and it held up
publishableIt would survive somebody else opening it

The seven dimensions: fidelity (is this what was asked for?), completeness (the whole thing, or a sketch?), structure (is the shape sound?), legibility (can a reader take it in?), rigour (do the numbers carry their weight?), accessibility (can everyone read it? every colour pair is simulated under three kinds of colour-vision deficiency and in greyscale) and portability (will it survive leaving flowss?).

curl -sS https://www.flowss.ai/api/v1/critique \
  -H "Authorization: Bearer $FLOWSS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"engine":"mermaid","code":"flowchart TD\n  A[Records identified] --> B[Screened]","request":"PRISMA flow: 1,284 records identified","gate":"sound"}'

Response (200), shortened:

{
  "pass": true,
  "gate": "sound",
  "findings": [
    { "id": "m626ai", "source": "critique", "level": "warning", "code": "critique/fidelity",
      "message": "The request reads as PRISMA 2020 flow, and this is drawn in Mermaid.",
      "detail": ["Either re-express it in PRISMA 2020 flow, or say in the caption why Mermaid is the better notation here."] }
  ],
  "readiness": {
    "level": "sound",
    "label": "Sound",
    "score": 0.6913,
    "reason": "Every mechanical check passes (1 advisory). Quality read: 77% …",
    "blocking": [
      "Only 2 nodes — this is a stub rather than a model.",
      "The request names a figure the diagram never states: 1284."
    ],
    "toAdvance": "Take a quality read and act on the top findings — sound is not the same as good."
  },
  "verification": { "ok": true, "summary": "Not fully checked — mermaid was not rendered", "defects": [ … ] },
  "critique": {
    "score": 0.7652,
    "band": "solid",
    "summary": "77% across 6 dimensions; weakest is fidelity. 1 dimension could not be judged and is left out of the score.",
    "unassessed": ["accessibility"],
    "dimensions": [
      { "id": "fidelity", "title": "Fidelity", "question": "…", "assessed": true, "score": 0.4, "findings": [ { "message": "…", "fix": "…" } ] }
    ],
    "moves": [
      { "label": "Add the entities the subject actually has; a real system usually has 8 or more.", "why": "Only 2 nodes — this is a stub rather than a model.", "dimension": "completeness" }
    ]
  },
  "next": [
    { "capabilityId": "critique-work", "title": "Critique it", "phase": "critique", "why": "…" }
  ]
}
FieldMeaning
passtrue when the readiness level is at or above gate. This is the field a CI job should read
readiness.blockingWhat stands between this diagram and the next level
readiness.toAdvanceOne sentence on how to reach the next level
findingsEvery defect from every checker, in the shared findings shape. Verification problems that block are error; advisory notes and every critique finding are warning
readiness.level, label, score, reasonThe level reached, its display name, a score from 0 to 1 and one sentence explaining it
verificationThe mechanical checks (ok, summary, defects, each defect blocking or advisory). Only verification can make a diagram draft
critique.score, band, summaryThe overall quality score from 0 to 1, a word for it, and one sentence naming the weakest dimension
critique.dimensionsEach dimension with its score (or null with unassessableBecause when it cannot be judged for this engine) and findings
critique.unassessedDimensions left out of the score. A dimension that cannot be judged is never counted as a pass
critique.movesThe concrete edits most likely to improve the score
nextSuggested next steps in flowss, with a route where one applies

Nothing in critique can fail pass on its own at the default gate: only broken mechanics drop a diagram below sound. Raise gate to reviewed or publishable to hold diagrams to the quality read too. A gate value that is not one of the four levels is treated as sound.

Where an engine has a checker of its own (for example a dependency loop that makes a schedule impossible, or two rules of a decision table that can both fire), its verdict is folded into the read as well.

Note: Some engines are drawn by your browser and cannot be drawn on the server. For those, verification.summary says the diagram was not fully checked, and a warning finding explains which checks did run. The diagram is not treated as broken.
StatusMessageMeaning
400Unknown engine "…". Send one of the … engine ids.Check the id against the engine reference
400Send a non-empty 'code'.Send the source
413Diagram too large.Over 512 KB

POST /api/v1/verify

An architecture test for CI: checks that a diagram still matches the thing it describes. You send the diagram (inline, or a reference to a figure stored in one of your cloud projects) and the source of truth (a compose file, a SQL schema, an OpenAPI spec or another diagram), and get a deterministic pass or fail.

Uses AINo
AllowanceDeterministic calls (counted before the body is read)
Time limit30 seconds
Body limit2 MB declared; diagram and truth each up to 512 KB

The body takes one of two shapes.

Stateless, everything inline:

{
  "diagram": { "engine": "mermaid", "code": "flowchart LR\n  api --> db\n  api --> cache[Redis]" },
  "truth": {
    "kind": "compose",
    "content": "services:\n  api:\n    depends_on: [db, worker]\n  db:\n    image: postgres:16\n  worker:\n    image: acme/worker"
  }
}

A stored figure, with optional inline truth:

{ "projectId": "<project id>", "figureId": "<figure id>", "truth": { "kind": "sql", "content": "CREATE TABLE …" } }
FieldMeaning
diagram.engineOne of mermaid (flowcharts), d2, graphviz, dbml, structurizr, erd, sql, openapi; a Weave board (lucidflow) is read too
diagram.codeThe diagram source, up to 512 KB
projectId, figureIdA figure in a cloud project the key's owner can open (as a member, or through the project's team). Any role will do, viewer included
truth.kindcompose, sql, openapi, or a diagram engine: mermaid, d2, graphviz, dbml, structurizr, erd (or lucidflow)
truth.contentThe source of truth's text, up to 512 KB

If you send diagram and projectId/figureId together, the inline diagram is used.

With a stored figure and no truth, flowss re-fetches the figure's own bound source on the server: the source you attached on the Live dashboard (see Integrations and extensions). The Live dashboard's CI button produces exactly this request with your ids filled in.

Response (200) to the inline example above, where the diagram is missing the worker service and still shows a Redis cache the compose file no longer has:

{
  "pass": false,
  "summary": "Drift — missing from diagram: worker; no longer in source: Redis.",
  "coverage": 0.667,
  "truth": "inline compose",
  "drift": {
    "missingFromDiagram": ["worker"],
    "staleInDiagram": ["Redis"],
    "missingEdges": [],
    "staleEdges": []
  },
  "findings": [
    { "id": "…", "source": "drift", "level": "error", "code": "drift/missing-entity",
      "message": "\"worker\" is in the source of truth but not in the diagram.",
      "where": { "subject": "worker" }, "detail": ["Compared against inline compose."] }
  ]
}
FieldMeaning
passtrue when every entity in the truth appears in the diagram, the diagram shows no entity the truth lacks, and every relation in the truth between matched entities is drawn. Extra relations in the diagram (staleEdges) are reported but do not fail the check
summaryOne sentence. The diagram matches the source. when it passes; otherwise Drift — followed by up to five names per list and the number of missing and stale connections
coverageShare of the truth's entities found in the diagram, from 0 to 1, rounded to three decimals
truthWhat was compared against: inline <kind>, or a description of the bound source
drift.missingFromDiagramIn the truth, not in the diagram: the diagram is out of date
drift.staleInDiagramIn the diagram, not in the truth: the diagram shows something that no longer exists
drift.missingEdges, staleEdgesThe same for relations, as { from, to }, compared only between entities found on both sides
findingsThe same drift as error findings, with codes drift/missing-entity, drift/stale-entity, drift/missing-edge and drift/stale-edge

How entities are matched: by name, not by internal id. Each node's label (or its id, if it has no label) is lower-cased, parenthetical notes are removed, everything that is not a letter or a digit (spaces included) is dropped, and the affixes service, svc, server and the are removed from the start or end of the name (never from a name only a few characters longer than the affix). So API service matches a compose service called api, and DB matches db. A node labelled Postgres does not match a service called db: name your diagram's nodes after the things in the source.

How relations are matched: a relation is compared only when both of its ends matched, because an edge to a missing entity is already reported as that entity. A relation drawn in the opposite direction from the truth is not reported as stale, but a relation in the truth still counts as missing unless the diagram draws it in the same direction.

Note: Because extra relations do not fail pass, a passing response can still carry drift/stale-edge findings. If your gate must reject those too, also fail the job when drift.staleEdges is not empty.
StatusMessageMeaning
400Invalid JSON body.The body is not a JSON object. This request has still been counted
400Send { diagram: {engine, code} } or { projectId, figureId }.Neither shape was sent
400No truth provided and the figure has no source binding — send truth: { kind, content }.Bind the figure on the Live dashboard, or send truth
404Project not found.The project does not exist or the key's owner cannot open it
404Figure not found.No figure with that id in the project
413Diagram too large. / Truth content too large.Over 512 KB
422Engine "…" has no structural parser — verify supports: mermaid, d2, graphviz, dbml, structurizr, erd, sql, openapi.Use a structural engine for the diagram
422Could not parse the truth content as "…".The truth is malformed, or the wrong kind
422A message about the bound source, for example The source is larger than 256 KB, and a drift check needs the whole file — bind a smaller file (for example one schema or one spec per binding).The bound source could not be fetched or read

POST /api/v1/synthesize

The Evidence studio's graph engine, without AI. You bring the claims (each a subject, a relation, an object and ideally a supporting quote); flowss assembles the concept graph, finds contradictions and gaps, rates certainty, checks quotes against source text if you supply it, and renders a view.

Uses AINo
AllowanceDeterministic calls (counted before the body is read)
Time limit20 seconds
Body limit8 MB; up to 5,000 claims

Request body:

FieldTypeRequiredMeaning
claimsarrayYesEach { id?, sourceId?, subject, relation, object, quote?, … }. Optional extras are population, method, effect, n, quoteLoc and arm-level data
sourcesarrayNoSource records, for example { id, title?, year? }. A source marked retracted: true stays listed, but its claims are left out (with a warning)
sourceTextobjectNo{ "<sourceId>": "full text" }, used to verify each claim's quote
requireGroundingbooleanNoWith sourceText, drop every claim whose quote cannot be found
viewstringNoconcept-map (default), dag, grade-table or prisma
appraisalsarrayNoRisk-of-bias appraisals: { sourceId, tool: "rob2" or "robins-i", domains or answers }

Relations: increases, decreases, causes, prevents, treats, correlates, associated, no_effect, moderates, supports, contradicts, part_of. A claim may also carry arm-level data, data: { kind: "binary", events, total, controlEvents, controlTotal } (or continuous or rate), so its effect is computed rather than read from text. GET /api/v1/synthesize lists the exact domain keys each risk-of-bias tool accepts.

curl -sS https://www.flowss.ai/api/v1/synthesize \
  -H "Authorization: Bearer $FLOWSS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"claims":[
    {"sourceId":"s1","subject":"sleep deprivation","relation":"decreases","object":"working memory","quote":"sleep deprivation decreases working memory"},
    {"sourceId":"s2","subject":"sleep deprivation","relation":"no effect","object":"working memory","quote":"no significant effect on working memory"}]}'

Response (200), shortened:

{
  "graph": { "concepts": [ … ], "edges": [ … ] },
  "claims": [ … ],
  "stats": { "concepts": 2, "edges": 2, "claims": 2, "sources": 0, "contradictions": 1 },
  "summary": "2 concepts, 2 links, 2 claims, 0 sources; 1 contradiction.",
  "contradictions": [ … ],
  "contradictionSummary": "1 contradiction: sleep deprivation→working memory (decreases vs no effect on).",
  "gaps": [ … ],
  "certainty": { "high": 0, "moderate": 0, "low": 0, "very_low": 2, "total": 2 },
  "riskOfBias": { "tools": [], "appraised": 0, "total": 2, "unmatched": [] },
  "view": { "name": "concept-map", "engine": "mermaid", "code": "…" },
  "warnings": []
}

A claim without an id is numbered claim-0, claim-1, … by its position; a claim without a sourceId is grouped with every other unattributed claim under one source, source-unattributed, so unattributed claims never count as independent studies. A claim with no subject or object, or with a relation flowss does not recognise, is dropped and named in warnings (for example claims[3] (…→…): unrecognized relation "…"). warnings also reports retracted sources, records still awaiting a screening decision, several sources that report one study (linked by a shared DOI or trial registration and counted once), and a claim whose quoted effect disagrees with its own arm-level counts.

grounding ({ total, verified, rejected: [{ id, reason }] }) is added when you send sourceText. riskOfBias.appraised: 0 is the honest reading of a synthesis nobody appraised: certainty ratings then state that the study-limitations domain was not assessed. riskOfBias.unmatched names appraisals whose sourceId matches no source. The view.code is diagram source you can render with the render API or open in the Studio.

StatusMessageMeaning
400'claims' must be an array of { subject, relation, object }.Send a claims array
400'claims' is empty.Send at least one claim
413Too many claims (>5000).Split the corpus
413Payload too large (>8 MB).Trim sourceText or split the corpus

POST /api/v1/analyze

Everything synthesize returns, plus the Evidence studio's full analytical layer: the evidence base by study design, leave-one-out robustness, trends over time, a GRADE Summary of Findings and a prioritised research agenda. No AI.

Uses AINo
AllowanceDeterministic calls (counted before the body is read)
Time limit30 seconds
Body limit8 MB; up to 5,000 claims

Request body: claims (required; each { subject, relation, object, quote?, method?, n?, effect? }), sources (optional; include year for trends over time), sourceText and requireGrounding (as for synthesize). Add method, n and effect to claims for GRADE certainty and effect synthesis.

The response carries every synthesize field plus:

FieldMeaning
evidenceBaseCounts by study design (systematic review, RCT, observational, unknown) and a one-line summary
robustnessFor each source, what would change if it were left out: the edges it alone supports, contradictions it creates, and a verdict of load-bearing, contributory or redundant
temporalDated and undated claims, the year span, claims per year and the cumulative growth of the graph
summaryOfFindingsGRADE Summary of Findings rows grouped by outcome: studies, participants, certainty, risk of bias
recommendationsA prioritised agenda, for example Resolve conflict: sleep deprivation → working memory with a reason
findingsContradictions (error), gaps (warning) and claims dropped for want of grounding (error), in the shared findings shape

The errors are the same as for synthesize.

POST /api/v1/science

Every checker and calculator the Science studio runs, one operation per request, chosen by the op field. No AI, no network and nothing stored: the same body always gives the same answer, so you can cache results indefinitely.

Uses AINo
AllowanceDeterministic calls (counted before the body is read)
Time limit30 seconds
Body limit2 MB

Every response includes ok. Send { "op": "…", …arguments }; the op is matched without regard to case or surrounding spaces.

curl -sS https://www.flowss.ai/api/v1/science \
  -H "Authorization: Bearer $FLOWSS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"op":"balance","equation":"CH4 + O2 -> CO2 + H2O"}'
{
  "ok": true,
  "outcome": "balanced",
  "coefficients": [1, 2, 1, 2],
  "equation": "CH4 + 2O2 -> CO2 + 2H2O",
  "message": "Balanced: CH4 + 2O2 -> CO2 + 2H2O",
  "species": [ { "formula": "CH4", "writtenCoefficient": 1, "side": "left", "counts": { "C": 1, "H": 4 }, "charge": 0 }, … ]
}

Checking figures and text

opSendYou get
verifyfigure: { nodes: [{ id, data: { label } }], edges: [{ source, target, label }], title?, caption? }, or labels: ["…"] (up to 2,000). Optional include limits the kinds of check to any of equation, formula, units, quantity, constant, identifier, statistics, notation, nomenclature, pathway, consistencyfindings (status, message, rule, fix, detail), checked, passed, issues, hasClaims, byKind, score, and ok, which is true only when something was checkable and none of it failed. Claims that are only well-formed identifiers, formulae or units do not count as verified
notationtextNotation findings with rule, severity, span and fix; the corrected text; formulae with subscripts; terms that should be italic
identifiervalue (one identifier) or text (free text to scan)Each identifier's scheme, validity, normalised form, canonical URL and checksum result
pathwayfigure and optional pathwayId, or labels, or queryCoverage of a canonical pathway and findings, or the matching pathways
taxonomyname, optional code (ICZN, ICN, ICNP, ICTV) and optional firstMentionDone (true when the full name has already appeared), or queryName findings with fixes, the corrected name, the organism and its lineage, Markdown and HTML italic forms, or up to 8 matching organisms
alt-textfigure: { nodes, edges, title? }Alt text and a long description
figure-standardsjournal and figure: { widthPx, heightPx, column?, fontsPx?, strokeWidthsPx?, format?, fileBytes?, dpi?, artworkType? }. Omit journal to list specs (optionally filtered by query)Findings with remedies and an export plan (width, height, dpi)
palettecolours (up to 64)Colour-difference for every pair under each colour-vision deficiency, problems, greyscale safety and a suggested palette
accessibilityAny of colours, textPairs: [{ foreground, background, largeText? }], colourIsOnlyChannel, figurePalette, greyscale and contrast findings, a colour-only warning and alt text

Journal specs available to figure-standards: nature, science, cell, pnas, plos, elife, elsevier, springer, wiley, ieee, acs, rsc, aps, bmj, lancet, frontiers, mdpi, thesis, poster-a0, slide-16x9.

Chemistry and physics

opSendYou get
balanceequationCoefficients, the balanced equation, outcome and species
redoxequationWhether it is a redox reaction, the changes, the oxidising and reducing agents
oxidation-statesformulaOxidation state of each element, with reasons
molar-massformula, for example CuSO4·5H2OgramsPerMole (249.677 for that example), per-element contributions and mass percentages
convertvalue (number), from, toThe converted value, its dimension and quantity. 1 eV to J gives 1.602176634e-19, dimension kg·m²·s⁻², quantity energy
dimensionunitCanonical form, dimension, exponents, SI factor, quantity name and other names
check-equationequation, for example E = 1/2 m v^2Whether the dimensions match, the reading of each symbol and a summary. An equation it will not judge returns checkable: false
quantitytext, for example 9.81 ± 0.02 m/s^2Value, unit, uncertainty, coverage factor, significant figures and dimension
comparea and b, two measured quantitiesWhether they agree, with z-score and normalised error
constantsymbol, numeric value and optional unit to check a quoted constant, or query to searchWhether the value agrees with the reference value and how closely, or up to 12 matching constants
spectrumOptional formula and any of ir, nmr1H, nmr13C (arrays of up to 200 numbers), ms: { molecularIon, peaks: [{ mz, intensity }] }With a formula: degree of unsaturation, readings per technique, isotope checks, and what is explained, unexplained or contradictory. Without one: each technique read on its own terms
beer-lambertThree of absorbance, epsilon, concentration, pathLengthThe fourth, with transmittance and working
nmr-frequencyspectrometerMHz and either ppm or hzThe conversion with working
hazardschemical (one name), chemicals (a list) or figureHazards, incompatibilities, standing hazards, waste streams, pictograms and PPE, always with a disclaimer. Advisory only: never a substitute for the safety data sheet. A chemical not in the reference table returns 404 with a message saying that this does not mean it is safe

Study design and reporting

opSendYou get
critique-designA design description, for example design: "rct", n, groups, controls, blinding, randomised, outcomesA verdict, score, findings with remedies, what was not assessed and the working
randomisen, groups (a count, or up to 32 labels), optional method (simple, the default, block, stratified-block or minimisation), blockSize, seed, subjects, strata: [{ name, n }], stratifyBy, minimisationProbabilityAn allocation sequence with group sizes, blocks, balance and warnings. An impossible plan answers 400 with ok: false
factorialfactors: [{ name, levels }] (up to 16), optional replicates, centrePointsCells, run count, runs, estimable effects, error degrees of freedom and a fraction suggestion. An impossible design answers 400
replicationtechnical, biological and optional unit, or unitOfRandomisation, unitOfAnalysis, nested, icc, unitsEffective sample size advice, or pseudoreplication detection with the design effect and remedies
reporting-guidelinestudyType and optional stage (protocol or report), or guideline (an id such as consort-2010). Send neither to list every guideline and study designThe primary guideline, additional ones, the risk-of-bias tool and a figure checklist. An unknown guideline answers 400 with the list of valid ids
risk-of-biasdesign, for example rct, cohort, case-controlThe right tool and its domains with signalling questions

Models drawn on a figure, and data

These operations read the models drawn on a Science figure (send the figure's JSON as figure: { nodes, edges, ledger? }), or work on pasted data (tab-separated text with a header row). Every computed number is recorded in a provenance ledger entry that names the method and its version; GET /api/v1/science lists every method in methods.

opWhat it does
modelReads the models in the figure and runs quick consistency checks
simulateSimulates them over time (t1, outputs, infectedIds, seed; series: true adds the time series)
steady-stateFinds steady states and their stability
r0Computes the basic reproduction number
fitFits a model to data: model is linear, exponential, rate-0, rate-1, rate-2, michaelis-menten, hill, logistic, arrhenius or drawn
sensitivityRanks parameters by elasticity of an output
montecarloSamples parameter uncertainty. seed is required and n may be at most 500; the same body gives a byte-identical answer
graphNetwork measures of the drawing: density, components, degree, centrality, cycles
describeDescriptive statistics of a data column, with a normality test
regressLinear, logistic or Poisson regression (family), with standard errors, VIF and influence flags
pi-groupsDimensionless groups from a list of variables with units, naming known ones such as Re and Fr
ledger-checkWhich ledger entries are current, stale or orphaned
quantitiesThe quantities written on the figure's labels, as typed

Server budgets for these operations: 20,000 integration steps, 20,000 fit evaluations and 500 Monte Carlo runs per request. An operation that hits a budget, lacks a seed or cannot identify a fit answers HTTP 200 with ok: false and a named refusal.

StatusMessageMeaning
400Missing 'op'. GET this endpoint for the list of operations.Send an op
400Unknown op "…". GET this endpoint for the list.The body includes the full list in operations
400ok: false with an error naming the missing argumentFor example 'equation' is required, e.g. "CH4 + O2 -> CO2 + H2O".
413Payload too large (>2 MB).Send less

These also accept your API key and are documented on their own pages.

EndpointWhat it doesWhere to read more
POST /api/renderTurns diagram source into an SVG or PNG file on the server, for engines that can be drawn there. Counts toward the same 60-a-day deterministic allowanceEmbedding and the render API
/embed and embed.jsLive diagrams inside any web page; diagram returns a ready embed_urlEmbedding and the render API
POST /api/mcpThe flowss MCP server for AI assistants and agents; its keyed tools call these endpoints for youThe flowss MCP server

The web app also calls other internal endpoints. Only the endpoints on this page and the ones above are the public API; anything else may change without notice.

Error reference

Errors shared by several endpoints:

StatuscodeMessageReturned byWhat to do
400Invalid JSON body (Invalid JSON body. on critique and verify)Every endpointSend a JSON object with Content-Type: application/json
401Invalid or missing API key. Send 'Authorization: Bearer glyp_<key>'.Every endpointCheck the header, and that the key was not revoked
413payload-too-largeRequest body is too large (>… KB).diagram (over 250 KB), generate, diff, synthesize (over 8 MB), critique, verify (over 2 MB)The declared body size is over the endpoint's limit. Nothing was counted
413Payload too large (>8 MB). or Payload too large (>2 MB).synthesize, analyze (8 MB), science (2 MB)Send less. On analyze and science the call has already been counted
413Conversion request is too large.convertKeep the body under 512,000 bytes
429rate-limited, user-cap or ip-capRate limit reached (…/day). Try later., Daily critique quota reached (…/day). or Daily verify quota reached (…/day).diagram and every deterministic endpointThe daily allowance is used up; it resets at midnight UTC
503limiter-unavailableWe couldn't check the request limits just now, so we haven't run this. Nothing has been charged — please try again in a moment.diagramRetry after Retry-After

Errors from AI calls (diagram, and convert or detect when they need AI). Remember that convert answers with its lossy built-in result instead of most of these when it has one, and detect answers with a low-confidence guess instead of every one except the key problems marked below:

StatuscodeMeaningReturned byWhat to do
402points-exhaustedNo hosted AI on this plan, or no points left. Body adds balance, tier, allowance, byok (whether your plan may bring its own key) and topUp (the url /account and the top-up packs)diagram, convertBuy a top-up pack on /account, wait for your monthly allowance, upgrade, or send your own key (Starter and up)
402byok-upgrade-requiredYou sent your own AI key from a Free account. Body adds tier, source and upgradediagram, convert, detectOwn keys need Starter or above
400bad-key-shapeThat doesn't look like an API key from a supported provider. Nothing was sent.diagram, convert, detectCheck X-User-Api-Key
401bad-keyYour API key was rejected: … (your own provider key)convert, detect (diagram reports this as a 502, see its table)Check the key and X-User-Ai-Provider
422output-truncatedoutput too large for this request — try a smaller diagramconvertConvert a smaller diagram
422refusalThe model declined this request. Rephrase it rather than resending it as is.convertRephrase
429rate-limit (bucket byok)Daily limit for AI requests on your own key reached (1000/day). It resets at midnight UTC — nothing was sent to your provider.diagram, convertWait for midnight UTC
429hosted-ai-account-daily-limitYour account reached today's hosted-AI limit. No points were taken. Body has resetsAt and byokdiagram, convertWait until resetsAt, or use your own key
502The AI provider faileddiagram, convertRetry with backoff
503plan-unavailableYour plan could not be read. Nothing was chargeddiagram, convert, detect (own key only)Retry in a moment
503hosted-ai-daily-limitHosted AI reached its platform-wide daily safety limit. Body has resetsAt and a Retry-After headerdiagram, convertWait until resetsAt, or use your own key
503hosted-ai-budget-unavailableHosted AI is briefly unavailable. Nothing was charged. Comes with Retry-After: 60diagram, convertRetry later, or use your own key
503no-keyAI is not configured on this servicediagram, convertUse the deterministic endpoints

Recipes

A shell script for CI

Fail the build if any architecture diagram is broken, and print why. Requires curl and jq.

#!/usr/bin/env bash
set -euo pipefail
: "${FLOWSS_API_KEY:?Set FLOWSS_API_KEY}"
API="https://www.flowss.ai/api/v1/critique"
status=0
for f in docs/diagrams/*.mmd; do
  body=$(jq -n --arg c "$(cat "$f")" '{engine:"mermaid", code:$c, gate:"sound"}')
  res=$(curl -sS -X POST "$API" \
    -H "Authorization: Bearer $FLOWSS_API_KEY" \
    -H "Content-Type: application/json" \
    -d "$body")
  if [ "$(echo "$res" | jq -r '.pass // empty')" = "true" ]; then
    echo "ok    $f: $(echo "$res" | jq -r .readiness.level)"
  else
    echo "FAIL  $f: $(echo "$res" | jq -r '.readiness.blocking[0] // .readiness.reason // .error')"
    status=1
  fi
done
exit $status

Each file is one deterministic call. With more than a few dozen diagrams, run this only on the files a pull request changed.

Diff every changed diagram in one call (Node.js 18 or later)

const res = await fetch("https://www.flowss.ai/api/v1/diff?format=markdown", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FLOWSS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    files: [
      { path: "docs/arch.d2", engine: "d2", before: oldArch, after: newArch },
      { path: "db/schema.sql", engine: "sql", before: oldSchema, after: newSchema },
    ],
  }),
});
if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).error}`);
const { results, changed, total, markdown } = await res.json();
console.log(`${changed} of ${total} diagrams changed`);
console.log(markdown); // paste into a pull-request comment

Generate a diagram and save it as SVG (Python)

import os, requests

KEY = os.environ["FLOWSS_API_KEY"]
H = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}

gen = requests.post("https://www.flowss.ai/api/v1/diagram", headers=H, timeout=60,
                    json={"prompt": "Order-to-cash process with credit check", "engine": "mermaid"})
gen.raise_for_status()
d = gen.json()
print(d["title"], d["embed_url"])

# Rasterise on the server where the engine allows it (see the render API page).
img = requests.post("https://www.flowss.ai/api/render", headers=H, timeout=60,
                    json={"engine": d["engine"], "code": d["code"], "format": "svg"})
if img.ok:
    open("diagram.svg", "wb").write(img.content)
else:
    print("Render not available here:", img.json().get("error"))

Retrying safely

  • Retry 502 and 503 responses with exponential backoff, and honour Retry-After when it is present.
  • Do not retry 400, 401, 402, 404, 413 or 422 unchanged: they will fail the same way.
  • Do not retry a 429 before midnight UTC; it will fail the same way until the allowance resets.

Tips

  • Start every new integration with a GET on the endpoint: it costs nothing and shows the exact body it accepts today.
  • Batch generate and diff calls. A pull request touching 40 diagrams costs one call in batch mode and 40 otherwise.
  • Always send sourceEngine to convert. Without it the built-in converters cannot run and every conversion uses AI.
  • Pin direction and title on generate so a committed artifact never changes for reasons other than its source.
  • Send request to critique when the diagram was made from a written brief: it is the only way the fidelity dimension is judged.
  • Name verified diagram nodes after the services, tables or endpoints they stand for, so verify can match them.
  • Keep one key per pipeline and check last used on your account page to find keys you can revoke.

Limits and known constraints

  • The deterministic allowance is 60 calls a day per account on every plan, and it is shared with the render API, living-diagram drift checks and some Evidence actions. Upgrading does not raise it.
  • Several endpoints count a request before reading its body, so a malformed request still spends one call.
  • generate, diff and verify understand eight structural notations plus Weave boards, and read Mermaid flowcharts only. Other engines get a text-only comparison on diff and are refused by generate (as an ok: false result) and verify.
  • verify matches entities by normalised name; differently named nodes for the same thing are reported as drift. Extra relations drawn in the diagram are reported but do not fail pass.
  • detect and convert are not counted against the 60-a-day deterministic allowance, but their AI path is limited by your points and your daily own-key ceiling.
  • critique cannot draw browser-rendered engines on the server, so for those the render check is reported as not run.
  • detect sends at most the first 4,000 characters of a snippet to AI.
  • The diagram endpoint returns an embed_url that encodes the whole source in the address. Very large diagrams make very long links; for those, save the source and use the embed script instead.
  • The API sends no CORS headers and is meant for servers, scripts and CI, not for browser code on other sites.
  • Keys cannot be scoped to particular endpoints or projects and do not expire; revoke unused keys.
  • A key saved to your account for the web app is not used by API calls; send X-User-Api-Key with each request instead.

Troubleshooting

SymptomCauseFix
401 on every callThe key is missing, mistyped, revoked, or sent without the glyp_ prefixCopy the key again from your secret store; if lost, create a new one
/api/render answers 401 sign-in-requiredNo key was sentSend Authorization: Bearer glyp_… (or X-Api-Key: glyp_…)
/api/render answers 402 engine-planA Lab (glyphscript) render on the Free planUpgrade to Starter, or render a Studio engine
402 points-exhausted on FreeFree includes no AIUse the deterministic endpoints, or upgrade
402 points-exhausted on StarterStarter has no hosted points, and a key saved to your account is not used by API callsSend X-User-Api-Key with your provider key on every request
429 on diagram on Starter although you send your own keyOwn-key diagram calls still count toward the 60-a-day diagram ceilingSpread the work over days, or upgrade for a higher ceiling
502 Generation failed — the upstream credentials were rejected. on diagramYour provider refused the key in X-User-Api-Key, or the provider header names the wrong providerCheck the key and send the matching X-User-Ai-Provider
A revoked key is back in your key listThe list can show revoked keys after a reloadIt no longer works. Revoke on it again answers Key not found or already revoked.
Create key shows an errorThe key could not be created at that momentTry again; nothing was created
429 by mid-morningSomething else on your account is spending the same allowance: another pipeline, render calls or in-app drift checksBatch calls, run checks only on changed files, and give each pipeline its own key so last used shows which ones run
diff says structured: falseThe engine has no structural reader, or a side failed to parseUse one of the eight structural engines, or check the source parses in the Studio
verify reports a service as missing and a node as stale that are the same thingTheir names do not match after normalisationRename the node to the source's name
convert returns transpiler-lossyAI was not available and the built-in converter could not carry everythingRead notes, or retry with points or your own key
detect keeps answering lowThe snippet is ambiguous and AI was not availableSend more of the source, or set the engine yourself
CORS error in the browser consoleThe API is not callable from browser code on another siteCall it from your server

Something unclear or out of date on this page? Tell us from the Support link in any studio — the flowss team reads every report.

© 2026 Voranox Inc. flowss — Flow Systems Studio. All rights reserved.

This documentation, its text and its examples are protected by copyright. Engine and format names are trademarks of their respective owners — see the terms and copyright and licences.