curl --request POST \
--url https://apis.viggle.ai/v1/scenes/generate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"sessionId": "living-room-01",
"referenceBatchIds": [
"<string>"
],
"targetC2w": [
[
1,
0,
0,
0
],
[
0,
1,
0,
0
],
[
0,
0,
1,
0
],
[
0,
0,
0,
1
]
],
"projection": "perspective",
"fov": 80,
"seed": 1,
"spzOutputUrl": "<string>",
"outputUris": {
"spz": "<string>",
"preview": "<string>",
"panorama": "<string>"
}
}
'import requests
url = "https://apis.viggle.ai/v1/scenes/generate"
payload = {
"sessionId": "living-room-01",
"referenceBatchIds": ["<string>"],
"targetC2w": [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
"projection": "perspective",
"fov": 80,
"seed": 1,
"spzOutputUrl": "<string>",
"outputUris": {
"spz": "<string>",
"preview": "<string>",
"panorama": "<string>"
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
sessionId: 'living-room-01',
referenceBatchIds: ['<string>'],
targetC2w: [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
projection: 'perspective',
fov: 80,
seed: 1,
spzOutputUrl: '<string>',
outputUris: {spz: '<string>', preview: '<string>', panorama: '<string>'}
})
};
fetch('https://apis.viggle.ai/v1/scenes/generate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://apis.viggle.ai/v1/scenes/generate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'sessionId' => 'living-room-01',
'referenceBatchIds' => [
'<string>'
],
'targetC2w' => [
[
1,
0,
0,
0
],
[
0,
1,
0,
0
],
[
0,
0,
1,
0
],
[
0,
0,
0,
1
]
],
'projection' => 'perspective',
'fov' => 80,
'seed' => 1,
'spzOutputUrl' => '<string>',
'outputUris' => [
'spz' => '<string>',
'preview' => '<string>',
'panorama' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://apis.viggle.ai/v1/scenes/generate"
payload := strings.NewReader("{\n \"sessionId\": \"living-room-01\",\n \"referenceBatchIds\": [\n \"<string>\"\n ],\n \"targetC2w\": [\n [\n 1,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 1,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 1,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 1\n ]\n ],\n \"projection\": \"perspective\",\n \"fov\": 80,\n \"seed\": 1,\n \"spzOutputUrl\": \"<string>\",\n \"outputUris\": {\n \"spz\": \"<string>\",\n \"preview\": \"<string>\",\n \"panorama\": \"<string>\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://apis.viggle.ai/v1/scenes/generate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"sessionId\": \"living-room-01\",\n \"referenceBatchIds\": [\n \"<string>\"\n ],\n \"targetC2w\": [\n [\n 1,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 1,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 1,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 1\n ]\n ],\n \"projection\": \"perspective\",\n \"fov\": 80,\n \"seed\": 1,\n \"spzOutputUrl\": \"<string>\",\n \"outputUris\": {\n \"spz\": \"<string>\",\n \"preview\": \"<string>\",\n \"panorama\": \"<string>\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/scenes/generate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"sessionId\": \"living-room-01\",\n \"referenceBatchIds\": [\n \"<string>\"\n ],\n \"targetC2w\": [\n [\n 1,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 1,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 1,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 1\n ]\n ],\n \"projection\": \"perspective\",\n \"fov\": 80,\n \"seed\": 1,\n \"spzOutputUrl\": \"<string>\",\n \"outputUris\": {\n \"spz\": \"<string>\",\n \"preview\": \"<string>\",\n \"panorama\": \"<string>\"\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"sessionId": "<string>",
"operation": "pano",
"status": "processing",
"createdAt": 123,
"completedAt": 123,
"result": {
"operation": "pano",
"sessionId": "<string>",
"spz": {
"url": "<string>"
},
"preview": {
"url": "<string>"
},
"panorama": {
"url": "<string>"
},
"outputBatch": {
"batchId": "<string>",
"createdAt": 123,
"manifest": {
"url": "<string>"
},
"kind": "initial",
"views": [
{
"index": 1,
"url": "<string>",
"c2w": [
[
1,
0,
0,
0
],
[
0,
1,
0,
0
],
[
0,
0,
1,
0
],
[
0,
0,
0,
1
]
],
"intrinsic": [
123
]
}
]
},
"referenceViewCount": 123,
"newViewCount": 123,
"seconds": {}
},
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}Generate Scene Views
Generate new images of a scene from a camera pose you choose, as a new image batch.
curl --request POST \
--url https://apis.viggle.ai/v1/scenes/generate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"sessionId": "living-room-01",
"referenceBatchIds": [
"<string>"
],
"targetC2w": [
[
1,
0,
0,
0
],
[
0,
1,
0,
0
],
[
0,
0,
1,
0
],
[
0,
0,
0,
1
]
],
"projection": "perspective",
"fov": 80,
"seed": 1,
"spzOutputUrl": "<string>",
"outputUris": {
"spz": "<string>",
"preview": "<string>",
"panorama": "<string>"
}
}
'import requests
url = "https://apis.viggle.ai/v1/scenes/generate"
payload = {
"sessionId": "living-room-01",
"referenceBatchIds": ["<string>"],
"targetC2w": [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
"projection": "perspective",
"fov": 80,
"seed": 1,
"spzOutputUrl": "<string>",
"outputUris": {
"spz": "<string>",
"preview": "<string>",
"panorama": "<string>"
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
sessionId: 'living-room-01',
referenceBatchIds: ['<string>'],
targetC2w: [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
projection: 'perspective',
fov: 80,
seed: 1,
spzOutputUrl: '<string>',
outputUris: {spz: '<string>', preview: '<string>', panorama: '<string>'}
})
};
fetch('https://apis.viggle.ai/v1/scenes/generate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://apis.viggle.ai/v1/scenes/generate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'sessionId' => 'living-room-01',
'referenceBatchIds' => [
'<string>'
],
'targetC2w' => [
[
1,
0,
0,
0
],
[
0,
1,
0,
0
],
[
0,
0,
1,
0
],
[
0,
0,
0,
1
]
],
'projection' => 'perspective',
'fov' => 80,
'seed' => 1,
'spzOutputUrl' => '<string>',
'outputUris' => [
'spz' => '<string>',
'preview' => '<string>',
'panorama' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://apis.viggle.ai/v1/scenes/generate"
payload := strings.NewReader("{\n \"sessionId\": \"living-room-01\",\n \"referenceBatchIds\": [\n \"<string>\"\n ],\n \"targetC2w\": [\n [\n 1,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 1,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 1,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 1\n ]\n ],\n \"projection\": \"perspective\",\n \"fov\": 80,\n \"seed\": 1,\n \"spzOutputUrl\": \"<string>\",\n \"outputUris\": {\n \"spz\": \"<string>\",\n \"preview\": \"<string>\",\n \"panorama\": \"<string>\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://apis.viggle.ai/v1/scenes/generate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"sessionId\": \"living-room-01\",\n \"referenceBatchIds\": [\n \"<string>\"\n ],\n \"targetC2w\": [\n [\n 1,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 1,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 1,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 1\n ]\n ],\n \"projection\": \"perspective\",\n \"fov\": 80,\n \"seed\": 1,\n \"spzOutputUrl\": \"<string>\",\n \"outputUris\": {\n \"spz\": \"<string>\",\n \"preview\": \"<string>\",\n \"panorama\": \"<string>\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/scenes/generate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"sessionId\": \"living-room-01\",\n \"referenceBatchIds\": [\n \"<string>\"\n ],\n \"targetC2w\": [\n [\n 1,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 1,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 1,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 1\n ]\n ],\n \"projection\": \"perspective\",\n \"fov\": 80,\n \"seed\": 1,\n \"spzOutputUrl\": \"<string>\",\n \"outputUris\": {\n \"spz\": \"<string>\",\n \"preview\": \"<string>\",\n \"panorama\": \"<string>\"\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"sessionId": "<string>",
"operation": "pano",
"status": "processing",
"createdAt": 123,
"completedAt": 123,
"result": {
"operation": "pano",
"sessionId": "<string>",
"spz": {
"url": "<string>"
},
"preview": {
"url": "<string>"
},
"panorama": {
"url": "<string>"
},
"outputBatch": {
"batchId": "<string>",
"createdAt": 123,
"manifest": {
"url": "<string>"
},
"kind": "initial",
"views": [
{
"index": 1,
"url": "<string>",
"c2w": [
[
1,
0,
0,
0
],
[
0,
1,
0,
0
],
[
0,
0,
1,
0
],
[
0,
0,
0,
1
]
],
"intrinsic": [
123
]
}
]
},
"referenceViewCount": 123,
"newViewCount": 123,
"seconds": {}
},
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}referenceBatchIds and renders new images as seen from targetC2w, which become a new generated batch in the session. Use them to fill in parts of the scene the existing views don’t cover, then pass the new batchId, together with the earlier ones, to Reconstruct Scene for an updated .spz.
The request is asynchronous: it answers with a processing Scene Task; poll it until it finishes.
Request parameters
Sendapplication/json, up to 1 MiB. Unknown fields are rejected.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
sessionId | string | Yes | — | The session whose scene to extend — see Sessions. |
referenceBatchIds | string[] | Yes | — | 1–35 batch IDs from this session, as returned in result.outputBatch.batchId. Lowercase UUIDs, no repeats. |
targetC2w | number[4][4] | Yes | — | Camera-to-world matrix to render from, in the same frame as the views’ c2w. The last row must be [0, 0, 0, 1]. |
projection | string | No | perspective | perspective for regular camera images, pano for a 360° panorama at targetC2w. |
fov | number | No | model default | Field of view in degrees, 20–140. |
seed | integer | No | 1 | 0–4294967295. The same inputs and seed give the same images. |
spzOutputUrl | string | No | — | Accepted as on every scene operation — see Upload to your own storage. |
outputUris | object | No | — | Your own upload URLs per file: spz, preview, panorama. Can’t be combined with spzOutputUrl. |
targetC2w is to start from a view’s c2w in a batch you already have and move or turn it. A camera close to the existing views gives the most consistent images.Response parameters
Returns200 OK with a Scene Task in processing state:
{
"id": "scn_0199f0c4-1b2c-7d3e-8f4a-5b6c7d8e9f0a.living-room-01",
"sessionId": "living-room-01",
"operation": "generate",
"status": "processing",
"createdAt": 1791856900000,
"completedAt": null,
"result": null,
"error": null
}
ready, result.outputBatch is the new generated batch, with every new image and its camera in views, and result.newViewCount is how many there are. result.spz is null — run Reconstruct Scene to fold the new views into the scene.
Common errors
| Error | Cause |
|---|---|
400 INVALID_REQUEST | A field is missing, invalid, or unknown — for example referenceBatchIds is empty, has more than 35 IDs or a repeat, or targetC2w isn’t a 4 × 4 matrix whose last row is [0, 0, 0, 1]. |
401 UNAUTHENTICATED | Missing or invalid API key. |
409 INVALID_REQUEST | The session already has a task processing; see error.details.running_task_id. |
503 SERVICE_BUSY | The scene model is temporarily unavailable. Retryable. |
INVALID_REQUEST.
Examples
curl -X POST https://apis.viggle.ai/v1/scenes/generate \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sessionId": "living-room-01",
"referenceBatchIds": ["3f0a6c1e-8d2b-4b7a-9e5c-1a2b3c4d5e6f"],
"targetC2w": [[1, 0, 0, 0.5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
"projection": "perspective",
"fov": 75,
"seed": 7
}'
import os, requests
response = requests.post(
"https://apis.viggle.ai/v1/scenes/generate",
headers={"Authorization": f"Bearer {os.environ['VIGGLE_API_KEY']}"},
json={
"sessionId": "living-room-01",
"referenceBatchIds": ["3f0a6c1e-8d2b-4b7a-9e5c-1a2b3c4d5e6f"],
"targetC2w": [[1, 0, 0, 0.5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
"projection": "perspective",
"fov": 75,
"seed": 7,
},
)
response.raise_for_status()
task = response.json()
const response = await fetch("https://apis.viggle.ai/v1/scenes/generate", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VIGGLE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
sessionId: "living-room-01",
referenceBatchIds: ["3f0a6c1e-8d2b-4b7a-9e5c-1a2b3c4d5e6f"],
targetC2w: [[1, 0, 0, 0.5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
projection: "perspective",
fov: 75,
seed: 7,
}),
});
const task = await response.json();
Quick Start: Scene Model
Authorizations
Server-side SDK clients use a project API key. Remote MCP clients use an OAuth access token. Never expose a project API key in browser code.
Headers
Optional caller-supplied correlation ID, up to 128 characters. The service returns the effective value in the response header for tracing and support.
1 - 128Optional source-channel label, up to 128 characters, used to attribute traffic to an SDK, integration, product surface, or internal workflow.
1 - 128Body
Body of POST /v1/scenes/generate. spzOutputUrl and outputUris are mutually exclusive.
Session chosen by the caller, scoped to the caller's account. A
session holds one scene and its image batches, and runs one task at
a time: a submission while a task is still processing answers
409 with that task in error.details.running_task_id.
1 - 128^[A-Za-z0-9_-]+$"living-room-01"
1–35 distinct batch IDs from the same session, as returned in result.outputBatch.batchId. A well-formed ID from another session fails the task with INVALID_REQUEST.
1 - 35 elements^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$4 × 4 camera-to-world matrix as four rows of four finite numbers; the last row must be [0, 0, 0, 1].
4 elements4 elements[
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
]
perspective for camera images, pano for a 360° panorama at targetC2w.
perspective, pano Field of view in degrees. Omitted or null, the model's default.
20 <= x <= 1400 <= x <= 4294967295Same as outputUris.spz.
The caller's own upload URLs; Viggle PUTs each finished file to
its URL. Null entries are ignored. An .spz sent here is not kept
by Viggle, and the result's spz.url is this URL; a preview or
panorama sent here is also kept, and the result links to Viggle's
copy. Each URL must be http(s), without credentials, on a host
that resolves only to public addresses.
Show child attributes
Show child attributes
Response
Scene task started.
A scene task (scn_ prefix), returned by every /v1/scenes
submission and by GET /v1/scenes/{task_id}. ready, failed, and
cancelled are terminal. A task stays readable for about 72 hours
after it finishes.
Task ID, scn_{runId}.{sessionId}.
A custom-pano task reports pano.
pano, generate, reconstruct processing, ready, failed, cancelled Start time, Unix milliseconds.
End time, Unix milliseconds; null while processing.
Set only when ready.
Show child attributes
Show child attributes
Set only when failed. code is INVALID_REQUEST (not
retryable — the model rejected the input, such as a batch from
another session) or PROCESSING_FAILED (retryable).
Show child attributes
Show child attributes

