API reference
API reference
Section titled “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.
Authentication
Section titled “Authentication”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:
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.
Engines
Section titled “Engines”GET /v1/enginesLists 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.
Generate (synchronous)
Section titled “Generate (synchronous)”POST /v1/generateSubmits 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— whenimage_urlstops being fetchable (the file remains in Drive).
Example:
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 }'Generate and stream
Section titled “Generate and stream”POST /v1/generate-and-streamSubmits 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). |
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"}'Stream an existing job
Section titled “Stream an existing job”GET /v1/jobs/{id}/streamOpens 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.
Fetch a job result
Section titled “Fetch a job result”GET /v1/jobs/{id}/resultReturns 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 inimage_urlso an<img src>can load the result without a session). An expired link returns410 Gone; an invalid signature returns401.
A result is fetchable for 1 hour after the job completes; after that the job state is reaped and the endpoint returns 404.
Error codes
Section titled “Error codes”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. |
Gallery
Section titled “Gallery”GET /v1/galleryReturns 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.
The image-generation agent
Section titled “The image-generation agent”POST /v1/chatThe 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.
Auth & session endpoints
Section titled “Auth & session endpoints”| 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. |
Generating images from Chat
Section titled “Generating images from Chat”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.
Configuration (operators)
Section titled “Configuration (operators)”These are the environment variables for self-hosting the image-generation service. Most have working defaults for local development.
API service
Section titled “API service”| 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). |
Worker (self-hosted only)
Section titled “Worker (self-hosted only)”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. |