Skip to main content
Turn a plain-language description of a process into a saved, laid-out diagram and a rendered image. Because generation runs an LLM and then renders the result, it can take many seconds — so the API is asynchronous: you create a generation job, then poll it until it finishes.
All requests are server-to-server only. Never expose jfi_sk_live_... keys in a browser or client app.

Why async?

A single generation involves an LLM call plus a server-side layout and render pass. That can comfortably exceed the lifetime of a normal HTTP request. Rather than hold a connection open (and risk timeouts), POST /v1/diagrams/generate returns immediately with 202 Accepted and a Generation job in queued status. You then poll GET /v1/generations/{id} until the job reaches a terminal state.
Webhooks for generation completion are planned for v2. Until then, poll the generation endpoint with backoff (see the example below).

The flow

1

Create the generation

POST /v1/diagrams/generate with your prompt. You get back 202 Accepted, a Generation with status: "queued", a Location header pointing at the poll URL, and a Retry-After hint.
2

Poll for status

GET the URL from the Location header. While status is queued or processing, wait and try again (respecting Retry-After and backing off).
3

Read the result

When status is succeeded, the diagram (the created, saved diagram) and image_url (the rendered image for the requested theme/format) are populated. When status is failed, error explains why.

Create a generation

Requires the generate scope. Counts against your monthly generation quota (500 generations per rolling 30 days).

Body

string
required
Natural-language description of the process to generate. Up to 20,000 characters; a longer prompt returns 400 invalid_parameter.
string
Theme for the rendered image: "light" (default) or "dark".
string
Image format: "png" (default) or "svg".
string
Optional name for the resulting diagram.
string | null
Optional folder (UUID) to place the diagram in, or null for the root.
array
Optional files the diagram should be based on: images, PDFs and text files. See Attachments.

Response

202 Accepted
string
Always "generation".
string
The generation job id, prefixed with gen_. Use it to poll.
string
One of "queued", "processing", "succeeded", "failed".
object | null
The created, saved Diagram once status is "succeeded"; otherwise null.
string | null
Path to the rendered image (per the requested theme/format) once status is "succeeded"; otherwise null. It is relative to the API base (https://justflow.it) — prepend the base to fetch it.
object | null
{ "code": string, "message": string } when status is "failed"; otherwise null.
string
ISO-8601 UTC timestamp.

Poll the generation

Requires the generate scope. Poll until status reaches a terminal state (succeeded or failed).

Response — succeeded

200 OK

Response — failed

200 OK
A failed generation returns 200 OK with status: "failed" — the HTTP request to poll succeeded; the job did not. Inspect generation.status and generation.error, not the HTTP status code, to decide outcome.

Full polling example

Poll the Location URL, respecting the initial Retry-After and then backing off, until the job is terminal.
Already have a saved diagram and just want to re-fetch or re-theme its image? Use GET /v1/diagrams/{id}/image with theme and format query params — no regeneration, no quota cost.

Edit an existing diagram

Change a saved diagram’s content with another natural-language instruction. Editing runs an LLM and a render too, so it uses the same asynchronous flow as generation: create an edit job, then poll. Requires the generate scope and counts against the generation quota.

Body

string
required
Natural-language instruction describing the change — e.g. “add a rejection path after the approval gateway” or “rename the first task to ‘Receive invoice’”. Up to 20,000 characters.
string
Theme for the re-rendered image: "light" (default) or "dark".
string
Image format: "png" (default) or "svg".
array
Optional files the diagram should be based on: images, PDFs and text files. See Attachments.
Returns 202 Accepted with a Generation (identical shape to generate). Poll GET /v1/generations/{id} exactly as above; on success, diagram is the updated diagram and image_url is the re-rendered image.
Editing applies the instruction to the diagram’s current content and updates it in place (same diagram id). It is the only way to change a diagram’s content — PATCH /v1/diagrams/{id} changes metadata (name, folder) only.

Attachments

generate and edit take an optional attachments array, so the AI can work from a process write-up, a spreadsheet, a whiteboard photo or a PDF. Each item has a name (with its extension) and one of:
  • data: the file’s bytes in base64. Works for every accepted type.
  • text: the file’s content as text. Only for text files.
Limits per request: up to 4 images and 4 documents (PDF or text), 2.5 MB in total, and 40 PDF pages in total. A text file can have up to 2 MB. The AI reads up to 100,000 characters of each text file and 150,000 across all of them; a file past that is cut, or left out once the total is reached, and the AI is told so. Images and PDFs are recognised by their bytes, whatever the name. Anything else needs one of the text extensions above and must really be text, on both data and text. Audio is not accepted. Files are sent to the AI and not stored.
Python (requests)
An attachment that is not accepted returns 400 before any job is created, with error.param naming it (e.g. attachments[1]):
  • unsupported_attachment: a type that is not accepted, a GIF, a .pdf that is not a readable PDF (damaged or password-protected), a text file that is not text, or text sent for an image or PDF.
  • invalid_parameter: a malformed item, invalid base64, more than 4 images or 4 documents, a PDF over 20 pages or PDFs over 40 pages in total, or a text file over 2 MB. More than 8 items names attachments.
Attachments over 2.5 MB in total return 413 payload_too_large. Request bodies over 4.5 MB are refused by the hosting platform with a plain 413 and no error envelope, so keep files within 2.5 MB.

Quota & rate limits

Generation is metered on two axes, both per account (shared by all of its API keys and connected AI assistants):

Generation quota

500 generations per rolling 30 days. Every POST /v1/diagrams/generate and POST /v1/diagrams/{id}/edit counts once, including requests refused with an error (for example a 400 for an attachment) and jobs that later end in failed. Check attachments on your side before sending.

Burst limit

120 requests / 60s across all endpoints, including your poll calls.
Every response carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (seconds). On a 429, a Retry-After (seconds) header tells you how long to wait.
When the monthly generation quota is exhausted, POST /v1/diagrams/generate returns 429 with error.code quota_exceeded. When you’re sending too fast, it returns 429 with rate_limit_exceeded. Back off using Retry-After.

Errors

Every error uses the standard envelope:
Note: when a job’s content is invalid (e.g. it can’t be laid out as valid BPMN), the poll returns 200 with status: "failed" and error.code: "validation_failed" — not an HTTP 422.