pixelkiln 0.92.1 → 0.92.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/ENDPOINTS.md CHANGED
@@ -27,6 +27,179 @@ listed so it is obvious what exists.
27
27
 
28
28
  ---
29
29
 
30
+ ## Validation ledger
31
+
32
+ Which PixelLab endpoints PixelKiln calls, and how much is actually known about
33
+ each, so live spending can be chosen by risk. Audited 5 November 2026 from
34
+ `src/client.ts`, `src/pixellab-vfx.ts`, this file, the other docs, the skill
35
+ references, the benchmark and showcase lockfiles, and git history. No PixelLab
36
+ call was made for it. Each row is one endpoint (or one tightly coupled family
37
+ of paths: a `POST` and the `GET` that polls it). The Evidence is the section
38
+ named in the "What is known" cell.
39
+
40
+ **Status**
41
+
42
+ - **Measured live**: a real call was billed and the completed response shape
43
+ was seen. The cell says what was measured.
44
+ - **Run live, not measured**: it ran against the account, but the bill or a
45
+ variant was not recorded.
46
+ - **Mock only**: wrapped and unit-tested against mocked HTTP, with no live run
47
+ in the docs.
48
+ - **Schema only**: wrapped from PixelLab's OpenAPI or MCP schema, with no live
49
+ run, and the docs call the completed-job shape or the price a guess.
50
+ - **Not wrapped**: PixelLab has it and PixelKiln does not call it.
51
+ - **Unknown**: the repo does not establish a status. See [Needs a human
52
+ look](#needs-a-human-look).
53
+
54
+ **Money at risk** is how much a wrong estimate or an unreadable response could
55
+ cost in one call: **Low** up to 3 generations, **Medium** 4 to 15, **High**
56
+ over 15, or billed per direction or per batch. Failed requests are not billed,
57
+ so a rejected body costs nothing; the exposure is an accepted job that bills
58
+ more than planned or finishes in a shape PixelKiln cannot read.
59
+
60
+ `target_palette` is sent on 17 routes since 0.92.0. Only `imagePro`,
61
+ `imageProFlash` and `tiles` ran live with it (each matched the plan); the other
62
+ 14 are mock only (`objectPro` base, state and `pro` animation, `1dir`, Pro
63
+ `terrain`, `uiAsset`, `uiElement`, Pro and Pro Flash character bases, states,
64
+ portraits, outfit transfer, `interpolate`, `edit-animation`), and their rows
65
+ say so.
66
+
67
+ **Free acceptance probe** (November 2026): for 13 of those 14 routes, a request
68
+ carrying only a valid four-colour `target_palette` and none of the required
69
+ fields was answered with `422`s that named the missing fields and nothing about
70
+ `target_palette`, while the same request with an invalid palette (a non-hex
71
+ colour, `max_colors` 999) was answered with two `target_palette` errors. So
72
+ PixelLab's validator knows the field and its shape on those routes, which a
73
+ mocked test cannot show. The probe cannot show that the palette is obeyed or what
74
+ the call costs, and a failed validation bills nothing. The 14th route,
75
+ `/objects/{id}/animations` (the `objectPro` `pro` animation), has no required
76
+ fields, so a palette-only request could have been accepted and billed; it was
77
+ not probed.
78
+
79
+ | Endpoint | PixelKiln feature | Status | What is known (cost / sizes / shape) | Gaps (never seen) | Money at risk |
80
+ |---|---|---|---|---|---|
81
+ | `GET /balance` | `doctor`, `gen` balance reads, gallery | Measured live | Free. Count is at `subscription.generations` ([Reproducing these numbers](#reproducing-these-numbers)). | Only the subscription shape is documented; a credit-only account's reply is not recorded. | Low |
82
+ | `GET /pro-flash/cost` | Pro Flash estimates for `character` and `imageProFlash` | Measured live | Free quote. For `operation=character`: image 5 up to 96px, 6 to 208px, 9 above, plus rotations; two live jobs (7 and 2) billed exactly the quote ([Characters, measured](#characters-measured)). | Quotes for `create`, `edit` and `inpaint` are recorded from PixelLab's MCP `get_pro_flash_capabilities`, not from this route (roadmap, "Pro Flash for plain image create, edit, and inpaint"). | Low |
83
+ | `GET /background-jobs/{id}` | every job poll; where the billed amount is read | Measured live | `usage` at the top level and again at `last_response.billing_usage`; confirmed for a `map` object (1), a standard character base (1) and the skeleton jobs ([Limits and billing](#limits-and-billing)). | Jobs are deleted 8 hours after finishing, so a bill not read in time is lost (the v3 base bill was). | Low |
84
+ | `POST /create-image-pixflux` | `pixflux` | Measured live | 1 generation, PNG inline, about 10 s; `color_image` is a hard palette lock (130 badges, 65 of 65 in each set) ([Single-image generators, measured](#single-image-generators-measured)). Ran in `benchmarks/provider-environments/pixellab`. | None recorded. | Low |
85
+ | `POST /map-objects` (+ `GET /map-objects/{id}`, falling back to `GET /objects`) | `map` | Measured live | 1 generation, job about 30 s; the job record expires after 8 hours but the image survives ([Object lifecycle](#object-lifecycle)). `color_image` returns 500. | The `scene` field (`background_image` + inpainting): "Unverified until a real call is billed" ([GENERATORS.md](./GENERATORS.md), `map`). | Low |
86
+ | `POST /generate-image-v2` | `imagePro` | Measured live | A 64×64 call billed 10 (40 before the cut) and 10 again with `target_palette`; the completed job was read and a candidate downloaded through `gen` ([October 2026 Pro price cut](#october-2026-pro-price-cut), [Target palette](#target-palette-target_palette)). | Canvases above 65536px² (15 and 25 tiers) are priced from the shared Pro ladder, not measured here; `reference_images` and `style_image` are unmodeled. | High |
87
+ | `POST /create-image-pro-flash` | `imageProFlash` | Measured live | A 32×32 still with a four-colour `target_palette` billed 5, in only those colours (November 2026; [GENERATORS.md](./GENERATORS.md#target-palette)). | Larger canvases (quote 9 at 256px) and the style-image path were never billed. | Medium |
88
+ | `POST /edit-image-pro-flash` | revision `engine: "pro-flash"`, `image-to-image` | Mock only | Quote 5 at 64px, provisional, from MCP (roadmap, "Pro Flash for plain image create, edit, and inpaint"). | Never billed; completed-job shape never read live; the `reference` edit method is unmodeled. | Medium |
89
+ | `POST /inpaint-image-pro-flash` | revision `engine: "pro-flash"`, `inpaint` | Mock only | Quotes 5 at 64px and 6 at 128px, provisional, from MCP (same roadmap entry). | Never billed; completed-job shape never read live; `context_image` and `output_methods` unmodeled. | Medium |
90
+ | `POST /image-to-pixelart-pro-flash` | `pixelkiln pixelate` | Measured live | A 256×256 and a 384×384 render each billed 6; the image is `last_response.images[0]`; the result is opaque on a near-white field ([Image to Pixel Art Pro Flash, measured](#image-to-pixel-art-pro-flash-measured)). | Whether 6 is flat above 384px or with a larger source. | Medium |
91
+ | `POST /inpaint-v3` | revision `inpaint` | Measured live | Nine pre-cut points from 32×32 to 512×512 (20 / 25 / 40); after the cut 256, 288, 352 and 384px billed 10, 15, 25, 25. `last_response.image` is a single object ([Limits and billing](#limits-and-billing), [October 2026 Pro price cut](#october-2026-pro-price-cut)). | The 20-to-25 break sits in (65536px², 82944px²] and 25-to-40 in (102400px², 123904px²]; 32px to 128px and 512px were not re-measured after the cut. | High |
92
+ | `POST /edit-images-v2` | revision `image-to-image` | Measured live | One 32×32 call billed 20 (before the cut); `images` is an array of `{type, base64, width, height}` ([Limits and billing](#limits-and-billing)). | Nothing above 32×32 and nothing after the cut; the plan borrows `proTierCost`. | High |
93
+ | `POST /remove-background` | revision `remove-background` | Measured live | 1 generation, synchronous `{usage, image}`; keeps the palette ([Post-processing utilities, measured](#post-processing-utilities-measured)). Measured as a loose call in the July 2026 reference. | The `complex` task and the foreground text hint (roadmap, "Remove background"). | Low |
94
+ | `POST /reduce-colors` | revision `reduce-colors` | Measured live | 0.1 generations at 32×32, balance diff and `usage` agree ([Cleanup tier, measured](#cleanup-tier-measured), September 2026). | Larger canvases; the price of a multi-frame set call is unmeasured. | Low |
95
+ | `POST /correct-pixelart` | revision `correct-pixelart` | Measured live | 0.1 generations at 32×32 ([Cleanup tier, measured](#cleanup-tier-measured)). | Larger canvases and multi-frame sets, as above. | Low |
96
+ | `POST /unzoom` | `pixelkiln unzoom` | Unknown | "Cost unmeasured; PixelLab's MCP tool description claims 0.1" ([Cleanup tier, measured](#cleanup-tier-measured)). Nothing records a live run. | Everything. | Low |
97
+ | `POST /normal-map` | revision `normal-map`, `pixelkiln normal-map` | Measured live | An 8-frame 1304×26 strip and a 64×64 revision each billed 0.5; the map comes back the input's size with matching alpha ([Normal map](#normal-map-post-normal-map-measured-once), October 2026). | The `directx` convention; strips near the 32-frame limit. | Low |
98
+ | `POST /generate-ui-v2` | `uiElement` | Schema only | Documented 10 to 25; "no call has been billed yet" ([GENERATORS.md](./GENERATORS.md#uielement)). Planned on the 10/15/25 ladder. `target_palette` sent, mock only. | The price, and the completed-job shape (`pollUiElement` reads it defensively); the concept-image path. | High |
99
+ | `POST /create-1-direction-object` (+ `GET /objects/{id}`) | `1dir` | Measured live | A 32px, 3-description `batch` billed 20 (the pre-cut floor) and `item_descriptions[0]` owned slot 0 ([Candidates](#candidates)); candidate counts follow size. `target_palette` sent, mock only. | No bill since the October 2026 cut (10 / 15 / 25 is PixelLab's published ladder); tiers above the floor under `item_descriptions`. | High |
100
+ | `POST /objects/{id}/select-frames` | `1dir` and `batch` pick | Run live, not measured | Promoted three candidates in the live batch run ([Candidates](#candidates)); `indices` is plural. | Its cost was not recorded. | Low |
101
+ | `GET /objects?limit=&offset=` | `adopt`, `salvage` | Measured live | Free; a March 2026 sprite still resolved four months on ([Object lifecycle](#object-lifecycle)). | None recorded. | Low |
102
+ | `POST /create-object-pro-flash` | `objectPro` base | Mock only | Assumed to price like `character` pro-flash: 6 at 64px, 1 from a `reference` ([GENERATORS.md](./GENERATORS.md#objectpro), "Cost is not independently measured"). `target_palette` sent, mock only. | Price, completed-job shape, 8-direction output. | Medium |
103
+ | `POST /objects/{id}/states` | `objectPro` state | Mock only | Same assumption. `target_palette` sent, mock only. | Price and shape. | Medium |
104
+ | `POST /objects/{id}/animations` | `objectPro` animation (`v3`, `pro`) | Mock only | Same assumption; `pro` mode is documented 10 to 25 per direction. `target_palette` sent on `pro` only, mock only. | Price, shape, per-direction billing. | High |
105
+ | `PATCH /objects/{id}/tags`, `PATCH /characters/{id}/tags` | tagging in `fetch`, `adopt`, `salvage` | Unknown | Only the negative is documented: pixflux, pixen and bitforge results "cannot be tagged" ([Object lifecycle](#object-lifecycle)). | A recorded live call. | Low |
106
+ | `DELETE /objects/{id}`, `POST /objects/{id}/dismiss-review`, `DELETE /characters/{id}` | `salvage`, account cleanup | Unknown | Not discussed in the docs. | A recorded live call. | Low |
107
+ | `DELETE /characters/{id}/animations` | replacing a loop's direction | Unknown | "Deleting one direction of a shared group has not been run live" ([Skeleton-driven motion, measured](#skeleton-driven-motion-measured)). | Whether the rest of the group keeps its motion. | Low |
108
+ | `POST /create-tiles-pro` (+ `GET /tiles-pro/{id}`) | `tiles` | Measured live | One numbered description, 4 tiles at 16px, with a Game Boy `target_palette`, billed 10, every candidate in the palette (November 2026). `GET` has no `status`: 423 while drawing ([Tilesets: measured once per kind](#tilesets-measured-once-per-kind)). | `tile_feature` sets (`roads`, `tileset`, `building`) and `tile_rules`; large tile-size × variation canvases that reach the top tier. | High |
109
+ | `POST /create-tileset` (+ `GET /tilesets/{id}`) | `terrain`, top-down | Measured live | Pro 32px billed 15 (three sets, one connected through `lower_base_tile_id`, one with edge slope); Pro 64px billed 25 and returned 25 tiles at transition 0.5; standard sets billed 3 each (planned 4), one connected through `upper_base_tile_id` ([Tilesets: measured once per kind](#tilesets-measured-once-per-kind), October 2026). `target_palette` on Pro, mock only. | Pro below 32px; standard above 32px; a standard set cannot connect to a Pro set. | High |
110
+ | `POST /create-tileset-sidescroller` | `terrain`, `terrainOrientation: "sidescroller"` | Measured live | One 16px set at transition 0.25 billed 3; 16 tiles; `usage` is null on the `GET`, so the bill comes from the job ([Tilesets: measured once per kind](#tilesets-measured-once-per-kind)). | 32px tiles and other transition sizes (tile counts assumed 16); the call returned `metadata.transition_size: 0.0` for 0.25. | Low |
111
+ | `POST /create-isometric-tile` (+ `GET /isometric-tiles/{id}`) | `isometricTile` | Measured live | One 32px `block` billed 1 generation, not the schema's `usd: 0.02` example ([Standalone isometric tile](#standalone-isometric-tile-schema-documented-cost-measured-once)). | Other sizes and shapes; flat pricing across 16 to 64px is an assumption. | Low |
112
+ | `POST /create-ui-asset` (+ `GET /ui-assets/{id}`) | `uiAsset` | Measured live | One 256×192 call billed 20, the low end of the quoted 20 to 40; the plan prices 40 ([UI panels](#ui-panels-one-endpoint-schema-documented-cost-unverified), September 2026). | A second size; `pixelkiln plan` over-reads. | High |
113
+ | `POST /generate-font-pro` (+ `GET /generate-font-pro/{id}`) | `pixelkiln font` | Unknown | Documented at 15 since the cut; "its cost is not yet measured" ([Not yet explored](#not-yet-explored)). Nothing records a live run. | Price and the non-`{usage, image}` response shape. | Medium |
114
+ | `POST /create-character-with-8-directions`, `/create-character-with-4-directions` | `character` base, `mode: standard` | Measured live | 1 generation (read from the job's `usage`); canvas 28px larger than `image_size` ([Limits and billing](#limits-and-billing), [Characters, measured](#characters-measured), September 2026). | The 4-direction body and quadruped templates are not recorded. | Low |
115
+ | `POST /create-character-v3` | `character` base, `mode: v3` | Run live, not measured | A base once recorded `billed: {amount: 0, unit: "usd"}` against an estimate of 2 while the submit reported 1 generation; the job had expired, so the real bill is unseen ([Limits and billing](#limits-and-billing)). | The real bill at any size. | Low (Medium at 256px, 9) |
116
+ | `POST /create-character-pro` | `character` base, `mode: pro` | Unknown | Planned 10 to 25 by canvas ([GENERATORS.md](./GENERATORS.md)). No bill recorded in the docs. `target_palette` sent, mock only. | Everything. | High |
117
+ | `POST /create-character-pro-flash` | `character` base, `mode: pro-flash` | Measured live | Two live jobs (7 and 2, the second from a `reference`) billed exactly the estimate; returns exactly the requested canvas ([Characters, measured](#characters-measured), September 2026). `target_palette` sent, mock only. | Sizes beyond 96px; `source_image_id` and the style-image path. | High |
118
+ | `POST /create-character-state` | `character` state | Run live, not measured | A 64px state "moved the balance by about 22 against the 20 to 40 tier", with other jobs running ([Characters, measured](#characters-measured)). `target_palette` sent, mock only. | A clean bill, and any bill since the cut. | High |
119
+ | `POST /animate-character`, `mode` template or v3 | `character` loops without skeleton-v3 | Run live, not measured | A v3 text loop on a 96px pro-flash base came back 108×108; template loops need a skeleton template ([Characters, measured](#characters-measured)). Plan: template 1, v3 `ceil(size² × frames / 65536)`. | Billed amounts were not recorded; billed per direction. | High |
120
+ | `POST /animate-character`, `mode: pro` | `character` loops, `mode: pro` | Unknown | Planned 10 to 25 by canvas ([GENERATORS.md](./GENERATORS.md)). Nothing records a live run. | Everything. | High |
121
+ | `POST /characters/animations`, `mode: skeleton-v3` | `character` loops, `mode: "skeleton-v3"` (template or sentence) | Measured live | On a v3 character: a template loop billed 3; a sentence loop (no template) billed 3, 8 frames at 96×96 from a 64px character; `west` added with `animation_group_id` billed 3 and joined the group. A standard-engine character is refused with a 500 "no stored 3D skeleton" ([Skeleton-driven motion, measured](#skeleton-driven-motion-measured), October 2026). | `pro` and `pro-flash` bases; other frame counts; the price per direction across 8 directions is untested. | High |
122
+ | `POST /portrait-character-pro` (+ `GET /portrait-character-pro/{job_id}`) | `character` portrait | Measured live | A 16px `character_to_portrait` billed 20 (before the cut); the `GET` reports `usage: null`, so read the job ([Characters, measured](#characters-measured)). `target_palette` sent, mock only. | The price after the cut (10 to 25); larger sizes. | High |
123
+ | `POST /characters/{id}/portrait` | `character` portrait attach | Measured live | Free (`usage.generations: 0`); the reply holds the only copy of the stored URL ([Characters, measured](#characters-measured)). | None recorded. | Low |
124
+ | `POST /transfer-outfit-v2` | `character` outfit | Measured live | A 2-frame 92×92 job billed 20 (before the cut); frames return inline in `last_response.quantized_images` ([Characters, measured](#characters-measured)). `target_palette` sent, mock only. | Other frame counts (2 to 16) and canvases; the price after the cut. | High |
125
+ | `GET /characters/{id}`, `GET /characters?limit=&offset=` | `adopt`, `fetch --refresh`, `salvage` | Measured live | Free; carries no `usage`; image URLs carry `?t=<updated_at>` ([Object lifecycle](#object-lifecycle)). | None recorded. | Low |
126
+ | `POST /animate-with-text-v3` | revision `animate` | Schema only | Request fields from the OpenAPI document; the completed-job shape is a guess ([REVISIONS.md](./REVISIONS.md#animation-and-interpolation)). Cost borrows `character`'s v3 formula. | Price, completed shape, frame-list field. | High |
127
+ | `POST /animate-pixminimax` | revision `animate-pixminimax` | Schema only | Same as above. PixelLab's MCP now prices it by time (64×64 at 4 / 8 / 16 / 40 frames: 1 / 1 / 2 / 6); the placeholder gives 1 / 1 / 1 / 3 ([roadmap](../skills/pixelkiln/references/pixellab-roadmap.md), "Generated effects with PixMiniMax"). | Price, completed shape, and whether the input frame is in the output. | High |
128
+ | `POST /animate-with-skeleton-v3` | revision `animate-skeleton` | Measured live | 8 frames at 64×64 billed 3 and returned 8 frames at 64×64; `description` and `action` are both required, and sent with neither the endpoint returns 422 ([Skeleton-driven motion, measured](#skeleton-driven-motion-measured), October 2026). | Other frame counts (3 = 2 and 15 = 4 are PixelLab's anchors, unbilled). | Low |
129
+ | `POST /estimate-skeleton` | `pixelkiln estimate-skeleton`, gallery pose helper | Run live, not measured | Ran on a 64×64 sprite before the 8-frame revision ([REVISIONS.md](./REVISIONS.md#skeleton-driven-animation)). | Its cost; the reply's `usage` is typed `unknown`. | Low |
130
+ | `POST /interpolation-v2` | revision `interpolate` | Schema only | No usage example in the schema; price borrows the 10/15/25 Pro tiers ([REVISIONS.md](./REVISIONS.md#interpolate-between-two-keyframes)). `target_palette` sent, mock only. | Price, completed shape. | High |
131
+ | `POST /edit-animation-v2` | revision `edit-animation` | Schema only | Same; priced on the packed frame grid (four 32×32 frames price as 64×64). `target_palette` sent, mock only. | Price, completed shape. | High |
132
+ | `POST /vfx`, lite artist, no sprite | `pixelkiln vfx --description`, `vfx` block `description` | Measured live | One effect billed 1 (balance 5104 to 5103); 163×26, 8 frames, sheet and atlas cut into 8 correct frames ([VFX artist](#vfx-artist-post-vfx-measured-once), October 2026). | The `files` shape (list or map) is undocumented; PixelKiln reads three. | Low |
133
+ | `POST /vfx`, sprite, pro artist, `effect_id` change or `game_pack` | same | Mock only | Quoted from the OpenAPI table: pro 6 (8 with a sprite), 14 and 28 for larger sprites; plans count twice the quote ([VFX artist](#vfx-artist-post-vfx-measured-once)). | Every variant above. | High |
134
+ | MCP `tools/call` `vfx_workbench` at `api.pixellab.ai/mcp` | `pixelkiln vfx`, `vfx` block `preset` or `recipe` | Measured live | Free on a subscription; same recipe re-rendered byte-identical; exports a sheet, GIF, atlas and optional game pack ([VFX workbench (MCP only), measured](#vfx-workbench-mcp-only-measured), October 2026). | "Busy, retry" is the only recorded error; sprites by asset are not wired. | Low |
135
+ | `POST /create-image-pixen`, `/create-image-bitforge` | none | Not wrapped | Each 1 generation; bitforge returned blobs for a simple prompt ([Single-image generators, measured](#single-image-generators-measured)). | n/a | n/a |
136
+ | `POST /rotate`, `/resize`, `/image-to-pixelart` | none | Not wrapped | Each 1 generation; all three break a locked palette ([Post-processing utilities, measured](#post-processing-utilities-measured)). | n/a | n/a |
137
+ | `POST /inpaint`, `/edit-image` (not v2 or v3) | none | Not wrapped | "Untouched by any of this" ([Not yet explored](#not-yet-explored)). | n/a | n/a |
138
+ | `POST /generate-with-style-v2`, `/image-to-pixelart-pro`, `/create-8-direction-object` | none | Not wrapped | Style transfer at 20 before the cut, not re-measured; `objectPro` covers the 8-direction object. | n/a | n/a |
139
+ | `POST /animate-with-text-v2`, `/generate-8-rotations-v2`, `/tilesets`, `/tilesets-sidescroller`, legacy `/animate-with-skeleton` | none | Not wrapped | PixelKiln calls the `/create-tileset*` aliases, not `/tilesets*`; the legacy skeleton route takes exactly 3 frames (roadmap, "The legacy `/animate-with-skeleton`"). | n/a | n/a |
140
+ | Character lip-sync, vocal animation, talking GIF, `portrait_to_character` | none | Not wrapped | Named out of scope in [PIXELLAB.md](./PIXELLAB.md). | n/a | n/a |
141
+
142
+ ### Where a wrong guess costs money
143
+
144
+ The High and Medium rows that are Mock only, Schema only or Run live, not
145
+ measured, most exposed first. A live run of the top ones also settles the most
146
+ uncertainty per generation.
147
+
148
+ 1. **`/animate-pixminimax`** (Schema only). The estimate under-reads long
149
+ clips (a 40-frame 64×64 clip costs 6, the placeholder says 3), so `--budget`
150
+ can be set too low, and the completed shape is a guess.
151
+ 2. **`/interpolation-v2` and `/edit-animation-v2`** (Schema only). Priced from
152
+ borrowed 10/15/25 tiers with no usage example; a whole frame set goes in one
153
+ call, so a miss is a full Pro tier, and the finished job is read defensively.
154
+ 3. **`/animate-with-text-v3`** (Schema only). Up to 16 generations a call by
155
+ PixelKiln's own formula, billed per animation, completed shape guessed.
156
+ 4. **`/objects/{id}/animations`, `/create-object-pro-flash`, `/objects/{id}/states`**
157
+ (Mock only). `objectPro` assumes `character` pro-flash prices; its `pro`
158
+ animations are 10 to 25 per direction.
159
+ 5. **`/generate-ui-v2`** (Schema only). 10 to 25, never billed, shape read
160
+ defensively.
161
+ 6. **`/animate-character` template and v3** (Run live, not measured). One job
162
+ per direction, so an 8-direction loop is eight bills no one has recorded.
163
+ 7. **`/create-character-state`** (Run live, not measured). One rough bill
164
+ (about 22) taken beside other jobs and before the cut.
165
+ 8. **`/vfx` with a sprite, the pro artist, a change or a game pack** (Mock
166
+ only). Up to 28 generations a call, and plans reserve twice the quote.
167
+ 9. **`/edit-image-pro-flash`, `/inpaint-image-pro-flash`** (Mock only). 5 to 6
168
+ per call on provisional quotes, no completed response seen.
169
+ 10. **`/create-character-v3`** (Run live, not measured). 2 at 64px, up to 9 at
170
+ 256px, and the one recorded bill was an unreadable zero.
171
+
172
+ Also worth a second look because they are measured on an old price list: the
173
+ `1dir`, `/portrait-character-pro`, `/transfer-outfit-v2` and `/edit-images-v2`
174
+ bills all predate the October 2026 cut, and `/inpaint-v3` was only re-measured
175
+ from 256px to 384px.
176
+
177
+ ### Needs a human look
178
+
179
+ The repo does not establish whether or how these ran live:
180
+
181
+ - `POST /create-character-pro` and `POST /animate-character` with
182
+ `mode: "pro"`: the docs plan 10 to 25 and record no bill.
183
+ - `POST /generate-font-pro`: documented price only (`pixelkiln font`).
184
+ - `POST /unzoom`: documented price only (`pixelkiln unzoom`).
185
+ - `PATCH` tags on objects and characters, `DELETE /objects/{id}`,
186
+ `DELETE /characters/{id}`, `POST /objects/{id}/dismiss-review`: used by
187
+ `fetch`, `adopt` and `salvage`, and no doc records a live call.
188
+ - `DELETE /characters/{id}/animations`: only the one-direction-of-a-shared-group
189
+ case is called out as not run.
190
+
191
+ ### Keeping the ledger current
192
+
193
+ Update the row in the same change that records a live call. When a call is
194
+ billed, take the amount from the finished job's `usage` (or
195
+ `last_response.billing_usage`), not from a balance diff taken straight after,
196
+ since billing lags; read it before the job record expires 8 hours later. Move
197
+ the row's status, write the measurement and its month into "What is known",
198
+ and remove the gap it closes. If a call ran but the bill was not captured,
199
+ set the row to Run live, not measured rather than leaving it Mock only.
200
+
201
+ ---
202
+
30
203
  ## Single-image generators, measured
31
204
 
32
205
  The endpoints that produce one standalone image. This is the decision that
@@ -54,10 +227,10 @@ canvas like the other Pro tools: a 64×64 call billed exactly 10.
54
227
  `generate-image-v2` is wrapped by pixelkiln's `imagePro` generator
55
228
  (skills/pixelkiln/references/pixellab.md): non-square and larger canvases
56
229
  than `pixflux` allows, at the 10–25 above. Its `reference_images` and
57
- `style_image` are not modeled, and its completed job's response shape is
58
- inferred from `edit-images-v2`'s (also unmeasured for this endpoint
59
- specifically) rather than confirmed live — see `pollImagePro` in
60
- `src/providers/pixellab.ts`. `generate-with-style-v2` is not wrapped at all.
230
+ `style_image` are not modeled. Its completed job was read live (a 64×64
231
+ call, October 2026, ran through `gen` and downloaded a picked candidate);
232
+ `pollImagePro` in `src/providers/pixellab.ts` still reads it defensively.
233
+ `generate-with-style-v2` is not wrapped at all.
61
234
 
62
235
  ### What the measurements actually showed
63
236
 
@@ -265,8 +438,10 @@ PixelLab 0.4.130 added a beta `target_palette` body field, then gave it to 27
265
438
  POST endpoints: `{ colors?: 2-256 hex, name?: string (≤100), max_colors?: 2-64,
266
439
  smart?: boolean }`. With `colors` every pixel is one of them; `max_colors` caps
267
440
  each image. The schema is documentation only so far: **cost with a palette is
268
- measured for `generate-image-v2` alone** (a 64×64 call billed 10, the same as
269
- without), and the rest are unmeasured. PixelKiln never sends `smart` (beta,
441
+ measured for three routes**: `generate-image-v2` (a 64×64 call billed 10, the
442
+ same as without), `create-image-pro-flash` (a 32×32 still billed 5) and
443
+ `create-tiles-pro` (4 tiles at 16px billed 10), each matching the plan. The
444
+ other 14 routes PixelKiln sends it to are mock only. PixelKiln never sends `smart` (beta,
270
445
  "single images only", needs `colors`, adds 15 to 60 s).
271
446
 
272
447
  | Endpoint | PixelKiln wrapper | Sent |
@@ -447,8 +622,8 @@ exactly its quote at 32px and 64px, and standard billed 3, under the plan.
447
622
 
448
623
  `POST /tilesets` (top-down) and `/tilesets-sidescroller` are the current
449
624
  versions; `/create-tileset*` are the older aliases with identical schemas.
450
- Both are wrapped by pixelkiln's `terrain` generator
451
- (skills/pixelkiln/references/pixellab.md), the sidescroller one with
625
+ pixelkiln's `terrain` generator
626
+ (skills/pixelkiln/references/pixellab.md) calls the aliases, the sidescroller one with
452
627
  `terrainOrientation: "sidescroller"`; the reference-image/`color_image`
453
628
  knobs below are not.
454
629
 
@@ -560,8 +735,9 @@ That is the only way to land new art on an existing sheet's ground plane.
560
735
 
561
736
  `GET /tiles-pro/{id}` carries no `status` field. It answers **423 while the set
562
737
  is still drawing** and 200 with `storage_urls` when it is done, so the HTTP code
563
- is the status. Cost is reported at submit time and lands on the same 20/25/40
564
- canvas tiers as `1dir`, but the canvas is tile size x variation count, not one
738
+ is the status. Cost is reported at submit time and lands on the same canvas
739
+ tiers as `1dir` (10/15/25 since the October 2026 cut, 20/25/40 before; a 4-tile
740
+ 16px set billed 10), but the canvas is tile size x variation count, not one
565
741
  sprite. A small tile in a large set still reaches the top tier.
566
742
 
567
743
  All response shapes used by the client are validated at runtime. The TypeScript
@@ -899,7 +1075,7 @@ Listed so the gaps are known rather than assumed away:
899
1075
  field names defensively rather than assume one. Cost borrows `character`'s
900
1076
  own measured v3-loop formula as a placeholder, for the same reason
901
1077
  `objectPro` borrowed `character` pro-flash's; `interpolate` and
902
- `edit-animation` borrow the 20/25/40 Pro canvas tiers instead, since
1078
+ `edit-animation` borrow the 10/15/25 Pro canvas tiers instead, since
903
1079
  neither schema carries a usage example.
904
1080
  - `/generate-font-pro` (+ `GET /generate-font-pro/{job_id}`) — an 80-glyph
905
1081
  atlas PNG plus a `.ttf` from a style description, `weight`
@@ -918,14 +1094,11 @@ Listed so the gaps are known rather than assumed away:
918
1094
  - `image-to-pixelart-pro` takes only `image` + `description`, no size fields.
919
1095
  The non-Pro version is characterised above, and the Pro Flash version,
920
1096
  which pixelkiln wraps, [below](#image-to-pixel-art-pro-flash-measured).
921
- - the tileset family, with schema documented above and costs unmeasured
922
- (the standalone isometric tile endpoint alongside it now has one real
923
- measured data point — see above)
924
1097
  - `generate-font-pro`, job-based, with a response shape not in the simple
925
1098
  `{usage, image}` form (wrapped by `pixelkiln font`, but its cost is not
926
1099
  yet measured)
927
1100
  - the character family beyond what the `character` generator uses: lip-sync,
928
- skeleton animation, `portrait_to_character` (a full character from a bust),
1101
+ `portrait_to_character` (a full character from a bust),
929
1102
  and `source_image_id` on Pro Flash. The four creation engines, states,
930
1103
  `animate-character`, portraits, and outfit transfer are covered in full; see
931
1104
  the September 2026 measurements below.
@@ -942,6 +1115,9 @@ generations and finished in about two minutes:
942
1115
  error for each, and a failed request bills nothing. The completed job's
943
1116
  `last_response` carries `images` (base64, `width`, `height`), `flow_kind`,
944
1117
  and `billing_usage` beside the top-level `usage`.
1118
+ - **`POST /characters/animations`, `mode: "skeleton-v3"` with a
1119
+ `template_animation_id`** (a template `skeleton-v3` loop): one loop on a v3
1120
+ character billed 3, inside PixelLab's quoted 2 to 4.
945
1121
  - **`POST /characters/animations`, `mode: "skeleton-v3"` with no
946
1122
  `template_animation_id`** (PixelLab's new custom motion): a one-sentence
947
1123
  `action_description` and `frame_count` 8 returned 8 frames at **96×96**
package/docs/PIXELLAB.md CHANGED
@@ -139,9 +139,10 @@ joints, and `skeleton-preview` draws them over the sprite before you spend:
139
139
  - Each billed exactly 3 generations for 8 frames, measured live. The sentence
140
140
  loop came back at 96×96 from a 64px character; the posed one stayed 64×64.
141
141
  [The home page](https://pixelkiln.griffen.codes/#updates) shows both.
142
- - Each direction of a sentence loop is its own request for now, so a loop with
143
- several directions can move a little differently in each. PixelLab can draw
144
- every direction from one motion, but PixelKiln does not send that yet.
142
+ - Each direction of a sentence loop is its own request, but the first
143
+ direction leads and every later one is sent into its animation group, so all
144
+ directions are drawn from one motion (a live append billed 3 and joined the
145
+ group).
145
146
 
146
147
  See [Loops from a sentence](./CHARACTERS.md#loops-from-a-sentence) and
147
148
  [Skeleton-driven animation](./REVISIONS.md#skeleton-driven-animation).
package/docs/REVISIONS.md CHANGED
@@ -545,7 +545,7 @@ calls it directly, but nothing in `resolveSpecs`/`estimate()`/`submitRevision`
545
545
  ever calls it — planning stays offline by construction (the keypoints file is
546
546
  read like any other manifest input, never a network call), and the only
547
547
  PixelLab endpoint `animate-skeleton`'s submission composes is
548
- `animate-with-skeleton-v3` itself, not two never-tested-live calls chained
548
+ `animate-with-skeleton-v3` itself, not two endpoints chained
549
549
  together. A future pipeline stage that budgets and tracks `estimate-skeleton`
550
550
  calls the way `gen` does everything else is tracked separately, once this
551
551
  mode has shipped and seen real use.
@@ -702,7 +702,7 @@ further — there is nothing to ask.
702
702
  example in the schema and no live measurement. `estimate()` borrows the
703
703
  same 10/15/25 canvas tiers the other unmeasured Pro edits use, sized on the
704
704
  keyframe for `interpolate` and on the packed frame grid for
705
- `edit-animation` (four 32×32 frames price as a 64×64 canvas, the 40 tier).
705
+ `edit-animation` (four 32×32 frames price as a 64×64 canvas, the 10 tier).
706
706
  Over-reading is the safe direction for `--budget`; expect a live bill at or
707
707
  below the plan.
708
708
  - `animate` calls `/animate-with-text-v3`, `animate-pixminimax` calls
@@ -722,9 +722,10 @@ further — there is nothing to ask.
722
722
  count comes from `keypointsFile`, not a manifest field). See
723
723
  [Skeleton-driven animation](#skeleton-driven-animation) above for the full
724
724
  field list, the `keypointsFile` shape, and the `estimateSkeleton`
725
- CLI-only-and-never-in-the-submission-path decision. Same unverified-live
726
- caveat as `animate`/`animate-pixminimax`, sourced from the live
727
- `animate_with_skeleton_v3` MCP tool schema rather than an observed call.
725
+ CLI-only-and-never-in-the-submission-path decision. Unlike
726
+ `animate`/`animate-pixminimax`, it has run live: an 8-frame revision billed
727
+ exactly 3 generations (see that section); its field names came from the
728
+ `animate_with_skeleton_v3` MCP tool schema.
728
729
  - `normal-map` calls `/normal-map` (PixelLab 0.4.130), a background job whose
729
730
  map is `last_response.image`; a loop's frames go and come back as one
730
731
  horizontal strip, cut into per-frame files on poll. See
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixelkiln",
3
- "version": "0.92.1",
3
+ "version": "0.92.2",
4
4
  "description": "Manifest-driven pixel-art generation, review, recovery, and packaging with deterministic provenance.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -26,6 +26,11 @@ confirmed gap with real demand rather than a guess. Nothing here is a
26
26
  commitment to build it — it is what is missing, with the evidence for why it
27
27
  might matter, so that decision can be made deliberately.
28
28
 
29
+ For which PixelLab endpoints PixelKiln already calls, and how much is measured
30
+ about each (so a live run can be chosen by risk), read the [validation
31
+ ledger](https://pixelkiln.griffen.codes/docs/endpoints#validation-ledger)
32
+ (`docs/ENDPOINTS.md`).
33
+
29
34
  Each open entry names the PixelLab tool, why pixelkiln doesn't have it today,
30
35
  and which tutorial(s) demonstrated real (not hypothetical) demand for it.
31
36
 
@@ -52,7 +57,8 @@ and which tutorial(s) demonstrated real (not hypothetical) demand for it.
52
57
  `mode: "skeleton-v3"` poses a template with the skeleton video model
53
58
  (2 to 4 generations and 3 to 5 minutes per direction, beta, Tier 1 and
54
59
  up). Now a character loop's `mode: "skeleton-v3"`; see
55
- `docs/CHARACTERS.md`. Not yet billed live.
60
+ `docs/CHARACTERS.md`. A template loop on a v3 character billed 3
61
+ generations live (October 2026).
56
62
 
57
63
  - **Masked inpainting and whole-image editing** — was the single largest gap
58
64
  found (routine in at least 6 of the 26 tutorials). Now the `revision` asset
@@ -118,8 +124,8 @@ and which tutorial(s) demonstrated real (not hypothetical) demand for it.
118
124
  generating a 384×216 scene background in "How to Make Animated Pixel Art
119
125
  Scenes with PixelLab." Closed by the `imagePro` generator; see
120
126
  `pixellab.md`. Reference images and a style image
121
- (`reference_images`/`style_image`) remain unmodeled, and the completed
122
- job's response shape has not itself been exercised live yet.
127
+ (`reference_images`/`style_image`) remain unmodeled. The completed job's
128
+ response shape has been read live (a 64×64 call billed 10).
123
129
  - **A standalone single isometric tile** (`/create-isometric-tile`, a third,
124
130
  separate path to isometric content distinct from both `tiles`'
125
131
  `tileType: "isometric"` and `terrain`'s `/create-tileset`, which is
@@ -325,9 +331,8 @@ and which tutorial(s) demonstrated real (not hypothetical) demand for it.
325
331
  `animate-with-skeleton-v3` against a *different* reference image reusing
326
332
  those keypoints — a two-endpoint composition a person can now build by
327
333
  hand with the primitives above, not a primitive pixelkiln itself
328
- automates. Neither `animate-with-skeleton-v3` nor `estimate-skeleton` has
329
- been exercised against a live account; request field names come from the
330
- MCP tool schema, not an observed call.
334
+ automates. Both endpoints have run live (see above); request field names
335
+ came from the MCP tool schema, and the live run found `description` is required.
331
336
  - **Remove background** (`/remove-background`, a transparent cutout of one
332
337
  image up to 400x400, `simple` or `complex` task, optional foreground text
333
338
  hint) — closed by the `revision` asset shape's `remove-background` mode.
@@ -451,7 +456,8 @@ and which tutorial(s) demonstrated real (not hypothetical) demand for it.
451
456
  - Cost quotes (provisional): `create` 5 at 32 and 64px, 9 at 256px;
452
457
  `edit`/`inpaint` 5 at 64px, `inpaint` 6 at 128px; `character` and
453
458
  `object` at 64px `image: 5, rotations: 1, total: 6`, matching
454
- pixelkiln's measured `proFlashCharacterCost`. None billed live yet.
459
+ pixelkiln's measured `proFlashCharacterCost`. Only `create` has been billed
460
+ live (a 32×32 still with a palette, 5); `edit` and `inpaint` have not.
455
461
 
456
462
  Still unmodeled: `edit`'s `"reference"` method, the
457
463
  `use_color_palette_correction` flag (snap the edit back to the source's
@@ -221,10 +221,9 @@ mapping is not wrapped: it is "single images only", and this endpoint
221
221
  returns a grid of candidates below 171px. Reference
222
222
  images and a style image (`reference_images`, `style_image` +
223
223
  `style_options`, up to 4 subject references plus one style reference) exist
224
- on this endpoint but are not modeled yet, and the completed job's exact
225
- response shape has not been exercised against a live account — see
226
- `pollImagePro` in `src/providers/pixellab.ts` if a real call ever
227
- contradicts what it assumes.
224
+ on this endpoint but are not modeled yet. The completed job has been read
225
+ live (a 64×64 call billed 10); `pollImagePro` in `src/providers/pixellab.ts`
226
+ still reads it defensively.
228
227
 
229
228
  `objectPro` wraps `/create-object-pro-flash` — the same base → state →
230
229
  animation family `character` uses (`docs/CHARACTERS.md`), for PixelLab's
@@ -529,17 +528,17 @@ written as a set (a character's directions, an animation's frames) goes to
529
528
  so every member shares one palette or edit; the other modes refuse a set
530
529
  parent. `reduce-colors`/`correct-pixelart` cost is **confirmed live**: a flat
531
530
  0.1 generations each (not the schema's dollar-denominated example), at least
532
- at a 32×32 source — `animate`/`animate-pixminimax`/`animate-skeleton`/
533
- `interpolate`/`edit-animation` remain schema-only and unexercised.
534
- `animate-skeleton`'s request field names come from the live
535
- `animate_with_skeleton_v3` MCP tool schema, a more authoritative source than
536
- the OpenAPI document the others are taken from, but still not an observed
537
- call. `estimate-skeleton` (auto-derive a reference image's own keypoints) is
531
+ at a 32×32 source — `animate`/`animate-pixminimax`/`interpolate`/
532
+ `edit-animation` remain schema-only and unexercised.
533
+ `animate-skeleton` has run live (8 frames billed exactly 3); its request
534
+ field names come from the `animate_with_skeleton_v3` MCP tool schema, a more
535
+ authoritative source than the OpenAPI document the others are taken from, and
536
+ the run found `description` is required. `estimate-skeleton` (auto-derive a reference image's own keypoints) is
538
537
  wrapped too, but deliberately as a standalone `pixelkiln estimate-skeleton`
539
538
  CLI command, never inside `submitRevision` — see
540
539
  [pixellab-roadmap.md](./pixellab-roadmap.md)'s now-closed skeleton-driven-
541
- animation entry for why composing two never-tested-live endpoints in one
542
- submission was rejected.
540
+ animation entry for why composing two endpoints in one submission was
541
+ rejected.
543
542
 
544
543
  `animate-pixminimax`'s `direction` + `enhancePrompt` combination has a
545
544
  measured-in-practice gotcha, not just a schema quirk: PixelLab's own tutorial
@@ -621,7 +620,11 @@ high-fidelity showcase asset) rather than a default, read
621
620
  [pixellab-fidelity.md](./pixellab-fidelity.md) before choosing `size`,
622
621
  `detail`, `shading`, or `outline`. For what PixelLab can do that this adapter
623
622
  does not yet reach at all, read [pixellab-roadmap.md](./pixellab-roadmap.md)
624
- before proposing new work.
623
+ before proposing new work. To pick which PixelLab endpoint to spend a live run
624
+ on, read the [validation
625
+ ledger](https://pixelkiln.griffen.codes/docs/endpoints#validation-ledger)
626
+ (`docs/ENDPOINTS.md`): it rates each endpoint by what has been measured and
627
+ what a wrong guess could cost.
625
628
 
626
629
  For setup and current field constraints, use
627
630
  <https://pixelkiln.griffen.codes/docs/pixellab>. When working in the PixelKiln