Skip to content

Ask AI

Ask anything about Destesi — setup, products, APIs.

Powered by Claude. Answers may be wrong — always verify against the docs.

API reference

Image generation exposes a small HTTP API at https://api.imagegen.destesi.io (in local development, http://localhost:9099). Note the api. prefix: imagegen.destesi.io is the web app, not the API.

Two arms — pick whichever fits the caller.

Browser session. The imagegen_session cookie your browser holds after signing in through Accounts. This is what the web app uses:

Terminal window
curl ... -b 'imagegen_session=<your-session>' ...

Personal Access Token. For scripts and backends, send a token plus the workspace to run in:

Authorization: Bearer idn_pat_<your-token>
X-Destesi-Workspace: <your-workspace-slug>

Mint tokens at account.destesi.io/settings/api-tokens; see API access & the dst CLI. A Bearer value that isn’t an idn_pat_ token is rejected with 401 unknown_bearer_scheme rather than falling back to the cookie, and a token without X-Destesi-Workspace fails 401 missing_workspace_header.

Generation is scoped to the workspace the request runs in. An unauthenticated request returns 401.

GET /v1/engines

Lists the engines available to your workspace.

{
"engines": [
{ "id": "default", "label": "Fast", "available": true },
{ "id": "openai", "label": "High quality (GPT)", "available": false }
]
}
Field Meaning
id Pass this as engine on a generate call. Omit engine (or send "default") for the fast engine.
label Human-readable name, as shown in the web app’s engine selector.
available Whether this workspace can actually use it right now.

openai runs on your own OpenAI account, connected through Connect. It is listed with available: false until you connect one — the entry is always present so a client can show it as a disabled option rather than pretending it doesn’t exist. Selecting an unavailable engine returns 409 engine_not_connected.

POST /v1/generate

Submits a prompt, waits for the image to render, and returns it in one response. The call blocks up to a 120-second ceiling; longer renders time out (deadline_exceeded) and should be retried, or moved to the streaming endpoint, which has no per-request deadline.

Request body:

Field Type Required Default Description
prompt string yes What to generate.
negative_prompt string no empty What to keep out of the image.
engine string no default Which engine runs the prompt — an id from GET /v1/engines.
width integer no 1024 Output width. One of 512, 576, 768, 1024.
height integer no 1024 Output height. One of 512, 576, 768, 1024.
steps integer no 30 Number of diffusion steps. Maximum 50.
guidance_scale number no 7.5 How strictly the model follows the prompt.

Success (200):

{
"job_id": "a1b2c3d4e5f6a7b8",
"image_url": "https://drive.destesi.io/...",
"drive_file_id": "file_...",
"mime": "image/png",
"expires_at": "2026-06-01T12:00:00Z"
}
  • image_url — a ready-to-load URL for the rendered PNG.
  • drive_file_id — the file’s permanent identifier in Drive.
  • expires_at — when image_url stops being fetchable (the file remains in Drive).

Example:

Terminal window
curl https://api.imagegen.destesi.io/v1/generate \
-H 'Content-Type: application/json' \
-b 'imagegen_session=<your-session>' \
-d '{
"prompt": "a watercolor fox in a misty forest, soft morning light",
"negative_prompt": "blurry, low quality, text, watermark",
"width": 1024,
"height": 1024
}'
POST /v1/generate-and-stream

Submits a prompt and returns a Server-Sent Event (SSE) stream of progress, ending in a terminal event. This is the endpoint a UI uses for a live progress bar — it has no per-request timeout (the stream stays open while the job runs).

The request body is identical to POST /v1/generate.

Events:

Event Data Meaning
queued { "job_id": "..." } Accepted, waiting for a worker.
progress { "job_id": "...", "step": 12, "total": 30 } Generating; current step out of total.
done { "job_id": "...", "image_url": "...", "drive_file_id": "..." } Rendered and registered in Drive.
error { "code": "..." } Terminal failure (see error codes).
Terminal window
curl -N https://api.imagegen.destesi.io/v1/generate-and-stream \
-H 'Content-Type: application/json' \
-b 'imagegen_session=<your-session>' \
-d '{"prompt": "isometric city at golden hour, vibrant", "engine": "openai"}'
GET /v1/jobs/{id}/stream

Opens an SSE stream against a job you already submitted, yielding the same queued / progress / done / error events until the job reaches a terminal state. The done event here carries a result_url pointing at the job’s result endpoint.

GET /v1/jobs/{id}/result

Returns the rendered PNG bytes (Content-Type: image/png). Two ways to authenticate:

  • Session cookie — for same-origin loads from the web app.
  • Signed URL — append ?exp=<unix-seconds>&sig=<signature> for cookie-less loads (this is the form embedded in image_url so an <img src> can load the result without a session). An expired link returns 410 Gone; an invalid signature returns 401.

A result is fetchable for 1 hour after the job completes; after that the job state is reaped and the endpoint returns 404.

Errors are returned as JSON {"error": "<code>"} with a matching HTTP status. Streaming endpoints surface failures as an error event carrying { "code": "..." }.

Code Status Meaning
missing_prompt 400 The prompt field was empty.
invalid_json 400 The request body was not valid JSON.
invalid_width 400 width was not one of 512, 576, 768, 1024.
invalid_height 400 height was not one of 512, 576, 768, 1024.
invalid_steps 400 steps exceeded the maximum of 50.
unknown_engine 400 The engine id isn’t one this service offers. Check GET /v1/engines.
missing_session / session_invalid 401 No valid session.
unknown_bearer_scheme 401 The Bearer value is not an idn_pat_ token. No cookie fallback.
missing_workspace_header 401 A token was sent without X-Destesi-Workspace.
signature_invalid 401 Signed result URL had a bad signature.
engine_not_connected 409 You asked for an engine that needs a connected account you don’t have. Connect it in Connect.
engine_revoked 409 The connected account backing that engine was revoked. Reconnect it.
signature_expired 410 Signed result URL has expired.
enqueue_failed 500 The job could not be queued.
streaming_not_supported 500 The connection can’t stream Server-Sent Events (a proxy is buffering). Use the non-streaming endpoint.
deadline_exceeded 504 The render didn’t finish within the 120-second synchronous ceiling.
job_failed 502 The model reported a generation failure.
job_incomplete 502 The job finished but produced no image.
poll_failed 502 The service lost track of the job while waiting for it. Retry.
drive_unavailable 502 / 503 The image couldn’t be registered in Drive (or Drive isn’t configured).
redis_not_configured 503 The job queue isn’t configured (operator setup issue).
quota_policy_unavailable 503 Plan allowances couldn’t be resolved, so the request is refused rather than served un-metered.
quota 429 The workspace is at its plan allowance — generations this month, or concurrent jobs.
timeout (stream) Streaming job exceeded the 10-minute stream budget.
GET /v1/gallery

Returns the images this workspace has generated, newest first — the same list the web app’s gallery shows. Use it to find an earlier image without holding on to its image_url.

POST /v1/chat

The web app is a chat interface, and this is the endpoint behind it: describe what you want in conversation and the agent generates it, honouring the engine you selected.

Endpoint Description
GET /v1/auth/me Returns the signed-in user (id, email, name).
POST /v1/auth/logout Clears the imagegen_session cookie.
GET /v1/me/products Lists products the workspace can open (powers the launcher).
GET /healthz, GET /readyz Liveness / readiness probes.

Chat can generate images on your behalf as part of a conversation — there’s nothing to call directly. When you ask Chat for an image, it produces one inline and the file lands in your workspace’s Drive library, attributed to Chat. Image sizes for the chat path are constrained to 512, 768, or 1024 pixels per side.

These are the environment variables for self-hosting the image-generation service. Most have working defaults for local development.

Variable Default Purpose
IMAGEGEN_ADDR :9099 HTTP listen address.
IDENTITY_API_URL http://localhost:9091 Accounts service base URL (validates sessions).
IMAGEGEN_WEB_URL http://localhost:4328 Default redirect target for the web app.
IMAGEGEN_WEB_URL_ALLOWLIST Comma-separated allowlist of post-login redirect origins.
IMAGEGEN_CORS_ALLOWED_ORIGINS Comma-separated allowed CORS origins.
IMAGEGEN_REDIS_URL redis://localhost:6379/2 Job queue transport. Must use the same Redis database number the worker connects to.
DRIVE_API_URL http://localhost:9106 Drive base URL. Generated images are streamed here.
SUITE_TOOL_SECRET Shared secret used to authenticate to Drive and to back the Chat image tool. Without it, generation returns drive_unavailable.
IMAGEGEN_PUBLIC_BASE_URL http://localhost:9099 Externally reachable base URL, used to build result URLs.
IMAGEGEN_SIGNED_URL_TTL 24h Lifetime of signed result URLs (Go duration, e.g. 24h, 30m).

The worker consumes jobs from the queue and runs the diffusion model. It writes nothing to disk except the model cache — generated PNG bytes flow back through the queue and are uploaded to Drive by the API service.

Variable Default Purpose
IMAGEGEN_REDIS_URL redis://localhost:6379/2 Job queue transport. Must agree with the API on the database number.
IMAGEGEN_QUEUE_NAME bull:create-av:v2:imageGeneration BullMQ queue name the worker subscribes to. Must match the queue the API enqueues to (the API’s name is fixed in code at bull:create-av:v2:imageGeneration), so leave this at the default.
IMAGEGEN_DEVICE auto auto picks CUDA when a GPU is present, otherwise CPU. Set cuda to require a GPU (falls back to CPU with a warning) or cpu to force CPU.
IMAGEGEN_MODEL_ID stabilityai/stable-diffusion-xl-base-1.0 The diffusion model the local worker loads. Stable Diffusion XL is the default here; it is not the engine behind the hosted product.
IMAGEGEN_DEFAULT_WIDTH 1024 Default output width.
IMAGEGEN_DEFAULT_HEIGHT 1024 Default output height.
IMAGEGEN_DEFAULT_STEPS 30 Default diffusion steps.
IMAGEGEN_DEFAULT_GUIDANCE 7.5 Default guidance scale.