Open to every account with a credit pack

Reference images unlock on your account after a first credit pack, and you turn them on from the reference images page. What we accept, how every image is screened and reviewed, and what you warrant when you supply one, is on the reference images section of the docs. This page is the endpoint itself.

Supplying reference images from your backend

One agreement covers every supply, so nothing needs signing per upload. Send images to this endpoint and, with automatic review enabled, the response already carries the character_id you can generate with.

Before your first call

  1. Sign the agreement, once. Go to Sign the agreement. Nothing is uploaded there: signing on its own creates the agreement that covers everything you send afterwards.
  2. We countersign it. You get an email with the countersigned copy. Until then this endpoint answers 409 agreement_not_countersigned.
  3. Then supply from your backend. Nothing further to sign, per image or per batch.
Signing is a one-time act per account. If you already have an active agreement, signing again is refused with 409 master_already_signed rather than creating a second one.

POST /v1/reference-images

Bearer-authenticated with the same API key you use everywhere else. Up to 10 images per call, JPEG, PNG or WebP. A request body can be about 6 MB in all and base64 adds a third, so keep a call to roughly 4 MB of images in total and send larger sets in several calls; a bigger body is refused by the host with a 413 that carries no CORS headers, which browsers show as a network error.

curl https://api.xavira.ai/v1/reference-images \
  -H "Authorization: Bearer $XAVIRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model_id": "anime-pure-v1",
    "origin": "ai-own",
    "generator_model": "your-generator-v2",
    "generated_on": "2026-08-26",
    "images": [
      { "data": "<base64>", "mime": "image/webp", "name": "character-01.webp" }
    ]
  }'
fieldrequiredmeaning
model_idnoThe model the character is built on: realistic-sharp-v1 (default), realistic-v2, semireal-v1, anime-v2 or anime-pure-v1. Applies to every image in the call, and it is fixed once the character exists. The supply form in the portal offers the same choice. What the reference carries differs per model, see below.
originyesai-own (your own model) or ai-thirdparty (someone else's). Photographic or uncertain material is not accepted. Same values the supply form uses.
generator_modelyesWhat produced the image. This is the provenance record we may ask you to evidence.
generated_onnoDate, if you have it.
images[].datayesbase64, no data-URL prefix.
images[].mimeyesimage/jpeg, image/png or image/webp.
images[].namenoBecomes the character's name.
Re-sending a file you already supplied is safe. We match on the file's content, so a repeat is answered with "duplicate": true and the character_id it already has. No second character is created and nothing is re-screened, so you can re-run an import over your whole set without producing a duplicate library. Two copies inside one request are treated the same way.

A 201 looks like this:

{
  "submitted": 2,
  "duplicates": 1,
  "images": [
    { "id": "5f2c…", "status": "approved", "character_id": "9a41…" },
    { "id": "7b8e…", "status": "pending",
      "note": "Held for manual review: the age check was not conclusive." },
    { "id": "1c04…", "status": "approved", "character_id": "3d77…", "duplicate": true,
      "note": "Already supplied under this account; the existing character is returned unchanged." }
  ]
}

images answers for every image you sent, in the order you sent them, so it lines up with your own list even when some are skipped. submitted counts only the new ones.

Generate with an approved character_id immediately, as shown below. A pending one is not a capability yet: poll GET /v1/reference-characters, which lists only what has been approved and turned into a character. Note that each row's character_id is the character; its id is the reference image it came from.

Generating from the character

The supply call is the character creation: an approved image comes back with a character_id and there is no second call to make it. From there it behaves like any other character.

curl https://api.xavira.ai/v1/images:generate \
  -H "Authorization: Bearer $XAVIRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "character_id": "9a41…",
    "prompt": "sitting by a window, soft afternoon light"
  }'

A 201 carries output_url and generation_id. Keep the generation_id: video needs it.

Anime output is age-checked. On anime-pure-v1, a generated image in a sexual context (your prompt asks for nudity, sex, lingerie, swimwear or similar, or the image shows exposed breasts) that reads as possibly under 18 is withheld: 422 output_blocked, category apparent minor, deleted and not charged. A clothed, everyday image is delivered either way. The age a character reads as comes mostly from the reference you supplied, since on anime its traits are read off that image: characters drawn with clearly adult features and proportions are not affected. If the age check cannot run on an image in a sexual context you get 503 moderation_unavailable, safe to retry.
A prompt that names no clothing comes back dressed. When the request names no garment and asks for no nudity, we add an ordinary outfit and keep it on. Ask for nudity in words (nude, topless) or name your own garment and nothing is added.

Video from a reference character

Video is seeded from a first frame, and for these characters that frame has to be an image we generated, not the file you supplied. So it is two calls: generate an image, then pass its generation_id.

curl https://api.xavira.ai/v1/videos:generate \
  -H "Authorization: Bearer $XAVIRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "character_id": "9a41…",
    "generation_id": "<from the images:generate call>",
    "prompt": "she turns her head towards the window"
  }'
Passing only character_id is refused with 400 reference_character_needs_generation. That is deliberate: without a generation to seed from, the first frame would be your own uploaded file.

What makes a good reference image

What the image does depends on the model_id you supply it for.

realistic-sharp-v1: the image becomes the character's face reference, and it is fed to the face model on every generation afterwards. The face itself carries over. So its quality is not a one-off: a weak reference is weak on every image you ever generate from it.

semireal-v1 and realistic-v2: the image becomes the character's avatar and rides along as the reference on every generation, so the face, hair and type carry over into each new scene. For semireal-v1 a drawn or painted reference works best: the output stays in the register of the image you supply. The same rule applies: a weak reference is weak on every image after it.

anime-v2: for flat anime and cel-shaded characters. Besides the traits, we read the drawing's look in words: hair colour (pink, purple, blue and silver included), fringe and hairstyle, eye colour, and accessories such as glasses or earrings, never clothing. It becomes the character's appearance and goes into every scene; read it back on the character and correct it with a PATCH if we got something wrong. It carries the look, not an exact copy of the face.

anime-pure-v1: there is no face model for illustrations. We read the character's traits off the image (hair colour and length, apparent age range, build and so on), build the character from those, and the image becomes its avatar. The drawn face is not copied line for line. Anything the traits cannot express, such as a hair accessory, a scar or a signature outfit, belongs in the character's appearance (PATCH /v1/characters/{id}), which is written into every generation. A trait we read wrong can be corrected the same way.

whatwhy
At least 320px on the short edgeBelow this the supply is refused outright. There is not enough image to build a character from.
Face at least 112px tall (realistic only)The face embedding is computed at 112×112. A smaller face has been upscaled from less than that, and the likeness drifts on every generation.
Head frontal or three-quarter (realistic only)A profile or a head turned away gives the embedding little to hold on to.
One clear, unobscured faceAn image with no findable face is refused on either model: there is no character in it to build.

A tight head-and-shoulders portrait at 1024×1280 or larger is the easy answer. A full-body shot is fine too as long as it is big enough that the face still clears 112px: at 1728px tall, a face that looks small in frame is usually still around 200px.

Aspect ratio does not matter here. It has no effect on what you generate later: output size comes from the resolution you pass to images:generate, not from the file you supplied.

A supply that is accepted but weak comes back approved, with the problem named:

{
  "submitted": 1,
  "images": [
    { "id": "5f2c…", "status": "approved", "character_id": "9a41…",
      "note": "Approved, but: the face is about 78px tall, under the 112px "
              "the face model needs — identity will be weaker on every generation." }
  ]
}

That is worth acting on rather than ignoring. One that is refused says so plainly, and no character is made:

{ "id": "7b8e…", "status": "rejected",
  "note": "automated checks flagged: face_usable (no face found — this cannot serve as a face reference)" }

Fewer rejections, fewer withheld images

Age is judged from the picture, twice: once when you supply the image, and again on every image generated from the character. A stated age, a character sheet or a caption does not count; how the character looks does. In anime and illustrated styles age is harder to read than in a photo, so these are the things that decide it.

dowhy
Show an adult body, not only a face: waist-up or three-quarter, with adult shoulders, bust and hips visibleA head-only drawing tells us nothing about the body, and a character built without one tends to be drawn slight and small in later scenes. That is the most common reason a generated image is withheld.
Adult facial structure: a defined jaw and cheekbones, eyes in adult proportion to the faceVery round faces, oversized eyes and chibi proportions read as young in any style.
Adult stylingSchool uniforms, childlike accessories, toys, oversized clothing and childlike poses (knees together, cupped hands, a finger to the mouth) all push the read younger. So does a hood or cape that frames a small face.
One character, face clearly visibleMasks, goggles, hair over the eyes or a face that is tiny in a large scene hide exactly what the check needs to see.
1024px or more on the short edge320px is the hard minimum. Small images lose the detail that separates an adult face from a young one.
Neutral lightingStrongly coloured light, such as blue night neon, changes how hair and eye colours are read, and the character is redrawn in the colours we read.

Pick the model that matches the style. Flat anime or cel shading: anime-v2. Soft webtoon or painted art: semireal-v1. Photographic: realistic-v2 or realistic-sharp-v1. A model far from the style of the image redraws the character in its own look, and likeness suffers.

After approval, check the character. GET /v1/characters/{id} shows the traits and the appearance we read. If the build or bust is missing or wrong, set it; on anime-v2 we fill an adult build when none could be read. A wrong hair or eye colour is corrected the same way, with PATCH /v1/characters/{id}.

Some generated images can still be withheld. In a sexual scene, an image that reads as possibly under 18 is withheld with 422 output_blocked and not charged. It happens most in close-ups where the face fills the frame; scenes that show more of the body pass more often. Another seed or a pose with more of the body in shot is the practical answer.

An image that reads as a minor at supply is refused outright and never stored, and no setting, note or reviewer changes that. If one of your characters is refused, supply a version drawn clearly as an adult.

What is still checked

Removing the paperwork did not remove the screening. Every image runs the same intake checks as a browser upload, and the same approval gate decides. Three outcomes:

  • approved — usable. A character is created and returned. Check note: an image can be approved and still carry a warning about how well the face will hold.
  • rejected — the gate refused. The reason is in note.
  • pending — the age check could not tell. A person decides these, and that is deliberate: an inconclusive age result is a judgement we take under a named reviewer. The one exception (since 2026-09-22): on a drawn or painted image, an inconclusive first read gets a second automated read that looks past the art style, and only a clearly adult result lets the image continue; anything else still goes to a person.
An image that reads as a minor is refused outright with 422 image_rejected and is never stored. That is not reviewable and no setting changes it.

Errors worth handling

statuscodewhat to do
403reference_images_not_enabledThe account lost the capability. Contact us.
409no_master_agreementNo live agreement, or it was revoked. Stop retrying and contact us.
409agreement_not_countersignedSigned by you, not yet by us. You will be emailed.
422image_rejectedThat image cannot be supplied. Do not retry it.
201status: "rejected"Not an HTTP error. The image was stored and refused — note says why. Common causes: no findable face, or under 320px on the short edge.
400generator_model_requiredAlways required. Name the system that produced the image.
400reference_character_needs_generationOn videos:generate. Generate an image first and pass its generation_id.

Questions

Reply to any of our emails, or write to hello@xavira.ai.