{"openapi":"3.1.0","info":{"title":"EasyRoomAI API","version":"1.0.0","description":"Generate room redesigns, virtual staging, 360° panoramas, exterior designs, furniture removal and region edits from your own code.\n\nEvery create call is asynchronous: it returns 202 with an id, and you poll `GET /generations/{id}`.\n\nLimits: 3 tasks in flight, 30 create requests per minute, 120 reads per minute per account. Repeated invalid API keys from one address are throttled.\n\nThe API is part of the Ultra plan. Credits are shared with the web tools, billed at API rates.","termsOfService":"https://www.easyroomai.com/terms"},"servers":[{"url":"https://www.easyroomai.com/api/v1"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Generations"},{"name":"Account"}],"paths":{"/generations":{"post":{"tags":["Generations"],"operationId":"createGeneration","summary":"Create a generation","description":"Credits are charged up front. If the provider cannot take the task you get a 502 and are not charged; a task that fails later is refunded automatically.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"1–64 characters of letters, digits, `_`, `-`, `:` or `.`. Repeating a request with the same key and body returns the original task without charging again; the same key with a different body is a 422. Keys are remembered for 24 hours.","schema":{"type":"string","maxLength":64}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGenerationRequest"}}}},"responses":{"200":{"description":"The `Idempotency-Key` was already used with this exact body; this is the original task (`Idempotent-Replayed: true`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"202":{"description":"Accepted. Poll the `Location` URL.","headers":{"Location":{"schema":{"type":"string"}},"X-Request-Id":{"description":"Quote this if you contact support.","schema":{"type":"string"}},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix seconds when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"400":{"description":"`invalid_request`, `invalid_image`, `invalid_mask` or `empty_mask`. `details.field` or `details.reason` says what.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`invalid_api_key`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"`insufficient_credits`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`subscription_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`request_in_progress`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The body is over the size limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`Content-Type` must be `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`idempotency_key_reuse`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`upstream_error` — not charged, safe to retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Generations"],"operationId":"listGenerations","summary":"List your API generations","description":"Newest first. Only tasks created through the API are listed.","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","description":"The `next_cursor` of the previous page.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of generations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationList"}}}},"400":{"description":"`invalid_request` (`details.field` is `limit` or `cursor`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`invalid_api_key`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`subscription_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/generations/{id}":{"get":{"tags":["Generations"],"operationId":"getGeneration","summary":"Get a generation","description":"Poll this until `status` is `succeeded` or `failed`. A task still pending after one hour is failed and refunded automatically.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^gen_"}}],"responses":{"200":{"description":"The generation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"401":{"description":"`invalid_api_key`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`subscription_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found` — unknown id, or not yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/credits":{"get":{"tags":["Account"],"operationId":"getCredits","summary":"Credit balance","responses":{"200":{"description":"Balance and plan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Credits"}}}},"401":{"description":"`invalid_api_key`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`subscription_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pricing":{"get":{"tags":["Account"],"operationId":"getPricing","summary":"Prices, options and limits","description":"Machine-readable list of every accepted style / room type / subject id, so you never hard-code them.","responses":{"200":{"description":"Pricing and options.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pricing"}}}},"401":{"description":"`invalid_api_key`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`subscription_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Create a key in Settings → API. Send it as `Authorization: Bearer rai_live_…`."}},"schemas":{"ImageInput":{"type":"object","description":"Exactly one of `url` or `base64`. JPEG, PNG or WebP, up to 15 MB, 40 MP, shortest side at least 256 px.","additionalProperties":false,"properties":{"url":{"type":"string","format":"uri","description":"Public https URL on port 443."},"base64":{"type":"string","description":"Plain base64 or a data: URI."}},"oneOf":[{"required":["url"]},{"required":["base64"]}]},"MaskInput":{"description":"A PNG, sent like an image. PNG with the same aspect ratio as the image (3% tolerance). It does not have to be the same pixel size. Transparent pixels (alpha below 128) mark the area to change; opaque pixels stay as they are. A fully opaque mask selects nothing and is rejected with `empty_mask`.","allOf":[{"$ref":"#/components/schemas/ImageInput"}]},"RedesignRequest":{"type":"object","title":"Room redesign","description":"Re-style an existing interior photo in one of our design styles. Costs 10 credits.","additionalProperties":false,"required":["type","image","style","room_type"],"properties":{"type":{"type":"string","const":"redesign"},"prompt":{"type":"string","maxLength":500,"description":"Optional extra direction."},"metadata":{"type":"object","description":"Your own data, echoed back on reads. At most 512 bytes serialized.","additionalProperties":true},"image":{"$ref":"#/components/schemas/ImageInput"},"style":{"type":"string","enum":["modern","minimalist","scandinavian","industrial","bohemian","japandi","mid_century","coastal","farmhouse","art_deco","traditional","contemporary","mediterranean","tropical","rustic","wabi_sabi"]},"room_type":{"type":"string","enum":["living_room","bedroom","kitchen","bathroom","dining_room","home_office","nursery","outdoor","hallway","basement","attic","studio"]},"aspect_ratio":{"type":"string","enum":["1:1","3:2","2:3","4:3","3:4","16:9","9:16"]}},"example":{"type":"redesign","image":{"url":"https://example.com/photos/living-room.jpg"},"style":"japandi","room_type":"living_room","aspect_ratio":"3:2"}},"StagingRequest":{"type":"object","title":"Virtual staging","description":"Furnish an empty room with a staging preset. Costs 15 credits.","additionalProperties":false,"required":["type","image","style","room_type"],"properties":{"type":{"type":"string","const":"staging"},"prompt":{"type":"string","maxLength":500,"description":"Optional extra direction."},"metadata":{"type":"object","description":"Your own data, echoed back on reads. At most 512 bytes serialized.","additionalProperties":true},"image":{"$ref":"#/components/schemas/ImageInput"},"style":{"type":"string","enum":["modern_neutral","transitional","coastal_light","warm_contemporary","luxury_contemporary","mid_century_staged","scandinavian_staged","family_friendly"]},"room_type":{"type":"string","enum":["living_room","bedroom","dining_room","kitchen","home_office","bathroom"]},"aspect_ratio":{"type":"string","enum":["1:1","3:2","2:3","4:3","3:4","16:9","9:16"]}},"example":{"type":"staging","image":{"url":"https://example.com/photos/living-room.jpg"},"style":"modern_neutral","room_type":"living_room"}},"ExteriorRequest":{"type":"object","title":"Exterior design","description":"Restyle the outside of a building or garden. Costs 10 credits.","additionalProperties":false,"required":["type","image","style","subject"],"properties":{"type":{"type":"string","const":"exterior"},"prompt":{"type":"string","maxLength":500,"description":"Optional extra direction."},"metadata":{"type":"object","description":"Your own data, echoed back on reads. At most 512 bytes serialized.","additionalProperties":true},"image":{"$ref":"#/components/schemas/ImageInput"},"style":{"type":"string","enum":["modern_farmhouse","mediterranean_villa","craftsman","contemporary_exterior","colonial","mid_century_exterior","coastal_exterior","mountain_rustic","spanish_revival","scandinavian_exterior","industrial_exterior","tropical_resort"]},"subject":{"type":"string","enum":["facade","front_yard","backyard","patio_deck","garden","pool"]}},"example":{"type":"exterior","image":{"url":"https://example.com/photos/living-room.jpg"},"style":"modern_farmhouse","subject":"facade"}},"Panorama360Request":{"type":"object","title":"360° panorama staging","description":"Stage a full 360° panorama and keep it a valid panorama. Costs 38 credits.","additionalProperties":false,"required":["type","image","style","room_type"],"properties":{"type":{"type":"string","const":"panorama360"},"prompt":{"type":"string","maxLength":500,"description":"Optional extra direction."},"metadata":{"type":"object","description":"Your own data, echoed back on reads. At most 512 bytes serialized.","additionalProperties":true},"image":{"$ref":"#/components/schemas/ImageInput","description":"Must be a 2:1 equirectangular panorama."},"style":{"type":"string","enum":["modern_neutral","transitional","coastal_light","warm_contemporary","luxury_contemporary","mid_century_staged","scandinavian_staged","family_friendly"]},"room_type":{"type":"string","enum":["living_room","bedroom","dining_room","kitchen","home_office","bathroom"]}},"example":{"type":"panorama360","image":{"url":"https://example.com/photos/panorama.jpg"},"style":"modern_neutral","room_type":"living_room"}},"FurnitureRemovalRequest":{"type":"object","title":"Furniture removal","description":"Empty a room automatically, or remove only what you paint over. Costs 10 credits.","additionalProperties":false,"required":["type","image","mode"],"properties":{"type":{"type":"string","const":"furniture_removal"},"prompt":{"type":"string","maxLength":500,"description":"Optional extra direction."},"metadata":{"type":"object","description":"Your own data, echoed back on reads. At most 512 bytes serialized.","additionalProperties":true},"image":{"$ref":"#/components/schemas/ImageInput"},"mode":{"type":"string","enum":["auto_empty","brush"],"description":"`brush` requires `mask`; `auto_empty` must not have one."},"mask":{"$ref":"#/components/schemas/MaskInput"},"floor_material":{"type":"string","maxLength":100}},"example":{"type":"furniture_removal","image":{"url":"https://example.com/photos/living-room.jpg"},"mode":"brush","mask":{"base64":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"}}},"RegionEditRequest":{"type":"object","title":"Region edit","description":"Change only a masked area; every pixel outside it stays exactly as it was. Costs 10 credits.","additionalProperties":false,"required":["type","mask"],"properties":{"type":{"type":"string","const":"region_edit"},"prompt":{"type":"string","maxLength":400,"description":"Required unless `reference_image` is given."},"metadata":{"type":"object","description":"Your own data, echoed back on reads. At most 512 bytes serialized.","additionalProperties":true},"image":{"$ref":"#/components/schemas/ImageInput","description":"Give this or `source_generation_id`, not both."},"source_generation_id":{"type":"string","pattern":"^gen_","description":"One of your own finished `region_edit` results."},"mask":{"$ref":"#/components/schemas/MaskInput"},"reference_image":{"$ref":"#/components/schemas/ImageInput"}},"example":{"type":"region_edit","image":{"url":"https://example.com/photos/living-room.jpg"},"mask":{"base64":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"},"prompt":"Replace the sofa with a green velvet sofa"}},"CreateGenerationRequest":{"oneOf":[{"$ref":"#/components/schemas/RedesignRequest"},{"$ref":"#/components/schemas/StagingRequest"},{"$ref":"#/components/schemas/ExteriorRequest"},{"$ref":"#/components/schemas/Panorama360Request"},{"$ref":"#/components/schemas/FurnitureRemovalRequest"},{"$ref":"#/components/schemas/RegionEditRequest"}],"discriminator":{"propertyName":"type","mapping":{"redesign":"#/components/schemas/RedesignRequest","staging":"#/components/schemas/StagingRequest","exterior":"#/components/schemas/ExteriorRequest","panorama360":"#/components/schemas/Panorama360Request","furniture_removal":"#/components/schemas/FurnitureRemovalRequest","region_edit":"#/components/schemas/RegionEditRequest"}}},"Generation":{"type":"object","required":["id","type","status","credits_charged","created_at","completed_at","metadata"],"properties":{"id":{"type":"string","example":"gen_3f2b8c1e-9d4a-4e6b-8f10-a1b2c3d4e5f6"},"type":{"type":"string","enum":["redesign","staging","panorama360","exterior","furniture_removal","region_edit"]},"status":{"type":"string","enum":["pending","succeeded","failed"]},"credits_charged":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"metadata":{"type":"object","additionalProperties":true},"output":{"type":"object","description":"Present when `status` is `succeeded`.","properties":{"url":{"type":"string","format":"uri"}}},"error":{"type":"object","description":"Present when `status` is `failed`.","properties":{"code":{"type":"string","enum":["generation_failed","timeout"]},"message":{"type":"string"}}},"refunded":{"type":"boolean","description":"Present and true when `status` is `failed`: the credits were returned."}}},"GenerationList":{"type":"object","required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Generation"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page. Opaque."}}},"Credits":{"type":"object","required":["balance","plan","period_end"],"properties":{"balance":{"type":"integer"},"plan":{"type":["string","null"]},"period_end":{"type":["string","null"],"format":"date-time"}}},"Pricing":{"type":"object","description":"Credits per type, accepted fields, the valid ids for every option, and the limits.","additionalProperties":true},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","enum":["invalid_request","invalid_image","invalid_mask","empty_mask","invalid_api_key","insufficient_credits","subscription_required","not_found","request_in_progress","idempotency_key_reuse","rate_limited","too_many_inflight","internal_error","upstream_error","capacity_exceeded","api_disabled"]},"message":{"type":"string"},"request_id":{"type":"string","example":"req_9f8e7d6c5b4a3210"},"details":{"type":"object","additionalProperties":true}}}}}}},"x-error-codes":{"invalid_request":{"status":400,"meaning":"The body is malformed or a field is missing, unknown or out of range. `details.field` names it. Also used for 413 (body too large) and 415 (not JSON)."},"invalid_image":{"status":400,"meaning":"An image could not be used. `details.reason` says why."},"invalid_mask":{"status":400,"meaning":"The mask could not be used (wrong aspect ratio, not a PNG, …)."},"empty_mask":{"status":400,"meaning":"The mask selects nothing."},"invalid_api_key":{"status":401,"meaning":"The key is missing, malformed, unknown or revoked."},"insufficient_credits":{"status":402,"meaning":"Not enough credits. `details.required` and `details.available` say by how much."},"subscription_required":{"status":403,"meaning":"The key is fine, but its owner does not have an active Ultra subscription."},"not_found":{"status":404,"meaning":"No such generation for this account."},"request_in_progress":{"status":409,"meaning":"A request with that `Idempotency-Key` is still being created. Retry in a moment."},"idempotency_key_reuse":{"status":422,"meaning":"That `Idempotency-Key` was already used with a different request body."},"rate_limited":{"status":429,"meaning":"Too many requests. Wait `Retry-After` seconds."},"too_many_inflight":{"status":429,"meaning":"You already have 3 tasks running. Wait for one to finish."},"internal_error":{"status":500,"meaning":"Something went wrong on our side. Quote the `request_id` if you contact support."},"upstream_error":{"status":502,"meaning":"Our image provider could not take the task. You were not charged; retrying is safe."},"capacity_exceeded":{"status":503,"meaning":"The platform is at capacity. Retry after `Retry-After` seconds; you were not charged."},"api_disabled":{"status":503,"meaning":"The API is temporarily switched off. Retry later."}}}