{
  "help": {
    "id": "flooring",
    "name": "Flooring",
    "what_it_does": "Lay a new floor: wood, tile, marble, vinyl or a brand sample.",
    "about": "Replaces the floor with the flooring you choose: wood, tile, marble, vinyl, or a photo of a real product. Walls and furniture stay as they are.",
    "the_photo": "A room photo where the floor is clearly visible.",
    "credits": 1,
    "credits_label": "1 credit per run",
    "pricing": "1 credit per run, refunded if the run fails.",
    "output_kind": "image",
    "output": "One image, saved to your Library. The file is at job.output.url.",
    "response": "Waits for the result (up to 50 s) and answers 200 with the finished job. If it takes longer, the answer is 202 with check.url to fetch it from. Send \"wait\": false to get the job id straight away.",
    "waits_for_result": true,
    "how_to_use": [
      {
        "title": "Keep your key in an environment variable",
        "detail": "Make a key on the Developers page. Every request sends it in the Authorization header.",
        "command": "export HOUSEKRAFT_KEY=hk_live_…"
      },
      {
        "title": "Upload your photo",
        "detail": "The answer has asset.id: use it in the run. You can also skip this step and put an https:// image URL straight into the run.",
        "command": "curl -s -X POST https://app.housekraft.co/api/v1/uploads \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -F image=@room.jpg"
      },
      {
        "title": "See the choices (optional)",
        "detail": "The same list the website shows for this tool.",
        "command": "curl -s https://app.housekraft.co/api/v1/tools/flooring/options"
      },
      {
        "title": "Run Flooring",
        "detail": "Waits for the result (up to 50 s) and answers with the finished job.",
        "command": "curl -s -X POST https://app.housekraft.co/api/v1/tools/flooring/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"image\": \"<asset id from POST /api/v1/uploads>\",\n  \"flooring_id\": \"oak_hardwood\"\n}'"
      },
      {
        "title": "Get the result",
        "detail": "If the answer says \"done\", the file is at job.output.url. If it says \"running\", fetch the job until it is done.",
        "command": "curl -s https://app.housekraft.co/api/v1/jobs/<job id> \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\""
      }
    ],
    "inputs": [
      {
        "name": "image",
        "type": "image",
        "required": true,
        "description": "The room photo to work on.",
        "help": "An asset id (from POST /api/v1/uploads or your Library) or an https:// image URL."
      },
      {
        "name": "flooring_id",
        "type": "text",
        "required": false,
        "description": "A flooring from the list (GET /api/v1/tools/flooring/options), a flooring name, or \"custom_upload\" together with flooring_sample."
      },
      {
        "name": "flooring_product",
        "type": "catalog",
        "required": false,
        "description": "Or a tile or floor from a brand in HouseKraft's catalogue, by name or id, e.g. \"Kajaria Beige Marble\" (GET /api/v1/catalog/flooring).",
        "catalog": "flooring",
        "help": "A name or id from the flooring catalogue (GET /api/v1/catalog/flooring). Several matches → the answer lists them to choose from."
      },
      {
        "name": "flooring_sample",
        "type": "image",
        "required": false,
        "description": "Optional. A photo of the tile or flooring to lay.",
        "help": "An asset id (from POST /api/v1/uploads or your Library) or an https:// image URL."
      }
    ],
    "required_one_of": [
      "flooring_id",
      "flooring_product"
    ],
    "options": "GET /api/v1/tools/flooring/options",
    "run_options": {
      "wait": "true or false. Whether the run request waits for the result (up to 50 s). Default for this tool: true.",
      "override_check": "true to run even if the image check rejects the photo. Charged as usual."
    },
    "images": "Each image field takes an asset id (from POST /api/v1/uploads, or an earlier result's output.id) or an https:// URL. JPG, PNG or WEBP, up to 16 MB.",
    "same_as_website": "This tool runs the website's own code: the same prompt, model, settings, price and Library record as the website.",
    "example_request": {
      "title": "Lay oak hardwood",
      "method": "POST",
      "url": "https://app.housekraft.co/api/v1/tools/flooring/run",
      "body": {
        "image": "<asset id from POST /api/v1/uploads>",
        "flooring_id": "oak_hardwood"
      }
    },
    "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/flooring/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"image\": \"<asset id from POST /api/v1/uploads>\",\n  \"flooring_id\": \"oak_hardwood\"\n}'",
    "example_python": "# pip install requests\nimport os\nimport time\n\nimport requests\n\nAPI = \"https://app.housekraft.co\"\nHEADERS = {\"Authorization\": f\"Bearer {os.environ['HOUSEKRAFT_KEY']}\"}\n\n\ndef call(method, path, **kwargs):\n    response = requests.request(method, API + path, headers=HEADERS, timeout=90, **kwargs)\n    data = response.json()\n    if response.status_code >= 400:\n        raise RuntimeError(f\"{data['error']['code']}: {data['error']['message']}\")\n    return data\n\n\ndef upload(path):\n    with open(path, \"rb\") as f:\n        return call(\"POST\", \"/api/v1/uploads\", files={\"image\": f})[\"asset\"][\"id\"]\n\n\n# 1. Upload the photo\nphoto = upload(\"room.jpg\")\n\n# 2. Run Flooring\njob = call(\"POST\", \"/api/v1/tools/flooring/run\", json={\n    \"image\": photo,\n    \"flooring_id\": \"oak_hardwood\",\n})[\"job\"]\n\n# 3. If it is still running, check again until it finishes\nwhile job[\"status\"] == \"running\":\n    time.sleep(job.get(\"poll_after_seconds\", 3))\n    job = call(\"GET\", f\"/api/v1/jobs/{job['id']}\")[\"job\"]\n\nif job[\"status\"] == \"done\":\n    print(\"Result:\", job[\"output\"][\"url\"])\nelse:\n    print(\"Failed:\", job[\"error\"][\"message\"], \"| refunded:\", job[\"credits\"][\"refunded\"])\n",
    "example_javascript": "// Node 18 or newer. Save as run.mjs, then: node run.mjs\nimport { readFile } from \"node:fs/promises\";\n\nconst API = \"https://app.housekraft.co\";\nconst HEADERS = { Authorization: `Bearer ${process.env.HOUSEKRAFT_KEY}` };\n\nasync function call(method, path, body) {\n  const options = { method, headers: { ...HEADERS } };\n  if (body instanceof FormData) {\n    options.body = body;\n  } else if (body) {\n    options.headers[\"Content-Type\"] = \"application/json\";\n    options.body = JSON.stringify(body);\n  }\n  const response = await fetch(API + path, options);\n  const data = await response.json();\n  if (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\n  return data;\n}\n\nasync function upload(path) {\n  const form = new FormData();\n  form.append(\"image\", new Blob([await readFile(path)]), path);\n  return (await call(\"POST\", \"/api/v1/uploads\", form)).asset.id;\n}\n\n// 1. Upload the photo\nconst photo = await upload(\"room.jpg\");\n\n// 2. Run Flooring\nlet { job } = await call(\"POST\", \"/api/v1/tools/flooring/run\", {\n  \"image\": photo,\n  \"flooring_id\": \"oak_hardwood\",\n});\n\n// 3. If it is still running, check again until it finishes\nwhile (job.status === \"running\") {\n  await new Promise((resolve) => setTimeout(resolve, (job.poll_after_seconds || 3) * 1000));\n  ({ job } = await call(\"GET\", `/api/v1/jobs/${job.id}`));\n}\n\nif (job.status === \"done\") console.log(\"Result:\", job.output.url);\nelse console.log(\"Failed:\", job.error.message, \"| refunded:\", job.credits.refunded);\n",
    "more_examples": [
      {
        "title": "A brand tile from the catalogue",
        "body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "flooring_product": "Kajaria Beige Marble"
        },
        "curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/flooring/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"image\": \"<asset id from POST /api/v1/uploads>\",\n  \"flooring_product\": \"Kajaria Beige Marble\"\n}'"
      },
      {
        "title": "Lay your own tile from a photo",
        "body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "flooring_id": "custom_upload",
          "flooring_sample": "<asset id of a tile photo>"
        },
        "curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/flooring/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"image\": \"<asset id from POST /api/v1/uploads>\",\n  \"flooring_id\": \"custom_upload\",\n  \"flooring_sample\": \"<asset id of a tile photo>\"\n}'"
      },
      {
        "title": "Describe the flooring by name",
        "body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "flooring_id": "herringbone oak"
        },
        "curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/flooring/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"image\": \"<asset id from POST /api/v1/uploads>\",\n  \"flooring_id\": \"herringbone oak\"\n}'"
      }
    ],
    "responses": [
      {
        "title": "Finished",
        "status": 200,
        "when": "The run answers like this when the result is ready within 50 s. GET /api/v1/jobs/<id> answers the same once the job is done.",
        "body": {
          "job": {
            "id": "7c1e9b02-5d4a-4e8f-b6a3-2f90d1c4e5a8",
            "tool": "flooring",
            "status": "done",
            "created_at": 1793700000,
            "finished_at": 1793700025,
            "credits": {
              "charged": 1,
              "refunded": false
            },
            "input": {
              "image": "<asset id from POST /api/v1/uploads>",
              "flooring_id": "oak_hardwood"
            },
            "warnings": [],
            "check_overridden": false,
            "output": {
              "id": "a3f0c6d1-8b27-4c5e-9f14-6e2b7d90c3b5",
              "type": "generated",
              "tool": "flooring",
              "url": "https://…/generated/a3f0c6d1-8b27-4c5e-9f14-6e2b7d90c3b5.jpg",
              "thumbnail_url": "https://…/thumbnails/a3f0c6d1-8b27-4c5e-9f14-6e2b7d90c3b5.jpg",
              "width": 1024,
              "height": 1024,
              "created_at": 1793700025,
              "kind": "image"
            },
            "error": null
          }
        }
      },
      {
        "title": "Still running",
        "status": 202,
        "when": "The run answers like this if the result takes longer than 50 s (or you sent \"wait\": false). Fetch check.url until status is \"done\".",
        "body": {
          "job": {
            "id": "7c1e9b02-5d4a-4e8f-b6a3-2f90d1c4e5a8",
            "tool": "flooring",
            "status": "running",
            "created_at": 1793700000,
            "finished_at": null,
            "credits": {
              "charged": 1,
              "refunded": false
            },
            "input": {
              "image": "<asset id from POST /api/v1/uploads>",
              "flooring_id": "oak_hardwood"
            },
            "warnings": [],
            "check_overridden": false,
            "output": null,
            "error": null,
            "poll_after_seconds": 3
          },
          "check": {
            "message": "Still running after 50 seconds. The job carries on; get the result from this URL. Nothing is lost if this connection closes, and GET /api/v1/jobs lists your recent jobs.",
            "url": "https://app.housekraft.co/api/v1/jobs/7c1e9b02-5d4a-4e8f-b6a3-2f90d1c4e5a8",
            "command": "curl -s https://app.housekraft.co/api/v1/jobs/7c1e9b02-5d4a-4e8f-b6a3-2f90d1c4e5a8 -H \"Authorization: Bearer $HOUSEKRAFT_KEY\""
          }
        }
      },
      {
        "title": "Failed",
        "status": 200,
        "when": "GET /api/v1/jobs/<id> answers like this if the model could not finish. The credits are refunded automatically.",
        "body": {
          "job": {
            "id": "7c1e9b02-5d4a-4e8f-b6a3-2f90d1c4e5a8",
            "tool": "flooring",
            "status": "failed",
            "created_at": 1793700000,
            "finished_at": 1793700025,
            "credits": {
              "charged": 1,
              "refunded": true
            },
            "input": {
              "image": "<asset id from POST /api/v1/uploads>",
              "flooring_id": "oak_hardwood"
            },
            "warnings": [],
            "check_overridden": false,
            "output": null,
            "error": {
              "message": "The image model could not finish this request."
            }
          }
        }
      }
    ],
    "checks": [
      "Image check: the website's own check of whether this photo suits this tool. If not, you get 422 image_check_failed with the reason and a fix, and no credits are used. Send the same request with \"override_check\": true to run it anyway.",
      "Misuse check: the photo must show a room, building or plan, and the request must be about the space, not people. A refusal is 422 request_not_allowed and cannot be overridden."
    ],
    "errors": [
      {
        "status": 400,
        "code": "invalid_request",
        "meaning": "A field is missing, unknown, or the wrong type. The message names the field.",
        "what_to_do": "Fix the field and send again. Nothing was charged."
      },
      {
        "status": 400,
        "code": "needs_more",
        "meaning": "The run is missing what the tool needs to be worth running (a style, a colour, a camera move, a video length…). message is the question to ask; suggestions lists good answers.",
        "what_to_do": "Ask the user, then send the run again with the answer. Nothing ran and nothing was charged."
      },
      {
        "status": 400,
        "code": "choose_one",
        "meaning": "A catalogue name matches several items. options lists them (id, name, details).",
        "what_to_do": "Ask which one, then send its name or id. Nothing was charged."
      },
      {
        "status": 400,
        "code": "not_in_catalog",
        "meaning": "No catalogue item (or category) matches that name.",
        "what_to_do": "Search the catalogue (GET /api/v1/catalog/{catalog}?search=…) and use a name or id from it."
      },
      {
        "status": 402,
        "code": "insufficient_credits",
        "meaning": "Not enough credits for this run.",
        "what_to_do": "Buy credits or upgrade your plan, then try again."
      },
      {
        "status": 404,
        "code": "image_not_found",
        "meaning": "An asset id is not in your account, or it has expired.",
        "what_to_do": "Upload the photo again with POST /api/v1/uploads."
      },
      {
        "status": 422,
        "code": "image_check_failed",
        "meaning": "The image check thinks this photo does not suit this tool. The answer has the reason (message, reasons) and a suggested fix.",
        "what_to_do": "Use a better photo, or send the same request with \"override_check\": true to run it anyway. Nothing was charged."
      },
      {
        "status": 422,
        "code": "request_not_allowed",
        "meaning": "The photo is not of a room, building or plan, or the request is about people rather than the space.",
        "what_to_do": "This cannot be overridden. Nothing was charged."
      },
      {
        "status": 429,
        "code": "too_many_running_jobs",
        "meaning": "You already have 3 jobs running.",
        "what_to_do": "Wait for one to finish, then start the next."
      },
      {
        "status": 502,
        "code": "provider_error",
        "meaning": "The image model could not be reached.",
        "what_to_do": "Try again. If credits were taken, they were refunded (the message says so)."
      }
    ],
    "tips": [
      "See GET /api/v1/tools/flooring/options for the flooring list.",
      "To use a product photo, send flooring_id \"custom_upload\" and the photo as flooring_sample.",
      "The floor must be visible in the photo."
    ],
    "help_url": "https://app.housekraft.co/api/v1/tools/flooring/help"
  }
}
