Image generation and editing
Generate images, send reference pictures, track progress, and retrieve private output.
Endpoint and credentials
Call POST https://api.kendr.org/api/v1/images/generations with a key scoped to models:invoke or an app/session credential. Use the kendr-image alias. This dedicated Kendr route differs from the upstream SDK’s /v1/images/generations path.
Generation returns private metadata, not base64 output or a public URL. Downloading /api/me/generated-images/{image_id} requires the same owner’s app session, browser cookie on its origin, or OAuth token with app scope. A kndr_live_... key alone cannot download the image.
Generate one image
curl https://api.kendr.org/api/v1/images/generations \
-H "Authorization: Bearer $KENDR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: product-428-hero-v1" \
-d '{
"model": "kendr-image",
"prompt": "A ceramic travel mug on a warm neutral studio background, soft side lighting",
"size": "1536x1024",
"quality": "medium"
}'
| Field | Accepted values | Behavior |
|---|---|---|
| prompt | Non-empty string, up to 32,000 characters | A selected provider may impose a smaller limit. |
| size | 1024x1024, 1536x1024, 1024x1536 | Default 1536x1024; provider dimensions can use a closest supported size. |
| quality | low, medium, high | Default medium. |
| input_images | Up to 4 PNG, JPEG, or WebP images | 8 MiB per image, 12 MiB combined; base64 without a data-URI prefix. |
| conversation_id | Optional valid chat-memory ID | Deleting the associated stored conversation also deletes its images. |
| stream | Boolean, default false | Progress SSE when supported; accept JSON replays too. |
One request generates one image. Batch n, seeds, masks, transparent-background controls, and output-format selectors are not in the supported contract.
Read the result
Illustrative response excerpt; IDs and charges vary:
{
"ok": true,
"request_id": "product-428-hero-v1",
"model": "kendr-image",
"image": {
"id": "img_0123456789abcdef01234567",
"url": "/api/me/generated-images/img_0123456789abcdef01234567",
"mimeType": "image/png",
"size": "1536x1024",
"quality": "medium"
}
}
Persist request_id, image.id, and the settled credit_micros_charged. Use the exact charge returned by Kendr; token counts alone do not represent image cost.
Edit with a reference image
Prepare this JSON body in Python, then send it to the same endpoint with a new request ID:
import base64
from pathlib import Path
payload = {
"model": "kendr-image",
"prompt": "Keep the mug shape and logo; change the background to pale blue.",
"input_images": [{
"name": "mug.png",
"media_type": "image/png",
"data": base64.b64encode(Path("mug.png").read_bytes()).decode("ascii"),
}],
}
The media type must match the actual bytes. Base64 expands data by about a third; the HTTP body limit is 24 MiB. If no enabled route supports reference images, the request fails instead of ignoring them. The image studio project includes runnable generation and editing commands.
Retrieve the private image
Use an app session for the same account that generated it. Replace the image ID and choose the extension from image.mimeType.
curl --fail-with-body \ https://api.kendr.org/api/me/generated-images/img_0123456789abcdef01234567 \ -H "X-Kendr-Session: $KENDR_SESSION_TOKEN" \ -o generated-image.png
An image URL is not a bearer credential. Keep session tokens on the server and check user authorization before serving bytes through your own application.
Observe image progress
Add stream: true and Accept: text/event-stream. Parse complete SSE events, then JSON-decode each event’s data.
| Event | Meaning |
|---|---|
| start | Validated request and reserved credits. |
| status | Generating or finalizing. |
| partial_image | Optional frame with b64 and mime_type; not the final deliverable. |
| done | Final generation result with image metadata and settled usage. |
| error | Terminal failure; inspect status and request_id. |
Admission errors and completed replays can return JSON. Partial frames are route-dependent and may never arrive. After a disconnect, the request can still finish server-side. Retry the exact image request with the same key to retrieve its result.
Troubleshoot and retry
Use a new key for a new image or edit and preserve it for recovery. In-progress duplicates can return HTTP 400; back off with the same key. A completed replay sets idempotent_replay: true and does not charge again. Never reuse a key for a different prompt.
| Failure | Next step |
|---|---|
| Invalid image or options | Check decoded size, media type, size, and quality. |
| 402 | Fund the account before creating another operation. |
| 502 | Use bounded backoff with the same body and key. |
| Download unauthorized | Use the owner’s app/session credential; an API key is insufficient. |