DX.GL

Integration Guide

HTML
# Integration Guide

Build 3D rendering into your product with the DX.GL pipeline.

---

## Why Integrate

Your users need product videos, turntable animations, or multi-view training datasets — but building a GPU rendering pipeline is a multi-year engineering effort. DX.GL handles the hard parts: model processing, GPU scheduling, shadow accumulation, video encoding, and asset delivery. You focus on your product.

What this means in practice:

- **Ship in days, not months** — a working integration takes hours. Upload a GLB, get back an MP4. The pipeline handles everything from model validation to poster extraction to web-optimized encoding.
- **No GPU infrastructure** — DX.GL runs the render farm. You don't manage NVIDIA drivers, ffmpeg builds, or encoding queues. Scale from 10 renders to 10,000 without provisioning anything.
- **Predictable costs** — credit-based pricing with volume discounts. Quote any batch before committing. No surprise compute bills.
- **Production-grade output** — 1080p/4K H.264, ProRes 4444 with alpha, multi-view datasets with depth maps and camera poses. Every render includes a web variant, thumbnail, and poster automatically.

Whether you're building an e-commerce platform, a 3D marketplace, a research data pipeline, or a content management system — the same API powers all of these.

---

## Architecture Overview

### Authentication

You use a single API key (`dxgl_sk_...`) created in the [Portal](https://dx.gl/portal) → API Keys. All renders, uploads, and downloads are billed to your account. You handle user mapping and billing on your side however you want.

### 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.

### Credit Accounting

Credits are deducted per render based on quality tier:

| Quality | Output | Credits |
|---|---|---|
| `standard` | 1080p H.264 MP4 | 1 |
| `4k` | 4K H.264 MP4 | 4 |
| `pro` | 4K ProRes 4444 with alpha | 16 |
| `dataset` 100×800 | 100-view training ZIP | 1 |
| `dataset` 196×1024 | 196-view training ZIP | 4 |
| `dataset` 400×2048 | 400-view training ZIP | 16 |

Use the `quote` endpoint to check costs before committing. Batch renders deduct atomically — if you don't have enough credits, nothing is charged.

---

## Quick Start: REST API

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 100 MB, 2-minute timeout). SHA-256 deduplication prevents re-uploading identical files.

### 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
    }
  }'
```

### 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. Renders for the same model share GPU frames — encoding multiple variants from one render pass.

### 5. Poll for completion

```bash
curl https://api.dx.gl/v1/renders/Xz7pQ4w8nR2k \
  -H "Authorization: Bearer dxgl_sk_..."
```

Status progression: `pending` → `poster-processing` → `poster-done` → `video-processing` → `done`

The poster is available at `poster-done` — you can show it to users immediately while the video finishes encoding.

### 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

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) {
  const form = new FormData();
  form.append('file', fs.createReadStream(filePath));
  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}`);
  }
}
```

---

## Quick Start: MCP Path

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 compatible host):

```json
{
  "mcpServers": {
    "dxgl": {
      "serverUrl": "https://mcp.dx.gl/",
      "headers": {
        "Authorization": "Bearer dxgl_sk_..."
      }
    }
  }
}
```

### Available Tools

| Tool | Description |
|---|---|
| `ingest_model` | Import a 3D model from a URL |
| `list_models` | List models with tag and pagination filters |
| `get_model` | Get model details and all its renders |
| `create_render` | Create a single render (video or dataset) |
| `create_batch_renders` | Create multiple renders atomically (max 100) |
| `get_render` | Check render status (poll until done) |
| `download_render` | Get download URL for a completed render |
| `get_account` | Check credit balance |
| `quote` | Estimate credit cost before committing |

### Example Prompts

**Catalog onboarding:**
> Import all GLB files from these URLs, tag them "client-acme", then render each in 16:9 white and 1:1 black with shadows. Quote the total first.

**Variant expansion:**
> For all models tagged "client-acme", create 9:16 renders with the branded gradient background (overlay ID abc123DEF456). Skip any that already have a 9:16 render.

**Dataset generation:**
> Generate 196×1024 hemisphere datasets for all models tagged "training-batch-7". Quote first, then proceed.

**Status check:**
> How many credits do I have left? List all pending renders and their status.

### When to Use MCP vs REST

| Use MCP when... | Use REST when... |
|---|---|
| Prototyping a new integration | Running in production |
| Ad-hoc batch operations | Automated backend pipeline |
| Internal tooling for ops | User-facing render triggers |
| Exploring the API interactively | Deterministic, testable code |

Many teams use both: MCP for operations and internal tooling, REST for the production path.

---

## Common Integration 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"]
    })
```

### On-Demand Rendering

User uploads a model through your UI → your backend forwards it to DX.GL → polls for completion → delivers the video.

```
User → Your Frontend → Your Backend → DX.GL API → Poll → Deliver video URL
```

The poster is available at `poster-done` status — show it immediately as a preview while the video finishes.

### 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 top-left anchor, keeping logos safe across all aspect ratios.

---

## Tags for Multi-Tenant Organization

Tags are the key to organizing models across clients, projects, and campaigns. Every model can have up to 20 tags (lowercased, trimmed).

### Tagging Strategy

```
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)
```

### Filtering

```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_..."
```

### Per-Client Billing

Tag every model with the client name. To calculate spend per client, list their models, count their renders, multiply by credit cost per quality tier. 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 } }
```

### 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": 6, "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.

### Monitoring

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

### Public Share URLs

Every completed render has a public share page at `https://dx.gl/portal/v/{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 |

### 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>
```

---

## Render Settings Reference

| Field | Type | Default | Description |
|---|---|---|---|
| `quality` | string | `"standard"` | `"standard"` (1080p), `"4k"` (4K), `"pro"` (4K ProRes with alpha) |
| `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` | integer | `6` | Duration in seconds (3–30) |
| `shadows` | boolean | `true` | Ground shadows |
| `reflector` | boolean | `false` | Reflective ground plane |
| `easing` | boolean | `true` | Smooth rotation start/stop |
| `rotateY` | integer | `0` | Starting angle (−180° to 180°) |
| `tiltX` | integer | `0` | Forward/back tilt (−90° to 90°) |
| `effect` | string | `"turntable"` | Camera motion: `"turntable"`, `"hero-spin"`, `"showcase"`, `"zoom-orbit"`, `"reveal"` |
| `output` | string | `"video"` | `"video"` or `"dataset"` |
| `datasetQuality` | string | — | `"100x800"`, `"196x1024"`, `"400x2048"` (when output is dataset) |
| `coverage` | string | `"hemisphere"` | `"hemisphere"` or `"sphere"` (dataset only) |

---

## 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 100 MB — reduce before upload |
| `invalid_format` | 400 | Only GLB, glTF, and ZIP (OBJ+MTL) are supported |
| `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/portal/docs) — Full REST API reference with all endpoints
- [API Documentation (Markdown)](https://dx.gl/portal/docs/markdown) — Raw markdown, paste into LLM context
- [MCP for Video](https://dx.gl/docs/mcp-video) — Automate video production with AI agents
- [MCP for Datasets](https://dx.gl/docs/mcp-datasets) — Automate dataset generation with AI agents
- [Portal Guide](https://dx.gl/portal/docs/guide) — Manual portal usage
- [Pricing](https://dx.gl/pricing) — Credit packs and volume pricing
- [Contact](https://dx.gl/contact) — Enterprise and integration inquiries