Skip to content

RorlAI API

Generate on-model images from your PIM, your ERP or your own build script. Same credits, same account, same models as the studios.

Quickstart

Five minutes from signup to your first image. Start with a test key: it returns fixture images, completes in about three seconds, and charges nothing.

1. Create a key

Go to Settings, API keys and create one. Pick Test mode while you build. The key is shown once.

2. Submit a generation

curl -X POST https://app.rorlai.app/api/v1/tryon/apparel \
  -H "Authorization: Bearer rrl_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "product_type": "dress",
    "product_images": ["https://cdn.example.com/dress.jpg"],
    "resolution": "2K",
    "aspect_ratio": "3:4"
  }'

# 202 Accepted
# { "data": { "id": "…", "status": "queued", "credits_charged": 0,
#             "estimated_seconds": 3, "sandbox": true }, "request_id": "req_…" }

3. Poll until it finishes

Every capability polls the same endpoint, so you write this loop once.

curl https://app.rorlai.app/api/v1/generations/GENERATION_ID \
  -H "Authorization: Bearer rrl_test_YOUR_KEY"

# 200 OK
# { "data": { "id": "…", "status": "completed",
#             "outputs": [{ "url": "https://…", "width": 2048, "height": 2731 }],
#             "credits_charged": 0, "duration_ms": 3000 }, "request_id": "req_…" }

4. Or take a webhook instead

Pass webhook_url on the request, or set a default on the key. We sign every delivery with HMAC-SHA256 over {timestamp}.{body} and retry five times: after 1m, 5m, 30m, 2h and 6h.

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const age = Math.floor(Date.now() / 1000) - Number(parts.t);
  if (!Number.isFinite(age) || Math.abs(age) > 300) return false;

  const expected = createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex');

  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 ?? '');
  return a.length === b.length && timingSafeEqual(a, b);
}

How it works

  • Async everywhere. Every POST returns a job id in well under a second. Nothing holds a connection open for four minutes.
  • One pattern for every endpoint. Same auth, same submit-and-poll shape, same error envelope. Learn one, ship all of them.
  • Shared credits. One balance across the app and the API, and one ledger to reconcile.
  • A real sandbox. Test keys exercise your whole integration, including the poll loop and webhooks, before you spend a credit.

Endpoints

POST /api/v1/tryon/apparelPut a garment on a model
POST /api/v1/tryon/jewelryPlace jewellery on a body-part model
POST /api/v1/tryon/eyewearPlace eyewear on a face model
POST /api/v1/tryon/shoePlace footwear on a foot model
POST /api/v1/ghost-mannequinInvisible-mannequin render from a flat or worn garment photo
POST /api/v1/product/scenePlace a product in a generated scene
POST /api/v1/background/removeCut the product out of its background
POST /api/v1/upscaleIncrease resolution without inventing detail
POST /api/v1/pose-changeRe-pose an existing on-model image
GET /api/v1/generations/:idPoll any generation
DELETE /api/v1/generations/:idCancel and refund

Resources

GET /api/v1/mannequinsModels, filterable by kind, gender and size
GET /api/v1/posesPoses, filterable by product type
GET /api/v1/backgroundsBackdrops
GET /api/v1/creditsYour balance, shared with the app
GET /api/v1/usageRequests and credits by endpoint

Rate limits

Requests a minute and concurrent generations, by plan. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; a 429 adds Retry-After.

PlanRequests / minConcurrent
free
hobby
seller302
brand305
studio6015

Errors

One envelope everywhere. Write the handler once. Every response carries a request_id, in the body and in the X-Request-Id header — quote it if you contact us.

{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "This request requires 5 credits. Your balance is 2.",
    "type": "billing_error",
    "docs_url": "https://docs.rorlai.app/errors#insufficient_credits",
    "request_id": "req_…"
  },
  "request_id": "req_…"
}
UNAUTHORIZED401authentication_error
KEY_REVOKED401authentication_error
FORBIDDEN403authorization_error
PLAN_NO_API403authorization_error
NOT_FOUND404not_found_error
INVALID_INPUT422invalid_request_error
INSUFFICIENT_CREDITS402billing_error
PLAN_LIMIT403authorization_error
RATE_LIMITED429rate_limit_error
CONCURRENCY_LIMIT429rate_limit_error
MODERATION_BLOCKED422moderation_error
PROVIDER_ERROR502provider_error
PROVIDER_TIMEOUT504provider_error
QUALITY_REJECTED422invalid_request_error
CAPABILITY_UNAVAILABLE503provider_error
URL_EXPIRED403authorization_error
INTERNAL500server_error

Output URLs

Output URLs are signed and valid for seven days, then return 403. Copy the bytes to your own storage as soon as you receive them. Add ?download=1 to any output URL to get it as a file attachment.

OpenAPI 3.1

The full machine-readable spec. Point your generator at it for a typed client in any language.

Download the spec