# Developer Docs DX.GL renders GLB and OBJ-scan models into turntable videos and multi-view datasets (a raw `.gltf` must be packed into a `.glb` first). There are two ways to integrate — a REST API for backend pipelines and an MCP server for AI agents — and both run on one pipeline and one credit system. --- ## Authentication & API Keys You use a single API key (`dxgl_sk_...`) created in [Studio](https://dx.gl/studio) → avatar menu → **API** → **Keys**. By default a key is personal: renders, uploads, and downloads bill your account. You handle user mapping and billing on your side however you want. A key can also be **bound to a team workspace** at creation (the workspace picker in the Create-key form; requires the editor role or above). A bound key operates entirely on that workspace — the whole team's models and renders, billed to the workspace owner's credit pool — and your membership is re-checked on every request, so a removed member's key stops working immediately. `GET /v1/account` tells you which workspace a key is bound to. The REST base URL is `https://api.dx.gl/v1`. Every request sends `Authorization: Bearer dxgl_sk_...` in the header. ### Two Integration Paths | Path | Best for | How it works | |---|---|---| | **REST API** | Production pipelines, backend services | Direct HTTP calls from your server. Deterministic, testable, fully scriptable. | | **MCP (Model Context Protocol)** | Prototyping, ad-hoc tasks, AI-powered workflows | AI agents call DX.GL tools via natural language. Great for internal tooling and rapid iteration. | Both paths use the same underlying API and the same credit system. Many integrators start with MCP for prototyping and add direct REST calls for production. --- ## REST Quick Start Base URL: `https://api.dx.gl/v1` All requests require `Authorization: Bearer dxgl_sk_...` in the header. ### 1. Upload a model ```bash curl -X POST https://api.dx.gl/v1/models \ -H "Authorization: Bearer dxgl_sk_..." \ -F "[email protected]" \ -F 'renderSettings={"aspect":"16:9","bgColor":"#ffffff","length":6}' ``` Response: ```json { "data": { "modelId": "Ab3kF9x2qL1m", "renderId": "Xz7pQ4w8nR2k" } } ``` The upload triggers a free system preview render automatically. If you pass `renderSettings`, a paid render is also queued. ### 2. Ingest from URL (no file upload) ```bash curl -X POST https://api.dx.gl/v1/models/ingest \ -H "Authorization: Bearer dxgl_sk_..." \ -H "Content-Type: application/json" \ -d '{"url": "https://your-cdn.com/model.glb", "renderSettings": {"aspect":"16:9"}}' ``` Same response format. The server downloads the file directly (max 1 GB; the download aborts after 45 seconds without progress, 10-minute ceiling). The URL can be a presigned S3/R2 GET URL — pull directly from your own bucket, no credentials shared. SHA-256 deduplication prevents re-uploading identical files. ### 2b. Staged upload (presigned PUT) For large files, flaky links, or bucket-to-bucket pipelines, upload straight to storage instead of through the API: ```bash # 1. mint a ticket + presigned PUT URL curl -X POST https://api.dx.gl/v1/models/create-upload-url \ -H "Authorization: Bearer dxgl_sk_..." # → { "data": { "url": "https://…", "ticket": "aB3kF9x2qL1m", "expiresIn": 3600, "maxBytes": 1073741824 } } # 2. PUT the GLB (resumable with your own s3 tooling) curl -T product.glb "<url>" # 3. register it — same validation, dedup, and free preview as a direct upload curl -X POST https://api.dx.gl/v1/models/finalize \ -H "Authorization: Bearer dxgl_sk_..." \ -H "Content-Type: application/json" \ -d '{"ticket": "aB3kF9x2qL1m", "filename": "product.glb"}' ``` The response is identical to a direct upload (`{ modelId, renderId }`). GLB only. ### 3. Create additional renders ```bash curl -X POST https://api.dx.gl/v1/renders \ -H "Authorization: Bearer dxgl_sk_..." \ -H "Content-Type: application/json" \ -d '{ "modelId": "Ab3kF9x2qL1m", "renderSettings": { "quality": "standard", "aspect": "1:1", "bgColor": "#000000", "length": 9, "shadows": true, "easing": true } }' ``` Retries are safe: send an `Idempotency-Key` header (any stable string per submit intent) and a timed-out-and-retried POST replays the original render with `deduped: true` instead of charging twice. Works on batch too — one key covers the whole batch. ### 4. Batch render (multiple variants at once) ```bash curl -X POST https://api.dx.gl/v1/renders/batch \ -H "Authorization: Bearer dxgl_sk_..." \ -H "Content-Type: application/json" \ -d '{ "renders": [ { "modelId": "Ab3kF9x2qL1m", "renderSettings": { "aspect": "16:9", "bgColor": "#ffffff" } }, { "modelId": "Ab3kF9x2qL1m", "renderSettings": { "aspect": "1:1", "bgColor": "#000000" } }, { "modelId": "Ab3kF9x2qL1m", "renderSettings": { "aspect": "9:16", "bgColor": "#ffffff" } } ] }' ``` Maximum 100 renders per batch. Credits deducted atomically. Same-model variants that share dimensions and scene settings (e.g. background-color variants at one aspect) render their GPU frames once and encode in parallel; variants that change the framing render separately. ### 5. Poll for completion ```bash curl https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k \ -H "Authorization: Bearer dxgl_sk_..." ``` Status progression: `pending` → `poster-processing` → `video-processing` → `done` (any status can move to `error`; `poster-done` is a rare recovery state, not part of the happy path — poll for `done` or `error` and treat the rest as opaque "in progress"). Errored renders carry `failureCode` (machine-readable) alongside `errorMessage`. ### 6. Download assets ```bash # Full video curl -o video.mp4 https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/video \ -H "Authorization: Bearer dxgl_sk_..." # Web-optimized variant (lower bitrate, faster loading) curl -o web.mp4 "https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/video?quality=web" \ -H "Authorization: Bearer dxgl_sk_..." # Poster image (full resolution) curl -o poster.png "https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/poster?quality=full" \ -H "Authorization: Bearer dxgl_sk_..." # All assets as ZIP curl -o assets.zip https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k/bundle \ -H "Authorization: Bearer dxgl_sk_..." ``` All asset endpoints return `Cache-Control: immutable` — renders never change after completion. Safe to cache aggressively. --- ## Python Example ```python import requests, time, json API = "https://api.dx.gl/v1" HEADERS = {"Authorization": "Bearer dxgl_sk_..."} def render_model(glb_path, settings): """Upload a model, render it, return the video URL.""" with open(glb_path, "rb") as f: r = requests.post(f"{API}/models", headers=HEADERS, files={"file": f}, data={"renderSettings": json.dumps(settings)}) data = r.json()["data"] render_id = data["renderId"] # Poll until done while True: r = requests.get(f"{API}/renders/{render_id}", headers=HEADERS) status = r.json()["data"]["status"] if status == "done": return f"{API}/renders/{render_id}/video" if status == "error": raise Exception(f"Render failed: {r.json()['data'].get('errorMessage')}") time.sleep(3) # Usage url = render_model("chair.glb", {"aspect": "16:9", "bgColor": "#ffffff", "length": 6}) ``` ## Node.js Example ```javascript const API = 'https://api.dx.gl/v1'; const headers = { 'Authorization': 'Bearer dxgl_sk_...' }; async function renderModel(filePath, settings) { // Node 20+: openAsBlob pairs with the built-in fetch/FormData // (a ReadStream would be stringified, not streamed) const form = new FormData(); form.append('file', await require('fs').openAsBlob(filePath), 'model.glb'); form.append('renderSettings', JSON.stringify(settings)); const upload = await fetch(`${API}/models`, { method: 'POST', headers, body: form }); const { renderId } = (await upload.json()).data; // Poll until done while (true) { await new Promise(r => setTimeout(r, 3000)); const res = await fetch(`${API}/renders/${renderId}`, { headers }); const { status, errorMessage } = (await res.json()).data; if (status === 'done') return `${API}/renders/${renderId}/video`; if (status === 'error') throw new Error(`Render failed: ${errorMessage}`); } } ``` --- ## Common Patterns ### Catalog Sync Upload new SKUs nightly from your product database. Tag each upload with a client or campaign identifier for easy filtering. ```python for sku in new_skus: r = requests.post(f"{API}/models/ingest", headers=HEADERS, json={ "url": sku["glb_url"], "renderSettings": {"aspect": "16:9", "bgColor": "#ffffff", "length": 6} }) model_id = r.json()["data"]["modelId"] # Tag the model requests.patch(f"{API}/models/{model_id}", headers=HEADERS, json={ "title": sku["name"], "sku": sku["sku_code"], "tags": ["client-acme", "fall-2026"] }) ``` ### Variant Matrix Generate every combination of aspect ratio, background, and quality for each model: ```python variants = [ {"aspect": "16:9", "bgColor": "#ffffff", "shadows": True}, {"aspect": "1:1", "bgColor": "#000000", "shadows": True}, {"aspect": "9:16", "bgColor": "#ffffff", "reflector": True}, ] renders = [] for model_id in model_ids: for v in variants: renders.append({"modelId": model_id, "renderSettings": v}) # Submit all at once (max 100 per batch) for batch in chunks(renders, 100): r = requests.post(f"{API}/renders/batch", headers=HEADERS, json={"renders": batch}) ``` ### Background Image Branding Upload your client's branded background once, then reference it across all renders: ```bash # Upload the background image curl -X POST https://api.dx.gl/v1/overlays \ -H "Authorization: Bearer dxgl_sk_..." \ -F "[email protected]" \ -F "layer=background" \ -F "name=Acme Brand Gradient" # Use it in renders curl -X POST https://api.dx.gl/v1/renders \ -H "Authorization: Bearer dxgl_sk_..." \ -H "Content-Type: application/json" \ -d '{"modelId": "...", "renderSettings": {"bgImageId": "overlay_id_here", "aspect": "16:9"}}' ``` The image is scaled to cover the output dimensions and cropped from the `bgAlign` anchor (nine positions, default `top-left`), keeping logos safe across all aspect ratios. Before re-uploading an asset you already have, `POST /v1/overlays/resolve` with the file's SHA-256 returns the existing id. ### Custom Materials & Environments Two more asset kinds slot into render settings the same way. **Textures** replace a material's base-color map — mint one, then key it to a material by name: ```bash curl -X POST https://api.dx.gl/v1/assets/textures \ -H "Authorization: Bearer dxgl_sk_..." \ -F "[email protected]" -F "name=Red colorway" # → { "data": { "id": "tex_id" } } (re-POSTing identical bytes returns the same id) curl -X POST https://api.dx.gl/v1/renders \ -H "Authorization: Bearer dxgl_sk_..." \ -H "Content-Type: application/json" \ -d '{"modelId": "...", "renderSettings": {"materialEdits": {"woven": {"mapAssetId": "tex_id"}}}}' ``` Material and mesh names come from `GET /v1/models/{id}` — it returns `materialNames` (the keys `materialEdits` matches), `meshNames` (for `hiddenMeshNames`, which hides named meshes per render), and `animations`. Names are exact-match; a wrong `mapAssetId` silently keeps the original map, so verify against the inventory. **Environments** are custom HDRIs for `envId`, uploaded as the pre-encoded gainmap triple ([gainmap-js](https://github.com/MONOGRID/gainmap-js) `encodeAndCompress` output — or upload through Studio's Environment panel and reuse the id). A bad `envId` fails the render loudly with a refund. `POST /v1/assets/envs`, `GET /v1/assets/envs`, quotas apply — full details in the [API reference](/docs/api). ### Tags for Multi-Tenant Organization Tags organize models across clients, projects, and campaigns. Every model can have up to 20 tags (lowercased, trimmed). ``` client-{name} → per-client isolation (client-acme, client-widgets-inc) campaign-{name} → per-campaign grouping (campaign-fall-2026) batch-{id} → per-import-batch tracking status-{state} → workflow state (status-pending-review, status-approved) ``` ```bash # List all models for a specific client curl "https://api.dx.gl/v1/models?tags=client-acme&limit=100" \ -H "Authorization: Bearer dxgl_sk_..." # List models matching any of multiple tags (OR filter) curl "https://api.dx.gl/v1/models?tags=client-acme,campaign-fall-2026" \ -H "Authorization: Bearer dxgl_sk_..." ``` Lists also take `status=active|archived|any`, keyset `cursor` pagination for large catalogs, and `updatedSince=<ISO 8601>` for cheap delta polling — poll renders with `updatedSince` + `status=done` to sweep completions across a big batch in one call. To calculate spend per client, list their models, count their renders, and total each render's credit cost — video is billed per second by quality tier, datasets are flat per set. The `quote` endpoint can help estimate future costs. --- ## Credit Management ### Check Balance ```bash curl https://api.dx.gl/v1/account \ -H "Authorization: Bearer dxgl_sk_..." # Response: { "data": { "credits": 847, "paid": 847, "free": 0, "total": 847, # "scopes": ["read", "render"], "workspace": null } } ``` For a workspace-bound key, `workspace` names the bound workspace and the balances are its owner's pool — the one that funds this key's renders. ### Quote Before Committing ```bash curl -X POST https://api.dx.gl/v1/quote \ -H "Authorization: Bearer dxgl_sk_..." \ -H "Content-Type: application/json" \ -d '{"renders": [{"quality": "standard"}, {"quality": "standard"}, {"quality": "4k"}]}' # Response: { "data": { "creditsRequired": 144, "creditsAvailable": 847, "sufficient": true } } ``` Always quote large batches before submitting. The quote is free and returns per-item breakdown, total cost, and whether you have enough credits. Batch renders deduct atomically — if you don't have enough credits, nothing is charged. Credit packs are $9.90, $79, and $490. Build a simple dashboard that checks `GET /v1/account` periodically and alerts when credits drop below a threshold. Contact us for volume pricing at scale. --- ## Asset Delivery Every completed render produces four assets: the full video, a web-optimized variant, a thumbnail, and a poster. ### Public Share URLs Every completed render has a public share page at `https://dx.gl/portal/v?id={renderId}`. No authentication required for viewing. Useful for sharing with stakeholders or embedding in emails. ### Direct Download All asset endpoints support `Range` headers for streaming and return `Cache-Control: immutable`. Assets per render: | Endpoint | Format | Use case | |---|---|---| | `GET /v1/renders/:id/video` | MP4 or MOV (Pro) | Full-quality download | | `GET /v1/renders/:id/video?quality=web` | MP4 (lower bitrate) | Web embedding, streaming | | `GET /v1/renders/:id/poster?quality=full` | PNG | Product page hero, social sharing | | `GET /v1/renders/:id/poster` | PNG (thumbnail) | Grid previews, hover states | | `GET /v1/renders/:id/thumb` | MP4 (quarter res) | Hover video preview | | `GET /v1/renders/:id/bundle` | ZIP | Bulk download of all assets | | `GET /v1/renders/:id/download-url` | Presigned URL | Large files (ProRes, dataset ZIPs) — no auth header on the download | | `GET /v1/models/:id/file-url` | Presigned URL | Source GLB, direct from storage | ### Embedding The web video variant is optimized for `<video>` tags: ```html <video autoplay loop muted playsinline> <source src="https://api.dx.gl/v1/renders/{id}/video?quality=web" type="video/mp4"> </video> ``` --- ## MCP for AI Agents The MCP server lets AI agents interact with DX.GL through tool calls. Add this to your MCP client configuration (Windsurf, Claude Desktop, Cursor, or any MCP-compatible host): ```json { "mcpServers": { "dxgl": { "serverUrl": "https://mcp.dx.gl/", "headers": { "Authorization": "Bearer dxgl_sk_..." } } } } ``` Replace `dxgl_sk_...` with your API key from [Studio](https://dx.gl/studio) → avatar menu → **API** → **Keys**. ### Your First Prompt Paste this into your AI agent to verify everything works: > List my models and tell me how many I have. The agent will call `list_models` and return a summary. If this works, you're connected. ### Prompt Examples **Render a single model in multiple formats (video):** > Render model Ab3kF9x2qL1m in all three aspect ratios (16:9, 1:1, 9:16) with a white background, 9 seconds, shadows enabled. The agent calls `create_batch_renders` with 3 variants and polls until all complete. **Full catalog in multiple variants (video):** > I just uploaded 30 shoe models tagged "ss26". Render each one in 16:9 white, 1:1 black, and 9:16 with shadows. Quote the total cost first. The agent will: 1. Call `list_models` with `tags: "ss26"` to find the 30 models 2. Call `quote` with 90 renders (30 models × 3 variants) to show total cost 3. Wait for your confirmation 4. Call `create_batch_renders` to submit all 90 at once 5. Credits are deducted atomically — if you don't have enough, nothing is charged 6. Poll and return video URLs when complete **Generate a dataset for one model:** > Generate a 196×1024 hemisphere dataset for model Ab3kF9x2qL1m. The agent calls `create_render` with `output: "dataset"`, `datasetQuality: "196x1024"`, `coverage: "hemisphere"`, then polls `get_render` until done, and returns the download URL. **Generate datasets for all models with a tag:** > Generate 100×800 hemisphere datasets for every model tagged "validation-set". Quote the cost first and wait for my approval before proceeding. The agent will: 1. Call `list_models` with `tags: "validation-set"` to find matching models 2. Call `quote` with the dataset configuration to show the total credit cost 3. Wait for your confirmation 4. Call `create_batch_renders` to submit all datasets at once 5. Poll `get_render` for each until complete 6. Return download URLs for all ZIPs ### Available Tools 37 tools covering the full API surface. **Models** | Tool | Description | |---|---| | `ingest_model` | Import a 3D model from a URL (public or presigned S3/R2) | | `upload_model` | Upload a GLB from bytes — local file path (stdio) or base64 | | `list_models` | List models — tag filter, `updatedSince` delta polling, cursor pagination | | `get_model` | Get model details and all its renders | | `update_model` | Update a model's title, SKU, and/or tags | | `archive_model` / `unarchive_model` | Hide / restore a model in the library | | `delete_model` | Soft-delete a model | | `check_hash` | Check whether a SHA-256 already exists (dedup pre-flight) | | `get_model_file` | Download URL for a model's original GLB | | `get_model_file_url` | Presigned GLB URL — downloads direct from storage, no auth header | **Renders** | Tool | Description | |---|---| | `create_render` | Create a single render (video or dataset) | | `create_batch_renders` | Create multiple renders atomically (max 100) | | `list_renders` | List render jobs — status filter, `updatedSince` delta polling | | `get_render` | Check render status (poll until done) | | `cancel_render` | Cancel a still-queued render and refund its credits | | `dismiss_render` | Acknowledge an errored render | | `delete_render` | Permanently delete a finished render and its assets | | `quote` | Estimate credit cost before committing | **Billing** | Tool | Description | |---|---| | `get_account` | Check credit balance | | `list_products` | List purchasable credit packs | | `get_purchases` | Credit ledger — purchases, spends, refunds | **Downloads** | Tool | Description | |---|---| | `download_render` | Get download URL for a completed render | | `get_download_url` | Presigned URL for any render asset — downloads direct from storage, no auth header | | `get_poster` | Poster image (PNG) URL | | `get_thumb` | Thumbnail loop video (MP4) URL | | `download_bundle` | ZIP of all assets for a completed render | | `download_dataset` | ZIP of a dataset render's multi-view images | | `get_hls` | HLS streaming status and playback URLs | **Overlays** | Tool | Description | |---|---| | `upload_overlay` | Upload a PNG/JPEG/WebP as a reusable overlay asset (background, foreground, decal, or texture layer) | | `list_overlays` | List overlay assets — ids feed backgrounds, decals, material edits | | `delete_overlay` | Delete an overlay asset you own | **Textures & Environments** | Tool | Description | |---|---| | `upload_texture` | Mint a base-color replacement texture — id feeds `materialEdits.<material>.mapAssetId` | | `upload_env` | Upload a custom HDRI (pre-encoded gainmap triple) — id feeds `envId` | | `list_assets` | List the workspace's texture or environment assets | | `delete_asset` | Delete an owned texture/environment asset | | `resolve_asset_sha` | Reuse an existing asset by content SHA-256 instead of re-uploading | **Team workspaces:** there's no MCP workspace configuration — the key implies the workspace. A workspace-bound key (minted in Studio) makes every tool operate on that workspace, billing its owner. ### Tips **Tag everything.** Tags are the key to efficient batch operations. Tag models on upload or in Studio, then target them in prompts: > Render all models tagged "new-arrivals" in 16:9 white with shadows. **Quote first on large batches.** The `quote` tool prevents surprises. Ask the agent to quote before any batch: > Quote 16:9 + 1:1 + 9:16 renders for my 80 shoe models, then proceed only if it's under 250 credits. **Atomic credit deduction.** Batch renders deduct all credits at once. If you don't have enough for the full batch, nothing is charged — the batch fails cleanly and you can adjust. **Presigned URLs for large files.** `get_download_url` (render assets) and `get_model_file_url` (source GLBs) return short-lived URLs that download directly from storage with no `Authorization` header — use them for ProRes videos, dataset ZIPs, or handing a download to another system. --- ## Tiers & Render Settings ### Video Quality Tiers | Tier (API value) | Label | Resolution | Codec | Alpha | Credits/sec | Best for | |---|---|---|---|---|---|---| | `share` | Standard | 960×540 | H.264 MP4 | No | 1 | Quick shares, web, messaging, social | | `standard` | HD | 1920×1080 | H.264 MP4 | No | 4 | Web, social media, product pages | | `4k` | 4K | 3840×2160 | H.264 MP4 | No | 16 | In-store displays, kiosks, high-DPI screens | | `pro` | ProRes 4444 | 3840×2160 | ProRes 4444 | Yes | 64 | Video editing, compositing, broadcast | Video is charged per second of output based on quality tier. ### Dataset Tiers | Tier | Views | Resolution | Credits | Best for | |---|---|---|---|---| | `100x800` | 100 | 800×800 | 40 | Quick experiments, proof-of-concept | | `196x1024` | 196 | 1024×1024 | 160 | Production training, best quality-to-cost ratio | | `400x2048` | 400 | 2048×2048 | 640 | Maximum fidelity, large-scale reconstruction | Datasets are a flat cost per set. **Coverage** - **Hemisphere** (default) — upper hemisphere views, suitable for objects typically seen from above - **Full sphere** — ±80° elevation range, captures undersides and all angles **Output ZIP contents** Every dataset ZIP contains: - `images/` — RGB PNG frames (composited on white background) - `depth/` — 8-bit grayscale depth PNGs - `depth_16bit/` — 16-bit grayscale depth PNGs (65,536 levels) - `normals/` — world-space normal map PNGs - `masks/` — foreground/background alpha masks - `transforms.json` — camera intrinsics + per-frame 4×4 transform matrices (nerfstudio / instant-ngp format) - `overview.webp` — 4-quadrant contact sheet After downloading a dataset ZIP, you can train directly with nerfstudio (the `--pipeline.model.background-color white` flag is required, since images are composited on white): ```bash unzip dataset.zip -d mymodel ns-train splatfacto --data ./mymodel \ --max-num-iterations 15000 \ --pipeline.model.sh-degree 3 \ --pipeline.model.background-color white ``` ### Render Settings | Field | Type | Default | Description | |---|---|---|---| | `quality` | string | `"standard"` | `"share"` (540p, 1 credit/sec), `"standard"` (HD 1080p, 4 credits/sec — **the default when omitted**: a default 6s render with no `quality` set costs 24 credits), `"4k"` (16/sec), `"pro"` (4K ProRes with alpha, 64/sec) | | `aspect` | string | `"16:9"` | `"16:9"`, `"1:1"`, `"9:16"` | | `bgColor` | string | `"#ffffff"` | Any 6-digit hex (ignored when `bgImageId` is set) | | `bgImageId` | string | — | Overlay asset ID for background image | | `length` | number | `6` | Duration in seconds — anything above 0 up to 120 (ProRes: 60), decimal OK e.g. `4.7`. Billed per second, 1-credit minimum. | | `animation` | string | — | Animation clip name (or `"*"` for first clip) — discover clips via `GET /v1/models/{id}` → `animations` | | `animationSpeed` | number | `1` | Animation speed multiplier (−10 to 10; negative plays in reverse) | | `shadows` | boolean | `true` | Ground shadows | | `reflector` | boolean | `false` | Reflective ground plane | | `easing` | boolean | `true` | Smooth rotation start/stop | | `rotateY` | number | `0` | Camera orbit start angle (−180° to 180°) — moves the camera, not the model | | `panX` | number | `0` | Model yaw (−180° to 180°), applied first | | `tiltX` | number | `0` | Model pitch (−180° to 180°), applied after yaw | | `rollZ` | number | `0` | Model roll (−180° to 180°), applied last | | `zoom` | number | `1.0` | Camera zoom (0.5–2.0; above 1 = closer) | | `bgAlign` | string | `"top-left"` | Background-image crop anchor (nine positions) | | `hiddenMeshNames` | string[] | — | Meshes to hide, by exact runtime name (`GET /v1/models/{id}` → `meshNames`) | | `materialEdits` | object | — | Per-material overrides keyed by material name — color, roughness, metalness, `mapAssetId` texture swaps, and more | | `envId` | string | — | Custom HDRI environment (see Custom Materials & Environments) | | `effect` | string | `"turntable"` | Camera motion: `"turntable"`, `"hero-spin"`, `"showcase"`, `"zoom-orbit"`, `"reveal"`, `"keyframes"` (custom fly-through) | | `output` | string | `"video"` | `"video"` or `"dataset"` | | `datasetQuality` | string | — | `"100x800"`, `"196x1024"`, `"400x2048"` (when output is dataset) | | `coverage` | string | `"hemisphere"` | `"hemisphere"` or `"sphere"` (dataset only) | This is the working set — the full surface (environment and lighting knobs, decals, keyframe camera paths, filters) is in the [API reference](/docs/api), and everything the Studio editor can author is accepted here. --- ## Error Handling All errors return a consistent format: ```json { "error": { "code": "no_credits", "message": "No render credits remaining", "status": 402 } } ``` Key error codes for integrators: | Code | Status | Action | |---|---|---| | `unauthorized` | 401 | Check API key | | `no_credits` | 402 | Purchase more credits or contact for volume pricing | | `file_too_large` | 400 | File exceeds 1 GB — reduce before upload | | `invalid_format` | 400 | Only `.glb` and `.zip` (OBJ+MTL) are supported — pack `.gltf` as `.glb` | | `model_not_found` | 404 | Model was deleted or ID is wrong | | `too_many` | 400 | Batch exceeds 100 renders — split into smaller batches | | `upload_limit` | 429 | Free upload limit reached — purchase credits to unlock | --- ## Further Reading - [API Documentation](https://dx.gl/docs/api) — Full REST API reference with all endpoints - [Use Cases](https://dx.gl/use-cases) — Product video production and other workflows at scale - [Datasets](https://dx.gl/datasets) — Multi-view datasets for 3D reconstruction, plus free CC0 samples - [Showcase](https://dx.gl/showcase) — Browse example videos - [Pricing](https://dx.gl/pricing) — Credit packs and volume pricing