{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/kamai-io/json-schema/kamai-io-blueprint-details-response-schema.json",
  "title": "BlueprintDetailsResponse",
  "x-generated": "2026-11-13",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/kamai-io-openapi.json#/components/schemas/BlueprintDetailsResponse",
  "properties": {
    "blueprint": {
      "$ref": "#/$defs/BlueprintPublic",
      "description": "Blueprint details"
    },
    "message": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Message",
      "description": "Additional information message"
    },
    "revision": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Revision",
      "description": "The sheet's revision this copy was read at. Send it back as `base_revision` on a PUT that carries `geojson`: if the sheet changed in between, that save is refused with 409 `stale_sheet` instead of silently undoing the change. Opaque; compare for equality only."
    },
    "integrity": {
      "anyOf": [
        {
          "additionalProperties": true,
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "title": "Integrity",
      "description": "The sheet's data-integrity mode, `{mode: legacy | adopted | derived, derivation}`. On a converted sheet the wall-surface pieces of a door or window are derived from it by the server and are read-only: edit the door or window. Absent while the feature is off."
    }
  },
  "type": "object",
  "required": [
    "blueprint"
  ],
  "$defs": {
    "BlueprintFeaturePropertiesPublic": {
      "properties": {
        "id": {
          "type": "string",
          "title": "Id",
          "description": "The feature's id, unique on this sheet. A re-analysis of the sheet issues new ids."
        },
        "kind": {
          "$ref": "#/$defs/FeatureKind",
          "description": "What the feature is geometrically: `area` (a polygon), `line`, `object` (a counted item such as a door or a sink) or `folder` (a group in the tree). Decides which measurements apply."
        },
        "class": {
          "$ref": "#/$defs/FeatureClass",
          "description": "What the feature is on the drawing: `room`, `wall`, `wall_centerline`, `wall_surface` (one room's side of a wall), `wall_surface_with_opening` (the piece of that side across a door or window), `opening` (doors and windows), `footprint` (the plan's outer boundary) or `other`."
        },
        "sub_class": {
          "anyOf": [
            {
              "$ref": "#/$defs/FeatureSubClass"
            },
            {
              "type": "null"
            }
          ],
          "description": "The specific type where known, e.g. 'single swing door', 'window', 'sink'. `wet room` and `wet wall` are derived from what a room contains (a toilet, a shower...), never detected, and are recomputed on every save."
        },
        "name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Name",
          "description": "The label shown in the Kamai app, e.g. a room's name."
        },
        "color": {
          "anyOf": [
            {
              "$ref": "#/$defs/FeatureColorPublic"
            },
            {
              "type": "null"
            }
          ],
          "description": "Display colour in the Kamai app."
        },
        "visible": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "title": "Visible",
          "description": "Visibility in this hierarchy."
        },
        "is_user_drawn": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "title": "Is User Drawn",
          "description": "True when a user drew it rather than the detector."
        },
        "parent_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Parent Id",
          "description": "Parent feature id; `null` on the root folder. With `position`, this is the ONLY hierarchy source — there is no reverse index."
        },
        "position": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Position",
          "description": "Sort order among siblings. A float so an item can be inserted between two others without renumbering."
        },
        "folder": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Folder",
          "description": "The parent's NAME — the group this feature lives under, resolved so grouping needs no client-side join over `parent_id`. `null` on roots and features outside the hierarchy."
        },
        "opening_width_units": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Opening Width Units",
          "description": "Door/window clear width in blueprint units (the coordinate space of `geometry`). See `measurements.opening_width_m` for metres."
        },
        "wall_thickness_units": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Wall Thickness Units",
          "description": "Walls (`class` `wall`) only: thickness face to face as drawn, in blueprint units. See `measurements.wall_thickness_m` for metres. `null` on every other class and where it was not measured. On PUT: a value replaces it, `null` clears it, and a wall that omits the key keeps its stored thickness while its geometry is unchanged (a reshaped wall drops it; where re-measuring is switched on it is re-measured from its new polygon instead, and a value a client sent is never re-measured over). A value on any other class is refused (422)."
        },
        "wall_thickness_source": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Wall Thickness Source",
          "description": "Walls only: where `wall_thickness_units` came from. `null` = the pipeline's measurement; `measured_on_edit` = re-measured from the wall's polygon after it was reshaped or drawn by hand; `user` = a value a client sent. On PUT it says whose the sent `wall_thickness_units` is: omitted, `null` or `user` = a value you state (an unchanged number is an echo); `pipeline` or `measured_on_edit` = a machine figure you echo, never stored: a wall the sheet holds keeps its own thickness, and a re-added wall gets none. It is never stored as sent."
        },
        "wall_group": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Wall Group",
          "description": "Walls, wall surfaces and surface-with-opening pieces: the texture group folder of the wall ('Group A'); a surface's is its wall's, read live. Read-only: ignored on PUT."
        },
        "height_units": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Height Units",
          "description": "Optional vertical height in blueprint units, e.g. a room's or an opening's height read off the drawing. Send it back on a PUT to keep it."
        },
        "tag": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Tag",
          "description": "The mark printed beside a door or window on the drawing, e.g. 'D-01', 'W2' or '80/210'; `null` when none was read. Free text, and NOT an identifier: openings often share a mark, so never join or de-duplicate on it."
        },
        "handing": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Handing",
          "description": "Single swing doors only: 'LH' or 'RH', read off the drawing's swing arc. It is the hinge side seen from the side the door swings away from (the push side, the North-American convention; European DIN handing is the mirror image). 'LHR'/'RHR' (reverse, pull-side handings) are never detected and are kept only when a client sets them. `null` on every other feature and where the arc could not be read. Round-trips unchanged on PUT. A value other than LH/RH/LHR/RHR is refused (422); on a feature that is not a single swing door it is dropped."
        },
        "handing_din": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Handing Din",
          "description": "`handing` under DIN 107, the European convention, which names the hinge side seen from the side the door opens towards: 'DIN-L' (DIN links) or 'DIN-R' (DIN rechts). RH is DIN-L and LH is DIN-R (the mirror image); LHR is DIN-L and RHR is DIN-R. Derived from `handing` on every read and never stored: `null` wherever `handing` is, and ignored on PUT (set `handing` instead)."
        },
        "relations": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Relations",
          "description": "Ids of features this one is spatially related to — an undirected edge set, so if A lists B then B lists A. Untyped by design: filter the ids by the neighbour's `class` for a typed view (a room's walls are the related ids whose class is `wall`/`wall_centerline`; its wall surfaces, cut at each opening on the room's side, are those whose class is `wall_surface`; the piece across a door/window is class `wall_surface_with_opening`; both turn `sub_class` `wet wall` with a wet room)."
        },
        "measurements": {
          "$ref": "#/$defs/FeatureMeasurementsPublic",
          "description": "Real-world size in m and m², derived by the server; read-only."
        }
      },
      "type": "object",
      "required": [
        "id",
        "kind",
        "class"
      ],
      "title": "BlueprintFeaturePropertiesPublic",
      "description": "One feature's properties in the coarse feature shape."
    },
    "BlueprintFeaturePublic": {
      "properties": {
        "type": {
          "type": "string",
          "title": "Type",
          "default": "Feature"
        },
        "id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Id"
        },
        "geometry": {
          "anyOf": [
            {
              "additionalProperties": true,
              "type": "object"
            },
            {
              "type": "null"
            }
          ],
          "title": "Geometry",
          "description": "GeoJSON geometry in blueprint units (see the blueprint's `url_width` and `url_height`), origin at the page's top-left corner. `null` for folders."
        },
        "properties": {
          "$ref": "#/$defs/BlueprintFeaturePropertiesPublic"
        }
      },
      "type": "object",
      "required": [
        "properties"
      ],
      "title": "BlueprintFeaturePublic",
      "description": "A GeoJSON Feature. `geometry` is `null` only for folders."
    },
    "BlueprintGeoJSONPublic": {
      "properties": {
        "type": {
          "type": "string",
          "title": "Type"
        },
        "features": {
          "items": {
            "$ref": "#/$defs/BlueprintFeaturePublic"
          },
          "type": "array",
          "title": "Features"
        }
      },
      "type": "object",
      "required": [
        "type"
      ],
      "title": "BlueprintGeoJSONPublic"
    },
    "BlueprintPublic": {
      "properties": {
        "id": {
          "type": "string",
          "title": "Id",
          "description": "The sheet's id (blueprint id)."
        },
        "name": {
          "type": "string",
          "title": "Name",
          "description": "The sheet's stored name, usually its upload file name; GET /v1/projects/{id}/sheets gives the name the app shows."
        },
        "scale": {
          "anyOf": [
            {
              "$ref": "#/$defs/BlueprintScalePublic"
            },
            {
              "type": "null"
            }
          ],
          "description": "The sheet's drawing scale, which turns drawing distances into real lengths and areas. `null` means the sheet has no scale yet. On a sheet without a usable scale (none, a ratio that is not positive and finite, or no `url_dpi`), every `measurements` value is then `null`, never zero."
        },
        "url_height": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Url Height",
          "description": "The sheet's height in blueprint units, the coordinate space of every `geometry`."
        },
        "url_width": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Url Width",
          "description": "The sheet's width in blueprint units, the coordinate space of every `geometry`."
        },
        "url_dpi": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Url Dpi",
          "description": "The resolution of the sheet image that feature coordinates are measured in (blueprint units per inch of paper). Together with the scale it converts drawing units to real ones. A sheet without `url_dpi` cannot be measured even if it has a scale: every `measurements` value is then `null`, never zero."
        },
        "geojson": {
          "anyOf": [
            {
              "$ref": "#/$defs/BlueprintGeoJSONPublic"
            },
            {
              "type": "null"
            }
          ],
          "description": "The sheet's features (rooms, walls, openings, objects) and their folder tree."
        },
        "text": {
          "items": {
            "$ref": "#/$defs/BlueprintTextPublic"
          },
          "type": "array",
          "title": "Text",
          "description": "The sheet's words. Read-only; ignored if sent on an update. Empty unless requested with `include_text`."
        },
        "text_total": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Text Total",
          "description": "Total words on the sheet, which may exceed len(text) because the page is capped — page with text_limit/text_offset until you have seen this many. None when text was not requested."
        },
        "identity": {
          "anyOf": [
            {
              "$ref": "#/$defs/SheetIdentityPublic"
            },
            {
              "type": "null"
            }
          ],
          "description": "What the sheet says it is. Every key null and `levels` empty on a sheet that was not classified: not classified, never a guess."
        },
        "rotation": {
          "type": "integer",
          "title": "Rotation",
          "description": "Degrees clockwise to turn the sheet for display: 0, 90, 180 or 270, relative to the tiles as served. A view setting: tiles, `url_width`/`url_height` and every `geometry` stay unturned, so apply it when you draw, to the image and the overlays alike. Set it with PUT /v1/blueprints/{blueprint_id}/rotation.",
          "default": 0
        },
        "auto_rotation": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Auto Rotation",
          "description": "Degrees clockwise (0, 90, 180, 270) Kamai turned the page at upload to bring it upright, on top of the PDF's own /Rotate. Already inside the tiles and every `geometry`: nothing to apply. Read-only. `null`: not decided (a sheet uploaded before the check existed)."
        },
        "created": {
          "type": "string",
          "format": "date-time",
          "title": "Created",
          "description": "When the sheet was created (ISO 8601)."
        },
        "updated": {
          "type": "string",
          "format": "date-time",
          "title": "Updated",
          "description": "When the sheet record last changed (ISO 8601)."
        }
      },
      "type": "object",
      "required": [
        "id",
        "name",
        "created",
        "updated"
      ],
      "title": "BlueprintPublic"
    },
    "BlueprintScalePublic": {
      "properties": {
        "drawing": {
          "type": "number",
          "exclusiveMinimum": 0.0,
          "title": "Drawing",
          "description": "The drawing side of the scale ratio: the 1 in \"1:50\", or the ¼ inch in 1/4\" = 1'-0\". Read it together with `real`; only the ratio real ÷ drawing matters."
        },
        "real": {
          "type": "number",
          "exclusiveMinimum": 0.0,
          "title": "Real",
          "description": "The real-world length that `drawing` stands for: the 50 in \"1:50\". The scale factor is real ÷ drawing, e.g. 50 for 1:50, or 48 for 1/4\" = 1'-0\"."
        },
        "label": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Label",
          "description": "The scale as printed on the sheet or entered by a person, e.g. \"1:50\" or 1/4\" = 1'-0\". It can be `null`; the ratio is always in `drawing` and `real`."
        },
        "type": {
          "$ref": "#/$defs/ScaleType",
          "description": "The family of the scale. It sets how the Kamai app displays values: `metric` shows metres and m², every other type feet and square feet; door and window widths show in centimetres when the type is `metric` or the units are `si`. It does not change the API: `measurements` are always SI (m, m²)."
        },
        "units": {
          "$ref": "#/$defs/ScaleUnits",
          "description": "The unit `drawing` and `real` are both expressed in. They share it, and only their ratio real ÷ drawing is used."
        },
        "manual_scaling_needed": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "title": "Manual Scaling Needed",
          "description": "`true` when Kamai set this scale automatically and no person has confirmed it yet: either the scale read from the sheet's title block, or a default for the detected measurement system when none could be read. Measurements use it normally. It clears when someone sets the scale in the Kamai app. A PUT stores the scale as a person's: `false` unless the body sends the flag. `false` or `null` means a person set or confirmed it, and a re-process keeps such a scale. Sending `true` marks the scale as Kamai's on purpose (unconfirmed): a re-process then recomputes it from the sheet."
        }
      },
      "type": "object",
      "required": [
        "drawing",
        "real",
        "type",
        "units"
      ],
      "title": "BlueprintScalePublic",
      "description": "The sheet's drawing scale: real length = drawing length × real ÷ drawing."
    },
    "BlueprintTextPublic": {
      "properties": {
        "id": {
          "type": "string",
          "title": "Id",
          "description": "Stable id for this word"
        },
        "text": {
          "type": "string",
          "title": "Text",
          "description": "The word itself"
        },
        "confidence": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Confidence",
          "description": "0..1. The OCR score for a recognised word; 1.0 for text read out of the PDF's own digital layer."
        },
        "geometry": {
          "anyOf": [
            {
              "additionalProperties": true,
              "type": "object"
            },
            {
              "type": "null"
            }
          ],
          "title": "Geometry",
          "description": "The word's box in blueprint coordinates, as GeoJSON. Follows the true (possibly rotated) glyph run, not an upright box."
        },
        "room_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Room Id",
          "description": "The room this word is printed in, if any."
        },
        "wall_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Wall Id",
          "description": "The wall this word is printed on, if any."
        }
      },
      "type": "object",
      "required": [
        "id",
        "text"
      ],
      "title": "BlueprintTextPublic",
      "description": "One word read off the sheet.\n\nSeparate from `features` on purpose: text is not part of the legend/folder\ntree — it has no folder, no position and no place in the hierarchy — and a\nsheet carries thousands of words against a few hundred real features."
    },
    "FeatureClass": {
      "type": "string",
      "enum": [
        "room",
        "wall",
        "wall_centerline",
        "wall_surface",
        "wall_surface_with_opening",
        "opening",
        "footprint",
        "other"
      ],
      "title": "FeatureClass"
    },
    "FeatureColorPublic": {
      "properties": {
        "r": {
          "type": "integer",
          "maximum": 255.0,
          "minimum": 0.0,
          "title": "R"
        },
        "g": {
          "type": "integer",
          "maximum": 255.0,
          "minimum": 0.0,
          "title": "G"
        },
        "b": {
          "type": "integer",
          "maximum": 255.0,
          "minimum": 0.0,
          "title": "B"
        },
        "a": {
          "type": "integer",
          "maximum": 255.0,
          "minimum": 0.0,
          "title": "A"
        }
      },
      "type": "object",
      "required": [
        "r",
        "g",
        "b",
        "a"
      ],
      "title": "FeatureColorPublic",
      "description": "Display colour, 0-255 per channel."
    },
    "FeatureKind": {
      "type": "string",
      "enum": [
        "area",
        "line",
        "object",
        "text",
        "folder"
      ],
      "title": "FeatureKind"
    },
    "FeatureMeasurementsPublic": {
      "properties": {
        "area_m2": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Area M2",
          "description": "Polygon area, m²."
        },
        "perimeter_m": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Perimeter M",
          "description": "Polygon boundary length, m (holes included)."
        },
        "length_m": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Length M",
          "description": "Line length, m."
        },
        "opening_width_m": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Opening Width M",
          "description": "Door/window clear width, m. Converted from `opening_width_units`."
        },
        "wall_thickness_m": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Wall Thickness M",
          "description": "Walls only: thickness face to face as drawn, m. Converted from `wall_thickness_units`. `null` on every other class and on a wall whose thickness was not measured."
        }
      },
      "type": "object",
      "title": "FeatureMeasurementsPublic",
      "description": "Real-world measurements the server derives from the geometry and the sheet's scale,\nalways in SI units (m, m²); each field's unit is in its name. Read-only: ignored on a PUT.\n\n``null`` means *not applicable or not measurable*, never a measured zero: a line has no\narea, a folder has no geometry, and a sheet without a usable scale has no measurements\nat all. They follow the GEOMETRY, not ``kind``: an object drawn as a polygon carries an\n``area_m2``. Objects are counted, not measured, so do not sum them into a room total."
    },
    "FeatureSubClass": {
      "type": "string",
      "enum": [
        "window",
        "swing window",
        "safe room window",
        "single swing door",
        "double swing door",
        "sliding door",
        "toilet",
        "sink",
        "bathtub",
        "shower",
        "cooktop",
        "stairs",
        "elevator",
        "wet room",
        "wet wall"
      ],
      "title": "FeatureSubClass"
    },
    "ScaleType": {
      "type": "string",
      "enum": [
        "architectural",
        "metric",
        "civil",
        "custom"
      ],
      "title": "ScaleType"
    },
    "ScaleUnits": {
      "type": "string",
      "enum": [
        "si",
        "imperial",
        "inches"
      ],
      "title": "ScaleUnits"
    },
    "SheetIdentityPublic": {
      "properties": {
        "sheet_number": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Sheet Number",
          "description": "The sheet number as printed in the title block, e.g. A-101. Not unique within a project."
        },
        "title": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Title",
          "description": "The sheet title as printed, e.g. FIRST FLOOR PLAN."
        },
        "discipline": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Discipline",
          "description": "Discipline letter of the US National CAD Standard, e.g. A (architectural), S (structural), M (mechanical)."
        },
        "sheet_type": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Sheet Type",
          "description": "Sheet-type digit of the US National CAD Standard, 0-9, e.g. 1 for plans."
        },
        "sheet_type_label": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Sheet Type Label",
          "description": "The sheet type's name, e.g. Plans."
        },
        "levels": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Levels",
          "description": "Every building level the title names, e.g. [\"L1\", \"L2\"]; empty when it names none."
        }
      },
      "type": "object",
      "title": "SheetIdentityPublic",
      "description": "What the sheet's title block says it is."
    }
  }
}