Build with the AI
Image generation
Generate images from a prompt with a synchronous, credit-metered endpoint.
The imagine endpoint turns a text prompt into an image and returns the finished image URL in a single request. It is synchronous: the connection is held open while the job runs, up to a bounded timeout, and the response carries the result directly. Any request that fails or does not finish in time is cancelled and refunded, so you are never charged for an image you did not receive. There is no separate fire-and-forget submit/poll flow in v1.
Endpoint
/api/v1/ai/imagine
Requires a krallo_live_… bearer key with the ai:imagine
scope. See Authentication for how
keys are issued and sent. The request body is JSON; only prompt is required.
Unlike the native REST resources, this endpoint returns a bare JSON object (no
{ "data": …, "meta": … } envelope), matching the style of the OpenAI-compatible
Chat completions endpoint. Errors raised by the endpoint
itself (400, 405, 502, 504) use the shape
{ "error": { "message": …, "type": … } }; the shared authentication, scope, and credit
refusals (401, 403, 402) use the native
{ "error": { "code": …, "message": … }, "meta": … } envelope.
Request parameters
All parameters live in the JSON request body.
| Name | Type | Required | Description |
|---|---|---|---|
prompt |
string | required | The text description of the image to generate. Must be present and non-blank, or the call returns 400 invalid_request_error. |
negative_prompt |
string | optional | Concepts to steer away from. Support depends on the underlying provider. |
seed |
integer | optional | Seed for reproducible sampling. Omit for a random seed on each call. |
steps |
integer | optional | Sampler step count. Omit to use the provider default. |
guidance |
number | optional | Prompt-adherence (guidance) strength. Omit to use the provider default. |
width |
integer | optional | Requested output width in pixels. Omit to use the provider default. |
height |
integer | optional | Requested output height in pixels. Omit to use the provider default. |
output_count |
integer | optional | Number of images the job should render. The synchronous response surfaces a single output_url (the primary image) regardless of count. |
strength |
number | optional | Denoising/transform strength for the sampler. Omit to use the provider default. |
project_id |
string | optional | Your own grouping/reference id, attached to the job for organization. It does not affect generation. |
wait_timeout_ms |
integer | optional | How long the request will wait for the job to finish, in milliseconds. Default 90000; clamped to the range 15000–180000. Values outside the range are pulled to the nearest bound. |
The mode is fixed to text-to-image. There is no image-input, mask, or edit parameter on this endpoint in v1; steps, guidance, and strength are passed through to the image pipeline as-is and are not range-validated by the API, so unsupported values fall back to provider behavior.
Synchronous wait behavior
When you POST, the request submits the job and blocks until one of the following happens:
- The job succeeds within the wait window — you get
200with the image URL. - The job fails (a terminal failed/cancelled state) — you get
502 upstream_errorand the charge is refunded. - The wait window elapses while the job is still running — the still-running job is cancelled, then briefly re-polled (about 3 seconds) for its authoritative terminal state. If it happened to finish in that window you still get the image; otherwise you get
504 timeoutand the charge is refunded.
Cancelling a timed-out job before refunding is deliberate: it prevents a background job from finishing and
delivering an image after you have already been refunded. Choose wait_timeout_ms to fit your
client’s own read timeout — a larger value tolerates slow provider queues, a smaller value returns a
retryable 504 sooner.
Example request
cURLcurl https://exe.krallo.com/api/v1/ai/imagine \
-H "Authorization: Bearer krallo_live_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "a lighthouse on a cliff at dawn, cinematic, volumetric light",
"negative_prompt": "text, watermark, low quality",
"width": 1024,
"height": 1024,
"seed": 42,
"wait_timeout_ms": 120000
}'
Node.js
const res = await fetch("https://exe.krallo.com/api/v1/ai/imagine", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KRALLO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt: "a lighthouse on a cliff at dawn, cinematic, volumetric light",
width: 1024,
height: 1024,
wait_timeout_ms: 120000,
}),
});
if (!res.ok) {
const err = await res.json();
throw new Error(`${res.status} ${err.error?.type}: ${err.error?.message}`);
}
const { output_url } = await res.json();
console.log("image:", output_url);
Success response
A completed job returns 200 OK with a bare object. output_url is an absolute,
publicly fetchable URL for the finished image.
{
"job_id": "9f2c1b7a-4e6d-40b1-a0c3-f5e8d2b1a6c4",
"status": "SUCCEEDED",
"output_url": "https://exe.krallo.com/img/ai/c8b1f0d2-7a3e-4f19-9d5c-2e6b8a4c0d17.png",
"provider_used": "..."
}
| Field | Type | Description |
|---|---|---|
job_id | string | Unique id for this generation job. Also the idempotency key on the webhook, if you subscribe to one. |
status | string | Always SUCCEEDED on a 200 response. |
output_url | string | Absolute URL of the generated image. |
provider_used | string | Optional. A label for the backend that rendered the image. Present when known; omitted otherwise. |
Billing and refunds
Image generation is billed at a flat per-image cost (default 10 credit units),
not by tokens — the price is known up front, so the cost is pre-charged before the job runs and refunded on
any non-delivery. Each app has a free daily allowance (default 10000 units) that resets at UTC
midnight; charges hit your prepaid wallet only after that allowance is exhausted. See
Billing for the credit model, free quota, and how the owner’s wallet
is debited.
- Refund on failure or timeout. A
502(failed job) or504(wait timeout, with the job cancelled) reverses both the wallet debit and the day-counter increment, so a refused image never keeps your credits or burns free quota. - Insufficient balance. If you are over the free allowance and the wallet cannot cover the cost, the call returns
402 insufficient_creditsbefore any image is generated. The error body carriesrequired_unitsand your currentbalance.
A successful image is charged once, on the developer’s wallet that owns the app’s key. Retrying a 502/504 is safe from a billing standpoint — the failed attempt was already refunded.
Webhook: imagine.completed
If you have a verified webhook endpoint subscribed to
imagine.completed, a successful generation also fires that event (best-effort, after the image is
finalized). The synchronous response is authoritative for a single call; the webhook is useful for auditing
or for reconciling generations across your fleet. The payload is URL-only — it never carries model, cost, or
other provider internals beyond the provider label.
{
"job_id": "9f2c1b7a-4e6d-40b1-a0c3-f5e8d2b1a6c4",
"status": "SUCCEEDED",
"output_url": "https://exe.krallo.com/img/ai/c8b1f0d2-7a3e-4f19-9d5c-2e6b8a4c0d17.png",
"provider_used": "...",
"mode": "text_to_image",
"width": 1024,
"height": 1024
}
Because job_id is a unique per-job UUID, it is a natural idempotency key — deduplicate on it if a
delivery is retried.
Errors
Errors raised by the endpoint itself (400, 405, 502,
504) use { "error": { "message": …, "type": … } }; authentication, scope,
and credit refusals (401, 403, 402) use the native envelope,
with the identifier in error.code. See Errors
for the shared model and correlation ids.
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | Body is not valid JSON, or prompt is missing/blank. |
| 401 | unauthorized / invalid_token / token_expired / token_revoked | Missing, malformed, expired, or revoked bearer key. See Authentication. |
| 402 | insufficient_credits | Over the free allowance and the wallet cannot cover the flat image cost. Body includes required_units and balance. |
| 403 | insufficient_scope / app_disabled / owner_suspended | Key lacks the ai:imagine scope (body includes required_scope), or the app/account is disabled. |
| 405 | method_not_allowed | Any method other than POST (or the pre-flight OPTIONS). |
| 429 | rate_limited | Per-key rate limit exceeded. Honor the Retry-After header. See Rate limits. |
| 502 | upstream_error | The job reached a terminal failed/cancelled state. Refunded; retryable. |
| 504 | timeout | The job did not finish within wait_timeout_ms; it was cancelled. Refunded; retryable. |
Rate limits
Imagine calls count against your key’s per-minute rate limit, which is driven by the app’s tier.
Every response past the authentication and scope checks carries advisory
X-RateLimit-Limit, X-RateLimit-Remaining, and
X-RateLimit-Reset headers; a 429 additionally sets Retry-After. Because
a request holds the connection for the full generation, keep your client’s read timeout at least as
large as your chosen wait_timeout_ms.
Related
- Chat completions — the token-metered text/agent endpoint.
- Billing — credits, free quota, and wallet debits.
- Scopes — grant
ai:imagineto a key. - Webhooks — subscribe to
imagine.completed. - Errors — the shared error model.