API reference
POST /ai/imagine
Generate an image from a prompt.
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. Fire-and-forget async submission is intentionally not exposed in v1 — every call blocks until the job settles or the wait window elapses.
Endpoint
/api/v1/ai/imagine
The request body is JSON. Only prompt is required. The mode is fixed to text-to-image; there is
no image-input, mask, or edit parameter on this endpoint in v1.
Like the OpenAI-compatible Chat completions endpoint — and unlike
the native REST resources — this endpoint returns a bare JSON object with no
{ "data": …, "meta": … } envelope. 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.
Authorization and scope
Send a krallo_live_… bearer key in the Authorization header. The key must hold the
ai:imagine scope (or the * wildcard). See
Authentication for how keys are issued and sent.
| Requirement | Value |
|---|---|
| Method | POST |
| Scope | ai:imagine |
| Auth | Bearer krallo_live_… key |
| Content type | application/json |
| Metering | Flat per-image credit cost, pre-charged and refunded on non-delivery |
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. Passed through as-is and not range-validated by the API. |
guidance |
number | optional | Prompt-adherence (guidance) strength. Omit to use the provider default. Passed through as-is. |
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. Passed through as-is. |
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 generation mode is always text-to-image. steps, guidance, and strength are forwarded to the image pipeline without range validation, so unsupported values fall back to provider behavior rather than being rejected.
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. Keep your HTTP read timeout at least as large as the wait you request.
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; relative pipeline paths are resolved to an absolute URL before
they are returned.
{
"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 natural idempotency key on the imagine.completed 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; the field is omitted when the provider label is unavailable. |
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 the owner’s prepaid wallet only after that allowance is exhausted and only while
billing is enabled. See Billing for the credit model, free quota, and how
the wallet is debited.
- Refund on failure or timeout. A
502(failed/cancelled 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 wallet of the developer who 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. The synchronous response is
authoritative for a single call; the webhook is useful for auditing or for reconciling generations across your
fleet. Because job_id is a unique per-job id, use it as an idempotency key to deduplicate a
retried delivery.
{
"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
}
Errors
Errors raised by the endpoint itself (400, 405, 502,
504) use the shape { "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 (pre-flight OPTIONS returns 204). |
| 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, or the orchestrator threw. Refunded; retryable. The message echoes the job’s error detail when one is available. |
| 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. See Rate limits for
tiers and header semantics.
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 and correlation ids.