Panorama Editing
Editing a world works the same way in the API as it does in the app: you edit the panorama behind a world, iterate until you like it, then create a new world from the final panorama.
Endpoints
| Endpoint | Scope | Purpose |
|---|---|---|
POST /v1/panoramas/edit | worlds:create | Edit a panorama and return the updated panorama. |
GET /v1/panoramas | worlds:read | List panoramas created by the current API key. |
GET /v1/panoramas/{panorama_id} | worlds:read | Get a panorama. |
GET /v1/panoramas/{panorama_id}/download | worlds:read | 302-redirect to a short-lived signed image URL. |
The edit loop
POST /v1/panoramas/edit { source: { type: "request_id", request_id }, prompt }
-> { panorama_id: "pano_1", ... }
POST /v1/panoramas/edit { source: { type: "panorama_id", panorama_id: "pano_1" }, prompt }
-> { panorama_id: "pano_2", parent_panorama_id: "pano_1", ... }
POST /v1/worlds { input: { type: "panorama_id", panorama_id: "pano_2" } }
-> { request_id: "req_...", status: "PENDING", ... } # normal world flow
Edit a panorama
The source is the panorama you want to edit. It can be:
request_id— an API-created world request owned by the same user (uses that world's panorama).world_id— theworld.idfrom a create result (API-created worlds only, owned by the same user even if created by a different API key).panorama_id— a panorama from a previous edit (this is how you iterate).
API_KEY="spt_live_..."
curl -sX POST "https://api.spaitial.ai/v1/panoramas/edit" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": { "type": "request_id", "request_id": "req_abc123" },
"prompt": "make it night time with warm street lights"
}'
The edit runs synchronously and returns 200 OK with the new panorama. Lineage
fields use public IDs (req_... / pano_...), not internal UUIDs:
{
"panorama_id": "pano_def456",
"status": "READY",
"panorama_url": "https://api.spaitial.ai/v1/panoramas/pano_def456/download",
"prompt": "make it night time with warm street lights",
"source_request_id": "req_abc123",
"source_world_id": "…",
"parent_panorama_id": null,
"consumed": false,
"created_at": "2026-05-20T10:42:00.000Z",
"expires_at": "2026-05-21T10:42:00.000Z"
}
Optional reference images
Provide up to 3 reference images to guide the edit. Each image is a file_id
(from POST /v1/files), an https url, or inline base64:
{
"source": { "type": "panorama_id", "panorama_id": "pano_def456" },
"prompt": "match the materials in the reference",
"images": [{ "type": "url", "image_url": "https://example.com/reference.jpg" }]
}
The prompt and all reference images are moderated before the edit runs. The output always preserves the source panorama aspect ratio so it can be used safely for world generation.
Iterate
Pass the returned panorama_id back as the source to keep refining. Each iteration
records parent_panorama_id so you can trace the chain.
curl -sX POST "https://api.spaitial.ai/v1/panoramas/edit" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": { "type": "panorama_id", "panorama_id": "pano_def456" },
"prompt": "add neon signs on the storefronts"
}'
Create a world from a panorama
When you are happy with a panorama, create a world from it. Generation starts from the panorama stage (it skips image-to-panorama), and the new world is linked back to its source world.
curl -sX POST "https://api.spaitial.ai/v1/worlds" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": { "type": "panorama_id", "panorama_id": "pano_def456" },
"title": "Edited world"
}'
This returns the usual 202 Accepted world job — poll its status_url or use a
webhook as with any other world. Creating a world from a panorama
does not consume the panorama permanently; you can still iterate or create again from it
until it expires.
Idempotency
POST /v1/panoramas/edit accepts an Idempotency-Key header. Replaying the same key
returns the previously created panorama instead of running a new edit; reusing a key
with a different body returns 409 IDEMPOTENCY_KEY_REUSED.
Errors
| HTTP | error.code | When it happens |
|---|---|---|
400 | INVALID_INPUT | Malformed request (e.g. more than 3 images, bad source). |
401 | UNAUTHORIZED | Missing, invalid, revoked, or expired API key. |
402 | INSUFFICIENT_CREDITS | Not enough credits to run the edit. |
403 | FORBIDDEN | API key lacks the required scope. |
403 | MODERATION_REJECTED | The prompt or a reference image failed moderation. |
404 | REQUEST_NOT_FOUND | request_id / world_id does not exist or belongs to another caller. |
404 | PANORAMA_NOT_FOUND | panorama_id does not exist or belongs to another caller. |
409 | RESOURCE_NOT_READY | The source world is not finished generating yet. |
410 | PANORAMA_EXPIRED | The panorama is past its 24-hour TTL. |
502 | EDIT_FAILED | The edit could not be completed; retry. |
See Moderation for content screening and Credits & Billing for edit pricing.