Characters, through code.

Generate with bake3d-1.3-enhanced, poll a durable job, then take the rig and its motion into your own application. For AI assistants, use the MCP server.

Authentication

Create a key in Account → API keys on a Pro or Studio account. Send it in the Authorization header. Keys use the owning account’s credits and permissions; keep them on your server or in your editor’s local environment. A Free account receives 403 with { "error": "API access needs a paid plan" }, including for keys created before a downgrade.

sh
Authorization: Bearer bk_live_YOUR_KEY

Reads and exports are free. Generation costs 30, 40 or 55 credits for a draft, standard or high mesh, plus 10 once when you send two to four views, plus 25 for rigging, plus 14 for faithfulFace on a high mesh (refunded when unused); a rigged standard character from one image is 65. Re-rigging an existing mesh costs 25 and new described motion 10. A paid request your balance cannot cover is refused with 402 before anything is fetched, checked or created. The job’s creditsCharged states what was charged. A failed job refunds what it did not deliver: its whole charge when nothing reached the project, and only the rig’s 25 when a generation’s mesh is in the project but its rig could not be made. Such a job ends with stage "Failed after delivering", and its creditsCharged then states what it kept for what it delivered. Rate limits apply to all public API and MCP calls together and are counted per account, across all of its keys; the default is 60 requests per minute. Every response that passes authentication and the plan check carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds); a 429 adds Retry-After in seconds. The 401, 403 and 503 answers that come before that check carry none.

The API is meant for servers, editor add-ons and game-engine tools; its endpoints do not answer browser cross-origin requests. Signed download links (/api/files/…?exp=…&sig=…) need no key and may be fetched from any origin, including a browser or WebGL build, until they expire.

Account

GET /api/v1/account

Returns { credits, plan, model, costs }: the current balance, the plan, the public model name, and the credit cost of every action by name (generateMesh, rig, customAnimation, export and the rest). Read it before starting paid work. Free.

Upload

POST /api/v1/uploads

Upload a local PNG, JPEG or WebP image of at most 12 MiB, then pass the returned imageAssetId to Generate. Send multipart/form-data with a file field (image is accepted too), or JSON { "dataBase64": "…", "contentType": "image/png", "filename": "fox.png" }. The bytes are checked and the detected type is stored. The body is read as a stream and refused with 413 as soon as it passes the limit, whether or not the request states its length. Uploads count against your storage quota and a per-account upload window; they cost no credits.

sh
curl https://bake3d.ai/api/v1/uploads \
  -H "Authorization: Bearer $BAKE3D_API_KEY" \
  -F "file=@fox.png"

Returns 201. url is a signed link valid until expiresAt.

Response
{
  "imageAssetId": "image-asset-id",
  "url": "https://bake3d.ai/api/files/…?exp=…&sig=…",
  "expiresAt": "2026-09-25T16:00:00.000Z",
  "contentType": "image/png",
  "sizeBytes": 482113,
  "width": 1024,
  "height": 1024
}

Generate

POST /api/v1/generate

Provide exactly one of prompt, imageUrl, imageUrls, an owned imageAssetId (from Upload), or imageAssetIds. Prompts are 3–1,024 characters and describe one 3D model: a character, creature, vehicle, machine, building, prop or a multi-piece prop such as a chess set or a rider on a horse, in as much detail as you like. A prompt that is not a single asset at all (a whole game, an app, a website or code, an open world, city or level, a video, or several unrelated characters meant as separate assets) is refused with 422 before anything is created or charged; details.code is prompt-not-one-subject and details.suggestions lists up to three single-subject prompts taken from your text. Bake3D does not accept nudity (exposed genitals or bare female breasts) or sexual content: a picture (any of the views) or prompt that shows it is declined with 422 before anything is created or charged, with details.code content-policy, details.source image or prompt, details.charged: false and details.contactUrl if you think it is a mistake. Swimwear, underwear, armour, revealing costumes, creatures and stylised characters are fine. A re-bake of the same uploaded picture reuses its verdict. Image URLs must serve PNG, JPEG or WebP images no larger than 12 MiB. A URL must be reachable by the service; a local filesystem path is not a URL. Downloaded pictures are stored in your library like uploads and count against your storage quota (413 with details.storage when they do not fit); pictures downloaded for a request that is then declined are not kept. The price is checked first: a balance below it answers 402 with details.needed and details.balance, before any picture is downloaded or checked and before any project is created.

sh
curl https://bake3d.ai/api/v1/generate \
  -H "Authorization: Bearer $BAKE3D_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5c1f0f8e-2d7b-4f7e-9d7a-1b2c3d4e5f60" \
  -d '{"prompt":"A ceramic fox standing on four legs","options":{"rig":true,"quality":"standard"}}'

Multiple views. imageUrls and imageAssetIds take one to four images of the same subject, together no larger than 32 MiB; entries must be distinct. The first image is the primary reference: it drives the source review, the character analysis and the thumbnail. The remaining angles (back, side, top) are passed to the geometry engine when it reads several views. The primary engine does; a fallback engine used when it is unavailable may read fewer of them (at least the primary view). The finished job reports which happened in output.views as { provided, used, provider }, where provider is primary or fallback, and adds a warning to output.warnings when used is smaller than provided. Keep the same subject, pose and scale in every view; a different pose in a second image confuses the reconstruction rather than helping it.

Multi-view request
curl https://bake3d.ai/api/v1/generate \
  -H "Authorization: Bearer $BAKE3D_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"imageUrls":["https://example.com/rover-front.png","https://example.com/rover-side.png","https://example.com/rover-back.png"],"options":{"rig":true}}'
# Finished job → "output": { "views": { "provided": 3, "used": 3, "provider": "primary" }, … }

Options: quality is draft, standard or high; rig enables the skeleton and skinning; analyze (default true) enables character analysis. Three engine options are passed to the geometry engine where it supports them: pbr (default true) asks for physically based materials, removeBackground (default true) separates the subject from its background first, and an integer seed makes a re-bake of the same input repeatable. model accepts only "bake3d-1.3-enhanced". These four are REST-only; the MCP tool uses their defaults. Successful rigging also attempts a four-second introductory animation from the actual rig, without another credit charge. When it cannot be made, the job still succeeds with the mesh and rig, output.warnings says so and output.initialAnimation has no animationId; create it later with POST /api/v1/projects/:id/initial-animation at no charge. Set initialAnimation: false to skip it. Mesh-only jobs do not create motion. Rigged image jobs check the pose first: when limbs touch, cross or are cut off, the mesh is built from a prepared rig-ready picture of the same subject (included; output.posePrep says what happened and the prepared picture is listed with the project's assets). A reference or turnaround sheet, a collage, a scene around the subject or text over the picture is reduced to its main subject alone on a plain background, for rigid objects too, and the reason says what was kept; a picture of several equal subjects is used as it is. A subject shown mid-action or in a posture that is not its default (running, jumping, flying, sitting, lying, coiled, rearing) is rebuilt in its own default resting pose, and that action becomes its included animation (output.posePrep.state). A travelling action (walking, trotting, running, galloping, jumping, crawling, slithering, swimming or flying; state.locomotion) is played on the spot by the rig's own fitted gait when one fits, with planted feet sliding back under the body. Rigid objects and good poses on a clean picture are used as uploaded; set posePrep: false to always use the upload. Text jobs paint a concept image from the prompt first; rigged text jobs paint it in a rig-ready pose and check it the same way, so a concept whose limbs touch or whose stance contradicts the prompt (for example upright when the prompt asks for all four legs) is re-posed before modelling. An action or posture the prompt asks for (a running cheetah, a sitting dog) is painted at rest and becomes the included animation. posePrep: false uses the concept as painted. Additional views must show the same subject in the same pose from other angles; one that shows something else (another person or outfit, a screenshot) is left out before modelling, and the extra-views charge is refunded when none remains. With a single image, generateViews: true also generates side and back views for the geometry engine (the extra-views price, refunded when none passes its consistency check). topology: "quads" ("Game-ready mesh") keeps the full-detail model and adds a game-ready copy at the same price: about 7–10K faces, mostly quads (under 20K triangles) with the same skeleton and animations; export it with mesh: "game". When the copy does not pass its checks, the job still succeeds with the full-detail model and output.warnings says so. faithfulFace: true ("Faithful face", High only, not with topology: "quads") keeps eyes and face shape closer to the picture on people and human-like characters (an organic face that is flat, with no muzzle, snout or beak), for 14 more credits; eye colour can shift. When the picture's subject has no such face, or the standard High engine builds the mesh, the 14 credits are refunded, and output.faithfulFace and output.warnings say so. Bake3D decides which engine runs; unknown option fields are ignored.

Send an Idempotency-Key header (any unique string up to 255 characters, such as a UUID) to make retries safe. See Retries.

Returns 202 with { project, job }. Save project.id and job.id. This abbreviated example shows the shape; timestamps are Unix milliseconds and progress is 0–100.

Abbreviated response
{
  "project": {
    "id": "project-id",
    "title": "A ceramic fox standing on four legs",
    "status": "generating",
    "model": "bake3d-1.3-enhanced",
    "hasRig": false,
    "assets": [],
    "animations": []
  },
  "job": {
    "id": "job-id",
    "projectId": "project-id",
    "type": "pipeline",
    "status": "queued",
    "progress": 0,
    "stage": null,
    "creditsCharged": 65,
    "error": null,
    "output": null,
    "createdAt": 1788800000000,
    "finishedAt": null
  }
}

Job status

GET /api/v1/jobs/:id

Poll every 3–5 seconds. Status is queued, running, succeeded, failed or canceled. Stop on a terminal status. Retry transient network errors with backoff; do not submit another generation just because a poll failed.

sh
curl https://bake3d.ai/api/v1/jobs/JOB_ID \
  -H "Authorization: Bearer $BAKE3D_API_KEY"

The response is the job object directly, with progress, stage, error, creditsCharged and output. A successful job’s projectId identifies the character to retrieve; a described motion’s output.animationId names the saved animation. Review links in a re-rig’s output.rigReview are signed URLs with an expiresAt.

GET /api/v1/jobs?projectId&limit

Lists your newest jobs, newest first, as { jobs } (limit 1–100, default 20), or those of one of your projects with projectId. Use it to find a job ID you lost. Free.

Cancel

POST /api/v1/jobs/:id/cancel

Cancels a queued or running job and refunds its whole charge, as long as none of its result is in the project yet; no body is needed. Returns { ok: true, job }. A generation puts its new mesh into the project as soon as it is stored (a re-bake of a rigged project holds it back until its rig is accepted), and its rig before the included animation and the last checks; a re-rig and a described motion deliver as they finish. Once a job’s mesh or rig is in the project, cancel returns 409 with details.code "already-delivered", the job in details.job and the message “Your character is already in your project; the remaining steps finish on their own. Nothing more is charged. If the job makes no progress for 25 minutes, you can cancel it then: what it delivered stays and stays charged, and the credits for anything it did not deliver come back.” The job then runs to its end. A job that has made no progress for 25 minutes (its steps stopped running) can be canceled even after it delivered: what it delivered stays in the project and stays charged, and the credits for the rest (the rig of a generation whose mesh is in the project) are refunded. One that stays without progress is ended automatically the same way. A job that already finished, or finishes while being canceled, returns 409 with the reason and the job in details.job; nothing is canceled or refunded then. Canceling never deletes the project or anything already in it.

Find projects

GET /api/v1/projects

Search your full library with q (title or category, up to 120 characters). Set filter to all, active, ready or needs-work, and sort to updated, created or name. Searches treat punctuation literally; ASCII letters are case-insensitive.

Returns projects, matched, account-wide counts, nextCursor, and activeProjects: up to 20 projects being generated or rigged right now, whatever the query, with the same signed links. The default page size is 24; limit accepts 1–100. Pass nextCursor as cursor with the same query, filter and sort for the next page. A null cursor means the last page. Updated projects may move between pages; start again without a cursor for a fresh view.

sh
curl "https://bake3d.ai/api/v1/projects?q=vehicle&filter=ready&sort=name&limit=24" -H "Authorization: Bearer $BAKE3D_API_KEY"

Project

GET /api/v1/projects/:id

Returns the project directly. assets is an array of asset records whose url is a signed absolute link with its expiresAt; thumbnailUrl and sourceImageUrl are signed the same way. downloads maps asset IDs to the same links, valid for two hours until downloadsExpireAt. Add ?detail=summary for IDs, counts, animation names and downloads without labels, motion programs and asset metadata. Use riggedAssetId to choose the rig, or meshAssetId for geometry. A rig whose asset metadata has rig.frame.yawDeg was built with the mesh turned that many degrees about the vertical axis (+Y, +Z toward +X) so a body the mesh lays diagonally lies straight; the rig, its game-ready copy and its exports are turned by that much from the mesh. Retrieve the project again to refresh expired links.

labels describes joint roles and chains. animations contains saved motion programs. initialAnimationId identifies the included motion for the current rig, or is null when none is available. Re-rigging does not make a previous rig’s initial motion the new default.

DELETE /api/v1/projects/:id

Permanently deletes the project with its mesh, rig, animations and files, and returns { deleted: true, projectId, title }. It cannot be undone and is never refunded. While any job runs on the project it answers 409 with details.jobId: cancel the job or let it finish first. An uploaded picture that another of your projects was baked from stays in your library.

Re-rig

POST /api/v1/projects/:id/rig

Rigs the project’s current mesh again for 25 credits; no body is needed. Returns 202 with { job }: poll it like a generation. The new rig replaces the current one only when it is accepted, and its review links are in output.rigReview. A project with no mesh yet, or with a job running, answers 409 before anything is charged. Accepts an Idempotency-Key header like Generate.

Presets

GET /api/v1/projects/:id/presets

Lists the built-in motion presets with their availability on the project’s current rig, by the same rule as the studio. available: true comes with fit.factor (amplitude) and fit.gaitFactor (stride) that the rig’s skin allows; available: false comes with the reason. An available gait may carry a note on what is left of the skin where it plays beyond the skin rule on this rig. Fits not measured yet are measured within the request; any still missing are marked pending: true with complete: false, so ask again after a few seconds. Hand-authored gaits replaced by a rig-fitted preset (supersededBy) are hidden; add ?all=1 to include them. Requires a rigged, labelled project (409 otherwise). Free.

Abbreviated response
{
  "projectId": "project-id",
  "rigAssetId": "rig-asset-id",
  "complete": true,
  "presets": [
    {
      "id": "adaptive-walk",
      "name": "Walk · fit to this rig",
      "description": "…",
      "duration": 4,
      "adaptive": true,
      "supersededBy": null,
      "available": true,
      "fit": {
        "factor": 1,
        "gaitFactor": 0.85
      },
      "summary": "Fitted to 85% stride"
    },
    {
      "id": "tail-wag",
      "name": "Tail wag",
      "description": "…",
      "duration": 2,
      "adaptive": false,
      "supersededBy": null,
      "available": false,
      "reason": "This rig has no tail chain",
      "summary": "This rig has no tail chain"
    }
  ]
}

Included animation

POST /api/v1/projects/:id/initial-animation

Create or recover the current rig’s introductory motion without an additional credit charge. Send an empty JSON object. Returns 202 with a job to poll, or an existing animationId with job: null; creditsCharged is always 0. Repeated requests return the active recovery job or the saved result. While another job runs on the project (its bake, which makes the included motion itself, a re-rig or a described motion) the request answers 409 with that job in details.jobId.

Recovery permits three jobs per rig per UTC day and six starts per account in a rolling 24-hour period. Each job makes at most two authoring attempts. A rig, usable labels and available AI animation are required. If initial motion cannot finish, the rig remains saved and the generation output includes a warning. Review it before retrying.

Animate

POST /api/v1/projects/:id/animate

json
{
  "prompt": "Look around curiously, then settle",
  "durationSeconds": 3,
  "loop": true
}

Requires a rigged, labelled project. prompt is 1–500 characters and durationSeconds is 0.5–20. Returns 201 with animation and diagnostics; save animation.id. This request waits up to five minutes for motion authoring and does not run a visual critique. If the motion is still being made then, it answers 504 with details.jobId. The motion was charged once and its job keeps running: poll GET /api/v1/jobs/:id until it succeeds (its output.animationId names the saved animation) or fails (and is refunded). Do not send the request again without its Idempotency-Key: that starts and charges a second motion. With the same key, a retry is safe at any time: see Retries. Inspect any unresolved roles before exporting.

Export

POST /api/v1/projects/:id/export

sh
curl https://bake3d.ai/api/v1/projects/PROJECT_ID/export \
  -H "Authorization: Bearer $BAKE3D_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"animationId":"ANIMATION_ID","fps":30,"rootMotion":false,"heightMeters":1.8,"format":"glb"}'

Choose animationId for a saved motion or presetId for a built-in preset. With neither field, the included initial animation is exported when available; otherwise the original rig is returned (as a rest-pose FBX or USDZ when format asks for one). For an FBX, animationIds (up to 32 saved animation IDs) writes every one of them into the same file, one take each, named by the animation and in the order given; Unity and Unreal list every take as a clip on import. It cannot be combined with animationId, presetId, restPose or the Roblox target, which takes one animation per FBX. Set restPose: true for a copy without animation clips. Do not combine that flag with an animation or preset ID. Requested motion is placed first in the GLB while older clips remain available.

presetId: "adaptive-walk", "adaptive-trot", "adaptive-run" (a run on two legs, a gallop on four, tripods on six or more) and "adaptive-jump" fit their motion to the actual leg chains, with feet planted by IK. It requires a standing rest pose and at least two valid, separate leg chains with usable segments. The preset measures stride and footfall phases from the rig; it does not add missing joints. Unsupported structures return HTTP 422 with a repair reason. Every preset is exported at the amplitude the rig’s deformation fit chose for it (the studio shows the same factor); a preset that damages the rig’s skin even at 30% amplitude is refused with the reason. See how to review the resulting motion.

fps supports 24, 30 or 60. rootMotion: true (the default, except for USDZ) bakes planar travel and heading onto the ground-level motion_root joint, so an engine’s root-motion extraction moves the character while the hips keep bob, sway and jump; rootMotion: false exports in place: motion_root stays still and horizontal travel is removed, while vertical motion such as a jump arc, and wheel roll, are kept. heightMeters (0.01–500, kept to the centimetre) scales the file uniformly so the character stands that tall. Omit it to use the project’s estimated real-world height when it has one (the result’s heightMeters says which), or send rigUnits: true to keep the rig’s own units; the two cannot be combined. target prepares the file for one engine: "godot" names looping clips <name>-loop so Godot’s importer loops them; "roblox" exports in place, facing −Z (Roblox’s forward), at 30 or 60 fps, uses the game-ready copy when the model has one, and refuses a mesh over 20,000 triangles or with more than one material with the reason (import with Keep Zero Influence Bones on and Scale Unit set to Meter for GLB or Centimeter for FBX); "generic", "unity", "unreal" and "blender" give the standard file. format is "glb" (default), "fbx" or "usdz". For a humanoid rig (two arms, two legs, one head), skeletonNames renames the skeleton to a standard so clips retarget directly: "mixamo" (mixamorig:Hips, mixamorig:Spine…), "unity" (Unity Humanoid names: Hips, Chest, LeftUpperArm…), "unreal" (UE5 Mannequin: root, pelvis, spine_01, clavicle_l…) or "vrm" (VRM 1.0 humanoid bones: hips, spine, chest, leftUpperArm…). Joints are mapped by role; the hierarchy, skin and motion are unchanged, and fingers and extra parts keep their names. Other rigs are refused with the reason. On a topology: "quads" ("Game-ready mesh") bake, or a rigged project whose copy was made later from the studio's Export tab, mesh: "game" exports the game-ready copy instead of the full-detail model: about 7–10K faces, mostly quads, under 20K triangles, with the same skeleton and motion (the FBX keeps the quads); it answers 409 when the project's current rig has no copy. The result includes asset (its url is the signed download), downloadUrl, expiresIn: 7200, expiresAt, format and creditsCharged: 0. Repeated identical exports reuse the stored file.

Every export stands on the ground plane (feet at y = 0), centred over the origin and facing +Z (the Roblox target faces −Z), with Y up. Every exported skeleton starts with an unweighted joint, motion_root, on the ground at the origin with identity rotation and unit scale; the rig’s original root bone is its child, so skin bindings are unchanged. No node above the skeleton carries a transform, so the root motion an engine reads from motion_root equals the travel it shows. Each clip records { fps, loop, rootMotion, duration } in its glTF extras.bake3d.

FBX exports are binary FBX 7.4 written by Bake3D’s own exporter: Y up, centimetres (glTF metres × 100), the skeleton as limb nodes, skin weights limited to the four strongest influences per vertex, one Phong material per glTF material carrying the base-colour factor and texture, the normal map, and roughness and metallic as values and, when the GLB packs them in a metallic-roughness map, as greyscale roughness and metallic maps split from it (a PNG map up to 4096 × 4096 pixels, a JPEG or WebP map up to 2048 × 2048, or 1024 × 1024 when the export already holds more than 16 MiB of mesh data; a larger packed map stays in the GLB and in the texture download, and that FBX keeps the Phong defaults for roughness and metallic), and the baked clips as linear keys at the export frame rate: one take per animation, and every take keys every joint the others key, so switching takes never leaves a joint in another take’s pose. FBX materials are single-sided. Morph targets are not written. Verified by importing into Blender 5.2 and Godot 4.7 and comparing posed vertices and root motion against the GLB export; Unity and Unreal import FBX natively but Bake3D has not run those imports. Details in docs/FBX-EXPORT-VALIDATION.md.

USDZ exports are an uncompressed USDZ package for AR Quick Look on iPhone, iPad and Vision Pro, Reality Composer Pro and other USD tools: a binary USD (usdc) layer with Y up and metres, a UsdSkel skeleton (the same motion_root and frozen placement as the FBX), skin weights with up to four influences per vertex, a UsdPreviewSurface material per glTF material with its base-colour, normal and packed metallic-roughness maps (PNG and JPEG; a WebP or KTX2 image is left out and its material keeps the factors), and one baked clip, or none for restPose: true. AR Quick Look loops the clip, so USDZ exports play in place unless you send rootMotion: true. A USDZ takes surfaces up to about 300,000 vertices (export the game-ready copy with mesh: "game" above that) and is refused with the Roblox target. Checked with OpenUSD’s usdchecker --arkit and by importing into Blender 5.2 against the GLB; Bake3D has not yet opened these files on an Apple device.

Server animation export supports self-contained source rigs up to 60 MiB (above 24 MiB, textures must be embedded in the GLB binary chunk), one shared skeleton of up to 256 joints, and clips up to 30 seconds; FBX additionally needs the skinned surface to fit a 64 MiB working budget (a 600k-vertex rig uses about 40 MB of it) and embedded textures up to 64 MiB. Use the studio exporter for larger GLB files. GLB preserves the skeleton, skin weights, textures and baked keyframes. Editor-specific import setup is described in the integration guide.

Feedback

POST /api/v1/projects/:id/feedback

json
{
  "subject": "export",
  "rating": "down",
  "format": "fbx",
  "engine": "unreal",
  "reasons": [
    "wrong-scale"
  ],
  "comment": "Imports ten times too large with default settings"
}

Tells the Bake3D team how a result turned out after you looked at it: subject is model, rig, animation (with animationId or presetId, and atSeconds for the moment you mean) or export (with format and engine). rating is up or down; with down, add reasons and a short comment. One rating per result: posting again updates it. Returns { feedback, message }. Free. The same rating as the MCP tool bake3d_feedback.

Retries

POST /api/v1/generate, POST /api/v1/projects/:id/rig and POST /api/v1/projects/:id/animate spend credits. Send an Idempotency-Key header with a new unique value for every intended result, and the same value when you retry after a timeout, a 504 or a network error. The key is bound to the job it paid for the moment that job is accepted, so within 24 hours a retry never charges again:

  • When the first request’s job delivered, the retry returns the same project and job (or the same animation) as the first request would have, with Idempotent-Replayed: true. For a generation or a re-rig that is as soon as the job is accepted: the retry returns it in its current state.
  • While a described motion’s job is still running, the retry answers 409 with details.jobId: poll that job, then retry with the same key to receive the animation.
  • When that job failed or was canceled (and so was refunded), the key is free again and the retry runs, and is charged, as a new request.
  • A request that failed before anything was charged (a 402, a declined picture, a busy project) releases its key, so it can be retried as is.

Keys belong to your account. Reusing a key for a different request returns 422; retrying while the first request has not yet reached its charge returns 409; a key whose project or animation was deleted returns 410.

Errors

REST errors use { "error": "message" }, sometimes with validation details.

StatusNext step
400Check request fields and JSON (details lists each problem as "field: message").
401Provide a valid API key.
402Add credits before starting a new job (details.needed and details.balance).
403API access needs a Pro or Studio plan; check account access.
404Use an ID owned by this account, and an endpoint listed here.
405Use the method the endpoint names in its Allow header.
409Wait for the active job (details.jobId names it), rig the character first, or retry an idempotent request shortly. details.code "already-delivered": the job’s result is in your project and it can no longer be canceled.
410The project or animation created with this Idempotency-Key was deleted.
413Reduce the file size or use the studio exporter; with details.storage, free storage or upgrade first.
415Upload a PNG, JPEG or WebP image.
422Describe one subject per text prompt (details.suggestions); use a picture or prompt without nudity or sexual content (details.code content-policy, nothing charged); choose motion that fits the rig; or use a new Idempotency-Key for a different request.
429Wait Retry-After seconds and reduce polling frequency.
502The motion could not be made; its credits were refunded. Try a different description.
503The requested feature is temporarily unavailable.
504The motion is still being made: poll details.jobId (charged once; see Retries).

The MCP endpoint answers transport problems with HTTP statuses too: 405 for anything but POST, 406 when Accept lacks application/json and text/event-stream, 415 for a body that is not JSON, and 413 past 17 MiB. Tool failures come back as tool results with isError, their text starting with the HTTP status and naming the jobId when there is one.