Measured endpoints

Tilesets: schema only, not yet measured

Called out separately because these are the most capable endpoints for level art and they are relevant to the disc-golf game. Costs below are unmeasured.

POST /tilesets (top-down) and /tilesets-sidescroller are the current versions; /create-tileset* are the older aliases with identical schemas.

They take lower_description + upper_description (+ optional transition_description). You describe two terrains and the transition between them, and get a tileset that blends them. Distinctively, they accept both color_image and per-layer reference images (lower_reference_image, upper_reference_image, transition_reference_image). They are the only family that combines palette forcing with style anchoring.

Useful knobs: tile_size, tileset_adherence / tileset_adherence_freedom (how strictly tiles must fit together), raggedness and slope_size for edge character, plus the usual outline / shading / detail.

create-tiles-pro is a different shape, a single description plus style_images, tile_view, building_* fields for structures.

Its style_images is a fourth convention, and not the one two lines up: TilesProStyleImage is flat, with all three fields required.

// create-tiles-pro
{ "style_images": [ { "base64": "...", "width": 32, "height": 32 } ] }

Sending generate-with-style-v2's nested {image: {...}} here is rejected as an extra field. Passing style images at all makes the endpoint ignore tile_type and tile_view and copy the reference's tile geometry instead. That is the only way to land new art on an existing sheet's ground plane.

GET /tiles-pro/{id} carries no status field. It answers 423 while the set is still drawing and 200 with storage_urls when it is done, so the HTTP code is the status. Cost is reported at submit time and lands on the same 20/25/40 canvas tiers as 1dir, but the canvas is tile size x variation count, not one sprite. A small tile in a large set still reaches the top tier.

All response shapes used by the client are validated at runtime. The TypeScript interfaces alone are not trusted at the HTTP boundary: missing ids, invalid object geometry, malformed tile URL maps, or balance fields with changed types produce an Invalid PixelLab response error before pipeline state is updated.

outline_mode defaults to outline, which draws a dark border around every tile. For ground tiles that is wrong: laid on a grid the borders read as quilting, with a seam at every cell edge. segmentation omits them and the same set tiles seamlessly, measured on a fairway-to-rough terrain set, where it was the difference between usable and unusable.

tile_feature and style_images are mutually exclusive. The API says "Connectable features (roads/tileset/building) cannot be combined with style tiles". So the geometry-anchoring trick above is unavailable for a connectable set, and its tiles land on whatever ground plane the view angle implies (measured: 32x24 with the diamond midline at y=7, against a 32x32 sheet wanting y=15).

tile_feature turns it from independent variations into a connectable set: roads (18-configuration path set), tileset (16-tile Wang corner set for a terrain transition; describe it as the transition, not one terrain), and building (floor/wall/doorway kit). These are sliced by index, so the returned order is load-bearing. They are structural multi-output results, not candidates to choose between: PixelKiln persists every URL in numeric order and labels the downloaded outputs tile-00, tile-01, … .

The completed response also carries tile_rules for connectable sets. Its placement contract is rule_type (edge, corner, or outline), arity (4 for square/isometric edges or corners, 6 for hex edges), connectivity (same/other), two terrain labels, and a tiles map of provider tile key to bitmask. Four-edge masks use N/E/S/W as bits 0/1/2/3. Four-corner masks use NW/NE/SW/SE as bits 3/2/1/0. Tiles absent from that map are stamp-only. PixelKiln stores the complete object in providerMetadata.pixellab.tileRules; exporters normalize the documented subset but retain the raw object in the generic file.

Given that color_image is honoured on pixflux but silently ignored on resize, verify the palette actually holds on a single tileset before committing to a set.