{
  "help": {
    "name": "HouseKraft API",
    "version": "v1",
    "base_url": "https://app.housekraft.co",
    "what_it_is": "Run HouseKraft's interior design tools from your own code: repaint walls, lay new floors, stage or empty rooms (one at a time or up to 50 in one job), change the light or the weather, try many colours, floors and themes on a room at once, and turn photos into 360° panoramas and walkthrough videos. Every tool runs the website's own code, so results, prices and your Library are exactly as on the website.",
    "who_can_use_it": "Accounts with an active paid plan, or that have bought credits.",
    "getting_started": [
      {
        "title": "Make a key",
        "detail": "Sign in and make an API key on the Developers page: https://app.housekraft.co/developers. It is shown once; keep it secret.",
        "command": "export HOUSEKRAFT_KEY=hk_live_…"
      },
      {
        "title": "Check it works",
        "detail": "Shows your credits and plan.",
        "command": "curl -s https://app.housekraft.co/api/v1/account -H \"Authorization: Bearer $HOUSEKRAFT_KEY\""
      },
      {
        "title": "Upload a photo",
        "detail": "The answer has asset.id. Or skip this and put an https:// image URL in 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": "Run a tool",
        "detail": "Quick tools answer with the finished result; slower ones answer with a job to check.",
        "command": "curl -s -X POST https://app.housekraft.co/api/v1/tools/walls/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  \"color_id\": \"sage green\"\n}'"
      },
      {
        "title": "Get the result",
        "detail": "If the answer says \"done\", the link to 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\""
      }
    ],
    "authentication": {
      "header": "Authorization: Bearer hk_live_…",
      "detail": "Every request except help, the tool list, pricing, options and health needs your API key. Keys are made and revoked on the Developers page. A revoked key stops working at once, and so do the file links it got; revoked keys stay listed there.",
      "kinds": [
        {
          "kind": "API key",
          "starts_with": "hk_live_",
          "page": "https://app.housekraft.co/developers",
          "for": "Your own code: this API."
        },
        {
          "kind": "MCP key",
          "starts_with": "hk_mcp_",
          "page": "https://app.housekraft.co/developers/mcp",
          "for": "AI assistants, through the MCP server. It does not work on the API."
        }
      ],
      "developers_page": "https://app.housekraft.co/developers"
    },
    "files": {
      "what": "Every url and thumbnail_url (job outputs, assets) is a HouseKraft file link. It opens the file directly, with no login and no key, for 60 minutes, so you can show it in a page or download it. Keep links private: anyone with one can open it until it expires.",
      "expired": "An expired link answers 410 link_expired: fetch the job or asset again for a fresh one. Revoking the key that got a link stops it at once.",
      "with_your_key": "Or fetch any of your files with your key, no link needed:",
      "command": "curl -s https://app.housekraft.co/api/v1/assets/<asset id>/file   -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" -o result.jpg",
      "keep_it": "To keep a file, download it: links expire, the file stays in your Library."
    },
    "asking_for_more": {
      "what": "If a run is missing what makes it worth running (a style to stage in, a colour, a camera move, a video length…), nothing runs and nothing is charged: the answer is 400 needs_more, with the question to ask (message) and good answers (suggestions).",
      "example": {
        "error": {
          "code": "needs_more",
          "message": "How long should the video be (1–15 seconds)? The length sets the price.",
          "suggestions": [
            "4 seconds: …",
            "6 seconds: …",
            "10 seconds: …"
          ],
          "what_to_do": "Ask the user, then send the run again with it. Nothing ran and nothing was charged."
        }
      },
      "tips": "A finished job can carry tips: ways the same run gets better (a brand paint from the catalogue, a photo of the exact piece…)."
    },
    "running_a_tool": {
      "request": "POST /api/v1/tools/{tool}/run with a JSON body of the tool's inputs. See each tool's help for its inputs.",
      "quick_tools": "walls, flooring, lighting, weather, stage, unstage, dollhouse wait for the result (up to 50 s) and answer 200 with the finished job. If it takes longer they answer 202 with check.url.",
      "slower_tools": "material, furniture, pov, editor, panorama, walkthrough, walkthrough_multi, bulk_stage, bulk_unstage, enmasse answer 202 at once with the job. Fetch GET /api/v1/jobs/{id} until status is \"done\".",
      "wait": "Add \"wait\": true to any run to wait up to 50 s, or \"wait\": false to get the job id straight away.",
      "statuses": {
        "running": "Still working. Fetch again after poll_after_seconds.",
        "done": "Finished. The link to the file is at job.output.url (for tools with several results, in job.outputs). job.tips may suggest how to do even better.",
        "failed": "Did not finish. job.error.message says why, and the credits are refunded (job.credits.refunded = true)."
      },
      "safe_to_disconnect": "A job carries on if your connection drops. GET /api/v1/jobs lists your recent jobs.",
      "several_results": {
        "tools": "bulk_stage, bulk_unstage, enmasse",
        "detail": "These give one result per photo (bulk) or per reference (En Masse). job.output is null; instead job.outputs lists every result in order, each with its own status, output (with url) and error, and job.progress counts them (total, done, failed). En Masse outputs also say which of your references each is for (reference, its place in your list) and its mode.",
        "item_statuses": {
          "waiting": "Queued: bulk photos go to the model a few at a time.",
          "running": "Being worked on.",
          "done": "Ready at output.url.",
          "failed": "Could not be finished; its credits are given back. error says why."
        },
        "partial": "A bulk job where some photos failed still ends \"done\", with those photos marked failed. It ends \"failed\" only if no photo could be finished.",
        "results_as_they_come": "Bulk results appear in outputs one by one while the job runs; En Masse results all arrive at the end.",
        "command": "curl -s https://app.housekraft.co/api/v1/jobs/<job id> -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  | jq -r '.job.outputs[] | \"\\(.index) \\(.status) \\(.output.url // .error)\"'"
      }
    },
    "images": {
      "how": "Each image field takes an asset id or an https:// URL.",
      "upload_a_file": "curl -s -X POST https://app.housekraft.co/api/v1/uploads \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -F image=@room.jpg",
      "upload_from_url": "curl -s -X POST https://app.housekraft.co/api/v1/uploads \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"url\": \"https://example.com/room.jpg\"}'",
      "reuse_a_result": "A finished job's output.id (or outputs[].output.id) is an asset id: send it to another tool to chain edits.",
      "several_photos": "Tools that take several photos (images) or references take a list of asset ids. Upload the photos first: one per request, several in one request (repeat the image field), or a .zip of up to 50 photos. Several files or a .zip answer with assets[] (each with its file name) and skipped[] (files that were left out, and why). Sending the same id twice counts as one photo.",
      "upload_a_zip": "curl -s -X POST https://app.housekraft.co/api/v1/uploads \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -F image=@listing.zip",
      "formats": "JPG, PNG or WEBP, up to 16 MB."
    },
    "chaining": [
      {
        "title": "Restyle a furnished room: unstage, then stage the empty result",
        "steps": [
          "curl -s -X POST https://app.housekraft.co/api/v1/tools/unstage/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"image\": \"<asset id from POST /api/v1/uploads>\"}'",
          "curl -s -X POST https://app.housekraft.co/api/v1/tools/stage/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"image\": \"<output.id from the unstage job>\",\n  \"prompt\": \"Japandi, warm wood and linen\"\n}'"
        ]
      },
      {
        "title": "New walls and a new floor",
        "steps": [
          "curl -s -X POST https://app.housekraft.co/api/v1/tools/walls/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  \"color_id\": \"warm white\"\n}'",
          "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\": \"<output.id from the walls job>\",\n  \"flooring_id\": \"oak_hardwood\"\n}'"
        ]
      },
      {
        "title": "Restage a whole furnished listing: bulk unstage, then bulk stage the results",
        "steps": [
          "curl -s -X POST https://app.housekraft.co/api/v1/tools/bulk_unstage/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"images\": [\"<photo 1>\", \"<photo 2>\", \"<photo 3>\"]}'",
          "curl -s -X POST https://app.housekraft.co/api/v1/tools/bulk_stage/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"images\": [\"<outputs[0].output.id>\", \"<outputs[1].output.id>\", \"<outputs[2].output.id>\"],\n  \"prompt\": \"Scandinavian, light oak\"\n}'"
        ]
      }
    ],
    "checks": [
      "Image check: whether this photo suits this tool (the website's own check). A rejection is 422 image_check_failed with the reason and a fix. Add \"override_check\": true to run 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.",
      "With several photos, every photo is checked before anything starts. If any fails, nothing runs and the error lists each photo that failed (photos[]), so you can replace just those.",
      "Both checks are free: nothing is charged for a rejected request."
    ],
    "credits": {
      "rules": [
        "Each tool is priced exactly as on the website, from the same price table and rules.",
        "Most tools take their credits when the run starts. If the run fails, the credits are refunded automatically and the job shows credits.refunded = true.",
        "Bulk Staging and Bulk Unstaging take credits photo by photo, as each photo starts, and give back the credits of any photo that fails. job.credits shows what was taken (charged) and given back (refunded_credits).",
        "En Masse takes its credits only once its results are ready, so a run that fails costs nothing.",
        "Free: requests rejected before running (invalid inputs, the image check, the misuse check, too many running jobs), uploads, option lists, help, and checking job status as often as you like.",
        "A run you push past the image check with \"override_check\": true is charged like any other run.",
        "Credits come from your account balance: your plan's credits plus any credit packs. API and website use the same balance."
      ],
      "pricing": "https://app.housekraft.co/api/v1/tools/pricing",
      "buy_credits": "https://app.housekraft.co/pricing",
      "check_balance": "https://app.housekraft.co/api/v1/account"
    },
    "limits": {
      "requests_per_minute": 120,
      "runs_per_minute": 10,
      "running_jobs_at_once": 3,
      "image_size_mb": 16,
      "photos_per_bulk_job": 50,
      "bulk_jobs_at_once": 1,
      "enmasse_references": "2–52"
    },
    "errors": {
      "shape": {
        "error": {
          "code": "image_check_failed",
          "message": "The walls are hidden behind furniture.",
          "fix": "Use a photo where more of the walls are visible.",
          "reasons": [
            "walls mostly covered"
          ],
          "can_override": true,
          "hint": "Send the same request with \"override_check\": true to run it anyway."
        }
      },
      "detail": "Every error has the same shape. code is stable, for programs; message is for people.",
      "list": [
        {
          "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": 401,
          "code": "missing_api_key",
          "meaning": "No key in the Authorization header.",
          "what_to_do": "Send Authorization: Bearer hk_live_…"
        },
        {
          "status": 401,
          "code": "invalid_api_key",
          "meaning": "The key is wrong or was revoked.",
          "what_to_do": "Check the key, or make a new one on the Developers page."
        },
        {
          "status": 403,
          "code": "wrong_key_kind",
          "meaning": "An MCP key (hk_mcp_…) was sent to the API. MCP keys work only through the MCP server.",
          "what_to_do": "Use an API key (hk_live_…) from the Developers page."
        },
        {
          "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": 403,
          "code": "paid_plan_required",
          "meaning": "The account has no active paid plan and has never bought credits.",
          "what_to_do": "Subscribe or buy a credit pack."
        },
        {
          "status": 403,
          "code": "account_suspended",
          "meaning": "The account is suspended.",
          "what_to_do": "Contact HouseKraft support."
        },
        {
          "status": 410,
          "code": "link_expired",
          "meaning": "A file link is older than 60 minutes.",
          "what_to_do": "Fetch the job or asset again for a fresh link."
        },
        {
          "status": 409,
          "code": "bulk_job_running",
          "meaning": "You already have a bulk job running (on the website or the API).",
          "what_to_do": "Wait for it to finish, then start the next one."
        },
        {
          "status": 404,
          "code": "unknown_tool",
          "meaning": "No tool with that id.",
          "what_to_do": "See GET /api/v1/tools for the tool ids."
        },
        {
          "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": 404,
          "code": "image_unavailable",
          "meaning": "The asset exists but its file could not be read.",
          "what_to_do": "Upload the photo again."
        },
        {
          "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": "rate_limited",
          "meaning": "Too many requests (120 a minute) or runs (10 a minute) with this key.",
          "what_to_do": "Wait the number of seconds in the Retry-After header."
        },
        {
          "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": 429,
          "code": "busy",
          "meaning": "The API is at capacity right now.",
          "what_to_do": "Try again in about 30 seconds."
        },
        {
          "status": 502,
          "code": "tool_error",
          "meaning": "The tool could not prepare this request.",
          "what_to_do": "Try again. Nothing was charged."
        },
        {
          "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)."
        },
        {
          "status": 503,
          "code": "check_unavailable",
          "meaning": "The safety check could not run just now.",
          "what_to_do": "Try again in a minute. Nothing was charged."
        },
        {
          "status": 503,
          "code": "start_timeout",
          "meaning": "The run took too long to start.",
          "what_to_do": "Try again. Nothing was charged."
        }
      ]
    },
    "endpoints": [
      {
        "method": "GET",
        "path": "/api/v1/help",
        "needs_key": false,
        "what": "This guide."
      },
      {
        "method": "GET",
        "path": "/api/v1/tools",
        "needs_key": false,
        "what": "Every tool, its inputs and its price."
      },
      {
        "method": "GET",
        "path": "/api/v1/tools/pricing",
        "needs_key": false,
        "what": "What each run costs, and how the price is worked out."
      },
      {
        "method": "GET",
        "path": "/api/v1/tools/{tool}",
        "needs_key": false,
        "what": "One tool's inputs."
      },
      {
        "method": "GET",
        "path": "/api/v1/tools/{tool}/help",
        "needs_key": false,
        "what": "One tool in full: inputs, examples in curl, Python and JavaScript, responses, tips."
      },
      {
        "method": "GET",
        "path": "/api/v1/tools/{tool}/options",
        "needs_key": false,
        "what": "The website's option list for a tool (colours, floorings, times of day…)."
      },
      {
        "method": "POST",
        "path": "/api/v1/uploads",
        "needs_key": true,
        "what": "Upload a photo (form field \"image\", or JSON {\"url\": \"https://…\"}): answers with asset.id. Several files, or a .zip of up to 50 photos, answer with assets[]."
      },
      {
        "method": "POST",
        "path": "/api/v1/tools/{tool}/run",
        "needs_key": true,
        "what": "Run a tool. Answers with the job."
      },
      {
        "method": "GET",
        "path": "/api/v1/jobs/{id}",
        "needs_key": true,
        "what": "One job. Fetch it until status is \"done\" or \"failed\"."
      },
      {
        "method": "GET",
        "path": "/api/v1/jobs",
        "needs_key": true,
        "what": "Your recent jobs (?limit=20&offset=0)."
      },
      {
        "method": "GET",
        "path": "/api/v1/assets",
        "needs_key": true,
        "what": "Your Library (?type=generated|uploaded|all&limit=20&offset=0)."
      },
      {
        "method": "GET",
        "path": "/api/v1/assets/{id}",
        "needs_key": true,
        "what": "One Library item."
      },
      {
        "method": "GET",
        "path": "/api/v1/assets/{id}/file",
        "needs_key": true,
        "what": "The file itself, with your key (no link needed)."
      },
      {
        "method": "GET",
        "path": "/api/v1/files/{id}?t=…",
        "needs_key": false,
        "what": "A file link from a job or asset: opens without a key for 60 minutes."
      },
      {
        "method": "GET",
        "path": "/api/v1/account",
        "needs_key": true,
        "what": "Your credits and plan."
      },
      {
        "method": "GET",
        "path": "/api/v1/catalog",
        "needs_key": true,
        "what": "The catalogues: themes, materials, and paint, flooring and furniture brands."
      },
      {
        "method": "GET",
        "path": "/api/v1/catalog/{catalog}",
        "needs_key": true,
        "what": "One page of a catalogue (?search=…&group=…&page=0&per_page=10). Each item says what to send to which tool to use it."
      },
      {
        "method": "GET",
        "path": "/api/v1/catalog/{catalog}/groups",
        "needs_key": true,
        "what": "The brands or categories a catalogue can be narrowed by (for ?group=…)."
      },
      {
        "method": "POST",
        "path": "/api/v1/mcp",
        "needs_key": true,
        "what": "The MCP server, for AI assistants (Claude Code, Cursor, VS Code, Claude Desktop). See \"Use with AI assistants\"."
      },
      {
        "method": "GET",
        "path": "/api/v1/health",
        "needs_key": false,
        "what": "Is the API up."
      }
    ],
    "catalog": {
      "what": "HouseKraft's catalogues, as on the website: browse them, then use an item in a run, such as staging a room in a theme or laying a brand's tile.",
      "catalogs": [
        {
          "id": "themes",
          "what": "Interior design themes: a whole style to stage or restyle a room in."
        },
        {
          "id": "materials",
          "what": "The material library: stones, woods, metals, fabrics and more, for surfaces."
        },
        {
          "id": "paints",
          "what": "Paint colours from paint brands, each with its colour code."
        },
        {
          "id": "flooring",
          "what": "Tiles and floors from flooring brands."
        },
        {
          "id": "furniture",
          "what": "Furniture from furniture brands."
        }
      ],
      "use": "Each item has \"use\": for each tool, the inputs to add to that tool's run, besides the room photo, to apply the item. A theme has use.stage, a tile use.flooring, a paint use.walls, a material use.material, a furniture piece use.furniture. For enmasse, add use.enmasse.reference to the references list.",
      "search": "curl -s \"https://app.housekraft.co/api/v1/catalog/flooring?search=beige%20marble&per_page=5\" \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\"",
      "groups": "curl -s https://app.housekraft.co/api/v1/catalog/paints/groups \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\"",
      "search_tips": "search matches names, brands and categories (\"Kajaria beige\", \"japandi\", \"oak\"). groups lists the brands (or categories) to narrow by: pass one as ?group=…. Pages hold up to 50 items; browsing is free.",
      "example_item": {
        "name": "Japandi Calm",
        "details": "Japandi",
        "image": "https://media.housekraft.co/…/japandi-calm.jpg",
        "thumbnail": "https://media.housekraft.co/…/japandi-calm.jpg",
        "use": {
          "stage": {
            "staging_mode": "reference",
            "reference_image": "https://media.housekraft.co/…/japandi-calm.jpg",
            "theme_name": "Japandi Calm"
          },
          "bulk_stage": {
            "reference_image": "https://media.housekraft.co/…/japandi-calm.jpg"
          },
          "dollhouse": {
            "reference_image": "https://media.housekraft.co/…/japandi-calm.jpg",
            "theme_name": "Japandi Calm"
          },
          "enmasse": {
            "reference": {
              "mode": "themes",
              "image": "https://media.housekraft.co/…/japandi-calm.jpg"
            }
          }
        }
      },
      "run_example": "curl -s -X POST https://app.housekraft.co/api/v1/tools/stage/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  \"staging_mode\": \"reference\",\n  \"reference_image\": \"https://media.housekraft.co/…/japandi-calm.jpg\",\n  \"theme_name\": \"Japandi Calm\"\n}'",
      "in_tools": {
        "what": "Or skip the lookup: give a catalogue item to a tool by its name or its id. A name that matches several items answers 400 choose_one with the options; one that matches nothing, not_in_catalog. Nothing is charged for either.",
        "inputs": [
          {
            "tool": "stage, bulk_stage, dollhouse",
            "input": "theme",
            "catalog": "themes",
            "example": "Japandi Calm"
          },
          {
            "tool": "walls",
            "input": "paint",
            "catalog": "paints",
            "example": "Sage Whisper"
          },
          {
            "tool": "flooring",
            "input": "flooring_product",
            "catalog": "flooring",
            "example": "Kajaria Beige Marble"
          },
          {
            "tool": "material",
            "input": "library_material",
            "catalog": "materials",
            "example": "Calacatta Gold Marble"
          },
          {
            "tool": "furniture",
            "input": "furniture_product",
            "catalog": "furniture",
            "example": "Ekenas Armchair"
          }
        ],
        "example": "curl -s -X POST https://app.housekraft.co/api/v1/tools/walls/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  \"paint\": \"Sage Whisper\"\n}'",
        "theme_category": "stage, bulk_stage and dollhouse also take theme_category (a category from GET /api/v1/catalog/themes/groups): the first theme in it is used, or a random one with \"random_theme\": true.",
        "category_example": "curl -s -X POST https://app.housekraft.co/api/v1/tools/bulk_stage/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"images\": [\"<photo 1>\", \"<photo 2>\"], \"theme_category\": \"Scandinavian\"}'",
        "pick": "En Masse references can pick many items at once: {\"pick\": \"paints\" | \"flooring\" | \"themes\", \"count\": N} with an optional \"group\" (a brand or category), \"search\" (e.g. \"green\") and \"random\": true. If the catalogue has fewer than N, the answer is needs_more with how many there are.",
        "pick_example": "curl -s -X POST https://app.housekraft.co/api/v1/tools/enmasse/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  \"references\": [\n    {\"pick\": \"paints\", \"group\": \"Asian Paints\", \"count\": 5},\n    {\"pick\": \"flooring\", \"group\": \"Kajaria\", \"count\": 3},\n    {\"pick\": \"themes\", \"group\": \"Japandi\", \"count\": 2}\n  ]\n}'"
      }
    },
    "mcp": {
      "what_it_is": "The same tools for AI assistants. Add HouseKraft to Claude Code, Cursor, VS Code or Claude Desktop, then ask in plain words (\"stage this living room in Scandinavian style\"): the assistant picks the tool, runs it on your account and shows you the result.",
      "url": "https://app.housekraft.co/api/v1/mcp",
      "key": "An MCP key, sent as \"Authorization: Bearer hk_mcp_…\". MCP keys are separate from API keys: make one on the MCP keys page (https://app.housekraft.co/developers/mcp); an API key does not work here, and an MCP key does not work on the API. The same paid-plan rule, credits and refunds apply, and results go to the same Library. Revoking the key stops the assistant at once.",
      "setup": [
        {
          "client": "Claude Code",
          "how": "Run this once in a terminal:",
          "config": "claude mcp add --transport http housekraft https://app.housekraft.co/api/v1/mcp \\\n  --header \"Authorization: Bearer $HOUSEKRAFT_MCP_KEY\""
        },
        {
          "client": "Cursor",
          "how": "Add this to ~/.cursor/mcp.json (or .cursor/mcp.json in a project):",
          "config": "{\n  \"mcpServers\": {\n    \"housekraft\": {\n      \"url\": \"https://app.housekraft.co/api/v1/mcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer hk_mcp_…\"\n      }\n    }\n  }\n}"
        },
        {
          "client": "VS Code",
          "how": "Add this to .vscode/mcp.json. VS Code asks for the key the first time:",
          "config": "{\n  \"inputs\": [\n    {\n      \"type\": \"promptString\",\n      \"id\": \"housekraft-mcp-key\",\n      \"description\": \"HouseKraft MCP key\",\n      \"password\": true\n    }\n  ],\n  \"servers\": {\n    \"housekraft\": {\n      \"type\": \"http\",\n      \"url\": \"https://app.housekraft.co/api/v1/mcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer ${input:housekraft-mcp-key}\"\n      }\n    }\n  }\n}"
        },
        {
          "client": "Claude Desktop",
          "how": "Settings → Developer → Edit Config, then add this to claude_desktop_config.json and restart. Needs Node.js:",
          "config": "{\n  \"mcpServers\": {\n    \"housekraft\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote\",\n        \"https://app.housekraft.co/api/v1/mcp\",\n        \"--header\",\n        \"Authorization:${HOUSEKRAFT_AUTH}\"\n      ],\n      \"env\": {\n        \"HOUSEKRAFT_AUTH\": \"Bearer hk_mcp_…\"\n      }\n    }\n  }\n}"
        },
        {
          "client": "Other",
          "how": "Any MCP client that speaks Streamable HTTP: this address, with this header:",
          "config": "https://app.housekraft.co/api/v1/mcp\nAuthorization: Bearer hk_mcp_…"
        }
      ],
      "photos": "Photos are given as an asset id or an https:// link. The assistant can upload a link or base64 data with upload_image, use a photo you uploaded on the website (list_library), or pass on an earlier result.",
      "local_files": "Photos on your computer: just name the file, or a .zip of photos for bulk staging (\"stage ~/Desktop/room.jpg in Japandi style\"). An assistant that can run commands, such as Claude Code, uploads them itself with a one-time link and carries on. Otherwise, or for a photo you pasted into the chat, the assistant gives you a link: drop the photos there and tell it you are done. Links work for 15 minutes, upload only, and stop if the key is revoked.",
      "results": "Quick tools wait and return the finished image, with a small preview the assistant can see, and a link to the file that opens without signing in for 60 minutes. Longer ones (videos, bulk jobs, En Masse) return a job, and the assistant checks it with get_job. If a request is missing something (a style, a colour, a video length), the assistant is told what to ask you first; nothing runs until then.",
      "catalog": "Name catalogue items in plain words: \"paint the walls Sage Whisper\", \"stage it in a Japandi theme\", \"try 10 green paints from Asian Paints on this room\". The assistant finds them in HouseKraft's catalogues and asks if a name matches several.",
      "transport": "Streamable HTTP (JSON-RPC over POST, plain JSON answers, no sessions). Protocol versions 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05.",
      "not_yet": "claude.ai and ChatGPT connectors sign in with OAuth, which is not offered yet.",
      "tools": [
        {
          "name": "walls",
          "title": "Wall Paint",
          "what": "Repaints the walls in the photo with the colour you choose."
        },
        {
          "name": "flooring",
          "title": "Flooring",
          "what": "Replaces the floor with the flooring you choose: wood, tile, marble, vinyl, or a photo of a real product."
        },
        {
          "name": "material",
          "title": "Material",
          "what": "Changes the material of a surface, such as a countertop, a wall, cabinets or a sofa, or of several marked areas at once, each with its own material."
        },
        {
          "name": "furniture",
          "title": "Object Placement",
          "what": "Adds a piece of furniture, swaps one piece for another, or removes a piece."
        },
        {
          "name": "lighting",
          "title": "Time of Day",
          "what": "Relights the room as it would look at another time of day, with the light coming through its windows."
        },
        {
          "name": "weather",
          "title": "Weather",
          "what": "Changes the weather and the view seen through the windows: rain, snow, fog, a beach or a city outside."
        },
        {
          "name": "stage",
          "title": "Stage",
          "what": "Furnishes an empty room: in a style you describe, in the style of a reference photo, or with photos of the exact pieces to use."
        },
        {
          "name": "unstage",
          "title": "Unstage",
          "what": "Removes furniture, rugs and decor, leaving the empty room."
        },
        {
          "name": "pov",
          "title": "POV",
          "what": "Shows the same room from another camera angle: turn left or right, tilt up or down, zoom in or out."
        },
        {
          "name": "editor",
          "title": "AI Annotate",
          "what": "Changes only what you point at."
        },
        {
          "name": "dollhouse",
          "title": "Floor Plan to Dollhouse",
          "what": "Turns a 2D floor plan into a top-down 3D \"dollhouse\" render of the home, furnished in the style you choose."
        },
        {
          "name": "panorama",
          "title": "360° Panorama",
          "what": "Builds a 360° panorama of a room from 1 to 6 photos taken from about the same spot."
        },
        {
          "name": "walkthrough",
          "title": "Walkthrough Video",
          "what": "Turns one room photo into a short video, as if walking through the room."
        },
        {
          "name": "walkthrough_multi",
          "title": "Walkthrough Video (multiple photos)",
          "what": "Makes one continuous video that moves through several photos in order: different rooms of a home, or several views of one room."
        },
        {
          "name": "bulk_stage",
          "title": "Bulk Staging",
          "what": "Stages a whole listing at once: up to 50 empty rooms in one job, every room in the same style."
        },
        {
          "name": "bulk_unstage",
          "title": "Bulk Unstaging",
          "what": "Empties up to 50 furnished rooms in one job: furniture, rugs and decor go, walls, floors, windows and built-ins stay."
        },
        {
          "name": "enmasse",
          "title": "En Masse",
          "what": "Shows one room with many options at once."
        },
        {
          "name": "upload_image",
          "title": "Upload a photo",
          "what": "Add a photo to the user's HouseKraft Library and get its asset id, to use as any tool's image."
        },
        {
          "name": "upload_files",
          "title": "Upload files from the computer",
          "what": "For photos on the user's computer, or a .zip of photos (bulk staging): returns a one-time curl command that uploads them to the user's HouseKraft Library and prints their asset ids (run it on the user's computer), and a link the user can open to drop photos in a browser instead."
        },
        {
          "name": "get_uploads",
          "title": "Photos sent to an upload link",
          "what": "The photos the user uploaded through an upload link from upload_files, with their asset ids."
        },
        {
          "name": "get_job",
          "title": "Get a job",
          "what": "A run's status and result."
        },
        {
          "name": "list_jobs",
          "title": "Recent jobs",
          "what": "The user's recent API and MCP runs, newest first."
        },
        {
          "name": "list_library",
          "title": "Library",
          "what": "The user's HouseKraft Library, newest first: photos they uploaded (on the website or here) and results."
        },
        {
          "name": "get_account",
          "title": "Account",
          "what": "The user's credit balance and plan."
        },
        {
          "name": "browse_catalog",
          "title": "HouseKraft catalogues",
          "what": "Search HouseKraft's catalogues: themes (whole interior styles), materials (stones, woods, metals, fabrics…), paints (paint brand colours, with colour codes), flooring (tiles and floors from flooring brands) and furniture (from furniture brands)."
        },
        {
          "name": "get_tool_options",
          "title": "Tool options",
          "what": "The website's built-in choices for a tool: wall colours, floorings, furniture, staging modes and styles, camera angle presets."
        }
      ],
      "try_it": "curl -s -X POST https://app.housekraft.co/api/v1/mcp \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_MCP_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"tools/list\"}'"
    },
    "tools": [
      {
        "id": "walls",
        "name": "Wall Paint",
        "what_it_does": "Repaints the walls in the photo with the colour you choose. Everything else in the room stays as it is.",
        "credits": 1,
        "credits_label": "1 credit per run",
        "pricing": "1 credit per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": true,
        "required": [
          "image"
        ],
        "required_one_of": [
          "color_id",
          "paint"
        ],
        "example_title": "Paint the walls sage green",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "color_id": "sage green"
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/walls/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  \"color_id\": \"sage green\"\n}'",
        "help": "https://app.housekraft.co/api/v1/tools/walls/help"
      },
      {
        "id": "flooring",
        "name": "Flooring",
        "what_it_does": "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.",
        "credits": 1,
        "credits_label": "1 credit per run",
        "pricing": "1 credit per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": true,
        "required": [
          "image"
        ],
        "required_one_of": [
          "flooring_id",
          "flooring_product"
        ],
        "example_title": "Lay oak hardwood",
        "example_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}'",
        "help": "https://app.housekraft.co/api/v1/tools/flooring/help"
      },
      {
        "id": "material",
        "name": "Material",
        "what_it_does": "Changes the material of a surface, such as a countertop, a wall, cabinets or a sofa, or of several marked areas at once, each with its own material.",
        "credits": 2,
        "credits_label": "2 credits per run",
        "pricing": "2 credits per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": false,
        "required": [
          "image"
        ],
        "required_one_of": [
          "material_id",
          "library_material",
          "custom_prompt",
          "placements"
        ],
        "example_title": "White marble on the kitchen countertop",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "material_id": "white_marble",
          "custom_prompt": "the kitchen countertop"
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/material/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  \"material_id\": \"white_marble\",\n  \"custom_prompt\": \"the kitchen countertop\"\n}'",
        "help": "https://app.housekraft.co/api/v1/tools/material/help"
      },
      {
        "id": "furniture",
        "name": "Object Placement",
        "what_it_does": "Adds a piece of furniture, swaps one piece for another, or removes a piece. Say where with a box on the photo, or let the model choose.",
        "credits": 2,
        "credits_label": "2 credits per run",
        "pricing": "2 credits per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": false,
        "required": [
          "image"
        ],
        "example_title": "Add a walnut side table",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "action": "add",
          "furniture_type": "walnut side table",
          "position": {
            "x": 50,
            "y": 65,
            "width": 22,
            "height": 25
          }
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/furniture/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  \"action\": \"add\",\n  \"furniture_type\": \"walnut side table\",\n  \"position\": {\"x\": 50, \"y\": 65, \"width\": 22, \"height\": 25}\n}'",
        "help": "https://app.housekraft.co/api/v1/tools/furniture/help"
      },
      {
        "id": "lighting",
        "name": "Time of Day",
        "what_it_does": "Relights the room as it would look at another time of day, with the light coming through its windows.",
        "credits": 1,
        "credits_label": "1 credit per run",
        "pricing": "1 credit per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": true,
        "required": [
          "image",
          "time_of_day"
        ],
        "example_title": "Change the light to sunset",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "time_of_day": "sunset"
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/lighting/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  \"time_of_day\": \"sunset\"\n}'",
        "help": "https://app.housekraft.co/api/v1/tools/lighting/help"
      },
      {
        "id": "weather",
        "name": "Weather",
        "what_it_does": "Changes the weather and the view seen through the windows: rain, snow, fog, a beach or a city outside.",
        "credits": 1,
        "credits_label": "1 credit per run",
        "pricing": "1 credit per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": true,
        "required": [
          "image"
        ],
        "required_one_of": [
          "weather",
          "location",
          "custom_prompt",
          "time_of_day"
        ],
        "example_title": "Make it snow outside",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "weather": "snowy"
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/weather/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"image\": \"<asset id from POST /api/v1/uploads>\", \"weather\": \"snowy\"}'",
        "help": "https://app.housekraft.co/api/v1/tools/weather/help"
      },
      {
        "id": "stage",
        "name": "Stage",
        "what_it_does": "Furnishes an empty room: in a style you describe, in the style of a reference photo, or with photos of the exact pieces to use.",
        "credits": 2,
        "credits_label": "2 credits per run",
        "pricing": "2 credits per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": true,
        "required": [
          "image"
        ],
        "example_title": "Stage in a Scandinavian style",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "prompt": "Scandinavian, light oak and linen"
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/stage/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  \"prompt\": \"Scandinavian, light oak and linen\"\n}'",
        "help": "https://app.housekraft.co/api/v1/tools/stage/help"
      },
      {
        "id": "unstage",
        "name": "Unstage",
        "what_it_does": "Removes furniture, rugs and decor, leaving the empty room. Walls, floor, windows and built-ins stay.",
        "credits": 1,
        "credits_label": "1 credit per run",
        "pricing": "1 credit per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": true,
        "required": [
          "image"
        ],
        "example_title": "Empty the room",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>"
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/unstage/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"image\": \"<asset id from POST /api/v1/uploads>\"}'",
        "help": "https://app.housekraft.co/api/v1/tools/unstage/help"
      },
      {
        "id": "pov",
        "name": "POV",
        "what_it_does": "Shows the same room from another camera angle: turn left or right, tilt up or down, zoom in or out.",
        "credits": 2,
        "credits_label": "2 credits per run",
        "pricing": "2 credits per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": false,
        "required": [
          "image"
        ],
        "example_title": "Turn the camera 45° to the left",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "azimuth": -45,
          "elevation": 0,
          "zoom": 5
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/pov/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  \"azimuth\": -45,\n  \"elevation\": 0,\n  \"zoom\": 5\n}'",
        "help": "https://app.housekraft.co/api/v1/tools/pov/help"
      },
      {
        "id": "editor",
        "name": "AI Annotate",
        "what_it_does": "Changes only what you point at. Mark the object with a brush stroke, an arrow or a text note and say what to change; the rest of the photo stays the same.",
        "credits": 3,
        "credits_label": "3 credits per run",
        "pricing": "3 credits per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": false,
        "required": [
          "image"
        ],
        "required_one_of": [
          "prompt",
          "annotations"
        ],
        "example_title": "Describe the change in words",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "prompt": "make the pendant lamp brass"
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/editor/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  \"prompt\": \"make the pendant lamp brass\"\n}'",
        "help": "https://app.housekraft.co/api/v1/tools/editor/help"
      },
      {
        "id": "dollhouse",
        "name": "Floor Plan to Dollhouse",
        "what_it_does": "Turns a 2D floor plan into a top-down 3D \"dollhouse\" render of the home, furnished in the style you choose.",
        "credits": 1,
        "credits_label": "1 credit per run",
        "pricing": "1 credit per run, refunded if the run fails.",
        "output": "image",
        "waits_for_result": true,
        "required": [
          "image"
        ],
        "example_title": "Render a floor plan in a Scandinavian style",
        "example_body": {
          "image": "<asset id of a floor plan>",
          "style": "scandinavian"
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/dollhouse/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"image\": \"<asset id of a floor plan>\", \"style\": \"scandinavian\"}'",
        "help": "https://app.housekraft.co/api/v1/tools/dollhouse/help"
      },
      {
        "id": "panorama",
        "name": "360° Panorama",
        "what_it_does": "Builds a 360° panorama of a room from 1 to 6 photos taken from about the same spot. The result is an equirectangular image (2:1) that any 360° viewer can show.",
        "credits": 3,
        "credits_label": "3 credits per run",
        "pricing": "3 credits per run, refunded if the run fails.",
        "output": "panorama",
        "waits_for_result": false,
        "required": [
          "images"
        ],
        "example_title": "A 360° panorama from three photos",
        "example_body": {
          "images": [
            "<photo 1>",
            "<photo 2>",
            "<photo 3>"
          ]
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/panorama/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"images\": [\"<photo 1>\", \"<photo 2>\", \"<photo 3>\"]}'",
        "help": "https://app.housekraft.co/api/v1/tools/panorama/help"
      },
      {
        "id": "walkthrough",
        "name": "Walkthrough Video",
        "what_it_does": "Turns one room photo into a short video, as if walking through the room.",
        "credits": 10,
        "credits_label": "10 credits for 6 s",
        "pricing": "Priced by video length; see GET /api/v1/tools/pricing. Refunded if the run fails.",
        "output": "video",
        "waits_for_result": false,
        "required": [
          "image"
        ],
        "example_title": "A 6-second walkthrough",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "duration": 6
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/walkthrough/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"image\": \"<asset id from POST /api/v1/uploads>\", \"duration\": 6}'",
        "help": "https://app.housekraft.co/api/v1/tools/walkthrough/help"
      },
      {
        "id": "walkthrough_multi",
        "name": "Walkthrough Video (multiple photos)",
        "what_it_does": "Makes one continuous video that moves through several photos in order: different rooms of a home, or several views of one room.",
        "credits": 2,
        "credits_label": "2 credits for 6 s and 1 photo",
        "pricing": "Priced by video length and number of photos; see GET /api/v1/tools/pricing. Refunded if the run fails.",
        "output": "video",
        "waits_for_result": false,
        "required": [
          "images"
        ],
        "example_title": "A walkthrough of a home from three photos",
        "example_body": {
          "images": [
            "<front of house>",
            "<hallway>",
            "<living room>"
          ],
          "duration": 8,
          "scope": "property"
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/walkthrough_multi/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"images\": [\"<front of house>\", \"<hallway>\", \"<living room>\"],\n  \"duration\": 8,\n  \"scope\": \"property\"\n}'",
        "help": "https://app.housekraft.co/api/v1/tools/walkthrough_multi/help"
      },
      {
        "id": "bulk_stage",
        "name": "Bulk Staging",
        "what_it_does": "Stages a whole listing at once: up to 50 empty rooms in one job, every room in the same style. The photos are worked on a few at a time, and each result is ready as soon as its photo is done.",
        "credits": 2,
        "credits_label": "2 credits per photo",
        "pricing": "2 credits per photo, each refunded if that photo fails.",
        "output": "images",
        "waits_for_result": false,
        "required": [
          "images"
        ],
        "example_title": "Stage three empty rooms in one style",
        "example_body": {
          "images": [
            "<photo 1>",
            "<photo 2>",
            "<photo 3>"
          ],
          "prompt": "Scandinavian, light oak and linen"
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/bulk_stage/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"images\": [\"<photo 1>\", \"<photo 2>\", \"<photo 3>\"],\n  \"prompt\": \"Scandinavian, light oak and linen\"\n}'",
        "help": "https://app.housekraft.co/api/v1/tools/bulk_stage/help"
      },
      {
        "id": "bulk_unstage",
        "name": "Bulk Unstaging",
        "what_it_does": "Empties up to 50 furnished rooms in one job: furniture, rugs and decor go, walls, floors, windows and built-ins stay. Each result is ready as soon as its photo is done.",
        "credits": 1,
        "credits_label": "1 credit per photo",
        "pricing": "1 credit per photo, each refunded if that photo fails.",
        "output": "images",
        "waits_for_result": false,
        "required": [
          "images"
        ],
        "example_title": "Empty three rooms",
        "example_body": {
          "images": [
            "<photo 1>",
            "<photo 2>",
            "<photo 3>"
          ]
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/bulk_unstage/run \\\n  -H \"Authorization: Bearer $HOUSEKRAFT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"images\": [\"<photo 1>\", \"<photo 2>\", \"<photo 3>\"]}'",
        "help": "https://app.housekraft.co/api/v1/tools/bulk_unstage/help"
      },
      {
        "id": "enmasse",
        "name": "En Masse",
        "what_it_does": "Shows one room with many options at once. Send a room photo and 2 to 52 wall colours, floors or themes, and get one result for each. Each result changes only its own part of the room.",
        "credits": 1,
        "credits_label": "1 credit per reference",
        "pricing": "1 credit per reference (each comes back as one image); nothing if the run fails.",
        "output": "images",
        "waits_for_result": false,
        "required": [
          "image",
          "references"
        ],
        "example_title": "Two wall colours and a floor",
        "example_body": {
          "image": "<asset id from POST /api/v1/uploads>",
          "references": [
            {
              "mode": "walls",
              "color": "#8A9A5B"
            },
            {
              "mode": "walls",
              "color": "#F2EFE6"
            },
            {
              "mode": "flooring",
              "image": "<asset id of a floor photo>"
            }
          ]
        },
        "example_curl": "curl -s -X POST https://app.housekraft.co/api/v1/tools/enmasse/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  \"references\": [\n    {\"mode\": \"walls\", \"color\": \"#8A9A5B\"},\n    {\"mode\": \"walls\", \"color\": \"#F2EFE6\"},\n    {\"mode\": \"flooring\", \"image\": \"<asset id of a floor photo>\"}\n  ]\n}'",
        "help": "https://app.housekraft.co/api/v1/tools/enmasse/help"
      }
    ],
    "code": {
      "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 Wall Paint\njob = call(\"POST\", \"/api/v1/tools/walls/run\", json={\n    \"image\": photo,\n    \"color_id\": \"sage green\",\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",
      "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 Wall Paint\nlet { job } = await call(\"POST\", \"/api/v1/tools/walls/run\", {\n  \"image\": photo,\n  \"color_id\": \"sage green\",\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"
    },
    "docs_page": "https://app.housekraft.co/developers/docs"
  }
}
