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.
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 thegenerate scope. Counts against your monthly generation quota (500 generations per rolling 30 days).
Body
string
required
Natural-language description of the process to generate.
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.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".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 thegenerate 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 theLocation URL, respecting the initial Retry-After and then backing off, until the job is terminal.
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 thegenerate 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’”.
string
Theme for the re-rendered image:
"light" (default) or "dark".string
Image format:
"png" (default) or "svg".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.Quota & rate limits
Generation is metered on two axes, both per key:Generation quota
500 generations per rolling 30 days. Each accepted
POST /v1/diagrams/generate (returned as 202 Accepted) counts once — including jobs that later end in failed.Burst limit
120 requests / 60s across all endpoints, including your poll calls.
RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (seconds). On a 429, a Retry-After (seconds) header tells you how long to wait.
Errors
Every error uses the standard envelope:Errors you may hit on this endpoint
Errors you may hit on this endpoint
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.