RoomagenDevelopersGet API key

Reference

Jobs

Create a job, read its state, and check the account balance.

A job is one image through one tool. Creating a job charges immediately and returns before generation finishes; you collect the result by polling or by webhook.

Create a job

POST /v1/jobs

Headers

HeaderTypeRequiredDescription
X-Api-Keystringrequired

Key issued in the developer portal, prefixed rmg_live_. Send it on every request. Keys are stored hashed and shown only once — rotate by creating a new key and revoking the old one. A revoked or disabled key answers 401 key_revoked.

Idempotency-Keystringmax 128 charactersoptional

Client-generated key that makes a retry safe. Repeating a request with the same key and the same body returns the original job — no second charge, no second render — and still answers 201. The same key with a different body is rejected with 409 idempotency_conflict. Keys are scoped to your API key; use a value derived from your own record (for example order-8842-image-1), not a random one, or retries will not match. Longer than 128 characters is rejected with 400 invalid_request rather than truncated.

Request body

ParameterTypeRequiredDescription
toolstringmax 100 charactersrequired

Tool slug, exactly as returned by GET /v1/tools. Marketing names used on roomagen.com are not slugs — GET /v1/tools is the authoritative list, and anything outside it is rejected with invalid_tool.

image_urlstring · urimax 2000 charactersoptional

Public https URL of the input image (JPEG, PNG or WebP, max 20 MB). Provide exactly one of image_url or image_base64 — neither or both is invalid_image. The URL is fetched server-side with a 10-second timeout; private, loopback and link-local hosts are refused, and at most 3 redirects are followed, each re-checked. A non-image Content-Type or a non-2xx status is reported as image_fetch_failed.

image_base64stringoptional

The image inline, base64-encoded, optionally with a data-URI prefix (data:image/png;base64,…). The prefix also selects the stored format; without one the image is treated as JPEG. Max 20 MB decoded, and the whole request body must stay under 30 MB.

optionsobjectoptional

Tool-specific options. For tool handlers, unknown keys are ignored and an out-of-range value for a key the tool does know is rejected with invalid_request.

Some tools require options: virtual-staging requires style, and rejects unknown keys outright rather than ignoring them — including resolution, tier and customPrompt, which do not apply to it.

See x-tool-options at the root of this document for per-tool keys, and x-common-options for the keys every tool handler accepts.

webhook_urlstring · urimax 2000 charactersoptional

Overrides the account webhook for this job. Must be a public https URL; private and loopback hosts are rejected with webhook_url_rejected. Omit it to use the endpoint configured in the developer portal, or to poll instead. See x-webhooks.

Response

201 with the job id and what it cost. A replayed Idempotency-Key also answers 201, re-returning the original job rather than 200.

ParameterTypeRequiredDescription
job_idstring · uuidrequired

Pass this to GET /v1/jobs/{id}. It is also the job_id in webhook payloads.

statusstringrequired

Always processing — the job is accepted, generation has not finished.

processing
images_chargedintegerrequired

Images charged for this job. 0 never appears; a 4K render charges 2.

The status is always processing — the job is accepted, generation has not finished.

Choosing an image source

Provide exactly one of image_url or image_base64. Neither or both is invalid_image.

image_url

A public https URL serving JPEG, PNG or WebP, up to 20 MB.

Roomagen fetches it server-side with a 10-second timeout. Private, loopback and link-local hosts are refused. At most 3 redirects are followed, and each hop is re-checked against the same rules. A non-image Content-Type or a non-2xx status comes back as image_fetch_failed.

image_base64

The image inline, base64-encoded, optionally with a data-URI prefix (data:image/png;base64,…). The prefix also selects the stored format; without one the image is treated as JPEG.

The decoded image may be up to 20 MB, and the whole request body must stay under 30 MB — but that larger body limit applies only when X-Api-Key is present. Anonymous requests are capped at 1 MB and rejected with payload_too_large, which is what you see if the header is missing or misspelled on a large upload.

For anything close to the limit, prefer image_url: it keeps your request small and avoids base64's 33% size overhead.

Idempotency

Send an Idempotency-Key header to make a retry safe after a timeout or a dropped connection.

  • Repeating a request with the same key and the same body returns the original job — no second charge, no second render — and still answers 201.
  • The same key with a different body is rejected with 409 idempotency_conflict. Send the original body to replay, or a new key for a genuinely new request.
  • Keys are scoped to your API key and may be at most 128 characters. Longer is rejected with 400 invalid_request rather than truncated.

Derive the key from your own records

Use a value you can reconstruct, such as order-8842-image-1. A random key that you do not store cannot be replayed, which defeats the point — on retry it looks like a new request and charges again.

Read a job

GET /v1/jobs/{id}, where id is the job_id returned when the job was created. Poll about once every 2–5 seconds; result_urls is empty until status is completed.

curl https://api.roomagen.com/api/v1/jobs/3f1c9d4e-5b2a-4c8e-9f10-7d6a2b3c4e5f \
  -H "X-Api-Key: rmg_live_YOUR_KEY"
Response
200
{
  "job_id": "3f1c9d4e-5b2a-4c8e-9f10-7d6a2b3c4e5f",
  "tool": "virtual-staging",
  "status": "completed",
  "images_charged": 1,
  "result_urls": [
    "https://api.roomagen.com/api/uploads/9c2f7a10-render.jpg"
  ],
  "error": null,
  "created_at": "2026-08-24T09:14:02.114Z",
  "completed_at": "2026-08-24T09:14:39.902Z",
  "processing_ms": 37788
}

A job id belonging to another account is reported as job_not_found — the API never confirms that someone else's id exists.

The job object

ParameterTypeRequiredDescription
job_idstring · uuidrequired
toolstringrequired

The slug that ran.

statusstringrequired

Terminal states are completed and failed; stop polling on either.

processingcompletedfailed
images_chargedintegerrequired

What this job cost at creation time. A failed job is refunded automatically — this field is not rewritten, so treat it as the charge, not the final balance impact.

result_urlsarray<string · uri>required

Rendered images, empty until status is completed. Download and store them; they are served from Roomagen storage and are subject to the account's gallery retention.

errorstring | nullrequired

Human-readable failure reason when status is failed, otherwise null.

created_atstring · date-timerequired
completed_atstring | null · date-timerequired

Set when the job reaches completed or failed.

processing_msinteger | nullrequired

Wall-clock generation time, available once the job is no longer processing.

completed and failed are terminal; stop polling on either. A failed job is refunded automatically, and images_charged is not rewritten — treat it as what was charged at creation, not the net effect on your balance.

Check your balance

GET /v1/account returns the identity of the key and how many images it can still render. Useful as a health check, and for a low-balance alert before a batch run.

curl https://api.roomagen.com/api/v1/account -H "X-Api-Key: rmg_live_YOUR_KEY"
Response
200
{
  "name": "BRANDers AB",
  "status": "active",
  "image_credits": 486
}
ParameterTypeRequiredDescription
namestringrequired

Client name recorded when the key was issued.

statusstringrequired

A disabled key cannot authenticate at all — it is rejected with key_revoked — so in practice this reads active whenever the call succeeds.

activedisabled
image_creditsintegerrequired

Images still renderable with the balance behind this key.

Rate limits

EndpointPer minute
POST /v1/jobs60 req/min
GET /v1/jobs/{id}300 req/min
GET /v1/tools, GET /v1/account120 req/min

Per API key, in a sliding 60-second window, counted separately per route group. Counters live in the API process, so they reset on deploy.

IP backstop

600 req/min

Per client IP across all /v1 routes, applied before the key is identified. A fleet behind one NAT egress IP shares this budget; contact support if you need it raised.

HeaderDescription
X-RateLimit-Limit

Requests allowed in the current window for this route and key.

X-RateLimit-Remaining

Requests left in the current window.

Retry-After

Seconds to wait, sent only with a 429.

When exceeded

429 · rate_limited

Retry after Retry-After seconds. The window slides rather than resetting on a fixed boundary, so a caller that keeps a steady rate under the limit never sees a 429.

Next step

Tools — the five slugs and the options each one accepts.

curl -X POST https://api.roomagen.com/api/v1/jobs \
  -H "X-Api-Key: rmg_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-8842-image-1" \
  -d '{
    "tool": "virtual-staging",
    "image_url": "https://example.com/empty-living-room.jpg",
    "options": { "style": "scandinavian", "roomType": "living-room" }
  }'
Response
201
{
  "job_id": "3f1c9d4e-5b2a-4c8e-9f10-7d6a2b3c4e5f",
  "status": "processing",
  "images_charged": 1
}