Skip to main content
How to Add Virtual Staging to Your App: A Developer's API Tutorial
Tutorials

How to Add Virtual Staging to Your App: A Developer's API Tutorial

A step-by-step developer tutorial for adding AI virtual staging to any app through a REST API. The code follows Roomagen's documented API: submit a job, verify signed webhooks, poll as a fallback, store results and build in disclosure.

Roomagen
Roomagen Team
August 3, 2026Updated: October 24, 202617 min read3,822 words
Table of Contents(12)

What changed in this update

  • Updated the API section: free API images now carry a Roomagen watermark until a pack is bought, and every tool is documented
  • Added the warnings list that names the options a tool ignores
Earlier updates(1)
    • Corrected every code sample to match Roomagen's live API: camelCase options such as roomType, UUID job IDs, the Idempotency-Key header and the v1= webhook signature computed over the timestamp and raw body
    • Updated access and pricing: self-serve keys since August 30, 2026 instead of a waitlist, 50 free images once on the first key, packs that never expire, and six documented tools instead of 40+
    • Rewrote the disclosure section around AB 723's statement-plus-link-to-original rule, California's AB 2025 for rental ads from January 2027, conflicting MLS label rules and EU AI Act Article 50
    • Added an error-code table, rate limits, a section on what the API does not do and a pre-launch checklist; removed an unsupported market-size figure and the free-regeneration and disclosure-label claims
    • Corrected job timing to the documented 20–60 seconds and the polling interval to 2–5 seconds, with measured median API times for staging, day to dusk and floor plan to 3D

To add virtual staging to an app, your backend sends each room photo to a virtual staging API as an asynchronous job, receives the result by signed webhook or polling, then stores it beside the original with disclosure data.

The tool for this

Free AI Virtual Staging — Stage Any Empty Room in Under a Minute

Stage vacant rooms with AI, usually in under a minute. 13 design styles, 15 room types, listing-ready photos for real estate, rentals and hospitality. 6 free credits, no credit card required.

Try it free

Adding virtual staging to an app comes down to one loop: your backend sends a room photo to a virtual staging API as a job, the provider renders it in the background, and your app collects the result, stores it next to the original and shows it with whatever disclosure the destination needs. This tutorial builds that loop against the Roomagen API, with code in cURL, Node.js and Python, and keeps the design portable to other providers.

The integration at a glance

  1. Send each photo from your server as a job: POST /v1/jobs with an Idempotency-Key.
  2. Store the returned job_id before doing anything else.
  3. Receive the finished job on a signed webhook, and reconcile misses with GET /v1/jobs/{id}.
  4. Re-host the result, keep the original beside it, and carry disclosure data to every export.

What You're Building: Architecture of a Staging Feature

By the end of this tutorial, your app will accept a room photo and show a furnished version of the same room, typically 20 to 60 seconds later according to Roomagen's documentation. Everything else here exists to make that loop reliable at production volume. The same loop serves listing portals, photography delivery platforms, property management software advertising vacant units, interior design apps pitching a concept on a client's own room, and furniture retailers showing a room dressed in a chosen style.

A render takes tens of seconds, far too long to hold an HTTP request open, so the design is asynchronous: submit the job, get a job ID immediately, collect the result later. Staging runs a multi-step pipeline (scene analysis, then prompt, then generation), so it is slower than tools such as day to dusk: in Roomagen's production data for the 30 days to October 1, 2026, the median API staging job took about 45 seconds, against about 25 seconds for day to dusk and for floor plan to 3D.

Stage Where it runs What to get right
Submit the job Your backend → POST /v1/jobs Key stays server-side; Idempotency-Key; store job_id
Render Roomagen Typically 20–60 seconds; staging always renders at 2K
Learn that it finished Webhook (push) plus polling (pull) Verify the signature; de-duplicate on job_id
Store and show Your storage and UI Re-host the result; keep the original; disclose

Roomagen is an AI photo editing and virtual staging platform with 40+ tools in its web app; the API documents every tool it accepts (65 as of October 2026) behind one job endpoint. Its image tools run on Google's Gemini image models, and the API adds the staging pipeline, job handling, automatic refunds for failed renders and signed webhooks.

Before You Start: Keys, Environments, and Image Requirements

Getting a key. Roomagen's API has been self-serve since its public launch on August 30, 2026; there is no waitlist. Sign in to the developer portal with a Roomagen account, verify your email and create a key, as the API quickstart describes. Your first key adds 50 free images, once per account, which is enough to build and test everything in this guide. Until the account buys a pack, every render carries a Roomagen watermark, so use them for building and testing, not for publishing.

Keys are rmg_live_ plus 64 hexadecimal characters, sent in an X-Api-Key header, and the portal shows each key only once. Keep it in a server-side secret store; never ship it in browser JavaScript or a mobile app, where anyone can extract it and spend your balance.

Environments. An account can hold up to 10 active keys, so create one per environment and revoke either without touching the other. There is no test mode: every key is live, and an account's keys share one image balance, one portal webhook and one signing secret, so give development jobs their own per-job webhook_url. There is no rotate button, by design: create the new key, deploy it, then revoke the old one.

Image inputs.

Requirement Roomagen API
Formats and size JPEG, PNG or WebP, up to 20 MB; exactly one of image_url or image_base64
image_url (preferred) Public https URL, fetched while your POST is handled: 10-second timeout, up to 3 redirects, private hosts refused
image_base64 Optional data-URI prefix; the request body may reach 30 MB, but only when X-Api-Key is present (1 MB without it)

Prefer image_url: requests stay small, you skip base64's 33% overhead, and because the fetch happens during your POST, a signed URL only has to be valid for that request. Send the full-size export, one room per frame, shot level and reasonably lit; the model can only keep detail it can see.

Check the balance in code. GET /v1/account returns image_credits, the images your balance can still render. Wire a low-balance alert to it now: when the balance can't cover a job, POST /v1/jobs answers 402 insufficient_credits and no job is created.

Step 1: Submit a Staging Job

One POST creates the job: the tool, the image, options and, optionally, a per-job webhook_url (without one, Roomagen uses the account webhook saved in the portal, or you poll).

curl -X POST https://api.roomagen.com/api/v1/jobs \
  -H "X-Api-Key: $ROOMAGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: photo-4821-3-stage" \
  -d '{
    "tool": "virtual-staging",
    "image_url": "https://cdn.example.com/rooms/4821-3.jpg",
    "options": { "style": "scandinavian", "roomType": "living-room", "mode": "empty-room" },
    "webhook_url": "https://app.example.com/hooks/roomagen"
  }'

The response arrives with status 201, before rendering finishes:

{ "job_id": "3f1c9d4e-5b2a-4c8e-9f10-7d6a2b3c4e5f", "status": "processing", "images_charged": 1 }

The same call from a Node.js 18+ backend:

async function submitStagingJob(photo) {
  const res = await fetch("https://api.roomagen.com/api/v1/jobs", {
    method: "POST",
    headers: {
      "X-Api-Key": process.env.ROOMAGEN_API_KEY,
      "Content-Type": "application/json",
      "Idempotency-Key": `photo-${photo.id}-stage`, // derived from your record, never random
    },
    body: JSON.stringify({
      tool: "virtual-staging",
      image_url: photo.signedUrl,
      options: { style: photo.style, roomType: photo.roomType, mode: "empty-room" },
      webhook_url: "https://app.example.com/hooks/roomagen",
    }),
  });
  const body = await res.json();
  if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
  await db.query(
    "INSERT INTO staging_jobs (photo_id, job_id, status, images_charged) VALUES ($1, $2, 'processing', $3)",
    [photo.id, body.job_id, body.images_charged],
  );
  return body.job_id;
}

Staging is stricter than the other tools. style is required, options are camelCase, and any key outside the list below, including room_type, resolution, tier and customPrompt, is rejected with invalid_request. Staging always renders at 2K and costs one image.

Option Values Default
style (required) modern, scandinavian, industrial, traditional, minimalist, bohemian, mid-century-modern, coastal, farmhouse, luxury, japandi, art-deco, mediterranean, or custom to let the model choose none
roomType 15 types, such as living-room, bedroom, kitchen, home-office, kids-room and terrace inferred from the photo
mode empty-room furnishes a bare room; restyle re-dresses a furnished one; recreate rebuilds it as a different room type empty-room
furnitureMode, furniture ai, or manual with a list such as "sectional sofa, floor lamp, rug" ai
changeWalls, changeFloor true lets the model repaint walls or replace flooring false

The three modes correspond to the staging modes in Roomagen's web app, so your product team can try them before you write code: AI virtual staging for empty rooms, redesign a furnished room for restyle and Room Repurpose for recreate.

Persist before anything else. Save job_id and images_charged against your own record. The Idempotency-Key header (up to 128 characters, scoped to your API key) makes retries safe: the same key with the same body returns the original job, with no second charge. Derive keys from your records so you can rebuild them after a crash, and resend the exact same body: the same key with a different body, including a re-signed image_url, returns 409 idempotency_conflict.

Step 2: Handle Completion — Webhooks vs Polling

There are two ways to learn that a job finished, and production integrations use both.

Dimension Webhooks (push) Polling (pull)
Latency Sent after the job finishes, with no requests from you Up to one interval; Roomagen suggests 2–5 seconds
Infrastructure Public https endpoint that answers 2xx within 10 seconds A worker or scheduler
Reliability At-least-once; 5 attempts over about 36 minutes, then abandoned You control the loop
Best for Production at volume Development, interactive waits, reconciliation

Use webhooks first and polling as the safety net. Roomagen's documentation calls webhooks "an optimisation, not a system of record", because a delivery is abandoned once the retry window ends. Polling alone does not scale either: at a 3-second interval each job costs 20 requests a minute, so the 300-per-minute limit on GET /v1/jobs/{id} covers about 15 jobs polling at once.

Set up the endpoint. Save an https endpoint in the portal's Webhook card, using the final URL because redirects are not followed. Store the whsec_ signing secret like a password, and fire a job.test delivery with Send test event while you get verification right. A per-job webhook_url uses the same secret.

const crypto = require("crypto");
const express = require("express");
const app = express();

// Raw body on this route, mounted before any global express.json()
app.post("/hooks/roomagen", express.raw({ type: "application/json" }), async (req, res) => {
  const ts = req.get("X-Roomagen-Timestamp");
  const sig = req.get("X-Roomagen-Signature");
  if (!ts || !sig) return res.sendStatus(401);

  const expected = "v1=" + crypto
    .createHmac("sha256", process.env.ROOMAGEN_WEBHOOK_SECRET) // whsec_...
    .update(`${ts}.`)
    .update(req.body) // the raw bytes, never re-serialized JSON
    .digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  if (sig.length !== expected.length || !fresh ||
      !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.sendStatus(401);
  }

  const job = JSON.parse(req.body);
  if (job.event === "job.test") return res.sendStatus(200);

  // Idempotent: only the first delivery (or poll) moves the row out of "processing"
  const { rowCount } = await db.query(
    "UPDATE staging_jobs SET status = $1, result_urls = $2, error = $3 WHERE job_id = $4 AND status = 'processing'",
    [job.status, job.result_urls, job.error, job.job_id],
  );
  if (rowCount === 1 && job.status === "completed") {
    await queue.add("store-results", { jobId: job.job_id }); // download off the request path
  }
  res.sendStatus(200);
});

Four details matter:

  1. Verify over the raw bytes. The header is v1= plus a hex HMAC-SHA256 of {X-Roomagen-Timestamp}.{raw body}; re-serialized JSON won't match.
  2. Compare in constant time, then check freshness. Node's timingSafeEqual throws on unequal lengths, so compare lengths first. Rejecting timestamps older than about five minutes blocks replays, the same defence Stripe's webhook documentation describes; keep server clocks synced.
  3. Answer fast. Roomagen waits 10 seconds for a 2xx; anything else, including a timeout, is a failed attempt and is retried.
  4. Be idempotent. Delivery is at-least-once and unordered, and your poller may have finished the job already, so de-duplicate on job_id.

For polling, during development or as a reconciler, the Python version is short:

import os, time, requests

API = "https://api.roomagen.com/api/v1"
HEADERS = {"X-Api-Key": os.environ["ROOMAGEN_API_KEY"]}

def wait_for_job(job_id, interval=3, max_wait=300):
    deadline = time.time() + max_wait
    while time.time() < deadline:
        r = requests.get(f"{API}/jobs/{job_id}", headers=HEADERS, timeout=30)
        r.raise_for_status()
        job = r.json()
        if job["status"] != "processing":
            return job  # "completed" or "failed"; both are final
        time.sleep(interval)
    return None  # still processing: leave it to the reconciler

A job ends as completed, with result_urls, or failed, with a reason in error and an automatic refund; log processing_ms to watch latency. Run a reconciler every minute or two for jobs still processing after a few minutes. Don't mark a slow job as failed just because a timer ran out: it may still complete, and it was charged when created.

Step 3: Deliver Results to Your Users

A completed job returns result_urls, an array of rendered JPEG images. Don't hotlink them.

Re-host the results. Roomagen serves results from its own storage, subject to the account's retention, so a background job should download each image as soon as the job completes, write it to your own bucket (S3, R2, GCS or similar) and serve it from your CDN. Your images then never depend on a provider's retention policy.

Keep the original beside the output. Store the source photo and the staged image as a linked pair. The pair powers a before/after slider, lets users revert, and is what listing disclosure rules ask for (Step 4).

Plan for variance. Generative output varies, and sometimes the sofa is wrong. A re-run is a new job that costs another image, because Roomagen refunds renders that fail, not results a user dislikes. Give each re-run its own Idempotency-Key, such as photo-4821-stage-2: reusing the first key returns the original job, or a 409 if the body changed. Offer a "Try again" button, and decide whether that cost is visible to users or built into your pricing.

Design the waiting and failure states. Staging usually lands within a minute, which is long in interface terms. Design the progress state, the failure state ("the render failed and was refunded") and the retry action before launch.

Step 4: Build Disclosure Into Your Data Model

If your users put staged photos in property ads, disclosure is their legal duty, and your data model decides whether it is easy. Roomagen's API adds no disclosure label or original-and-edited pairing, and Roomagen's terms make disclosure the user's responsibility, so your integration has to carry it.

  • California AB 723, in force since January 1, 2026. A broker or salesperson, or someone acting for them, who uses a digitally altered image in an ad for a property sale must state, on or next to the image, that it was altered, and add a link, URL or QR code to a public site showing the original, unaltered image. California's real estate regulator adds that when an AI tool produces misleading advertising, responsibility rests "with the licensee and their responsible broker, not the technology provider."
  • California rental ads from January 1, 2027. AB 2025 extends similar disclosure to rental advertising, covering housing providers and others, not only licensees.
  • MLS rules disagree on placement. Canopy MLS requires the label on the image itself, not only in captions, while NWMLS will not accept photos with superimposed graphics or text.
  • EU-facing products. Since 2 August 2026, Article 50 of the EU AI Act requires deployers of AI systems that generate or manipulate "deep fake" images (content resembling existing places or objects that would falsely appear authentic) to disclose it, a definition AI-edited photos of a real property can plausibly meet (see our EU AI Act Article 50 guide).

So store disclosure as data, not pixels: a public URL for the original photo, an altered flag with the tool and options used, and a disclosure text and label placement for each destination. Render the label when you export, not when you store.

Tool How AB 723's definition of a digitally altered image applies
virtual-staging Adds furniture, a named element: disclose and link the original
item-removal Removes elements from the image: disclose
virtual-renovation Changes fixtures, flooring, walls or paint color, all named elements: disclose
day-to-dusk The lighting exclusion covers only edits that don't change how the property is represented, and a new viewType replaces the view through windows, a named element: the cautious route is to disclose
image-enhancement Lighting, white balance, color correction and exposure are excluded if they don't change how the property is represented
floor-plan-to-3d Floor plans are a named element; present the render as an illustration, not a photo

This is a summary for builders, not legal advice; our AB 723 guide and MLS photo requirements guide go deeper.

Production Concerns: Rate Limits, Retries, and Credit Budgeting

Rate limits. Per key, in a sliding 60-second window: 60 requests a minute for POST /v1/jobs, 300 for GET /v1/jobs/{id} and 120 for GET /v1/tools and GET /v1/account, plus 600 a minute per client IP, shared by every server behind one NAT address. Successful responses carry X-RateLimit-Limit and X-RateLimit-Remaining; a 429 rate_limited carries Retry-After in seconds, so honour it rather than inventing your own backoff. For bulk work, queue submissions: at 60 jobs a minute, 500 photos take just over eight minutes to submit.

Retries and error codes. Every error has the shape { "error": { "code", "message", "doc_url" } }. Branch on code, never on message:

Response What your code should do
400 invalid_request, invalid_tool Fix the request (option names, values, or a website name used instead of the API slug); don't retry unchanged
400 invalid_image, image_fetch_failed Send exactly one image source, from a public https URL that serves the image directly
401 invalid_api_key, key_revoked Alert a human; don't retry
402 insufficient_credits No job was created: top up, then retry
409 idempotency_conflict Resend the original body, or use a new key for a genuinely new request
413 payload_too_large Switch to image_url, and check that the X-Api-Key header was sent
429 rate_limited, 500 internal_error Wait (Retry-After for a 429), then retry with the same Idempotency-Key
Job status: "failed" Refunded automatically; retry once, and if it fails again, check the photo

Failure economics. A job that fails during generation is refunded automatically, but images_charged on the job is not rewritten, so treat GET /v1/account as the source of truth for your balance. When comparing providers, check failure policies next to price.

Credit budgeting. Roomagen bills the API in images, bought up front in packs from the developer portal:

Pack Images Price Per image
Free grant (first key, once) 50 $0 —
Pilot 500 $125 $0.25
Growth 2,500 $550 $0.22
Scale 10,000 $2,000 $0.20
Volume 50,000+ By arrangement —

Packs never expire and stack. Each documented tool costs one image per render at 2K, resolution: "4K" doubles that on tools that accept it, and staging is always one image. To budget, include re-runs: a platform staging 1,200 photos a month that expects one in ten to be re-run needs about 1,320 images, so a Growth pack lasts almost two months.

Published prices vary widely (checked October 2026): ModelsLab lists $0.02 per interior render, Decor8 $0.20 per image for all API operations, and AI HomeDesign $0.24 per photo with yearly billing or about $0.49 with monthly billing. Compare tool scope, webhooks, failure policy and output quality as well as unit price; our virtual staging API pricing guide and comparison of staging APIs go provider by provider.

An honest caveat on maturity. Roomagen's API is young: version 1 launched on August 30, 2026, with one job endpoint, and its docs have covered every tool it accepts only since October 3, 2026, so it has current conventions but not years of public track record. Breaking changes are announced by email to accounts with an active key before they ship; additive changes can land at any time, so ignore response fields you don't recognise.

Extending the Integration: Other Documented Tools

Once the loop works for staging, a "twilight version" or "remove clutter" button changes only tool and options:

Tool Slug Options
Virtual renovation virtual-renovation style (10 values), roomType (8 values)
AI photo enhancement image-enhancement intensity: light, medium (default) or heavy
Day to dusk photo editing day-to-dusk timeShift (hour 0–23, default 20), viewType, season, hasWindows
Object & item removal item-removal None: omit options to clear obvious clutter, or send tier: "custom" with a customPrompt naming the item
Floor plan to 3D floor-plan-to-3d outputStyle, interiorStyle, furnitureMode, fp3dOutdoor, fp3dLabels, inputType

These tools also accept resolution (2K or 4K), tier (auto, guided or custom) and customPrompt (up to 500 characters). Unlike staging, they ignore unknown keys, naming each one in the response's warnings list, but still reject out-of-range values. For windowless rooms, send hasWindows: false to day to dusk so the prompt drops window and view instructions.

GET /v1/tools returns every slug your key can call, with its cost in images, and every slug it returns is documented. Read the list instead of hardcoding it, and remember that tool names on roomagen.com are not API slugs.

What the API Does Not Do

  • No batch endpoint. One job is one image through one tool; the web app's batch processing is not part of the API, so queue jobs within the rate limits.
  • No disclosure labels. Outputs carry no disclosure label or before/after pair. Renders are watermarked until the account buys a pack, and carry no Roomagen branding after that.
  • No free regeneration. A re-run is a new job and is charged like one.
  • No measurements or CAD files. Outputs are JPEG images, not dimensions, vectors or DWG files. If you need room measurements from photos, a generative image API is the wrong tool.
  • No guaranteed output. Generative models can produce artifacts or inaccuracies, so review output before it reaches a listing.
  • No app-only features. Refine, the Library and the Design Companion chat live in Roomagen's web app.

Common Mistakes in Staging API Integrations

  1. Blocking the request thread. Holding a user's request open while a job renders runs into load-balancer timeouts. Return 202 Accepted from your own API and push updates to the client.
  2. Wrong option names. room_type or resolution on a staging job returns invalid_request; staging wants camelCase keys from its own list, and always style.
  3. Trusting webhooks alone. After about 36 minutes of retries, Roomagen stops trying. Without a reconciler, that job sits in "processing" forever.
  4. Verifying the signature wrongly. Hashing parsed JSON, dropping the timestamp prefix, comparing with === or skipping the freshness check all weaken verification.
  5. A webhook URL that redirects. Plain http and private hosts are refused up front with webhook_url_rejected, and redirects are not followed, so a trailing-slash or www redirect fails every delivery.
  6. Random idempotency keys or re-signed URLs. A key you didn't store can't be replayed, so the retry charges again; a re-signed image_url under the same key returns 409.
  7. Hotlinking result URLs. Results are subject to the provider's retention. Re-host them on completion.
  8. One watermark for every destination. A burned-in "Virtually Staged" label can break MLS rules against superimposed text, and without a link to the original it does not meet AB 723 on its own.

Pre-Launch Checklist

  • The API key lives only on your servers, one per environment.
  • Staging requests always send style and nothing outside the staging option list.
  • Every job has an Idempotency-Key derived from your records, and retries resend the original body.
  • The webhook endpoint is the final https URL, verifies v1= signatures over the raw body, rejects stale timestamps and answers within 10 seconds.
  • A job.test delivery from the portal passes verification.
  • A reconciler checks jobs still processing after a few minutes.
  • Results are re-hosted, each with its original alongside.
  • Disclosure data (original URL, altered flag, per-destination label) reaches every export path.
  • A low-balance alert reads image_credits, and every error code maps to a clear message for your users.

The Bottom Line: Ship the Loop, Then Extend It

Adding virtual staging to an app is a small integration: one POST with an idempotency key, one verified webhook handler backed by a reconciler, and a storage step that keeps the original beside the result. The happy path is a few dozen lines; most of the work is production hardening and disclosure data. None of it is tied to one vendor: asynchronous submission, push-plus-pull completion, re-hosted results and an original/output pair fit any staging API.

To build against the worked example, get a Roomagen API key: the first key includes 50 free images, and the quickstart, webhook guide and error reference cover the details. The same job loop then runs virtual staging, day to dusk, item removal, photo enhancement, virtual renovation and floor plan to 3D behind one integration.

Ready to transform your listings?

Try Roomagen's AI virtual staging for free. Upload your first photo and see the result, usually in under a minute.

Start Free

Frequently Asked Questions

A virtual staging API is a web service that takes a room photo and returns an AI-furnished version of it. Developers call it from their own backend to add staging to listing portals, photography delivery tools, property management software, interior design apps or furniture retail sites.

Roomagen's documentation says jobs are typically ready in 20 to 60 seconds, and staging sits toward the slower end. Because that is too long to hold a request open, submit the job asynchronously and collect the result by webhook, polling every 2 to 5 seconds as a fallback.

Roomagen sells prepaid packs: 500 images for $125 ($0.25 each), 2,500 for $550 ($0.22) and 10,000 for $2,000 ($0.20), and the first key includes 50 free images. Other providers' published prices vary widely, for example $0.02 per render at ModelsLab and about $0.49 per photo with AI HomeDesign's monthly billing.

Yes. Creating your first Roomagen API key adds 50 free images, once per account, which is enough to build and test a complete integration; they are watermarked until you buy a pack. You need a verified email address, and creating additional keys does not add more free images.

Use both. Webhooks deliver the finished job without constant requests, but a delivery can be missed during a deploy or outage, and Roomagen stops retrying after about 36 minutes. Register a webhook for every job and reconcile anything still processing with a status request.

Roomagen's API accepts JPEG, PNG or WebP images up to 20 MB, sent either as a public https image_url or inline as image_base64. A URL keeps requests small, and base64 request bodies may reach 30 MB only when the X-Api-Key header is present.

Not on Roomagen. A job that fails during generation is refunded automatically and shows status failed with a reason. A completed render is charged even if your user dislikes it, so a re-run is a new job that costs another image.

For Roomagen, compute an HMAC-SHA256 of the X-Roomagen-Timestamp value, a dot and the raw request body, using your whsec_ signing secret. Compare "v1=" plus the hex digest with the X-Roomagen-Signature header in constant time, and reject timestamps older than about five minutes.

Often, yes. Since January 1, 2026, California's AB 723 requires real estate licensees' sale ads with digitally altered images to state the alteration and link to the original, and MLS rules differ on label placement. Roomagen's API adds no label, so your app must handle disclosure.

Yes. Roomagen's job endpoint documents every tool it accepts, including virtual staging, virtual renovation, image enhancement, day to dusk, item removal and floor plan to 3D. You change the tool slug and options, while job handling, webhooks and result storage stay the same.
Roomagen

Written by

Roomagen Team

The Roomagen team creates in-depth guides about AI virtual staging, real estate photography, and property marketing strategies to help agents and professionals stay ahead.

Related Articles

Best Virtual Staging APIs 2026: 10 Compared on Price & Scope

Best Virtual Staging APIs 2026: 10 Compared on Price & Scope

We compared ten virtual staging APIs on published price per image, free trials, webhooks, white-label output and failure policies, using vendor pages checked in October 2026. Includes monthly cost scenarios from 500 to 10,000 images.

Read more
Virtual Staging API Pricing 2026: Cost per Image Compared

Virtual Staging API Pricing 2026: Cost per Image Compared

Virtual staging API prices now run from $0.02 to about $0.53 per image. We compare eight providers' published rates, the hidden costs behind them, and what 500, 2,500 and 10,000 images a month really cost.

Read more
AB 723 & MLS Rules: Are AI-Edited Listing Photos Compliant?

AB 723 & MLS Rules: Are AI-Edited Listing Photos Compliant?

California's AB 723 requires a conspicuous disclosure and a path to the original for every AI-altered listing photo, and MLSs such as CRMLS refuse some edits even with a label. Here is what counts as altered, who is liable, what changes for rentals in 2027, and an 8-question photo checker.

Read more
Day-to-Dusk API: Automate Twilight Photos, 5 APIs Compared

Day-to-Dusk API: Automate Twilight Photos, 5 APIs Compared

Day-to-dusk conversion now runs as an API call. We compare five providers on price, options and delivery, walk through a job-and-webhook integration, and cover QA, batch strategy and disclosure.

Read more
Floor Plan & Room Measurement APIs Compared (2026 Prices)

Floor Plan & Room Measurement APIs Compared (2026 Prices)

Only scan-based tools such as CubiCasa and Apple RoomPlan measure the space itself; recognition APIs turn drawings into data, and generative APIs such as Roomagen, ModelsLab and Decor8 render new images from plans and sketches. Here is how they compare on input, output, price and accuracy.

Read more
How Roomagen’s AI Virtual Staging Works: 7 Steps (2026)

How Roomagen’s AI Virtual Staging Works: 7 Steps (2026)

A step-by-step look at what happens to your photo in Roomagen: the scene analysis, the instruction sent to Google’s Gemini image models, measured speeds, credits and refunds, and what you still need to check and disclose.

Read more