Pro API: generate and retouch images

One image costs 1 credit, whatever the mode. Generation from a prompt and retouching of an image you supply both go through the same route.

POST /images/quote takes the same body and answers the price without creating anything, always { "cost": 1, ... } here. It exists for symmetry with videos, where the price does vary.

Submit an image

POST /images

json
{ "prompt": "portrait of a woman in a rainy street at night, neon reflections, cinematic",
  "width": 832, "height": 1216 }
FieldTypeDefaultWhat it does
promptstringrequiredThe scene. English gives the most precise results.
width, heightint1024 x 1024Pixel size. Good shapes: 1024 x 1024 (square), 832 x 1216 (portrait), 1216 x 832 (landscape).
modestringgenerationedit, img2img or inpaint, see below. Absent means generate from the prompt.
image_b64stringSource image in base64 (PNG or JPEG), required by all three retouching modes. 12 MB decoded at most.
mask_b64stringMask in base64: required by inpaint, optional for edit. Paint the areas to retouch in red, at the source image's size.
lorastringA trained character on the account: the name returned by GET /loras. Put its trigger in the prompt.
lora2, lora2_strengthstring, numberA second character and its strength (0 to 1, default 1) for a two-person scene.
countint1How many images to render from this one request, 1 to 8. Each gets its own fresh seed; with seed, they take seed, seed+1, seed+2... This is the app's Images per generation selector.
seedintrandomSeed of the render, from 0 to 4294967295. Same seed, same prompt and same settings: the same render, with very slight differences if the graphics card changes. Keeping the seed and adjusting the prompt gives a variation that keeps the composition.
stepsintengineLeave empty. The engine knows its own value.

The answer:

json
{ "jobId": "test-79634842-4586-46d4-ad52-d9ea236ae00d", "seed": 178078967, "cost": 1 }

The seed is returned in the answer, whether you gave it or it was drawn at random: keep it to render the same image again, or a variation of it.

Asking for several at once

With count above 1, one request starts several renders, each with its own seed, and the answer lists them:

json
{ "count": 4, "requested": 4, "cost_total": 4,
  "jobs": [ { "jobId": "...", "seed": 178078967, "cost": 1 }, { "jobId": "...", "seed": 903412, "cost": 1 } ],
  "errors": [],
  "jobId": "...", "seed": 178078967, "cost": 1 }

Two things to read carefully.

The jobId, seed and cost of the first render are repeated at the top level. That is there so clients written before batches keep working unchanged, not because the first one matters more. Poll every entry of jobs, not just that one.

And compare count with requested. A refusal partway through, for want of credits or because the key hit its daily cap, stops the batch where it is: requested is what you asked for, count is what actually started, and errors says why it stopped. A client that assumes the two are equal quietly loses renders.

POST /images/quote takes count too, and prices the whole batch. Videos have no count: one request, one clip.

The three retouching modes

All three run on the same instruction-driven editing engine, which preserves the look of the source image. When you supply a source image, width and height are read from the image itself and do not need to be sent.

modeInputsWhat the engine does
editimage_b64 + prompt (+ optional mask_b64)Applies the instruction to the whole image ("add sunglasses", "make it snow"). With a mask, the change concentrates inside the red area.
inpaintimage_b64 + mask_b64 + promptRegenerates only the red area of the mask, following the instruction. The rest is recomposed pixel for pixel.
img2imgimage_b64 + promptReworks the whole image along the prompt while keeping its composition and look: a variation.

In edit, the server picks the best way to handle the edit on its own, like the app:

  • with a mask: it reads the instruction and chooses between replacing what the area holds (another object, another garment, a face: the area is redrawn from what should appear there) and modifying what is there (colour, material, light). With a character lora, the area is always redrawn, face included;
  • without a mask, with a character lora ("replace the woman with kara..."): the server finds the person in the photo and redraws them as the character; if it finds no one, the instruction applies to the whole image, as before.

Same price in every case (1 credit per image). The choice shows in the result, not in the response.

bash
IMG=$(base64 -w0 photo.png)
curl -s $B/images -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d "{\"mode\":\"edit\",\"prompt\":\"add a red scarf, keep everything else identical\",\"image_b64\":\"$IMG\"}"

Create an image with characters, objects and a location

Add a multiref object to POST /images and the server plays your characters (Characters), your objects and animals and a location (Subjects) together in a single image, each one recognisable, like the app's "Create an image" screen. Call each one by @Name in the prompt, in English.

json
{ "prompt": "@Mia hands a cup of coffee to @Tom on a sunny terrace, both smiling",
  "multiref": {
    "characters": [
      { "character_id": "chr_8f3a...", "name": "Mia", "view": 1 },
      { "character_id": "chr_21bc...", "name": "Tom", "lora": "tom_v1", "strength": 1.0, "lora_pass": 0.55 }
    ],
    "subjects": [ { "subject_id": "sub_...", "name": "Mug" } ],
    "location": { "subject_id": "sub_...", "name": "Terrace" }
  },
  "width": 1344, "height": 768, "seed": 42, "count": 1 }
Field of multirefWhat it does
characters[].character_id, nameA character from GET /characters; name is the word written after @ (letters, digits, _).
characters[].viewThe view of its sheet that stands for it, 1 to 5 (1, the front bust, by default).
characters[].sheet_idOptional: the sheet to use (the most recent by default).
characters[].loraOptional: its image LoRA, a name from the image list of GET /loras. The character is then played by its LoRA, not by its view.
characters[].strengthLoRA strength, 0 to 1.5 (1 by default).
characters[].lora_passShare of the image repainted by its LoRA pass, 0.3 to 0.9 (0.55 by default); useful with several subjects.
characters[].descriptionOptional, in English: outfit, age...
subjects[]Objects and animals from /subjects: subject_id, name (the first word of its name by default), description, image (which of its images, 1 by default).
locationA location from /subjects (subject_id, name), or a one-off background: background_b64 and background_description.
count1 or 2: the images are rendered one after the other.
  • At most 4 subjects (characters, objects and animals). Without a LoRA, from images only: 4 references at most, the location included. The server picks the method from the references, and places each one in the order of characters then subjects, from left to right.
  • An @Name that matches no reference is refused (unknown_mention); a reference missing from the prompt is sent all the same.
  • Price: one pass costs the price of one image (1 credit). A simple image is 1 pass; several characters played by a LoRA, 1 plus one pass per LoRA (4 at most); from images only, one pass per reference beyond the first; a mix, both. The total (passes x count) is reserved at submission and debited once, at the final image: a failure or a cancel costs nothing. POST /images/quote with the same body gives the exact price, the method and its passes, without starting anything.
  • Answer 202: { "jobId": "mri_...", "seed", "cost", "method", "passes", "billed_passes" } (with count 2: jobs and cost_total). Polling, download and cancel use the usual routes below; while it renders, progress gives the current pass ("2/3"). An image takes 1 to 3 minutes.
  • The final image stays available for 1 hour, and 10 minutes after its first download. A failed pass gives status: "failed" with reason: "render_failed"; a cancel, reason: "cancelled".
  • Errors: invalid_count, invalid_view, invalid_lora_pass, invalid_subject_image, invalid_size, character_not_ready, plus those of Multi-Ref (unknown_character, unknown_subject, unknown_lora, unknown_mention, multiref_too_many_references, invalid_strength...). See Limits and errors.

Poll

GET /images/{jobId}

AnswerMeaning
{ "status": "pending", "phase": "IN_QUEUE" }Queued
{ "status": "pending", "phase": "IN_PROGRESS" }Rendering
{ "status": "completed", "cost": 1, "file_url": "/pro/v1/images/{jobId}/file", ... }Done, file ready
{ "status": "failed", "detail": "..." }Failed, and nothing is charged

Poll every 3 seconds, no faster: the queue is shared.

Download

GET /images/{jobId}/file

The PNG itself, as the response body (Content-Type: image/png). Available as soon as the status is completed, and 404 media_unavailable once the render is gone. Download it straight away, it is not archived.

Cancel

POST /images/{jobId}/cancel

Cancels a queued or running render. Answers { "ok": true }. Nothing is charged, and the cost reserved against the key's daily cap is given back.

Windows · Mac · Cloud or local

Generate privately. Tonight.

Download tendre.AI for free and generate without limits, privately, on your own GPU.

OS Windows 10/11GPU NVIDIA 8GB+VRAM 12GB recommendedDisk 20GB

Mac: Apple silicon (M1 or later), macOS 13 or later. Intel Macs are not supported.

Local image generation on Mac needs an M4 with 24 GB of memory, or an M2 or M3 Pro, Max or Ultra with 24 GB. Below that, everything runs online with credits. Retouching and video run online on Mac for now.

Everything about tendre.AI for Mac

No NVIDIA GPU? Start 100% in the cloud: 5 free images and 1 free video every day. An NVIDIA card (8GB+) unlocks unlimited local generation.