Integration details
Description
HZplan turns floor-plan images or written dimensions into structured, editable home plans your assistant can reason about. Users can create and refine rooms, walls, doors, windows, furniture, and surface finishes; import and calibrate floor-plan images; inspect dimensions and topology; add notes and custom attributes; make targeted geometry edits; create materials from reference images; generate 3D object models; and export storeys as PNG or SVG.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Hotel Search & Booking
- Secondary Subcategories
- None listed
- Brand
- HZplan
- Access
- Account required
- First tracked
- 2026-06-12
- Tool count
- 41
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Get alerts for HZplan
Get updates when HZplan’s Discoverability Score or category rank changes.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competing in ChatGPT CAD & 3D Design Tool Control
View Category41 tools agents can invoke
Step 2 of 2 in the image → hzplan pipeline. Runs full inference + conversion on ONE floor (picked by floor_id from a prior split_floor_plan response) and either mints a new hzplan (plan_id omitted) or appends the floor as a new storey to an existing plan (plan_id given). Burns 1 unit of `plan_from_image_monthly_*` quota per call. WHEN TO CALL After split_floor_plan returned successfully, the user has seen the previews, and they've told you which floor(s) to import. Call this ONCE per chosen floor. For a 3-floor plan the user wants fully imported, that's 1 split_floor_plan call + 3 convert_floor_to_plan calls. DO NOT retry a successful call. DO NOT call this before split_floor_plan. WHAT TO PASS image: the SAME image used in split_floor_plan (ChatGPT auto-fills again from the user's upload). floor_id: the floor_id from the split_floor_plan response. storey_name: human label for this storey, e.g. 'ground', 'first', 'kitchen floor'. Default to the suggested_name from split_floor_plan if the user doesn't say otherwise. plan_id: OMIT on the first conversion (mints a brand-new plan). PASS the previous call's plan_id on all subsequent conversions so all floors land on ONE plan as separate storeys. plan_title: only relevant when plan_id is omitted — title for the new plan. WHAT YOU GET BACK ON SUCCESS { ok: true, plan_id, storey_id, quota, summary } - plan_id: pass this to the NEXT convert_floor_to_plan call if there are more floors to import. - storey_id: the storey id assigned inside the plan ('s1', 's2', ...). - quota: current usage info. - summary: diagnostic counts (walls, openings, mm_per_px) — usually fine to ignore. NEXT STEPS - After all chosen floors are converted, tell the user the plan is ready. Render it with render_storey_widget(plan_id, storey_id) to show the result. - The user can then refine the plan with upsert_entity / delete_entity / set_object_feature_side etc. — those operate on walls / openings / objects inside the storey. ERROR CODES feature_not_enabled: account lacks plan_from_image entitlement. Tell the user; don't retry. quota_exceeded: monthly limit reached. Tell the user. floor_id_not_found: floor_id doesn't match the image you passed. Did the image change? Call split_floor_plan again with the current image. hzplan_validation_failed: the converted plan didn't pass our solver. Report to the user. service_*: transient pipeline error. Safe to retry ONCE. image_url_fetch_failed: user's image URL expired. Ask them to re-upload, then start over with split_floor_plan.
convert_floor_to_plan
Turn an image into a reusable custom floor, wall or ceiling material in the user's private library, then apply it with a surface_finish material_asset(...) fact. NEVER TINTABLE: materials made this way carry the image's own colour, so the finish must use material_asset(...) alone — adding color_hex to it is rejected as finish_material_not_tintable. WHAT THIS DOES: it tiles the image you give it, unchanged. There is no enhancement, no seam repair, no perspective correction and no quality check. HOW THE IMAGE IS CONSUMED: it is tiled edge-to-edge across the WHOLE surface at the real-world size you declare, then viewed at eye level in a 3D walk-through. Only its base colour is used — surfaces are flat-shaded, with no relief or bump. So: - Opposite edges must meet seamlessly: left against right, top against bottom. - MIRRORING IS A TRAP: don't mirror an image to force its edges to match. Every tile then becomes symmetric about that axis, which at room scale reads as an obvious repeating band — usually more visible than the seam it removed. - One-off features repeat on EVERY tile: a single knot, stain, logo or baked-in shadow becomes a grid of them. BEST INPUT: an image YOU generate — a flat, straight-on, evenly lit, seamlessly tileable square texture with no room, furniture, shadow, border or text. Generate it, show the user, let them approve it, then call this tool with it. A photo of a real floor or wall usually looks bad tiled, because its perspective, shadows and distinctive marks repeat on every tile. If the user insists on one, warn them first and let the result speak. PASSING THE IMAGE (do NOT base64-encode it yourself — image bytes won't fit in a tool argument): - Preferred: pass the image you just generated (or the user's uploaded one) as the `image` FILE REFERENCE — ChatGPT fills download_url + file_id automatically. - FALLBACK whenever `image` doesn't populate — and an image you generated that won't attach is exactly that case: call request_image_upload, have the user open the link and save/drop the image there, then pass image_ref:{ upload_id }. This is the reliable path when attaching the file directly fails. - Non-ChatGPT clients only: `raw_url` (public http(s) URL or data:...;base64,... URI). SIZE IS REQUIRED: texture_width_mm/texture_height_mm are the real-world size of ONE tile. Choose deliberately — 4-6 plank widths for wood (typically 1000-1500mm), the tile pitch for tiles, about 1000mm for carpet — and tell the user the number you chose. The millimetres must match what the image ACTUALLY depicts, not the pixel canvas: if you drew planks laid out for a 1050mm tile on a 1200px canvas, declare 1050. Nothing downstream can detect a mismatch — every feature simply renders at the wrong real-world size. LIMITS: PNG/JPEG/WebP up to 10MB, 50 custom materials per account. Search search_material_assets first — the curated catalog is higher quality than most generated tiles. IMMUTABLE ONCE CREATED: a material has exactly one revision and tintable is false, so nothing can adjust it afterwards — there is no edit, no re-tint and no new revision. A follow-up like 'make it a bit darker' means generating a NEW image and calling this tool again, which burns another slot against the per-account cap. Settle the image with the user BEFORE calling. ON SUCCESS: structuredContent.advisories[] may carry one warning (currently aspect_mismatch, when the image's pixel aspect disagrees with the declared repeat by more than 1.2x). It is advisory, never a rejection — the material is created either way. It is also the only quality signal this tool produces, so relay it to the user. The same text is appended to this tool's text result. ERROR CODES unsupported_image_format: not PNG/JPEG/WebP. Convert and retry. image_too_large: over 10MB. Use a smaller image. material_limit_reached: delete one with delete_surface_material first. storage_failed: transient. Safe to retry ONCE. storage_unavailable: server misconfiguration. Don't retry; tell the user.
create_surface_material_from_image
Create or update a private SVG illustration in the current user's library. Omit illustration_id to create a new illustration (name and svg_markup are then required); pass illustration_id to update an existing one (only the fields you include change, the rest are preserved). render_svg_preview only previews — this tool persists.
upsert_illustration
Use this when the user wants to start a brand-new house plan from scratch. Creates an empty private plan owned by the current user and returns its plan_id; follow up with create_storey, then add walls/openings/objects via upsert_entity. To build a plan from a floor-plan image instead, use split_floor_plan + convert_floor_to_plan.
create_plan
Create a storey. Optionally follow with upsert_entity(type=storey, assertions=[size_mm(width_mm, depth_mm)]) to pin a fixed rectangle (image-converted plans always author one); without it the storey extent is derived from the walls.
create_storey
Permanently delete one of the user's own custom materials (a mat.user.* ID) and its stored image. Frees a slot against the per-account limit. IMPORTANT: plans that already use this material KEEP their authored material_asset fact and will render the missing-material fallback. This never edits a plan. Tell the user how many of their plans referenced it (returned as referencing_plan_count) before they rely on the result. Only the user's own custom materials can be deleted. Catalog materials cannot. ERROR CODES material_not_found: not a custom material in this user's library (already deleted, a catalog ID, or someone else's). Don't retry. storage_unavailable: server misconfiguration. Don't retry; tell the user.
delete_surface_material
Delete one wall, opening, object, or auxiliary height level from the current plan head by its entity id. Structural floor/ceiling levels are removed only with delete_storey. Deletion fails if another entity still depends on this one through host, offset, or attached_to; the response lists blocking entity ids so they can be re-hosted or re-bound first. storey_id: the floor this entity is on — required only when the same id exists on more than one floor, or when creating on a plan with several floors.
delete_entity
Archive one private illustration from the current user's library.
delete_illustration
Use this when the user wants to permanently delete an entire plan. Removes the plan and all of its storeys, along with any uploaded floor-plan images and public snapshots tied to it. This is destructive and cannot be undone. Prefer this over deleting storeys one by one when the whole plan should go; confirm with the user first because there is no recovery.
delete_plan
Use this when the user wants to remove an entire floor. Deletes one storey and everything on it — its rooms, spaces, objects, walls, and openings — from the current plan head. This is destructive and is not reversible through the API. It fails only if an entity on another storey still references something on this one; the response then lists the blocking entity ids so they can be re-anchored or deleted first, then retry.
delete_storey
Render one storey to a downloadable PNG (default) or SVG and return a link (valid 1 hour). Toggle room names (show_names), furniture (draw_objects), and measurements (show_measurements, default off). When show_measurements is true the dimensions are AI-estimated — tell the user to verify, and suggest rescale_storey to calibrate. The exported image always carries an 'AI-generated' mark.
export_storey
Generate a 3D model (.glb) for an existing object from a reference image and attach it to that object. The object must already exist with solved width/depth, persisted height_mm, and a physical attached_to fact (create/place it via upsert_entity first). Plan facts are authoritative: the legacy optional height_mm input is accepted but never changes the object's authored height. Async: returns a job_id; poll get_object_3d_status until succeeded — on success the model is auto-attached (confirm with get_object, or use set_object_3d_asset if attached is false). PASSING THE IMAGE (do NOT base64-encode it yourself — image bytes won't fit in a tool argument): - Preferred: pass the user's uploaded or in-chat generated image as the `image` FILE REFERENCE — ChatGPT fills download_url + file_id automatically. - FALLBACK whenever `image` doesn't populate (a generated image that won't attach, a mobile upload, or 'file too large'): call request_image_upload, have the user open the link and save/drop the image there, then pass image_ref:{ upload_id }. - Non-ChatGPT clients only: `raw_url` (public http(s) URL or data:...;base64,... URI). ERROR CODES feature_not_enabled: account lacks 3D generation entitlement. Tell the user; don't retry. too_many_in_flight: concurrent job cap reached. Wait for a running job to finish, then retry. quota_exceeded: monthly 3D generation limit reached. Tell the user. rate_limited: too many requests in a short window. Wait a moment, then retry. object_not_found: plan_id or object_id doesn't exist. Verify and correct. storey_required: the object id exists on more than one floor. Pass storey_id (details.storeys lists them). footprint_unavailable: object has no solved width/depth yet. Ensure the object is fully constrained via upsert_entity first. vertical_placement_unavailable: object lacks authored height_mm or attached_to. Place it physically before generating an asset. image_too_large: image exceeds the size limit. Use a smaller or compressed image. unsupported_image_format: image isn't PNG/JPEG/WEBP. Convert and retry.
generate_object_3d
Poll a 3D-generation job started by generate_object_3d. Returns status (queued/running/succeeded/failed/cancelled), the asset_id once succeeded, and `attached` (whether the model_3d link landed on the object — if false after success, call set_object_3d_asset).
get_object_3d_status
Get one illustration, including its stored SVG markup. Use this tool for illustration data; MCP resources are widget HTML only.
get_illustration
Get one object with its current constraints and solved placement metadata. Use set_object_illustration for illustration-only changes; use this before upsert_entity when you need to preserve existing assertions during broader object edits. storey_id: the floor this entity is on — required only when the same id exists on more than one floor, or when creating on a plan with several floors.
get_object
Get the stored top-level summary for one plan. This returns summary data, not the full plan document.
get_plan
Get the plan's spatial room topology. Returns one node per derived free-space region (the rooms shown in the UI) with its storey, an interior `center` point, area, and bound `name`/`kind` (null until named); and edges for the openings/doorways connecting rooms. To name a room, pass that node's `center` as the (x_mm, y_mm) to name_space_at_point.
get_plan_topology
Get bounded structured detail for one storey. Use storey_id from get_plan when possible. Pass include_surface_context=true only when decorating: it adds current space-by-space wall-face intervals and floor/ceiling targets.
get_storey_plan
List accessible system illustrations and the current user's private illustrations. Use this tool for illustration discovery; do not use MCP resources for library data.
list_illustrations
Use this when you need to find a plan_id or show the user what plans they have. Lists the current user's private plans with their ids and titles. Read-only.
list_plans
List current objects in one room.
list_room_objects
Patch-merge geometry assertions into an EXISTING entity by (predicate, anchor) key: each provided assertion replaces the one with the same key and ALL other assertions are preserved. Use this to change one field (e.g. thickness_mm) without resending the whole set — unlike upsert_entity, which replaces the entire assertion list. Does not create entities (use upsert_entity for that). For `offset`, the anchor (the first arg, e.g. "left", "top", "center_x") is part of the key, so different anchors merge independently. Custom attributes (set via `set_entity_attributes`) are unaffected. The result echoes the entity's full current assertion set. storey_id: the floor this entity is on — required only when the same id exists on more than one floor, or when creating on a plan with several floors.
set_entity_assertions
Create or update a semantic space name by dropping an interior pin on a solved storey. Use this for named floor areas such as bathroom, bedroom, hall, kitchen, or garage after walls and openings already define the enclosure. This does not author new geometry. Instead it stores a semantic pin and binds the name onto the derived free-space region that contains that point. The point must fall inside exactly one derived free-space region. Provide space_id to rename or move an existing named space; omit it to create a new one.
name_space_at_point
Remove geometry assertions from an EXISTING entity by (predicate, anchor) key; all other assertions are preserved. Provide the same {predicate, args} shape — only the key is used (values ignored). For `offset`, pass the anchor in args[0] (e.g. {predicate:"offset", args:["left"]}). Removing a key the entity does not have is a no-op. Removing a load-bearing constraint may leave the entity under-constrained — the solver result will say so. The result echoes the entity's full current assertion set. storey_id: the floor this entity is on — required only when the same id exists on more than one floor, or when creating on a plan with several floors.
remove_entity_assertions
Render one illustration SVG preview in the ChatGPT widget. Preview only; this does not save anything. Use upsert_illustration to persist SVG markup.
render_svg_preview
Render one named room from a storey in the ChatGPT widget.
render_named_room_widget
Render the whole-plan topology graph in the ChatGPT widget.
render_plan_topology_widget
Render one storey in the ChatGPT widget.
render_storey_widget
Get a one-time upload link for a floor-plan image when you can't attach it directly or the file is too large to pass inline. Works for ANY client (Claude, ChatGPT, etc.). Quota-free. FLOW 1. Call this (no args) → { upload_id, upload_url }. 2. Show the user the upload_url and ask them to open it and drop their floor-plan image (one tap). 3. Then call split_floor_plan with image_ref: { upload_id }. If they haven't finished you'll get code 'upload_pending' — wait a moment and retry. 4. Reuse the SAME upload_id for convert_floor_to_plan — one upload covers split + convert. ChatGPT: prefer attaching the file (pass `image`). But if that keeps failing or the file is too large, FALL BACK to this upload link — the user just opens it and drops the image, and you only ever pass back image_ref:{ upload_id } (no base64). LAST-RESORT FALLBACK: if the user can't or won't use the link, tell them they can create the plan directly in the web app: https://hzplan.uk/plans/new/from-image — never leave them with no way forward.
request_image_upload
Sets the SCALE of `storey_id` losslessly: it records a per-storey scale factor instead of rewriting geometry. real_mm = abstract × factor, so reads report the rescaled dimensions while the stored geometry is untouched — the operation is fully reversible (×2 then ×0.5 returns to the original). Repeated calls COMPOSE (the effective factor multiplies). Wall thicknesses, image bboxes, and angles are unaffected. Runs the geometry engine at the rescaled (real-mm) size and refuses to commit if the storey becomes infeasible. WHEN TO CALL After convert_floor_to_plan if the AI inspects the image and decides the plan's scale is wrong (door-width heuristic is typically ±20% off). Also usable on hand-authored plans where the user wants to scale a storey up/down. Call ONCE per scale change — if the call succeeds, the plan is updated; if it fails (solver_infeasible / solver_ambiguous), the plan is unchanged. WHAT TO PASS plan_id: the plan owning the storey. storey_id: the storey to rescale (e.g. 's1', 's2'). factor: number applied to the CURRENT scale; the resulting effective factor must stay in [0.1, 10]. Compute as `target_real_world_length / current_plan_length`. Example: AI sees a wall on the image and thinks it's 4000 mm in real life; the plan currently reports that wall as 3500 mm; factor = 4000 / 3500 ≈ 1.143. WHAT YOU GET BACK ON SUCCESS { ok: true, plan_id, storey_id, content_version, factor, storey_size_mm: [w, h] } - factor is the new EFFECTIVE scale factor for the storey. - storey_size_mm is the new outer dimensions in mm. ERROR CODES plan_not_found / storey_not_found: bad ids. invalid_factor: the resulting effective factor is out of [0.1, 10]. solver_infeasible: rescaled storey is overconstrained (e.g. a fixed-size object no longer fits). Plan was NOT modified. Try a different factor or fix the object first. solver_ambiguous: rescaled storey is underconstrained. Add missing offsets and retry, or revert. solver_error: the geometry engine itself failed; surface to user. Do NOT retry on success. Compose multiple rescales by calling once per storey.
rescale_storey
Search the accessible surface-material catalog before creating a finish with material_asset(...). Returns stable logical IDs, compatible surface kinds, tintability, base colour, physical texture repeat, style tags, and optional preview URL. A user may also choose color_hex alone without calling this tool.
search_material_assets
Send the user's feedback to the HZplan developers. Use when a tool failed, behaved unexpectedly, was confusing, or the user has a complaint, suggestion, or praise. Quota-free. CONSENT — REQUIRED: first draft the message, show the user the EXACT text you will send, and get their explicit agreement. Never send feedback the user hasn't seen and approved, and never include anything they didn't agree to share.
send_feedback
Attach free-form key→value metadata (e.g. material=brick, condition=poor, note="window is broken") to ANY entity (wall, opening, object, storey, room, house). Passive: stored and shown read-only in the UI, never affects geometry, the solver, or rendering. Merge semantics — `set` adds/overwrites the named keys, `unset` removes them, all other attributes are preserved. Prefer this over upsert_entity for metadata: upsert_entity replaces the whole assertion list. Values are strings (≤2048 chars); keys ≤64 chars; ≤32 keys per entity. storey_id: the floor this entity is on — required only when the same id exists on more than one floor, or when creating on a plan with several floors.
set_entity_attributes
Attach, replace, or clear a generated 3D model (asset_id) on an existing object while preserving its other assertions. Use after generate_object_3d if get_object_3d_status reports attached:false, to re-attach a previously generated asset, or pass asset_id:null to remove the model. Prefer this over upsert_entity, which replaces the whole assertion list and silently drops attachments. storey_id: the floor this object is on — required only when the same object id exists on more than one floor.
set_object_3d_asset
Update only the directional `feature_side` on an existing object (top/bottom/left/right, or null to clear) while preserving every other assertion — kind, size_mm, offsets, illustration_id, model_3d, etc. ALWAYS prefer this over upsert_entity when changing only the facing direction; upsert_entity replaces the whole assertion list and will silently drop attachments like model_3d. storey_id: the floor this entity is on — required only when the same id exists on more than one floor, or when creating on a plan with several floors.
set_object_feature_side
Update only the persisted `height_mm` on an existing object (positive millimetres, or legacy null which now materializes the global 1000 mm default) while preserving every other assertion — kind, size_mm, offsets, physical attachments, illustration_id, model_3d, feature_side, etc. Prefer this over upsert_entity when changing only height. Generated 3D assets are scaled to this authored dimension and never decide it. storey_id: the floor this entity is on — required only when the same id exists on more than one floor, or when creating on a plan with several floors.
set_object_height
Attach, replace, or clear an illustration on an existing object while preserving the object's other assertions automatically. Prefer this over upsert_entity when you only want to change illustration_id. storey_id: the floor this entity is on — required only when the same id exists on more than one floor, or when creating on a plan with several floors.
set_object_illustration
Step 1 of 2 in the image → hzplan pipeline. Detects how many distinct floor plans are present on a single uploaded image (residential plans frequently show ground + first + sometimes basement on one page) and returns one bounding box + preview crop per detected floor. Quota-free. WHEN TO CALL Call this ONCE per image the user wants to convert into an hzplan. If it returns ok:true, USE the floors[] result — show previews to the user, ask which to import, then call convert_floor_to_plan for each chosen floor. DO NOT retry a successful call. DO NOT issue duplicate calls back-to-back. WHAT TO PASS image: the user's uploaded (or in-chat generated) image as a FILE REFERENCE — ChatGPT fills download_url + file_id automatically. Never base64-encode the image yourself; do not paste image bytes into any argument. FALLBACK (use this whenever `image` doesn't populate — e.g. a generated image, a mobile upload, or 'file too large'): call request_image_upload, have the user open the link and drop/save the image there, then pass image_ref:{ upload_id }. This is the reliable path when attaching the file directly fails. Non-ChatGPT clients only: `raw_url` accepts a public http(s) URL or a data:...;base64,... URI. Last resort: point the user to the web app: https://hzplan.uk/plans/new/from-image WHAT YOU GET BACK ON SUCCESS { ok: true, floors: [ { floor_id, suggested_name, bbox_px, source } ] } PLUS one preview image per floor attached as image content (in the same order as floors[]). - floor_id: opaque string. PASS THIS to convert_floor_to_plan to convert that specific floor. - suggested_name: 'ground' / 'first' / 'second' / 'third' / 'fourth' or 'floor N' for >5 floors. Use as the default storey_name unless the user overrides. - bbox_px: [x1, y1, x2, y2] location of the floor in the source image's pixel space. - the attached preview images let the user confirm which floor is which; floors[][i] corresponds to attached image i. NEXT STEPS AFTER A SUCCESSFUL CALL 1. Show the user the attached floor preview images (one per entry in floors[], same order). 2. Ask which floors to import and confirm storey names. 3. For each chosen floor, call convert_floor_to_plan with: same image, the floor_id, the storey_name, and plan_id (omit on the first conversion to mint a new plan; pass on subsequent ones to keep adding storeys to the same plan). ERROR CODES service_misconfigured: pipeline isn't deployed. Don't retry; tell the user. splitter_no_floors: image probably isn't a residential floor plan. Ask the user for a clearer image. image_url_fetch_failed: the user's image URL expired (signed URLs are short-lived). Ask the user to re-upload. unsupported_image_format: image isn't PNG/JPEG/WEBP. service_http_error / service_timeout: transient pipeline error — safe to retry ONCE.
split_floor_plan
Call this first when authoring or editing plans if you need the supported predicates, recommended sequencing, or the MCP usage rule that resources are widget shells only while tools handle all reads and writes.
get_authoring_guide
Create or update one authored entity in the current plan head. Supported entity types are storey, wall, opening, object, height_level, and surface_finish. Assertions are entity-local: do not repeat entity_id in args. Each assertion is an object {"predicate": <name>, "args": [...]}, e.g. assertions: [{"predicate":"on_storey","args":["s1"]}, {"predicate":"kind","args":["outer"]}, {"predicate":"offset","args":["left","s1.left",0]}]. The canonical-tuple form that get_storey_plan shows (["on_storey","s1"], ["offset","left","s1.left",0]) is also accepted, and a leading subject that repeats THIS entity (e.g. ["offset","w1.left","s1.left",0] or ["kind","w1","outer"] while authoring w1) is reduced to entity-local form automatically — so you can replay what you read. Use tools for all data reads and writes; MCP resources are widget HTML only and are never required for CRUD. When changing only an existing object's illustration link, prefer set_object_illustration instead of rebuilding the full assertion list manually. To give an object a 3D model from a reference image, call generate_object_3d only after it has solved width/depth, persisted height_mm, and a physical attached_to fact; then poll get_object_3d_status and optionally call set_object_3d_asset. Storey workflow: (optional) after create_storey, upsert the storey with size_mm(width_mm, depth_mm) to pin a fixed rectangle; omit it and the storey extent derives from the walls, which leaves only s1.left and s1.top anchorable — anchor everything else to walls you have already placed. Only walls and openings create or divide rooms. Use name_space_at_point(...) to name enclosed floor areas such as bathroom, bedroom, hall, kitchen, or garage after walls and openings define them. Do not create those spaces as objects. Objects are furniture, fixtures, and equipment such as cupboards, sofas, beds, tables, toilets, sinks, and tubs; they may align to walls or surrounding spaces but they do not replace partition walls. Some furniture is directional: feature_side(top|bottom|left|right) is the direction the object FACES — a sofa, armchair, or toilet whose feature_side is left faces (opens toward) the left. To face a window on the left wall, set feature_side=left. Exceptions: a bed's feature_side is its headboard side, and a sink/bath's is its plumbing (wall/tap) side. When updating an existing entity, fetch it first and resend every assertion you want to keep. To change or drop a single field on an existing entity, prefer set_entity_assertions / remove_entity_assertions (merge by key) over resending the entire assertion set. Storey predicates: ceiling_height_mm(number_mm), eye_height_mm(number_mm), size_mm(width_mm, depth_mm). Wall predicates: on_storey(storey_id), kind(value), axis(horizontal|vertical), angle_deg(number), thickness_mm(number_mm), length_mm(number_mm), height_mm(number_mm), attached_to(child_face, target_id, target_face), offset(anchor, reference_anchor, delta_mm). Offsets constrain solved rectangle edges, not wall centerlines, so think in left, right, top, and bottom edges. When you want a gap between two entities, offset the closest pair of edges that represents that gap. Outer walls should usually be positioned relative to storey anchors: offset(left, s1.left, 0) works on every storey, while offset(bottom, s1.bottom, 0) needs an authored size_mm — s1.right, s1.bottom, s1.center_x and s1.center_y are derived edges that a size-less storey does not expose, so anchor to a wall there instead. For interior wall corners, place the first wall from storey anchors, then place the closing wall by sharing a corner with the existing wall instead of pinning both walls absolutely again. Prefer left, right, top, and bottom for wall placement; use start or end only when you explicitly need along-wall anchors, and center for a wall's or opening's along-axis midpoint. A wall keeps its fixed length_mm and will NOT stretch when the storey is resized; to make an outer wall grow or shrink with the storey — which presumes an authored size_mm, since a size-less storey has no fixed edges to follow — anchor BOTH ends to the moving storey edges (for example offset(left, s1.left, 0)+offset(right, s1.right, 0) on a horizontal wall) and omit length_mm. A wall is either axis-aligned (use axis(horizontal|vertical)) or angled (use angle_deg(number) where 0 is east and 90 is north). Never combine axis and angle_deg on one wall. length_mm is always measured along the wall's own direction, and offsets still anchor the wall's axis-aligned bounding box. Opening predicates: on_storey(storey_id), kind(value), length_mm(number_mm), height_mm(number_mm), sill_height_mm(number_mm), head_height_mm(number_mm), host(target_id), hinge(start|end), opens_to(left|right), elevation_mm(number_mm), attached_to(child_face, target_id, target_face), offset(anchor, reference_anchor, delta_mm). Use arch as a friendly alias for archway. Examples (every s1.right/s1.bottom below presumes an authored size_mm on the storey; on a size-less one substitute a wall anchor): a west outer wall uses kind(outer)+axis(vertical)+length_mm(...)+offset(left, s1.left, 0)+offset(top, s1.top, 0); a south outer wall uses kind(outer)+axis(horizontal)+length_mm(...)+offset(left, s1.left, 0)+offset(bottom, s1.bottom, 0); a horizontal partition that closes against an existing vertical wall can use kind(inner)+axis(horizontal)+length_mm(...)+offset(right, w_existing.left, 0)+offset(top, w_existing.top, 0); a 45° diagonal wall of 1 m bounding box uses kind(outer)+angle_deg(45)+length_mm(1414)+thickness_mm(200)+offset(left, w_west.left, 0)+offset(top, w_north.bottom, 0); a single hinged door uses kind(door)+hinge+opens_to; a garden door uses kind(garden_door)+opens_to; a built-in wardrobe / cupboard door uses kind(cupboard_door)+opens_to (no hinge); a garage door uses kind(garage_door) (panel style) or kind(roller_door) (corrugated roll-up) and must not include hinge or opens_to; an archway uses kind(archway) and must not include hinge or opens_to. Object predicates: on_storey(storey_id), kind(value), category(value), movable(boolean), height_mm(number_mm), size_mm(width_mm, depth_mm), connects(target_id), feature_side(top|bottom|left|right), elevation_mm(number_mm), model_3d(value), illustration_id(illustration_id), attached_to(child_face, target_id, target_face), offset(anchor, reference_anchor, delta_mm). Height-level predicates: on_storey(storey_id), kind(value), elevation_mm(number_mm). A storey already owns floor and ceiling levels; create an auxiliary level only to coordinate several wall/opening bounds. Objects attach to physical storey/object/wall hosts, never directly to levels. Surface-finish predicates: on_storey(storey_id), kind(value), thickness_mm(number_mm), pin_mm(x_mm, y_mm), material_asset(material_id), color_hex(#RRGGBB), attached_to(child_face, target_id, target_face), offset(anchor, reference_anchor, delta_mm). A finish requires color_hex or material_asset; colour alone is valid. Search material IDs with search_material_assets. A colour may accompany only a tintable material and then modulates its base colour while physical maps remain unchanged. Wall finishes attach their inward face to one axial wall face and span with offsets; floor/ceiling finishes own pin_mm and attach to storey.floor/storey.ceiling. Vertical placement uses attached_to(child_face,target_id,target_face). Floor objects use attached_to(base,storey_id,floor), stacks use attached_to(base,support_object_id,cap), and ceiling objects use attached_to(cap,storey_id,ceiling). Every object has persisted height_mm; omission materializes the global 1000 mm default. A pendant's height includes its complete canopy/cable/body assembly. Objects must have enough offsets to solve both horizontal and vertical placement: give size_mm(width_mm, depth_mm) plus exactly one horizontal and one vertical anchor — for example offset(center_x, s1.center_x, 0)+offset(center_y, s1.center_y, 0) to center it (s1.center_x/center_y anchors require an authored size_mm; on derived storeys anchor to walls), or offset(left, w1.right, 100)+offset(top, w2.bottom, 100) to seat it off two walls. Do NOT anchor both faces of one axis (left and right, or center_x and left together): size_mm already fixes the width, so a second same-axis anchor is redundant and overconstrains the solve (it then reports infeasible). Objects may share a 2D footprint when vertical attachments place them in a valid stack or at separated heights; actual 3D solid intersections remain invalid. storey_id: the floor this entity is on — required only when the same id exists on more than one floor, or when creating on a plan with several floors.
upsert_entity
Apply many entity creations/updates to one plan in a single call. Intended for blast-then-repair authoring when importing from a floor plan image: emit the whole plan (storey size + all walls + openings + objects) as one batch, read the per-item and global solver report, then resubmit a follow-up batch that only contains the corrections. Items are applied in order against the same in-memory document; if any item fails to apply OR the solver reports infeasible/ambiguous/overlapping storeys, nothing is persisted and the full per-item + per-storey report is returned so you can fix and retry. Prefer create_storey first (so s1 exists) and pass entity_id on items you want to update instead of create. storey_id: the floor this entity is on — required only when the same id exists on more than one floor, or when creating on a plan with several floors.
upsert_entities
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are HZplan alternatives on ChatGPT?
As of 2026-09-28, HZplan competes with Aspicio, Flatma, kernelCAD, OctoEverywhere, Scopture, skima, WalkMyPlan — Floor Plans in 3D in ChatGPT CAD & 3D Design Tool Control, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.