# SpAItial Developer Docs > SpAItial turns a single image into an immersive 3D world. Product guides and developer API reference. ## Product Documentation ### Notifications Control how SpAItial communicates with you. #### Notification Types[​](#notification-types "Direct link to Notification Types") ##### Generation Notifications[​](#generation-notifications "Direct link to Generation Notifications") * World generation complete * Generation failed ##### Account Notifications[​](#account-notifications "Direct link to Account Notifications") * Security alerts * Important updates #### Notification Channels[​](#notification-channels "Direct link to Notification Channels") ##### In-App[​](#in-app "Direct link to In-App") Notifications within the SpAItial interface. This is the default channel. ##### Push[​](#push "Direct link to Push") The SpAItial Capture iPhone app can send a system notification when a capture-generated world completes or fails. Push notifications require both permission in iOS and the Push toggle in **Settings > Notifications**. ##### Email[​](#email "Direct link to Email") Notifications sent to your registered email. Based on your notification preferences. #### Managing Preferences[​](#managing-preferences "Direct link to Managing Preferences") 1. Go to **Settings** > **Notifications** 2. Toggle individual notification types 3. Choose channels for each type 4. Save preferences #### Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") ##### Not Receiving Notifications[​](#not-receiving-notifications "Direct link to Not Receiving Notifications") * Check spam folder * Verify email address * Review notification settings * For Capture push notifications, check **iOS Settings > Notifications > SpAItial Capture** * Check browser permissions for dashboard notifications --- ### Profile Customize your SpAItial profile and public presence. #### Profile Information[​](#profile-information "Direct link to Profile Information") ##### Display Name[​](#display-name "Direct link to Display Name") Your name as shown to other users. ##### Username[​](#username "Direct link to Username") Your unique identifier, used in profile URLs. ##### Avatar[​](#avatar "Direct link to Avatar") Your profile picture, visible when you share worlds or appear in community features. ##### Bio[​](#bio "Direct link to Bio") A short description about yourself (optional). #### Editing Your Profile[​](#editing-your-profile "Direct link to Editing Your Profile") 1. Go to **Settings** > **Profile** 2. Update desired fields 3. Click **Save** #### Profile Visibility[​](#profile-visibility "Direct link to Profile Visibility") ##### What's Public[​](#whats-public "Direct link to What's Public") If you share worlds publicly: * Display name * Avatar * Public worlds ##### What's Private[​](#whats-private "Direct link to What's Private") * Email address * Account settings * Private worlds #### Avatar Guidelines[​](#avatar-guidelines "Direct link to Avatar Guidelines") * Square images work best * Minimum size: 200x200 pixels * Supported formats: JPEG, PNG * Keep it appropriate #### Username Rules[​](#username-rules "Direct link to Username Rules") * Must be unique * Alphanumeric characters * No spaces * May have length limits #### Best Practices[​](#best-practices "Direct link to Best Practices") ##### For Visibility[​](#for-visibility "Direct link to For Visibility") * Use a recognizable name * Add a clear avatar * Write a brief bio ##### For Privacy[​](#for-privacy "Direct link to For Privacy") * Don't include personal information you don't want public * Review privacy settings * Be mindful of what worlds you make public --- ### Sign Up & Login Get started with SpAItial by creating an account. #### Creating an Account[​](#creating-an-account "Direct link to Creating an Account") ##### Step 1: Visit SpAItial[​](#step-1-visit-spaitial "Direct link to Step 1: Visit SpAItial") Go to [app.spaitial.ai/login](https://app.spaitial.ai/login). * Sign in/up with your email and password * Sign in/up with your Google account ##### Step 2: Verify Email (if you used email and password)[​](#step-2-verify-email-if-you-used-email-and-password "Direct link to Step 2: Verify Email (if you used email and password)") Check your email for a verification link and click to verify. #### Password Requirements[​](#password-requirements "Direct link to Password Requirements") * Minimum 8 characters * Mix of letters and numbers recommended * Avoid common passwords #### Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") ##### Can't Create Account[​](#cant-create-account "Direct link to Can't Create Account") * Check email format is valid * Ensure password meets requirements * Try a different browser ##### Can't Sign In[​](#cant-sign-in "Direct link to Can't Sign In") * Verify email and password * Check for typos * Try password reset * Clear browser cache ##### Email Not Received[​](#email-not-received "Direct link to Email Not Received") * Check spam/junk folder * Verify email address * Request new verification email --- ### Create Your First World Ready to create your first immersive 3D world? Follow this guide to get started. #### Prerequisites[​](#prerequisites "Direct link to Prerequisites") * A SpAItial account. [Sign up here](https://app.spaitial.ai/signup) * Available credits. Free users receive 800 credits each month, enough for about 5 Echo-2 generations. Unused free credits do not roll over. #### Step 1: Navigate to Create[​](#step-1-navigate-to-create "Direct link to Step 1: Navigate to Create") From the dashboard, click the **Create** button or navigate to the creation page. #### Step 2: Choose Your Method[​](#step-2-choose-your-method "Direct link to Step 2: Choose Your Method") Select how you want to create your world: * **[Text-to-World](https://docs.spaitial.ai/create/text-to-world.md)**: Describe your vision in words * **[Image-to-World](https://docs.spaitial.ai/create/image-to-world.md)**: Upload a reference image * **[Panorama-to-World](https://docs.spaitial.ai/create/panorama-to-world.md)**: Upload a 360° panorama #### Step 3: Enter Your Input[​](#step-3-enter-your-input "Direct link to Step 3: Enter Your Input") ##### For Text-to-World[​](#for-text-to-world "Direct link to For Text-to-World") Write a descriptive prompt. For example: > "A cozy Japanese café with warm lighting, wooden furniture, and plants by the window" ##### For Image-to-World[​](#for-image-to-world "Direct link to For Image-to-World") Upload a clear image that represents the space you want to create. ##### For Panorama-to-World[​](#for-panorama-to-world "Direct link to For Panorama-to-World") Upload a 360° panorama of the space you want to create. #### Step 4: Generate[​](#step-4-generate "Direct link to Step 4: Generate") Click **Generate** to start the creation process. This typically takes 6-8 minutes. You will be notified when it is completed via email and an in-app notification. #### Step 5: Explore[​](#step-5-explore "Direct link to Step 5: Explore") Once complete, your world will appear in your worlds page. Click to explore it in 3D! #### Tips for Success[​](#tips-for-success "Direct link to Tips for Success") * Be specific in your descriptions * Include details about lighting, materials, and atmosphere * Use high-quality reference images * Start simple and iterate #### Next Steps[​](#next-steps "Direct link to Next Steps") * Learn about [prompt writing](https://docs.spaitial.ai/create/prompt-guide.md) * Understand [image requirements](https://docs.spaitial.ai/create/image-guide.md) * See [panorama requirements](https://docs.spaitial.ai/create/panorama-to-world.md) * Explore [editing options](https://docs.spaitial.ai/edit/overview.md) --- ### Generation States Learn what each generation state means and what to expect. #### State Overview[​](#state-overview "Direct link to State Overview") When you create a world, it progresses through several states: ```text Queued → Processing → Completed ``` #### State Details[​](#state-details "Direct link to State Details") ##### Queued[​](#queued "Direct link to Queued") Your generation request has been received and is waiting to start. **What's happening:** * Request validated * Added to processing queue * Waiting for available resources **Duration:** Typically seconds to a few minutes ##### Processing[​](#processing "Direct link to Processing") Our models are actively generating your world. **What's happening:** * Our models are working on your world * Our system is preparing your world for viewing **Duration:** 6-8 minutes typically ##### Completed[​](#completed "Direct link to Completed") Your world is complete and ready to explore! **You can now:** * View and explore the world * Edit specific regions of the world * Share with others via a public link * Export in various formats including USDZ and Collision Mesh ##### Failed[​](#failed "Direct link to Failed") The generation process encountered an error. **Possible causes:** * Invalid input * Technical issue * Timeout or rate limit exceeded **What to do:** * Check your input and try again * Contact support at if the issue persists #### Monitoring Progress[​](#monitoring-progress "Direct link to Monitoring Progress") * Stay on the page to see real-time updates and notifications * Or navigate away - your world will appear in your worlds page when ready * Email notifications are sent when generation completes (if enabled) --- ### Image Guide Get the best results from image-to-world generation with these guidelines. #### Ideal Images[​](#ideal-images "Direct link to Ideal Images") ##### Characteristics[​](#characteristics "Direct link to Characteristics") * **Clear and sharp**: No blur or motion artifacts * **Well-lit**: Visible details without harsh shadows * **Single space**: One coherent environment * **Standard angle**: Natural viewing perspective ##### Resolution[​](#resolution "Direct link to Resolution") * Minimum: 512x512 pixels * Recommended: 1024x1024 or higher * Maximum file size: 25MB #### Image Types That Work Well[​](#image-types-that-work-well "Direct link to Image Types That Work Well") ##### Photography[​](#photography "Direct link to Photography") * Interior photography * Real estate photos * Travel and location photos * Architectural photography ##### Digital Art[​](#digital-art "Direct link to Digital Art") * Concept art * 3D renders * Architectural visualizations * Game environment screenshots #### Images to Avoid[​](#images-to-avoid "Direct link to Images to Avoid") ##### Technical Issues[​](#technical-issues "Direct link to Technical Issues") * Blurry or out-of-focus images * Heavily compressed JPEGs * Very dark or overexposed photos * Low resolution images ##### Composition Issues[​](#composition-issues "Direct link to Composition Issues") * Extreme wide-angle distortion * Fish-eye lens effects * Collages or split images * Heavy cropping ##### Content Issues[​](#content-issues "Direct link to Content Issues") * Multiple separate spaces * Primarily people or objects (not spaces) * Heavy text overlays * Strong filters or effects #### Preparing Your Images[​](#preparing-your-images "Direct link to Preparing Your Images") 1. **Check resolution**: Ensure it's at least 512x512 2. **Review clarity**: Image should be sharp and clear 3. **Verify content**: Shows a single, coherent space 4. **Consider perspective**: Standard angles work best #### Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") | Issue | Solution | | ---------------- | ------------------------------------ | | Poor depth | Try an image with more visible floor | | Missing areas | Use an image with wider coverage | | Distorted result | Avoid wide-angle or fish-eye images | | Low detail | Use a higher resolution source | --- ### Image-to-World Transform photos and images into explorable 3D environments. #### How It Works[​](#how-it-works "Direct link to How It Works") 1. Upload a reference image 2. Review the preview image and refine it if needed 3. We generate a 3D world based on the image 4. You can explore and refine the result by editing the world #### Supported Image Types[​](#supported-image-types "Direct link to Supported Image Types") * **Photos**: Real photographs of spaces * **Renders**: 3D renders and architectural visualizations * **Artwork**: Illustrations and concept art * **Screenshots**: Video game or virtual environments #### Image Requirements[​](#image-requirements "Direct link to Image Requirements") ##### Format[​](#format "Direct link to Format") * JPEG, PNG, or WebP * Maximum file size: 25MB ##### Quality[​](#quality "Direct link to Quality") * Clear, well-lit images work best * Higher resolution produces better results * Avoid heavily compressed images ##### Composition[​](#composition "Direct link to Composition") * Single-space images (one room or area) * Standard perspective (not extreme angles) * Avoid heavy obstructions in the foreground #### Best Practices[​](#best-practices "Direct link to Best Practices") ##### Do[​](#do "Direct link to Do") * Use well-lit, clear photos * Choose images with visible floor/ground * Choose images with a visible horizon ##### Avoid[​](#avoid "Direct link to Avoid") * Blurry or low-quality images * Extreme close-ups * Heavy filters or effects * Collages or split images * Images with watermarks, logos, or text #### Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") If results aren't as expected: * Try a different image angle * Ensure the image is clear and unobstructed * Check that lighting shows the space well --- ### Panorama-to-World Turn an existing 360° panorama into a fully navigable 3D world. Skips the image-to-panorama step, so generation is the fastest of the three creation modes. #### How It Works[​](#how-it-works "Direct link to How It Works") 1. Drop a 360° equirectangular panorama into the create page (same upload zone as a regular image) 2. SpAItial detects the 2:1 panorama aspect ratio and switches into panorama mode 3. The image preview opens in a 360° viewer so you can look around before generating 4. Click **Create world from pano** to start generation 5. Explore and refine the result with the standard editing tools #### Panorama Requirements[​](#panorama-requirements "Direct link to Panorama Requirements") ##### Format[​](#format "Direct link to Format") * Equirectangular projection with a 2:1 aspect ratio (e.g. 4096×2048, 6144×3072) * JPG, PNG, or WebP ##### Resolution[​](#resolution "Direct link to Resolution") * Minimum width: 2048 pixels (auto-detection threshold) * Higher resolution produces sharper results ##### Size[​](#size "Direct link to Size") * Maximum file size: 25 MB If a 2:1 image is uploaded but you intended a flat image, use the **Use as image** link on the preview to override the auto-detection. #### Capturing or Sourcing Panoramas[​](#capturing-or-sourcing-panoramas "Direct link to Capturing or Sourcing Panoramas") Any source that produces a true equirectangular projection will work: * **360° cameras**: Insta360, Ricoh Theta, GoPro Max, and similar * **Phone panorama-stitching apps**: Google Street View, Hugin, or any app that exports a full 360°×180° equirectangular image * **3D renders**: Blender, Unreal, Unity panoramic exports * **AI-generated panoramas**: any model that outputs equirectangular 2:1 images #### When to Use Panorama-to-World[​](#when-to-use-panorama-to-world "Direct link to When to Use Panorama-to-World") * You already have a 360° capture of a real space (apartments, venues, retail, exhibits) * You want the fastest generation path — typically 4–6 minutes vs 6–8 minutes for text or image inputs * You want the generated world to closely match a specific real-world location rather than an AI-imagined interpretation #### Limitations[​](#limitations "Direct link to Limitations") * The image must be a true equirectangular 2:1 projection — partial or cylindrical panoramas won't be detected as a panorama * Stitching seams, lens distortion, or visible tripods in the source carry through into the world * Panorama inputs can be previewed in 360° but not prompt-refined before generation — prompt-based edits only apply once the world is created #### Tips[​](#tips "Direct link to Tips") * Capture from chest height in the center of the space for the most natural-feeling result * Keep the camera level — a tilted horizon will tilt the entire generated world * Avoid heavy foreground occlusions (a hand, a strap, a person right next to the camera) * Use the in-page 360° preview to spot stitching issues before spending a generation --- ### Prompt Guide Learn how to write prompts that produce the best results. #### Prompt Structure[​](#prompt-structure "Direct link to Prompt Structure") A well-structured prompt typically includes: 1. **Space Type**: What kind of environment 2. **Style**: Design aesthetic 3. **Key Elements**: Important objects or features 4. **Atmosphere**: Mood and lighting #### Prompt Templates[​](#prompt-templates "Direct link to Prompt Templates") ##### Interior Space[​](#interior-space "Direct link to Interior Space") ```text A [style] [room type] with [key furniture], [materials/textures], and [lighting condition] ``` **Example:** > "A mid-century modern living room with a teal velvet sofa, walnut wood accents, brass fixtures, and warm evening lighting" ##### Exterior Space[​](#exterior-space "Direct link to Exterior Space") ```text A [style] [outdoor space] featuring [landscape elements], [architectural elements], and [time of day/weather] ``` **Example:** > "A Mediterranean courtyard garden with terracotta tiles, a central fountain, olive trees, and soft afternoon sunlight" #### Do's and Don'ts[​](#dos-and-donts "Direct link to Do's and Don'ts") ##### Do[​](#do "Direct link to Do") * Be specific about materials and colors * Mention lighting conditions * Include atmosphere descriptors * Reference specific design styles ##### Don't[​](#dont "Direct link to Don't") * Use vague terms like "nice" or "good" * List too many conflicting elements * Request specific brands or copyrighted content * Include text or signage requirements #### Style Keywords[​](#style-keywords "Direct link to Style Keywords") ##### Design Styles[​](#design-styles "Direct link to Design Styles") * Modern, contemporary, minimalist * Traditional, classic, vintage * Industrial, rustic, bohemian * Scandinavian, Japanese, Mediterranean ##### Lighting[​](#lighting "Direct link to Lighting") * Natural light, golden hour, soft ambient * Dramatic, moody, bright and airy * Warm, cool, neutral ##### Atmosphere[​](#atmosphere "Direct link to Atmosphere") * Cozy, professional, luxurious * Peaceful, energetic, mysterious * Inviting, spacious, intimate --- ### Text-to-World Transform your ideas into immersive 3D environments using natural language descriptions. #### How It Works[​](#how-it-works "Direct link to How It Works") 1. Enter a text description of your desired environment 2. We generate a preview image of the world based on your description 3. You can refine the preview image by editing the image or providing a new description 4. We generate a 3D world based on your description 5. You can explore and refine the result by editing the world #### Writing Effective Prompts[​](#writing-effective-prompts "Direct link to Writing Effective Prompts") ##### Be Descriptive[​](#be-descriptive "Direct link to Be Descriptive") Include details about: * **Space type**: room, outdoor area, building * **Style**: modern, vintage, minimalist, ornate * **Lighting**: natural, warm, dramatic, soft * **Materials**: wood, metal, fabric, stone * **Atmosphere**: cozy, professional, mysterious ##### Example Prompts[​](#example-prompts "Direct link to Example Prompts") **Good prompt:** > "A modern minimalist living room with floor-to-ceiling windows, white walls, a gray sectional sofa, and warm afternoon sunlight streaming in" **Better prompt:** > "A Scandinavian-style living room with light oak flooring, a cream-colored linen sofa, large windows overlooking a forest, a round wooden coffee table, and soft natural lighting" #### Limitations[​](#limitations "Direct link to Limitations") * Very abstract concepts may not translate well * Specific branded items won't be reproduced * Extremely detailed prompts may have varying results #### Tips[​](#tips "Direct link to Tips") * Don't overcrowd your prompt with too many little details. Focus on the overall space and the main features. * Mention lighting conditions for better atmosphere * Include materials for more realistic textures * Iterate by generating multiple times with variations --- ### Edit Prompts Write effective instructions for region edits. #### Prompt Structure[​](#prompt-structure "Direct link to Prompt Structure") Edit prompts should be clear and specific: ```text [Action] [Target] with/to [Description] ``` ##### Examples[​](#examples "Direct link to Examples") **Replacing objects:** > "Replace the chair with a mid-century modern armchair in walnut and gray fabric" **Changing colors:** > "Change the wall color to a warm terracotta" **Adding elements:** > "Add a large fiddle leaf fig plant in a white ceramic pot" **Removing items:** > "Remove the coffee table, leaving empty floor space" #### Effective Edit Prompts[​](#effective-edit-prompts "Direct link to Effective Edit Prompts") ##### Be Specific[​](#be-specific "Direct link to Be Specific") Instead of "make it better," say what specifically to improve: * ❌ "Make the room nicer" * ✅ "Replace the lamp with a brass arc floor lamp" ##### Include Details[​](#include-details "Direct link to Include Details") Describe materials, colors, and styles: * ❌ "Add a rug" * ✅ "Add a cream-colored wool rug with a subtle geometric pattern" ##### Reference Context[​](#reference-context "Direct link to Reference Context") Mention how changes relate to existing elements: * ❌ "Add plants" * ✅ "Add trailing pothos plants on the bookshelf" #### Common Edit Actions[​](#common-edit-actions "Direct link to Common Edit Actions") ##### Replace[​](#replace "Direct link to Replace") Substitute one thing for another: > "Replace the dining table with a round marble-top table" ##### Change[​](#change "Direct link to Change") Modify properties of existing items: > "Change the sofa fabric to navy blue velvet" ##### Add[​](#add "Direct link to Add") Introduce new elements: > "Add a gallery wall with black frames above the sofa" ##### Remove[​](#remove "Direct link to Remove") Take away elements: > "Remove the floor lamp" ##### Modify[​](#modify "Direct link to Modify") Adjust characteristics: > "Make the window larger and add sheer curtains" #### Tips[​](#tips "Direct link to Tips") * Match the style of changes to the existing room * Consider lighting when changing colors * Don't try to change too much in one edit * Use undo if results aren't right --- ### Finalize Draft Apply your edits permanently by finalizing your draft. #### What is Finalizing?[​](#what-is-finalizing "Direct link to What is Finalizing?") When you edit a world, changes are saved as a **draft**. Finalizing commits these changes, making them permanent. #### When to Finalize[​](#when-to-finalize "Direct link to When to Finalize") Finalize when you're satisfied with all your edits and ready to commit them. ##### Ready to Finalize[​](#ready-to-finalize "Direct link to Ready to Finalize") * Edits look good * No more changes needed * Ready to share or export ##### Not Ready Yet[​](#not-ready-yet "Direct link to Not Ready Yet") * Still making adjustments * Want to try more variations * Unsure about current changes #### How to Finalize[​](#how-to-finalize "Direct link to How to Finalize") 1. Complete your edits 2. Review the current state 3. Click **Create world** 4. A new world is created from your edits, your old world is left as is. ##### Best Practice[​](#best-practice "Direct link to Best Practice") * Take your time with edits * Review carefully before finalizing * Consider the complete picture, not just the last edit --- ### Editing Overview Refine and perfect your worlds with SpAItial's editing tools. #### What You Can Edit[​](#what-you-can-edit "Direct link to What You Can Edit") SpAItial allows you to modify specific regions of your generated worlds: * Change furniture or objects * Modify colors and materials * Add or remove elements * Adjust lighting and atmosphere #### Editing Workflow[​](#editing-workflow "Direct link to Editing Workflow") 1. **Select a World**: Open a completed world from My Worlds 2. **Enter Edit Mode**: Click the Edit button 3. **Select a Region**: Choose the area you want to modify 4. **Provide Instructions**: Describe the changes you want 5. **Generate**: AI processes your edit request 6. **Review**: Check the result 7. **Finalize or Continue**: Keep editing or finalize your changes #### Edit Types[​](#edit-types "Direct link to Edit Types") ##### Region Edit[​](#region-edit "Direct link to Region Edit") Select a specific area using our region selection tool and regenerate just that portion with new instructions. ##### Pano Edit[​](#pano-edit "Direct link to Pano Edit") Edit based on the panoramic view of your world using our region selection tool. #### Important Concepts[​](#important-concepts "Direct link to Important Concepts") ##### Drafts[​](#drafts "Direct link to Drafts") Edits create a draft version. You can: * Continue making more edits * Finalize to apply changes to the world * Discard to revert to the original ##### Undo/Redo[​](#undoredo "Direct link to Undo/Redo") Made a mistake? Use versions toggle to browse and use any previous version as a starting point for your next edit. #### Getting Started[​](#getting-started "Direct link to Getting Started") Ready to edit? Start with: * [Pano Edit](https://docs.spaitial.ai/edit/pano-edit.md) for visual region selection * [Select Regions](https://docs.spaitial.ai/edit/select-regions.md) for precise selection * [Edit Prompts](https://docs.spaitial.ai/edit/edit-prompts.md) for writing effective instructions --- ### Pano Edit Edit worlds using an intuitive panoramic interface. #### What is Pano Edit?[​](#what-is-pano-edit "Direct link to What is Pano Edit?") Pano Edit provides a 360° view of your world, allowing you to select and edit regions visually. #### Using Pano Edit[​](#using-pano-edit "Direct link to Using Pano Edit") ##### Step 1: Enter Pano Edit Mode[​](#step-1-enter-pano-edit-mode "Direct link to Step 1: Enter Pano Edit Mode") 1. Open a completed world 2. Click **Edit World** in the editor toolbar 3. This opens the **Pano Edit** view ##### Step 2: Navigate the View[​](#step-2-navigate-the-view "Direct link to Step 2: Navigate the View") * Click and drag to look around * Use scroll to zoom in and out * Find the area you want to modify ##### Step 3: Select a Region[​](#step-3-select-a-region "Direct link to Step 3: Select a Region") * Click the annotate icon to go into region selection mode * Click and drag on the area you want to edit. The selected region will be highlighted in red. * Add a text label to the region by clicking the text icon and typing in the label. ##### Step 4: (Optional) Describe general changes to panoramic view[​](#step-4-optional-describe-general-changes-to-panoramic-view "Direct link to Step 4: (Optional) Describe general changes to panoramic view") If you want to make general changes to the panoramic view, you can enter your edit instructions in the text area: > "Replace the sofa with a modern white sectional" #### Tips for Pano Edit[​](#tips-for-pano-edit "Direct link to Tips for Pano Edit") ##### Region Selection[​](#region-selection "Direct link to Region Selection") * Select enough area to include context * Don't select too large an area unless needed * Ensure your selection fully covers the target object ##### Instructions[​](#instructions "Direct link to Instructions") * Be specific about what to change * Reference the current state if helpful * Include style and material details #### Limitations[​](#limitations "Direct link to Limitations") * Very large selections may produce varied results * Complex changes may need multiple edits * Some structural changes have limits --- ### Select Regions Master region selection for precise edits. #### Selection Methods[​](#selection-methods "Direct link to Selection Methods") ##### Brush Selection[​](#brush-selection "Direct link to Brush Selection") Draw a freehand selection over the area you want to edit. * Adjust brush size as needed * A circle covering the selected area will be highlighted in red. * Add a text label to the region by clicking the text icon and typing in the label. #### Selection Best Practices[​](#selection-best-practices "Direct link to Selection Best Practices") ##### Size Matters[​](#size-matters "Direct link to Size Matters") * **Too small**: May not capture enough context * **Too large**: May affect unwanted areas * **Just right**: Include the target plus a bit of surrounding area ##### Include Context[​](#include-context "Direct link to Include Context") The AI works better when it can see: * The floor or surface the object sits on * Surrounding objects for scale * Lighting context ##### Clean Boundaries[​](#clean-boundaries "Direct link to Clean Boundaries") * Don't split objects awkwardly * Include complete items in selection * Avoid cutting through important features #### Common Selection Scenarios[​](#common-selection-scenarios "Direct link to Common Selection Scenarios") | Goal | Selection Approach | | ----------------- | -------------------------------------------- | | Change furniture | Select the entire piece plus a bit of floor | | Modify wall color | Select the visible wall area | | Add an object | Select the empty space where it should go | | Remove something | Select the object and immediate surroundings | --- ### Editing Troubleshooting Solutions for common editing issues. #### Edit Not Working as Expected[​](#edit-not-working-as-expected "Direct link to Edit Not Working as Expected") ##### Problem: Changes don't match instructions[​](#problem-changes-dont-match-instructions "Direct link to Problem: Changes don't match instructions") **Solutions:** * Be more specific in your prompt * Select a different region size * Try rephrasing your instructions * Include more context about desired style ##### Problem: Edit affects unwanted areas[​](#problem-edit-affects-unwanted-areas "Direct link to Problem: Edit affects unwanted areas") **Solutions:** * Make a more precise selection * Use a smaller brush size * Ensure selection doesn't overlap other objects ##### Problem: New object doesn't fit the scene[​](#problem-new-object-doesnt-fit-the-scene "Direct link to Problem: New object doesn't fit the scene") **Solutions:** * Mention the existing style in your prompt * Specify colors that match the room * Reference existing materials #### Technical Issues[​](#technical-issues "Direct link to Technical Issues") ##### Problem: Edit is taking too long[​](#problem-edit-is-taking-too-long "Direct link to Problem: Edit is taking too long") **What to do:** * Wait - complex edits may take longer * Check your internet connection * Refresh and check if it completed * Contact support if stuck over 15 minutes ##### Problem: Edit failed[​](#problem-edit-failed "Direct link to Problem: Edit failed") **Solutions:** * Try again with the same parameters * Simplify your edit request * Check if the world itself has issues * Report persistent failures to support ##### Problem: Selection tools not working[​](#problem-selection-tools-not-working "Direct link to Problem: Selection tools not working") **Solutions:** * Refresh the page * Try a different browser * Clear browser cache * Check for browser extensions interfering #### Quality Issues[​](#quality-issues "Direct link to Quality Issues") ##### Problem: Blurry or low quality result[​](#problem-blurry-or-low-quality-result "Direct link to Problem: Blurry or low quality result") **Solutions:** * Wait for full processing * Try a more specific prompt * Edit a smaller region ##### Problem: Unrealistic results[​](#problem-unrealistic-results "Direct link to Problem: Unrealistic results") **Solutions:** * Use more descriptive prompts * Reference real-world styles * Keep changes realistic #### Prevention Tips[​](#prevention-tips "Direct link to Prevention Tips") * Save copies before major edits * Make incremental changes * Test with small edits first * Write clear, specific prompts --- ### Edit Versions Every region edit creates a new version of your panorama. You can step back and forward through versions at any time during an editing session. #### How It Works[​](#how-it-works "Direct link to How It Works") * Each successful edit appends a new version to the panorama's history. * The current version indicator (`v3/5`) shows which version you're viewing and how many exist. * Versions are kept per world and persist if you leave and come back to the editor. #### Navigating Versions[​](#navigating-versions "Direct link to Navigating Versions") In the top right of the editor, you'll see two arrow buttons with a version counter between them: * **Previous version (←)**: Step back to an earlier version. * **Next version (→)**: Step forward toward your latest edit. If you make a new edit while viewing an older version, versions after that point are discarded and replaced by the new branch. #### Finalizing[​](#finalizing "Direct link to Finalizing") When you're happy with a version, finalize it. The finalized version becomes the new base panorama and earlier versions are cleared from the editor. #### Tips[​](#tips "Direct link to Tips") * Make incremental edits to keep granular versions to fall back to. * Compare a few versions before finalizing — once finalized, earlier drafts are gone. * If an edit didn't land the way you wanted, step back one version, refine your selection or prompt, and try again. --- ### Credits and Generation Learn how credits work in SpAItial. #### How Credits Work[​](#how-credits-work "Direct link to How Credits Work") SpAItial uses a shared credit balance for the web app and Developer API: * 1 credit = $0.01 * Free users receive 800 credits each month, enough for about 5 Echo 2 - Standard generations * Free and subscription credits expire at the end of their monthly credit period and do not roll over * Purchased top-up credits roll over and do not expire * Credits are spent from the soonest-expiring balance first * Standard and Pro subscribers can purchase additional credits shared across app, web app, and API usage #### Model Pricing[​](#model-pricing "Direct link to Model Pricing") | Model | Best for | Typical time | Credits | | ----------------- | --------------------------------------- | ------------ | ------- | | Echo 2 - Standard | Fast, general-purpose world generation | ~8 minutes | 160 | | Echo 2 (HQ) | Higher-detail, higher-resolution worlds | ~60 minutes | 800 | #### Plans[​](#plans "Direct link to Plans") | Plan | Included credits | Echo-3 capture minutes | Echo-2 generations | Echo HQ generations | Available features | Price | | ---------- | -------------------- | ---------------------- | ------------------ | ------------------- | ------------------------------------------------------------------------------------- | ---------------------------- | | Free | 800 credits/month | 0 included | ~5/month | ~1/month | Capture app, downloads, SOG/PLY/SPZ exports, world editing, and collider mesh export. | $0 | | Standard | 7,500 credits/month | 25/month | ~46 | ~9 | Everything in Free, Echo-2 Standard and Echo HQ API access; non-commercial use. | $49.99/month or $479.99/year | | Pro | 15,000 credits/month | 50/month | ~93 | ~18 | Everything in Free, Echo-2 Standard and Echo HQ API access with commercial rights. | $99.99/month or $959.99/year | | Enterprise | Custom volume | Custom | Custom volume | Custom volume | Everything in Free, multiple users, commercial rights, and Echo-3 API access. | Contact us | New paid subscriptions include a card-required first month free. Annual plans are discounted 20% and still grant credits every month. #### What Uses Credits[​](#what-uses-credits "Direct link to What Uses Credits") | Action | Echo 2 - Standard | Echo 2 (HQ) | | ------------------------------ | ----------------- | ----------- | | Text-to-World | 160 credits | 800 credits | | Image-to-World | 160 credits | 800 credits | | Panorama-to-World | 160 credits | 800 credits | | Developer API world generation | 160 credits | 800 credits | #### What Doesn't Count[​](#what-doesnt-count "Direct link to What Doesn't Count") * Viewing worlds * Editing world details (title, description) * Sharing worlds * Downloading, SOG/PLY/SPZ exports, and collider mesh export #### When You Need More Credits[​](#when-you-need-more-credits "Direct link to When You Need More Credits") If your balance is too low to generate: * Purchase 1,600, 3,200, or 8,000-credit packs from billing * Upgrade to Standard, Pro, or Custom/Enterprise for monthly included credits Top-up packs remain available across subscription periods and do not expire. #### Tips for Managing Credits[​](#tips-for-managing-credits "Direct link to Tips for Managing Credits") * Plan your generations thoughtfully * Review prompts before generating * Use edits efficiently when available * Keep enough purchased credits for API workloads that may run outside your normal app usage --- ### Credits FAQ Common questions about credits, plans, and generation costs. #### Credits[​](#credits "Direct link to Credits") ##### How much is one credit worth?[​](#how-much-is-one-credit-worth "Direct link to How much is one credit worth?") One credit equals $0.01. ##### How many credits does a world cost?[​](#how-many-credits-does-a-world-cost "Direct link to How many credits does a world cost?") | Model | Best for | Typical time | Credits | | --------------- | --------------------------------------- | ------------ | ------- | | Echo-2 Standard | Fast, general-purpose world generation | ~8 minutes | 160 | | Echo HQ | Higher-detail, higher-resolution worlds | ~60 minutes | 800 | ##### Do free users get credits?[​](#do-free-users-get-credits "Direct link to Do free users get credits?") Yes. Free users receive 800 credits each month, enough for about 5 Echo-2 generations. Credits work across Capture, the web app, and the Developer API. Unused free credits expire after one month and do not roll over. Free does not include Capture scan minutes. ##### Which credits are used first?[​](#which-credits-are-used-first "Direct link to Which credits are used first?") The wallet spends the soonest-expiring credits first: monthly free credits, subscription credits, purchased top-ups, then permanent grants. #### Plans and Purchases[​](#plans-and-purchases "Direct link to Plans and Purchases") ##### What's included in Standard?[​](#whats-included-in-standard "Direct link to What's included in Standard?") Standard includes 7,500 credits per month for non-commercial use. Launch pricing is $49.99/month or $479.99/year, with the first month free when a card is provided. The plan includes 25 Capture scan minutes per month. | Echo-2 generations | Echo HQ generations | | ------------------ | ------------------- | | ~46 | ~9 | ##### What's included in Pro?[​](#whats-included-in-pro "Direct link to What's included in Pro?") Pro includes 15,000 credits per month and commercial rights. Launch pricing is $99.99/month or $959.99/year, with the first month free when a card is provided. The plan includes 50 Capture scan minutes per month. | Echo-2 generations | Echo HQ generations | | ------------------ | ------------------- | | ~93 | ~18 | ##### What if I need more volume?[​](#what-if-i-need-more-volume "Direct link to What if I need more volume?") Use Enterprise for larger volume, multiple users, Echo-3 API access, or procurement needs. ##### Can I purchase credits without changing plans?[​](#can-i-purchase-credits-without-changing-plans "Direct link to Can I purchase credits without changing plans?") Standard and Pro subscribers can purchase additional credits. Purchased credits are shared across the app, web app, and Developer API. They roll over between subscription periods and do not expire. #### Developer API[​](#developer-api "Direct link to Developer API") ##### Who can use the Developer API?[​](#who-can-use-the-developer-api "Direct link to Who can use the Developer API?") Developer API access is public for all users. API keys use the same shared credit balance as the app. ##### Does the API have separate billing?[​](#does-the-api-have-separate-billing "Direct link to Does the API have separate billing?") No. World generation uses the same account credits whether it starts from the app or API. Reads, status polling, and downloads are not charged as generation. --- ### SpAItial App Service Description *Last updated: September 23, 2026* This page describes the customer-facing scope, principal features, and current limitations of the SpAItial App. It is intended to help customers understand what the service provides before and while using it. #### Service scope[​](#service-scope "Direct link to Service scope") The SpAItial App is a hosted web application available at [app.spaitial.ai](https://app.spaitial.ai). It enables customers to create, view, edit, organize, share, and export AI-generated 3D worlds. Customers provide text prompts, images, or equirectangular 360° panoramas. SpAItial processes those inputs asynchronously and produces navigable 3D Gaussian Splat worlds that can be viewed in a supported browser. This description covers the interactive web application. Programmatic access is a separate service described in the [SpAItial Developer API Service Description](https://docs.spaitial.ai/api/service-description.md). #### Primary features[​](#primary-features "Direct link to Primary features") | Feature | Description | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | World generation | Generate a 3D world from a text prompt, supported image, or 360° panorama. | | Browser-based exploration | Navigate completed worlds with the interactive 3D viewer. | | AI-assisted editing | Select regions of a generated world, describe a change, review draft versions, and finalize an edit. | | World and workspace management | Organize generated worlds in workspaces and manage their details. | | Privacy and sharing | Keep a world private, share an unlisted public link, or make a world discoverable in public galleries. | | Downloads and exports | Download supported Gaussian Splat formats, including SPZ, PLY, compressed PLY, and SOG. Some export features depend on the customer's plan. | | Credits and billing | Use account credits for generation and manage available plans and additional credit purchases. | #### Access and service requirements[​](#access-and-service-requirements "Direct link to Access and service requirements") Customers need: * A SpAItial account and a stable internet connection. * A supported modern browser with WebGL 2.0. * Hardware capable of rendering interactive 3D content. * Sufficient credits for credit-consuming generation operations. See [Platform Compatibility](https://docs.spaitial.ai/overview/platform-compatibility.md) and [Credits and Generation](https://docs.spaitial.ai/limits/daily-generation-limits.md) for current requirements and plan details. #### Limitations and customer considerations[​](#limitations-and-customer-considerations "Direct link to Limitations and customer considerations") * **Generated results vary.** Worlds and edits are produced by generative AI. Results may not precisely reproduce an input or prompt and may contain visual, geometric, or semantic artifacts. * **Generation is asynchronous.** Published generation times are typical estimates, not guaranteed completion times. Queue position, system load, input complexity, and model selection can affect processing time. A job may also fail or be cancelled. * **Input quality affects output quality.** Clear, high-quality scene images generally produce better results. Blurry, heavily compressed, obstructed, or non-scene images may produce poor results. * **Rendering depends on the customer's device.** Older devices may have reduced performance. WebGL 2.0 is required, and VR headset browsing is not currently supported. * **Exports require compatible software.** The primary 3D outputs are Gaussian Splat formats. Format support and coordinate conventions vary among third-party tools, and an exported file may require orientation or viewer configuration outside SpAItial. * **Public links are accessible to others.** Anyone who receives an unlisted public link can view and forward it. Listed worlds are discoverable in public galleries. New worlds are private by default, and customers control when their visibility changes. * **Entitlements vary.** Available models, credits, usage capacity, and some export features depend on the customer's plan or separately agreed terms. #### Customer responsibilities[​](#customer-responsibilities "Direct link to Customer responsibilities") Customers are responsible for: * Providing content they are authorized to use. * Reviewing generated results before relying on or distributing them. * Selecting appropriate world privacy settings before sharing content. * Confirming that exported formats are compatible with their downstream tools. * Protecting their account credentials and promptly reporting suspected unauthorized access. Use of the service is also governed by the [Terms of Service](https://app.spaitial.ai/terms) and [Privacy Policy](https://app.spaitial.ai/privacy). #### Related documentation and support[​](#related-documentation-and-support "Direct link to Related documentation and support") * [Create your first world](https://docs.spaitial.ai/create/first-world.md) * [Editing overview](https://docs.spaitial.ai/edit/overview.md) * [Generation times](https://docs.spaitial.ai/overview/generation-times.md) * [World privacy](https://docs.spaitial.ai/worlds/privacy.md) * [Download and export](https://docs.spaitial.ai/worlds/download-export.md) * [Credits FAQ](https://docs.spaitial.ai/limits/limits-faq.md) * [Security](https://spaitial.ai/security) * [Subprocessors](https://spaitial.ai/subprocessors) For service questions or support, use the [Spaitial Support page](https://spaitial.ai/support) for troubleshooting and the contact form. This description is updated when material changes are made to the App's scope, features, requirements, or limitations. Product changes are also summarized in the [release notes](https://docs.spaitial.ai/overview/release-notes.md). --- ### Core Concepts Before diving in, familiarize yourself with these key concepts in SpAItial. #### Worlds[​](#worlds "Direct link to Worlds") A **World** is the primary unit in SpAItial - a 3D environment you can explore, edit, and share. Worlds are generated from text prompts or images using AI. ##### World States[​](#world-states "Direct link to World States") * **Processing**: The AI is creating your world * **Completed**: Your world is complete and viewable * **Cancelled**: The generation process was cancelled by the user * **Failed**: The generation process encountered an error #### Workspaces[​](#workspaces "Direct link to Workspaces") **Workspaces** help you organize your worlds into collections. You might have separate workspaces for different projects, clients, or themes. #### Generation[​](#generation "Direct link to Generation") **Generation** is the process of creating a world from your input. This involves: 1. Processing your prompt or image 2. AI model inference 3. 3D reconstruction 4. Final rendering #### Edits[​](#edits "Direct link to Edits") **Edits** allow you to modify specific regions of an existing world. Select an area, provide new instructions, and SpAItial will regenerate just that portion. #### Privacy[​](#privacy "Direct link to Privacy") Every world has a **privacy setting**: * **Private**: Only you can view * **Public**: Anyone with the link can view * **Listed**: Appears in [community galleries](https://app.spaitial.ai/explore?source=community) #### Credits[​](#credits "Direct link to Credits") SpAItial uses credits for world generation across the web app and Developer API. One credit equals $0.01. | Model | Best for | Typical time | Credits | | ----------------- | --------------------------------------- | ------------ | ------- | | Echo 2 - Standard | Fast, general-purpose world generation | ~8 minutes | 160 | | Echo 2 (HQ) | Higher-detail, higher-resolution worlds | ~60 minutes | 800 | Free users receive a one-time 800 credit starter balance, enough for 5 Echo 2 - Standard generations. Paid plans include monthly credits, and subscribers can purchase 1,600, 3,200, or 8,000-credit packs when they need more. Included subscription credits are used before purchased credits. --- ### Generation Times World generation involves complex AI processing. Here's what to expect. #### Typical Times[​](#typical-times "Direct link to Typical Times") | Generation Type | Echo 2 - Standard | Echo 2 (HQ) | | ----------------- | ------------------------ | ------------------------- | | Text-to-World | ~8 minutes, 160 credits | ~60 minutes, 800 credits | | Image-to-World | ~8 minutes, 160 credits | ~60 minutes, 800 credits | | Panorama-to-World | ~8 minutes, 160 credits | ~60 minutes, 800 credits | | Region Edit | 4–6 minutes | 4–6 minutes | Echo 2 (HQ) is the high quality model for more detail and resolution. It takes longer than Standard because it runs a heavier generation pipeline. #### Factors that may affect Time[​](#factors-that-may-affect-time "Direct link to Factors that may affect Time") ##### Queue Position[​](#queue-position "Direct link to Queue Position") * During peak times, there may be a queue * Jobs are processed in order received ##### System Load[​](#system-load "Direct link to System Load") * Generation times may vary based on overall platform usage #### Generation States[​](#generation-states "Direct link to Generation States") While your world generates, you'll see status updates: 1. **Queued**: Waiting to start 2. **Processing**: The generation process is working 3. **Completed**: Preparing for viewing 4. **Cancelled**: The generation process was cancelled by the user 5. **Failed**: The generation process encountered an error #### Timeouts[​](#timeouts "Direct link to Timeouts") If generation takes longer than expected: * The system will continue working and you will be notified when it is completed * You can navigate away and return to My Worlds to check the status or retry the generation * If it persists, you can contact support and we will investigate the issue --- ### Platform Compatibility SpAItial works across modern browsers and devices. Here's what you need for the best experience. #### Supported Browsers[​](#supported-browsers "Direct link to Supported Browsers") ##### Recommended[​](#recommended "Direct link to Recommended") * **Chrome** 90+ (best performance) * **Edge** 90+ * **Firefox** 90+ * **Safari** 15+ ##### Mobile Browsers[​](#mobile-browsers "Direct link to Mobile Browsers") * Chrome for Android * Safari for iOS (iOS 15+) #### Device Requirements[​](#device-requirements "Direct link to Device Requirements") ##### Desktop[​](#desktop "Direct link to Desktop") * Modern GPU recommended for smooth 3D viewing * 4GB+ RAM * Stable internet connection ##### Mobile[​](#mobile "Direct link to Mobile") * iPhone 8 or newer (iOS 15+) * Android devices with WebGL 2.0 support * Tablet support for iOS and Android #### WebGL Support[​](#webgl-support "Direct link to WebGL Support") SpAItial requires WebGL 2.0 for 3D rendering. Most modern browsers and devices support this, but some older hardware may have limitations. To check WebGL support, visit [get.webgl.org](https://get.webgl.org/). #### Known Limitations[​](#known-limitations "Direct link to Known Limitations") * Older mobile devices may experience reduced performance * VR headset browsing is not currently supported * Some corporate networks may block WebGL content --- ### Release Notes Stay up to date with the latest improvements to SpAItial. *** #### September 2026[​](#september-2026 "Direct link to September 2026") ##### New Features[​](#new-features "Direct link to New Features") * Dollhouse view, cinematic recording, and floor plans are now available to everyone * Redesigned minimap controls with quick settings and pinch-to-zoom ##### Improvements[​](#improvements "Direct link to Improvements") * Viewer assets are now generated in the background so new worlds are ready to explore sooner * Expanded security audit logging and alerting across the platform ##### Bug Fixes[​](#bug-fixes "Direct link to Bug Fixes") * Shared worlds now re-prompt for the password when a session expires instead of failing silently * Notifications now show the final generated world title * More reliable retries for interrupted world generations *** #### August 2026[​](#august-2026 "Direct link to August 2026") ##### New Features[​](#new-features-1 "Direct link to New Features") * Sign in with Apple * Interactive dollhouse navigation with adjustable roof cut ##### Improvements[​](#improvements-1 "Direct link to Improvements") * New cookie policy page and clearer privacy contact routing ##### Bug Fixes[​](#bug-fixes-1 "Direct link to Bug Fixes") * Closed a number of third-party dependency security advisories *** #### July 2026[​](#july-2026 "Direct link to July 2026") ##### New Features[​](#new-features-2 "Direct link to New Features") * Build worlds from multiple panoramas through the developer API * Path studio with keyframe management for authoring camera paths * Invite teammates to your workspace ##### Improvements[​](#improvements-2 "Direct link to Improvements") * Image upload limit raised from 25MB to 100MB * Sharper streamed level-of-detail rendering, especially on mobile * The viewer now waits for finer detail to load before revealing the scene * New worlds now default to unlisted for better privacy *** #### June 2026[​](#june-2026 "Direct link to June 2026") ##### New Features[​](#new-features-3 "Direct link to New Features") * New in-browser viewer with progressive streaming for faster loading of large worlds * Point cloud viewing mode with adjustable point size * Auto top-up keeps your credit balance ready for large generations * Edit panoramas programmatically through the developer API * New educational tutorials to help you get the most out of SpAItial ##### Improvements[​](#improvements-3 "Direct link to Improvements") * Tuned splat rendering quality and memory use on mobile devices * More responsive worlds list and dialogs on mobile ##### Bug Fixes[​](#bug-fixes-2 "Direct link to Bug Fixes") * Fixed password recovery redirects and expired reset link handling * More accurate walk-mode collisions * Fixed curated mesh download paths *** #### May 2026[​](#may-2026 "Direct link to May 2026") ##### New Features[​](#new-features-4 "Direct link to New Features") * Generate worlds directly from 360° panorama images * Automatic image cleanup detects and corrects common upload issues before generation * Reservable usernames and public profile pages * Additional splat download formats, including PLY * Community gallery for sharing public worlds ##### Improvements[​](#improvements-4 "Direct link to Improvements") * Smarter content moderation with fewer false positives on benign terms * Page refresh now restores the current scene instead of returning to the first * Filter the community gallery by input type and creator ##### Bug Fixes[​](#bug-fixes-3 "Direct link to Bug Fixes") * Fixed a case where progress notifications could mark a completed world as still generating * Improved reliability of the image auto-fix flow * Standardized cancellation messages across the app *** #### April 2026[​](#april-2026 "Direct link to April 2026") ##### New Features[​](#new-features-5 "Direct link to New Features") * Higher-quality world generation with improved visual fidelity * Upgraded in-browser viewer with sharper detail and smoother rendering on large scenes * Cinematic recorder exports smooth MP4 videos of your world * Rename worlds from the worlds list * Redesigned create-world flow with an image-first composer and clearer calls to action ##### Improvements[​](#improvements-5 "Direct link to Improvements") * Faster gallery and worlds list loading * Mobile-friendly worlds list and editor toolbar * Generation progress now includes a remaining time estimate * Faster thumbnail generation ##### Bug Fixes[​](#bug-fixes-4 "Direct link to Bug Fixes") * Fixed color artifacts that could appear on certain scenes * Fixed cancellation of an in-progress recording * Smoother transition from cinematic fly-in into orbit * Thumbnails now respect the original photo orientation * More reliable live progress updates * Worlds list now refreshes promptly when generations finish *** #### March 2026[​](#march-2026 "Direct link to March 2026") ##### New Features[​](#new-features-6 "Direct link to New Features") * Auto camera with multiple cinematic modes and smooth transitions * Short clip recording with MP4 export and resolution presets * Persistent image edit history with versioning * World creation opens as an in-page overlay so you keep your context ##### Improvements[​](#improvements-6 "Direct link to Improvements") * Mouse orbit defaults to a more natural drag direction * Faster world loading with animated reveal and on-device caching * Smoother camera deceleration when moving with WASD * Collapsible editor toolbar on mobile ##### Bug Fixes[​](#bug-fixes-5 "Direct link to Bug Fixes") * Failed worlds now surface a clear error * Fixed Space-key camera control and a rendering glitch in the editor * Cancellation now works while editing * Cleaner previews when sharing world links *** #### Coming Soon[​](#coming-soon "Direct link to Coming Soon") We're always working to improve SpAItial. Planned features include: * Additional export formats including USDZ and Collision Mesh * Collaboration features * Advanced editing capabilities Check back for updates as we continue to enhance the platform. --- ### Welcome to SpAItial [](https://pub-fd1d4d4f8ef34c7e8597ee625f885bf9.r2.dev/final_app_intro.mp4) #### What is SpAItial?[​](#what-is-spaitial "Direct link to What is SpAItial?") SpAItial can generate 3D environments from simple inputs like text prompts, images, or 360° panoramas. The platform creates fully navigable worlds that you can explore, edit, share, and export. #### Quick Start[​](#quick-start "Direct link to Quick Start") 1. **Sign up** for a SpAItial account at [app.spaitial.ai](https://app.spaitial.ai) 2. **Create your first world** using text, an image, or a 360° panorama 3. **Explore** your generated world in the browser 4. **Edit and refine** specific regions of your world using our editing tools 5. **Share** your creation with others via a public link #### Key Features[​](#key-features "Direct link to Key Features") * **[Text-to-World](https://docs.spaitial.ai/create/text-to-world.md)**: Describe your vision and watch it come to life * **[Image-to-World](https://docs.spaitial.ai/create/image-to-world.md)**: Transform photos or artwork into explorable spaces * **[Panorama-to-World](https://docs.spaitial.ai/create/panorama-to-world.md)**: Turn a 360° panorama into a navigable 3D world * **[Editing Tools](https://docs.spaitial.ai/edit/overview.md)**: Refine specific regions of your world * **[Sharing](https://docs.spaitial.ai/worlds/share.md)**: Make worlds public or share with specific users * **[Export](https://docs.spaitial.ai/worlds/download-export.md)**: Download your creations in various formats #### Getting Help[​](#getting-help "Direct link to Getting Help") * Browse this documentation for detailed guides * Check the [Credits FAQ](https://docs.spaitial.ai/limits/limits-faq.md) for common billing questions * Use the [Support page](https://spaitial.ai/support) for how-tos, troubleshooting, and the contact form --- ### What You Can Create SpAItial enables you to create a wide variety of immersive 3D environments. Here's an overview of what's possible. #### Environment Types[​](#environment-types "Direct link to Environment Types") Each item below links to a live example in the [public gallery](https://app.spaitial.ai/explore). ##### Interior Spaces[​](#interior-spaces "Direct link to Interior Spaces") * Living rooms, bedrooms, kitchens — [Coastal Loft Living](https://app.spaitial.ai/worlds/0c7b4de0-0928-4abb-a2f2-49b5371ad631), [Plant-Wall Bedroom Retreat](https://app.spaitial.ai/worlds/3252674a-e65b-4e56-9a42-66516dd747cf), [Bohemian Kitchenette Glow](https://app.spaitial.ai/worlds/26d7db6c-36bd-4879-bf6b-7c337279fe0c) * Offices and workspaces — [Velvet Home Office Nook](https://app.spaitial.ai/worlds/a03ab0cc-f3e6-446a-a9d4-1cbef91e8ed2), [Academic Dorm Room Set](https://app.spaitial.ai/worlds/b2969ad7-91ca-4044-b2c9-3401eb9b3957) * Restaurants, cafes, and retail spaces — [Modern Bistro Dining Room](https://app.spaitial.ai/worlds/b8a2f40a-b68a-4c77-a066-8170f0f63c43), [Oak Barrel Wine Tavern](https://app.spaitial.ai/worlds/16e4a98c-8c00-4a00-9fd9-d3396420819e), [Minimal Restaurant Interior](https://app.spaitial.ai/worlds/4e4a3c4a-cb5f-40e4-88db-d8981f54012c) * Museums and galleries — [Monochrome Art Gallery](https://app.spaitial.ai/worlds/d5778b86-ce29-45af-a34a-084e6b63a534), [Contemporary Gallery Walkthrough](https://app.spaitial.ai/worlds/a57bd5a9-5719-4dfa-a910-2c43b62a9d23), [Classic Library Interior](https://app.spaitial.ai/worlds/f213715a-a402-4436-8ae4-9b9227fe73ba) ##### Exterior Environments[​](#exterior-environments "Direct link to Exterior Environments") * Gardens and outdoor spaces — [Villa Borghese Garden View](https://app.spaitial.ai/worlds/27c0850d-3dfb-4dd2-b82a-9ed911c286c3), [Whimsical Shoe House Garden](https://app.spaitial.ai/worlds/74849f7f-fccb-42cc-88d8-ff6d8b25211b) * Cityscapes and urban environments — [Vertical Ocean Megacity](https://app.spaitial.ai/worlds/3f5ef396-c807-4924-9e28-91397010b291), [Vibrant Urban Alleyway](https://app.spaitial.ai/worlds/96114c4a-1a6b-4f4a-911d-7f0235163706), [Vintage City Alley](https://app.spaitial.ai/worlds/df8cec35-593e-4114-a7e4-2ef6a4ea3bef) * Natural landscapes — [Snow-Capped Mountain Vista](https://app.spaitial.ai/worlds/284c4b3f-2be9-49a5-a717-e4bbf0d2e4f4), [Tropical Rainforest Calm](https://app.spaitial.ai/worlds/1dadf708-4c98-4e82-a9e0-c09e090dd87b), [Serene Forest Escape](https://app.spaitial.ai/worlds/e51ea369-daa2-4317-94ec-2d8017b37a14) * Architectural exteriors — [Iconic Landmark Reflection](https://app.spaitial.ai/worlds/1a85b849-4053-4d23-b3e7-ad213eab8934), [Ancient Landscape Discovery](https://app.spaitial.ai/worlds/43bba1cc-8a78-4e15-8a61-9ae6d35f0565) ##### Fantasy & Sci-Fi[​](#fantasy--sci-fi "Direct link to Fantasy & Sci-Fi") * Futuristic cities — [Eco Futuristic Village](https://app.spaitial.ai/worlds/cb454017-bff6-4d82-8b0c-5e801821cf8f), [Neon Futuristic Lounge](https://app.spaitial.ai/worlds/a93c78cc-7e4c-41f0-812f-febf9af73967), [Cyberpunk Neon Bedroom](https://app.spaitial.ai/worlds/68f3f8f6-18dc-47ec-999e-aafb6038a298) * Medieval castles and palaces — [Chambord Castle Grandeur](https://app.spaitial.ai/worlds/7a027724-8ae6-4ebc-a15e-4062908c5c5b), [Regal Palace Ballroom](https://app.spaitial.ai/worlds/f91c1fac-1140-4af6-bf9f-c7121ddbc3f5), [Ornate Cathedral Interior](https://app.spaitial.ai/worlds/40aa98b0-4c48-4c9c-913b-c922ce61533c) * Storybook and enchanted scenes — [Enchanted Mushroom House](https://app.spaitial.ai/worlds/1336a427-8d07-42df-b5b2-2764411ca3e4), [Storybook Treehouse Interior](https://app.spaitial.ai/worlds/01972107-ec28-49f7-8bc6-2848dc61de3f), [Enchanted Forest Cottage](https://app.spaitial.ai/worlds/e95816fa-1b5b-4ee5-a0c8-a829fb44b1c2) * Post-apocalyptic and abandoned worlds — [Post-Apocalyptic Pumping Station](https://app.spaitial.ai/worlds/f8925468-d8d1-4fd7-b03b-2aab942ec5a7), [Abandoned Zinc Mine](https://app.spaitial.ai/worlds/e1654cdf-878b-4592-a4ac-04f215674a5c), [Creepy Examination Waiting Room](https://app.spaitial.ai/worlds/2709e449-ed84-4a10-9886-e1112fe8da4b) ##### Real Estate & Architecture[​](#real-estate--architecture "Direct link to Real Estate & Architecture") * Property visualizations — [Luxury Modern Apartment Interior](https://app.spaitial.ai/worlds/a3f41a36-5b63-4a59-9cb9-32b8a72f1fc9), [Sunlit Luxury Living Room](https://app.spaitial.ai/worlds/dffb1df4-c39b-44c8-8f49-66c06c8c4f7c) * Architectural concepts — [Photorealistic Interior Concept](https://app.spaitial.ai/worlds/4d15ca72-1390-4d40-9829-941c9881703c), [Photorealistic Designer Room](https://app.spaitial.ai/worlds/dd1407d2-690b-4046-a5da-68e675435d0f), [Grand Two-Storey Fireplace Room](https://app.spaitial.ai/worlds/1ef8b58c-71af-48e0-8e27-5ab871bbe951) * Interior design mockups — [Designer Bath & Laundry Nook](https://app.spaitial.ai/worlds/7b0e0276-b9dc-432e-87e3-7cefe942b4e2), [Contemporary Studio Kitchen](https://app.spaitial.ai/worlds/dff21ab0-5255-4ba0-a49a-bb41f2b5212f), [Art Deco Attic Sanctuary](https://app.spaitial.ai/worlds/f36fa2d6-af80-45af-94ba-1c69febb43f2) * Renovation planning — [Renovated Basement Living Space](https://app.spaitial.ai/worlds/97e4ab30-7d80-41ae-b72d-8434848ce6c5), [Exposed-Brick Basement Potential](https://app.spaitial.ai/worlds/6a34fc8f-a9b8-4ee9-98d9-462aa1bf201c), [Shabby Chic Basement Lounge](https://app.spaitial.ai/worlds/8050f26f-db2c-4e1e-b08c-04ef4417b094) #### Use Cases[​](#use-cases "Direct link to Use Cases") * **Creative Projects**: Concept art, game environments, storytelling * **Professional**: Real estate, architecture, interior design * **Personal**: Memory preservation, dream visualization * **Educational**: Historical recreations, science concepts #### Best Results[​](#best-results "Direct link to Best Results") For best results, provide detailed descriptions or high-quality reference images. The more context you give, the better SpAItial can understand and create your vision. --- ### Create Workspace Set up new workspaces to organize your world collections. #### How to Create a Workspace[​](#how-to-create-a-workspace "Direct link to How to Create a Workspace") 1. Open the workspace selector 2. Click **Create Workspace** or the **+** button 3. Enter a name for your workspace 4. Confirm creation #### Naming Your Workspace[​](#naming-your-workspace "Direct link to Naming Your Workspace") ##### Good Names[​](#good-names "Direct link to Good Names") * Descriptive: "Real Estate Projects" * Specific: "Client ABC - Spring 2026" * Functional: "Experiments" ##### Avoid[​](#avoid "Direct link to Avoid") * Generic names: "Workspace 2" * Overly long names * Special characters that may cause issues #### After Creation[​](#after-creation "Direct link to After Creation") Your new workspace: * Appears in the workspace list * Is empty and ready for worlds * Is selected as active by default #### Tips[​](#tips "Direct link to Tips") * Create workspaces before starting new projects * Use a consistent naming convention * Don't create more workspaces than you need --- ### Manage Workspaces You can manage workspaces by renaming and deleting them from the **Workspaces** tab in **Settings**. This is the only place to make these changes. #### Open the Workspaces Tab[​](#open-the-workspaces-tab "Direct link to Open the Workspaces Tab") 1. Open **Settings** from your account menu. 2. Switch to the **Workspaces** tab. 3. Find the workspace you want to manage in the list. #### Rename a Workspace[​](#rename-a-workspace "Direct link to Rename a Workspace") 1. Click the workspace's more options button and select **Rename**. 2. Enter the new name and save. What stays the same: * All worlds in the workspace * Workspace settings #### Delete a Workspace[​](#delete-a-workspace "Direct link to Delete a Workspace") Deleting a workspace is permanent and cannot be undone. Before you delete: * Review what's in the workspace — deleted worlds cannot be recovered. To delete: 1. Click the workspace's **Delete** button. 2. Confirm the deletion in the prompt. ##### Restrictions[​](#restrictions "Direct link to Restrictions") * The default workspace cannot be deleted. --- ### Workspaces Overview Workspaces help you organize your worlds into logical collections. #### What Are Workspaces?[​](#what-are-workspaces "Direct link to What Are Workspaces?") Workspaces are containers for your worlds. Think of them like folders - they help you keep related worlds together. #### Use Cases[​](#use-cases "Direct link to Use Cases") ##### By Project[​](#by-project "Direct link to By Project") * Client A workspace * Client B workspace * Personal projects workspace ##### By Type[​](#by-type "Direct link to By Type") * Interior designs * Exterior environments * Fantasy worlds ##### By Status[​](#by-status "Direct link to By Status") * Active projects * Archived work * Experiments #### Default Workspace[​](#default-workspace "Direct link to Default Workspace") Every account has a default workspace where new worlds are created unless you specify otherwise. #### Workspace Features[​](#workspace-features "Direct link to Workspace Features") * **Create** new workspaces * **Rename** workspaces * **Switch** between workspaces * **Delete** workspaces with all its worlds (except the default workspace) #### Getting Started[​](#getting-started "Direct link to Getting Started") 1. Start with the default workspace 2. Create additional workspaces as needed 3. Create new worlds in the desired workspace 4. Switch workspaces to focus on different projects #### Best Practices[​](#best-practices "Direct link to Best Practices") * Use descriptive workspace names * Don't create too many workspaces * Archive completed projects * Keep active work easily accessible --- ### Switch Workspaces Navigate between your workspaces to access different world collections. #### How to Switch[​](#how-to-switch "Direct link to How to Switch") ##### From the Dashboard[​](#from-the-dashboard "Direct link to From the Dashboard") 1. Look for the workspace selector in the sidebar 2. Click to open the workspace list 3. Select the workspace you want to view #### What Changes[​](#what-changes "Direct link to What Changes") When you switch workspaces: * My Worlds shows worlds from the selected workspace * New worlds are created in the active workspace #### What Stays the Same[​](#what-stays-the-same "Direct link to What Stays the Same") * Your account settings * Billing, credits, and API keys * General navigation #### Tips[​](#tips "Direct link to Tips") ##### Workspace Indicator[​](#workspace-indicator "Direct link to Workspace Indicator") Always check which workspace is active before creating new worlds. #### Can't Find a World?[​](#cant-find-a-world "Direct link to Can't Find a World?") If you can't find a world: 1. Check that you're in the right workspace 2. Check other workspaces 3. Verify the world wasn't deleted --- ### Download & Export Export your worlds for use in other applications. #### Export Options[​](#export-options "Direct link to Export Options") ##### Available Formats[​](#available-formats "Direct link to Available Formats") SpAItial supports exporting worlds in formats suitable for various use cases: * **3D Viewer Format**: Optimized for web viewing * **Standard 3D Formats**: For use in 3D software (availability varies) * **Image Captures**: Screenshots and renders #### How to Export[​](#how-to-export "Direct link to How to Export") 1. Open the world you want to export 2. Click **Download** in the world toolbar 3. Select your desired format 4. Wait for processing. We send you an in-app notification when the file is ready. #### Format available[​](#format-available "Direct link to Format available") * **SPZ**: Standard 3D Gaussians file format developed by [Niantic](https://github.com/nianticlabs/spz) * **PLY**: Polygon File Format. * **Compressed PLY**: Compressed Polygon File Format. This is a compressed version of the PLY format. * **SOG**: Spatially Ordered Gaussians file format developed by [Playcanvas](https://developer.playcanvas.com/user-manual/gaussian-splatting/formats/sog/) #### Coordinate convention (SPZ orientation)[​](#coordinate-convention-spz-orientation "Direct link to Coordinate convention (SPZ orientation)") SpAItial exports Gaussian positions in the **RDF / OpenCV / COLMAP** world frame: **x-right, y-down, z-forward**. The positions are written into the `.spz` container as-is (packed as `UNSPECIFIED`), so the numeric values are RDF — they are **not** re-oriented to the right-up-back (RUB, y-up/z-back) frame that many other 3DGS SPZ exporters use. Because of this, up is interpreted differently by different viewers: * Viewers that assume **RUB** (the Three.js-native / WebGL frame), or that read SPZ via `@playcanvas/splat-transform` (which loads with `to: RDF`), need a **180° rotation about the X axis** to display the scene upright. RDF and RUB differ by exactly this rotation `(x, y, z) → (x, -y, -z)`. * Our own viewers apply this automatically (SPZ is rotated 180° about X on load), so you don't need to do anything when viewing in-app. If you wire a SpAItial `.spz` into a third-party viewer and it renders upside-down, apply a 180° rotation about X (or flip the viewer's up-axis). This is a general 3DGS ecosystem quirk around SPZ up-axis interpretation, not specific to SpAItial. --- ### Worlds **Worlds** is the home for everything you've created. Open it from the main navigation to see your full collection. #### Organizing[​](#organizing "Direct link to Organizing") ##### Workspaces[​](#workspaces "Direct link to Workspaces") Group related worlds into workspaces to keep large collections tidy. See [Workspaces](https://docs.spaitial.ai/workspaces/overview.md) for more information. #### World Cards[​](#world-cards "Direct link to World Cards") Each card shows: * A thumbnail preview of the world * World description * Privacy setting * A more menu with actions you can take on the world #### Quick Actions[​](#quick-actions "Direct link to Quick Actions") From a world card you can: * **Edit World** — refine regions of the panorama and regenerate * **Rename** — give your world a new name * **Share** — generate a share link * **Privacy settings** — toggle between public and private * **Download** — export as SPZ, PLY, Compressed PLY, and SOT format, plus input image or panorama * **Report a bug** — send feedback about issues * **Delete** — permanently remove the world --- ### World Privacy Control the visibility of your worlds with privacy settings. #### Privacy Options[​](#privacy-options "Direct link to Privacy Options") ##### Private[​](#private "Direct link to Private") Only you can see this world. **Use for:** * Work in progress * Personal projects * Testing and experiments * Sensitive content ##### Public (Unlisted)[​](#public-unlisted "Direct link to Public (Unlisted)") Anyone with the link can view, but it won't appear in public galleries. **Use for:** * Sharing with specific people * Client reviews * Limited distribution ##### Public (Listed)[​](#public-listed "Direct link to Public (Listed)") Visible in explore and community galleries. **Use for:** * Showcasing your work * Community participation * Building your creator profile #### Changing Privacy[​](#changing-privacy "Direct link to Changing Privacy") 1. Open the world 2. Navigate to settings or details 3. Select your privacy preference 4. Save changes #### Privacy Considerations[​](#privacy-considerations "Direct link to Privacy Considerations") ##### What's Visible[​](#whats-visible "Direct link to What's Visible") * **Private**: Only visible to you when logged in * **Unlisted**: Visible via direct link only * **Listed**: Appears in public explore pages ##### Link Sharing[​](#link-sharing "Direct link to Link Sharing") * Private worlds can't be shared via link * Unlisted worlds can be shared via direct link * Listed worlds can be found through explore ##### Reversibility[​](#reversibility "Direct link to Reversibility") You can change privacy settings at any time. #### Default Privacy[​](#default-privacy "Direct link to Default Privacy") New worlds are **Private** by default. Change to public when ready to share. --- ### Share Worlds Share your creations with friends, clients, or the world. #### Share Options[​](#share-options "Direct link to Share Options") ##### Direct Link[​](#direct-link "Direct link to Direct Link") Generate a shareable URL that others can use to view your world. ##### Social Sharing[​](#social-sharing "Direct link to Social Sharing") Share directly to social platforms (when available). #### Generating a Share Link[​](#generating-a-share-link "Direct link to Generating a Share Link") 1. Open the world you want to share 2. Click the **Share** button 3. Copy the generated link 4. Send to your intended audience #### Link Permissions[​](#link-permissions "Direct link to Link Permissions") The share link respects your privacy settings: * **Private**: Link won't work for others * **Unlisted**: Link works for anyone who has it * **Listed**: Link works and world is discoverable #### Viewer Experience[​](#viewer-experience "Direct link to Viewer Experience") When someone opens your share link, they can: * Explore the 3D world * View world information * Access viewer controls * Share further (if public) They cannot: * Edit your world * Access your account #### Revoking Access[​](#revoking-access "Direct link to Revoking Access") To stop sharing: 1. Change privacy to **Private** 2. Existing links will stop working --- ### View World Learn how to navigate and explore your 3D worlds. #### Opening a World[​](#opening-a-world "Direct link to Opening a World") Click any world in My Worlds or in the gallery to open the 3D viewer. #### Navigation Controls[​](#navigation-controls "Direct link to Navigation Controls") ##### Mouse/Trackpad[​](#mousetrackpad "Direct link to Mouse/Trackpad") * **Click and drag**: Look around * **Scroll**: Zoom in/out * **Right-click drag**: Pan ##### Touch (Mobile)[​](#touch-mobile "Direct link to Touch (Mobile)") * **Single finger drag**: Look around * **Pinch**: Zoom in/out * **Two finger drag**: Pan ##### Keyboard[​](#keyboard "Direct link to Keyboard") * **Arrow keys**: Look around * **W/S**: Move forward/backward * **A/D**: Strafe left/right * **Q/E**: Move up/down --- ## API Reference ### Authentication The SpAItial Developer API uses bearer tokens. Every request must include an `Authorization` header carrying a key issued to your account. Your API key is a secret Keys are shown to you exactly once at creation. Store them in a secret manager, never commit them, and never ship them inside a client-side bundle. #### Obtaining a key[​](#obtaining-a-key "Direct link to Obtaining a key") 1. Create a key in the dashboard Keys are created from the developer portal in the SpAItial dashboard. Each key belongs to your account and spends from the same shared credit balance used by the app. 2. Pick the right scopes Each key has an explicit scope set (for example `worlds:create`, `worlds:read`, `worlds:write`, `files:create`). The scope is enforced server-side; calls outside the granted scopes return `403 forbidden`. Panorama edits use `worlds:create`; listing, reading, or downloading edited panoramas uses `worlds:read`. 3. Check your credits World generation spends from your shared app and API credit balance. See [Credits and Billing](https://docs.spaitial.ai/api/credits-billing.md) for plan credits, purchases, and what API actions cost. #### Key format[​](#key-format "Direct link to Key format") Production keys look like: ```text spt_live_abc123def456... ``` Test or sandbox keys (if your workspace has them enabled) use a different prefix. The prefix is part of the key and must be sent verbatim. #### Sending the key[​](#sending-the-key "Direct link to Sending the key") Pass the key in the standard `Authorization: Bearer ` header: ```bash curl https://api.spaitial.ai/v1/models \ -H "Authorization: Bearer spt_live_..." ``` A request with no `Authorization` header, an unparseable header, or a revoked key returns `401 unauthorized` with the [error codes](https://docs.spaitial.ai/api/errors.md). #### Revoking[​](#revoking "Direct link to Revoking") Revoke any key that may have leaked from the developer portal. Revocation takes effect on the next request. To replace a key, create a new one and revoke the old one explicitly; creating a new key does not invalidate existing ones. #### What not to do[​](#what-not-to-do "Direct link to What not to do") Don't leak your keys * Do not put API keys in client-side code, mobile apps, or anything shipped to end users. Calls must be made from a server you control. * Do not commit keys to source control. Add them to your environment or secret manager. * Do not share a single key across unrelated services — keys are also the unit of rate limiting and observability. --- ### Credits and Billing The Developer API uses a credit system. You can buy credits in addition to your plan at any time. #### Credit Basics[​](#credit-basics "Direct link to Credit Basics") | Rule | Value | | -------------------- | ---------------------- | | Credit value | `1 credit = $0.01` | | Free plan | `800 credits/month` | | Standard plan | `7,500 credits/month` | | Pro plan | `15,000 credits/month` | | Purchased top-up TTL | Does not expire | You can buy credits in addition to your plan at any time. #### Model Pricing[​](#model-pricing "Direct link to Model Pricing") | Model | Best for | Typical time | Credits | | ---------------------------------------------------------------------------- | --------------------------------------- | ------------ | ------- | | [Echo-2 Standard](https://docs.spaitial.ai/api/generation-models.md#default) | Fast, general-purpose world generation | ~8 minutes | 160 | | [Echo HQ](https://docs.spaitial.ai/api/generation-models.md#echo-hq) | Higher-detail, higher-resolution worlds | ~60 minutes | 800 | #### What API Actions Cost[​](#what-api-actions-cost "Direct link to What API Actions Cost") | API action | Echo-2 Standard | Echo HQ | | ---------------------------- | --------------- | ----------- | | Text-to-world generation | 160 credits | 800 credits | | Image-to-world generation | 160 credits | 800 credits | | Panorama-to-world generation | 160 credits | 800 credits | #### Plans[​](#plans "Direct link to Plans") | Plan | Credits | Echo-2 generations | Echo HQ generations | API usage fit | | ---------- | ------------ | ------------------ | ------------------- | ----------------------------------------------- | | Free | 800/month | ~5/month | ~1/month | Evaluation and prototypes | | Standard | 7,500/month | ~46 | ~9 | Echo-2 Standard and Echo HQ; non-commercial use | | Pro | 15,000/month | ~93 | ~18 | Echo-2 Standard and Echo HQ; commercial use | | Enterprise | Custom | Custom | Custom | Multiple users and Echo-3 API access | Standard and Pro subscribers can purchase additional credits. Purchased credits are shared across the app, web app, and API. Paid plans are available monthly or annually; annual plans save 20% and grant credits monthly. New subscriptions include a card-required first month free. #### Expiry and Spend Order[​](#expiry-and-spend-order "Direct link to Expiry and Spend Order") Every current credit source works across Capture, the web app, and the Developer API. | Source | Expiry | | ------------------- | ------------------------------------------------------------------- | | Free monthly credit | Expires after one month; unused credit does not roll over | | Subscription credit | Expires at the next monthly grant; unused credit does not roll over | | Purchased top-up | Rolls over between subscription periods; does not expire | | Permanent grant | No automatic expiry | The wallet spends the soonest-expiring credit first: free monthly, subscription, purchased top-up, then permanent grants. #### Insufficient Credits[​](#insufficient-credits "Direct link to Insufficient Credits") If a world-generation request cannot reserve enough credit, the API returns a `402` with `error.code = "insufficient_credits"`. Purchase credits or upgrade your plan in the developer portal before retrying. ```json { "error": { "code": "insufficient_credits", "message": "You are out of credits. Choose a plan or purchase additional credits in the dashboard." } } ``` --- ### Errors The SpAItial Developer API uses conventional HTTP status codes and returns a stable JSON envelope on every error. #### Error shape[​](#error-shape "Direct link to Error shape") All errors emitted by the API share the same shape: ```json { "error": { "code": "INVALID_INPUT", "message": "Human-readable description of what went wrong.", "request_id": "optional trace id", "details": { "optional": "structured context" } } } ``` Always branch on `error.code`, not on the human-readable `message`. The `code` field is stable. #### HTTP status codes[​](#http-status-codes "Direct link to HTTP status codes") | Status | Meaning | Typical `error.code` | | ------ | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | | `400` | Malformed request — bad JSON, missing required field, wrong content type. | `INVALID_INPUT` | | `401` | Missing, malformed, or revoked API key. | `UNAUTHORIZED` | | `402` | Account does not have enough generation credits. `details` has `required_credits` and `available_credits`. | `INSUFFICIENT_CREDITS` | | `403` | Authenticated but the key lacks the required scope, or moderation blocked the input. | `FORBIDDEN`, `MODERATION_REJECTED` | | `404` | Resource does not exist (e.g. unknown `request_id`, `file_id`, or `panorama_id`). | `REQUEST_NOT_FOUND`, `FILE_NOT_FOUND`, `PANORAMA_NOT_FOUND` | | `409` | Idempotency key was reused with a different payload, or the requested world/artifact is not ready yet. | `IDEMPOTENCY_KEY_REUSED`, `RESOURCE_NOT_READY` | | `410` | Short-lived resource has expired. | `PANORAMA_EXPIRED` | | `422` | Payload parsed but semantically invalid (validation rule violated). | `VALIDATION_FAILED` | | `429` | Rate limit exceeded, or too many of your worlds are already generating — see [Rate Limits](https://docs.spaitial.ai/api/rate-limits.md). | `RATE_LIMIT_EXCEEDED`, `WORLD_USER_LIMIT` | | `503` | World generation intake is paused or temporarily unavailable. Retry later. | `WORLD_INTAKE_PAUSED`, `WORLD_INTAKE_UNAVAILABLE` | | `5xx` | Unexpected server error. Safe to retry with backoff. | `INTERNAL_ERROR`, `EDIT_FAILED` | #### Retry strategy[​](#retry-strategy "Direct link to Retry strategy") * `4xx` responses are not retryable by themselves — fix the request first. The exceptions are `409 RESOURCE_NOT_READY` (poll again later), `409 IDEMPOTENCY_KEY_REUSED` (resolve the collision and pick a new key), `429 RATE_LIMIT_EXCEEDED` (wait until the window resets — the response includes `Retry-After`), and `429 WORLD_USER_LIMIT` (wait for one of your in-flight worlds to finish). * `5xx` responses are retryable. Use exponential backoff with jitter, capped at a reasonable ceiling (for example 30s). Pair retries with an `Idempotency-Key` on mutating endpoints so retries do not produce duplicates. #### Panorama edit errors[​](#panorama-edit-errors "Direct link to Panorama edit errors") Panorama editing adds a few resource-specific codes: | Code | HTTP | Meaning | | -------------------- | ----- | ----------------------------------------------------------------------------------------------------------- | | `PANORAMA_NOT_FOUND` | `404` | The `pano_...` does not exist or is not owned by the caller. | | `PANORAMA_EXPIRED` | `410` | The edited panorama has passed its 24-hour TTL. | | `EDIT_FAILED` | `503` | The upstream panorama edit failed after the request was accepted. Retry with a fresh or idempotent request. | #### Validation errors[​](#validation-errors "Direct link to Validation errors") `400 INVALID_INPUT` responses for a malformed body, path parameter, or query parameter name the failing fields in `message` and list every issue in `details.issues`. Use it to surface actionable feedback to your users without re-implementing the schema check. ```json { "error": { "code": "INVALID_INPUT", "message": "Validation failed: webhook.url: Invalid URL", "details": { "issues": [{ "path": "webhook.url", "message": "Invalid URL" }] } } } ``` --- ### Files Use the files API when you want to upload image bytes once, receive a `file_id`, and then reference that file from `POST /v1/worlds`. Files are temporary inputs Uploaded files live in private storage for 24 hours. They are owner-scoped to your API key user, moderated before storage, and intended as generation inputs rather than permanent assets. #### Endpoints[​](#endpoints "Direct link to Endpoints") | Endpoint | Scope | Purpose | | ---------------- | -------------- | ------------------------------------------- | | POST `/v1/files` | `files:create` | Upload an image and receive a `file_id`. | | GET `/v1/files` | `files:read` | List files uploaded by the current API key. | #### Upload a file[​](#upload-a-file "Direct link to Upload a file") Send a `multipart/form-data` request with a single `file` field: ```bash API_KEY="spt_live_..." curl -sX POST "https://api.spaitial.ai/v1/files" \ -H "Authorization: Bearer $API_KEY" \ -F "file=@photo.jpg" ``` Successful uploads return `201 Created`: ```json { "file_id": "file_abc123", "content_type": "image/jpeg", "file_size": 1843921, "expires_at": "2026-05-21T10:42:00.000Z" } ``` Allowed file types are JPEG, PNG, WebP, and GIF. The maximum file size is 25 MB. The API checks the detected MIME type from the file bytes, not just the uploaded filename or form content type. #### Use the file in a world request[​](#use-the-file-in-a-world-request "Direct link to Use the file in a world request") Pass the `file_id` as the world input: ```bash FILE_ID=$(curl -sX POST "https://api.spaitial.ai/v1/files" \ -H "Authorization: Bearer $API_KEY" \ -F "file=@photo.jpg" | jq -r .file_id) curl -sX POST "https://api.spaitial.ai/v1/worlds" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"input\": { \"type\": \"file_id\", \"file_id\": \"$FILE_ID\" }, \"model\": \"echo-2\", \"title\": \"Uploaded kitchen reference\" }" ``` Using a file marks it as consumed. This is informational and visible in `GET /v1/files`; the object stays private until its lifecycle cleanup. Treat `file_id` values as one-generation inputs and upload again when you need a fresh reusable reference. For an uploaded 360 panorama, use the same flow and add `is_pano: true` when creating the world: ```bash PANO_FILE_ID=$(curl -sX POST "https://api.spaitial.ai/v1/files" \ -H "Authorization: Bearer $API_KEY" \ -F "file=@panorama.jpg" | jq -r .file_id) curl -sX POST "https://api.spaitial.ai/v1/worlds" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"input\": { \"type\": \"file_id\", \"file_id\": \"$PANO_FILE_ID\", \"is_pano\": true }, \"model\": \"echo-2\", \"title\": \"Uploaded panorama\" }" ``` Only set `is_pano` for equirectangular 360 panoramas. The API trusts the flag and skips suitability validation for that request. #### List files[​](#list-files "Direct link to List files") List files owned by the current API key user: ```bash curl -s "https://api.spaitial.ai/v1/files?limit=20&offset=0" \ -H "Authorization: Bearer $API_KEY" ``` Response: ```json { "files": [ { "file_id": "file_abc123", "content_type": "image/jpeg", "file_size": 1843921, "expires_at": "2026-05-21T10:42:00.000Z", "created_at": "2026-05-20T10:42:00.000Z", "consumed_at": null, "status": "available" } ], "limit": 20, "offset": 0, "has_more": false } ``` `limit` defaults to `20` and can be `1`-`100`. `offset` defaults to `0`. #### File statuses[​](#file-statuses "Direct link to File statuses") | Status | Meaning | | ----------- | ------------------------------------------------------------------- | | `available` | The file is still within its 24-hour TTL and has not been used yet. | | `consumed` | The file has been referenced by a world-generation request. | | `expired` | The file is past `expires_at` and can no longer be used. | #### Errors[​](#errors "Direct link to Errors") | HTTP | `error.code` | When it happens | | ----- | --------------------- | -------------------------------------------------------------------------- | | `400` | `INVALID_INPUT` | Missing `file`, unsupported image type, or malformed list query. | | `401` | `UNAUTHORIZED` | Missing, invalid, revoked, or expired API key. | | `403` | `FORBIDDEN` | API key lacks `files:create` or `files:read`. | | `403` | `MODERATION_REJECTED` | Uploaded image failed content moderation. | | `404` | `FILE_NOT_FOUND` | `file_id` does not exist or belongs to another caller. | | `404` | `FILE_EXPIRED` | `file_id` is expired or already consumed when resolving it for generation. | | `413` | `INVALID_INPUT` | Uploaded file exceeds 25 MB. | | `429` | `RATE_LIMIT_EXCEEDED` | The key exceeded its current rate-limit bucket. | See [Moderation](https://docs.spaitial.ai/api/moderation.md) for content screening and [Validation](https://docs.spaitial.ai/api/validation.md) for generation-suitability checks. --- ### Models Every `POST /v1/worlds` request must set `model` to one of the ids your key can use. There is no default: a missing `model` (or `"default"`) fails with `400 INVALID_INPUT`, and the error lists the models enabled for your key. Discover them with `GET /v1/models` — each entry includes `docs_url`. Only models you can access are listed `GET /v1/models` is filtered per account. Requesting a model that is not enabled for API access on your account returns `403` with `MODEL_FORBIDDEN`. Unknown ids return `404` with `MODEL_NOT_FOUND`. A model built for a different input type returns `400` with `INVALID_INPUT`. Every error message lists the models your key can use. #### Endpoints[​](#endpoints "Direct link to Endpoints") | Endpoint | Scope | Purpose | | ---------------- | ------------- | ------------------------------------------------- | | GET `/v1/models` | `worlds:read` | List generation models available to this API key. | #### List models[​](#list-models "Direct link to List models") ```bash API_KEY="spt_live_..." curl -s https://api.spaitial.ai/v1/models \ -H "Authorization: Bearer $API_KEY" ``` ```json { "models": [ { "id": "echo-2", "name": "Echo 2", "description": "Standard image/panorama to 3D Gaussian splat pipeline.", "is_default": false, "credit_cost": 160, "estimated_duration_seconds": 522, "docs_url": "https://docs.spaitial.ai/api/generation-models#default" }, { "id": "echo-hq", "name": "Echo 2 (HQ)", "description": "Higher-detail, higher-resolution worlds.", "is_default": false, "credit_cost": 800, "estimated_duration_seconds": 3600, "docs_url": "https://docs.spaitial.ai/api/generation-models#echo-hq" } ] } ``` Pass one of the listed `id` values (`echo-2`, `echo-hq`, …) as `model`. `id` is a stable lowercase slug that never changes; `name` is the display label and may be renamed. #### Public models[​](#public-models "Direct link to Public models") These two are enabled on every developer account unless your account is configured otherwise. ##### Echo 2[​](#default "Direct link to Echo 2") Echo 2 is the fast, general-purpose pipeline. | Field | Value | | ------------ | -------------------------------- | | API id | `echo-2` | | Best for | Text, image, or panorama → world | | Typical time | ~8 minutes | | Credits | 160 | ```bash curl -sX POST https://api.spaitial.ai/v1/worlds \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "echo-2", "input": { "type": "text", "prompt": "A sunlit Tuscan villa courtyard at golden hour" } }' ``` ##### Echo HQ[​](#echo-hq "Direct link to Echo HQ") Higher detail and resolution. Same input types as Echo 2; longer runtime and higher credit cost. | Field | Value | | ------------ | ---------------------------------------- | | API id | `echo-hq` | | Best for | Higher-detail / higher-resolution worlds | | Typical time | ~60 minutes | | Credits | 800 | ```bash curl -sX POST https://api.spaitial.ai/v1/worlds \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "echo-hq", "input": { "type": "text", "prompt": "A sunlit Tuscan villa courtyard at golden hour" } }' ``` #### Using a model[​](#using-a-model "Direct link to Using a model") ```bash curl -sX POST https://api.spaitial.ai/v1/worlds \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "echo-hq", "title": "Tuscan courtyard", "input": { "type": "text", "prompt": "A sunlit Tuscan villa courtyard at golden hour" } }' ``` See [Create a world](https://docs.spaitial.ai/api/reference/v-1-worlds-create-job.md) for the full request body, and [Credits and Billing](https://docs.spaitial.ai/api/credits-billing.md) for plan capacity. #### Private models[​](#private-models "Direct link to Private models") Some accounts are granted extra models. Those appear in `GET /v1/models` only after they are granted, and their `docs_url` points at the matching private docs in the [developer portal](https://developers.spaitial.ai/models). --- ### Getting Started The SpAItial Developer API is a REST interface for generating 3D worlds from text prompts or images. #### Base URL[​](#base-url "Direct link to Base URL") Base URL All requests go to `https://api.spaitial.ai`. The examples on this page assume that base. If you're pointing at a non-production environment, swap it for the one issued to you. #### Prerequisites[​](#prerequisites "Direct link to Prerequisites") 1. A developer account for SpAItial Sign up at [developers.spaitial.ai](https://developers.spaitial.ai). API access is public for all users. 2. An API key Keys look like `spt_live_…` and are issued from the developer portal — see [Authentication](https://docs.spaitial.ai/api/authentication.md). Treat them like passwords and keep them secret. 3. Credits on your account World generation uses credits shared across the app and API; reads, status polling, and downloads are not charged as generation. One credit equals $0.01. Echo-2 Standard costs 160 credits, and Echo HQ costs 800 credits. Free users receive 800 credits each month, enough for about 5 Echo-2 generations or 1 Echo HQ generation. Unused monthly credits do not roll over. Integrating with an AI coding assistant? Install the SpAItial API skill so your coding assistant has the full API reference, auth rules, endpoint schemas, webhooks, errors, and rate limits available locally. ```bash npx skills add spaitial-dev/spaitial-api-skill ``` You can also load the [LLM Skills](https://docs.spaitial.ai/api/llm-skills.md) page directly as a single self-contained reference. #### Billing at a glance[​](#billing-at-a-glance "Direct link to Billing at a glance") | Plan | Included credits | Echo-2 generations | Echo HQ generations | | -------- | ---------------- | ------------------ | ------------------- | | Free | 800/month | ~5/month | ~1/month | | Standard | 7,500/month | ~46/month | ~9/month | | Pro | 15,000/month | ~93/month | ~18/month | The wallet spends monthly credits before purchased credits. Standard and Pro subscribers can purchase additional credits; top-ups roll over and expire one year after purchase. Larger workloads and Echo-3 API access require Enterprise. #### Your first request[​](#your-first-request "Direct link to Your first request") List the models available to your key. This is a good way to confirm auth is set up correctly. See [Models](https://docs.spaitial.ai/api/generation-models.md) for Echo-2 and Echo HQ. ```bash curl https://api.spaitial.ai/v1/models \ -H "Authorization: Bearer spt_live_..." ``` A successful response returns a JSON object describing the models available to your account. ##### Build the request from JavaScript[​](#build-the-request-from-javascript "Direct link to Build the request from JavaScript") Tweak the prompt below and click **Run** to see the shape of the request your client would send. No network calls are made — this is purely for shape verification. Live Editor Reset Result Loading... #### Generating a world[​](#generating-a-world "Direct link to Generating a world") Generation is asynchronous. You submit a job, then either **poll** for status or let the API call your **webhook** when it's done. When status becomes `COMPLETED`, you can fetch the result, which contains signed URLs for the splat, panorama, and thumbnail. ##### Lifecycle at a glance[​](#lifecycle-at-a-glance "Direct link to Lifecycle at a glance") ##### Endpoints used[​](#endpoints-used "Direct link to Endpoints used") | Step | Endpoint | Notes | | --------------------------- | ---------------------------------------------- | ------------------------------------------------------------------ | | 1. Upload (image flow only) | POST `/v1/files` | Returns a `file_id` to reference in step 2. Skip for text prompts. | | 2. Submit | POST `/v1/worlds` | Returns `202` with `request_id` and `status: "PENDING"`. | | 3. Poll | GET `/v1/worlds/requests/{request_id}` | Returns `status` and `progress` (0–1). | | 4. Fetch result | GET `/v1/worlds/requests/{request_id}/result` | Available once `status` is `COMPLETED`. | | Cancel | POST `/v1/worlds/requests/{request_id}/cancel` | Only valid while `PENDING` or `PROCESSING`. | ##### Polling cadence[​](#polling-cadence "Direct link to Polling cadence") Most jobs finish in **6–8 minutes**. A reasonable poll loop is every 5–10 seconds with backoff after the first minute. Prefer a webhook over tight polling. Pass `webhook: { url: "https://your-host/spaitial" }` in the create request and we'll POST `COMPLETED` / `FAILED` / `CANCELLED` events to it. See the API Reference (in the left sidebar) for full request/response schemas, then [Rate Limits](https://docs.spaitial.ai/api/rate-limits.md) for per-key throughput and [Errors](https://docs.spaitial.ai/api/errors.md) for the error envelope. --- ### LLM Skills A self-contained, single-page reference designed to be loaded as a skill by AI coding assistants (Cursor, Claude Code, Cline, etc.). Drop this URL (or its `.md` companion) into a prompt or skill manifest and an agent can integrate end-to-end without browsing the rest of the docs. If you're a human reading this, the [Getting Started](https://docs.spaitial.ai/api/getting-started.md) page is friendlier. The content below intentionally repeats schema, examples, and edge cases that are split across multiple pages elsewhere, so it stays useful on its own. Install the API skill For assistants that support Skills, install the SpAItial API skill directly: ```bash npx skills add spaitial-dev/spaitial-api-skill ``` #### Base URL & Auth[​](#base-url--auth "Direct link to Base URL & Auth") ```text https://api.spaitial.ai ``` Every request needs a Bearer token issued by the developers site (`https://developers.spaitial.ai`): ```text Authorization: Bearer spt_live_<32-char-base32> ``` Test keys use the `spt_test_` prefix and have the same scopes with lower rate limits. #### Billing & Credits[​](#billing--credits "Direct link to Billing & Credits") Developer API access is public for all users and shares the same account credits as the app: * 1 credit = $0.01 * Free users receive 800 credits a month, enough for about 5 Echo-2 generations or 1 Echo HQ generation * Custom/Enterprise supports larger volume * Purchased credits use 1,600, 3,200, or 8,000-credit packs shared across app and API * Monthly credits do not roll over; purchased credits roll over and do not expire Model prices: | Model | Best for | Typical time | Credits | | --------------- | --------------------------------------- | ------------ | ------- | | Echo-2 Standard | Fast, general-purpose world generation | ~8 minutes | 160 | | Echo HQ | Higher-detail, higher-resolution worlds | ~60 minutes | 800 | Plan capacity: | Plan | Included credits | Echo-2 generations | Echo HQ generations | | -------- | ---------------- | ------------------ | ------------------- | | Free | 800/month | ~5/month | ~1/month | | Standard | 7,500/month | ~46/month | ~9/month | | Pro | 15,000/month | ~93/month | ~18/month | Reads, status polling, downloads, and exports do not consume generation credits. Required scopes per endpoint: | Endpoint | Scope | | ------------------------------------------------------------------ | --------------- | | `POST /v1/worlds` | `worlds:create` | | `GET /v1/worlds/requests` | `worlds:read` | | `GET /v1/worlds/requests/:id` (+ `/status`, `/splat`, `/panorama`) | `worlds:read` | | `GET /v1/worlds/requests/:id/exports` (+ `/:type`) | `worlds:read` | | `PATCH /v1/worlds/requests/:id` | `worlds:write` | | `POST /v1/worlds/requests/:id/cancel` | `worlds:write` | | `POST /v1/worlds/requests/:id/exports/:type` | `worlds:write` | | `POST /v1/files` | `files:create` | | `GET /v1/files` | `files:read` | | `POST /v1/panoramas/edit` | `worlds:create` | | `GET /v1/panoramas` (+ `/:id`, `/:id/download`) | `worlds:read` | | `GET /v1/models` | `worlds:read` | #### Endpoints at a glance[​](#endpoints-at-a-glance "Direct link to Endpoints at a glance") ```text POST /v1/worlds Create a world generation job GET /v1/worlds/requests List API jobs on this account GET /v1/worlds/requests/:request_id Full job result (post-completion) GET /v1/worlds/requests/:request_id/status Poll status (cheap, cached 3s) PATCH /v1/worlds/requests/:request_id Update world visibility/title POST /v1/worlds/requests/:request_id/cancel Best-effort cancel GET /v1/worlds/requests/:request_id/splat 302 → fresh signed splat URL GET /v1/worlds/requests/:request_id/panorama 302 → fresh signed panorama URL POST /v1/worlds/requests/:request_id/exports/:type Start an export, e.g. mesh GET /v1/worlds/requests/:request_id/exports/:type Export status; READY includes download_url GET /v1/worlds/requests/:request_id/exports List export statuses POST /v1/files Upload an input file, returns file_id GET /v1/files List uploaded files for this API key POST /v1/panoramas/edit Edit a world/request/panorama and return a pano_... artifact GET /v1/panoramas List edited panoramas for this API key GET /v1/panoramas/:panorama_id Get an edited panorama GET /v1/panoramas/:panorama_id/download 302 → fresh signed edited-panorama URL GET /v1/models List available generation models GET /v1/openapi.json Machine-readable spec GET /v1/docs Swagger UI ``` #### Quickstart: submit + poll[​](#quickstart-submit--poll "Direct link to Quickstart: submit + poll") ```bash API_KEY="spt_live_..." # 1. Submit RES=$(curl -sX POST https://api.spaitial.ai/v1/worlds \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "type": "url", "image_url": "https://images.unsplash.com/photo-1600596542815-ffad4c1539a9?w=800" }, "model": "echo-2", "title": "Cozy reading nook" }') REQ_ID=$(echo "$RES" | jq -r .request_id) # 2. Poll while true; do STATUS=$(curl -s "https://api.spaitial.ai/v1/worlds/requests/$REQ_ID/status" \ -H "Authorization: Bearer $API_KEY" | jq -r .status) echo "status=$STATUS" [[ "$STATUS" == "COMPLETED" || "$STATUS" == "FAILED" || "$STATUS" == "CANCELLED" ]] && break sleep 5 done # 3. Fetch result curl -s "https://api.spaitial.ai/v1/worlds/requests/$REQ_ID" \ -H "Authorization: Bearer $API_KEY" | jq # 4. Download splat (302 → signed URL, follow with -L) curl -L -o world.spz "https://api.spaitial.ai/v1/worlds/requests/$REQ_ID/splat" \ -H "Authorization: Bearer $API_KEY" ``` Generation takes 5–10 minutes per world. #### Input types[​](#input-types "Direct link to Input types") `POST /v1/worlds` accepts a discriminated `input.type`. Every request also needs `model` (see [Models](#models)); it is shown on each example below. ##### `url` — external HTTPS image[​](#url--external-https-image "Direct link to url--external-https-image") ```json { "input": { "type": "url", "image_url": "https://example.com/photo.jpg" }, "model": "echo-2" } ``` Server fetches under SSRF guards. HTTPS only, ≤25 MB, JPEG/PNG/WebP/GIF. For a 360 panorama image, keep the same input shape and add `is_pano: true`: ```json { "input": { "type": "url", "image_url": "https://example.com/panorama.jpg", "is_pano": true }, "model": "echo-2" } ``` The API trusts that the image is an equirectangular 360 panorama, starts generation after the image-to-panorama stage, and skips suitability validation even if `validation.skip` is `false`. Content moderation still applies. ##### `base64` — inline data URI[​](#base64--inline-data-uri "Direct link to base64--inline-data-uri") ```json { "input": { "type": "base64", "image_base64": "data:image/png;base64,iVBORw0KGgo..." }, "model": "echo-2" } ``` ≤25 MB after decode. ##### `file_id` — upload first, reference later[​](#file_id--upload-first-reference-later "Direct link to file_id--upload-first-reference-later") ```bash # Step 1: upload FILE=$(curl -sX POST https://api.spaitial.ai/v1/files \ -H "Authorization: Bearer $API_KEY" \ -F "file=@photo.jpg" | jq -r .file_id) # Step 2: submit curl -sX POST https://api.spaitial.ai/v1/worlds \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d "{\"input\":{\"type\":\"file_id\",\"file_id\":\"$FILE\"},\"model\":\"echo-2\"}" ``` Uploads live in a private bucket with a 24-hour TTL. Owner-scoped — another caller's `file_id` returns 404. Using a `file_id` marks it as consumed; `GET /v1/files` returns `available`, `consumed`, and `expired` statuses. Uploaded panoramas use the same file flow: ```bash PANO_FILE=$(curl -sX POST https://api.spaitial.ai/v1/files \ -H "Authorization: Bearer $API_KEY" \ -F "file=@panorama.jpg" | jq -r .file_id) curl -sX POST https://api.spaitial.ai/v1/worlds \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d "{\"input\":{\"type\":\"file_id\",\"file_id\":\"$PANO_FILE\",\"is_pano\":true},\"model\":\"echo-2\"}" ``` List existing uploads for the current API key: ```bash curl -s "https://api.spaitial.ai/v1/files?limit=20&offset=0" \ -H "Authorization: Bearer $API_KEY" ``` The response includes `status` (`available`, `consumed`, or `expired`) plus `expires_at` and `consumed_at`; it never exposes private storage keys. ##### `text` — prompt → image → world[​](#text--prompt--image--world "Direct link to text--prompt--image--world") ```json { "input": { "type": "text", "prompt": "a cozy sunlit reading nook with bookshelves" }, "model": "echo-2" } ``` Charged for both prompt-to-image and world generation. ##### `panorama_id` — create from an edited panorama[​](#panorama_id--create-from-an-edited-panorama "Direct link to panorama_id--create-from-an-edited-panorama") ```json { "input": { "type": "panorama_id", "panorama_id": "pano_abc123" }, "model": "echo-2", "title": "Edited world" } ``` `panorama_id` values come from `POST /v1/panoramas/edit`. World generation starts at the pano2video stage and keeps lineage to the source world. Edited panoramas live for 24 hours; creating a world marks them as consumed for visibility, but they can still be reused until expiry. #### Panorama editing loop[​](#panorama-editing-loop "Direct link to Panorama editing loop") Use panorama editing when a user wants the app-style "edit the panorama, inspect it, iterate, then generate a new world" workflow. ```bash # 1. Edit the panorama behind a completed API-created world/request. EDIT=$(curl -sX POST "https://api.spaitial.ai/v1/panoramas/edit" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "source": { "type": "world_id", "world_id": "" }, "prompt": "change the rug and chair to yellow" }') PANO_ID=$(echo "$EDIT" | jq -r .panorama_id) # 2. Inspect the edited panorama (302 -> signed URL; follow with -L). curl -L -o edited.png "https://api.spaitial.ai/v1/panoramas/$PANO_ID/download" \ -H "Authorization: Bearer $API_KEY" # 3. Iterate by feeding the pano_... back as the source. NEXT=$(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_ID\" }, \"prompt\": \"add a sound system next to the window\" }") FINAL_PANO_ID=$(echo "$NEXT" | jq -r .panorama_id) # 4. Create a world from the final panorama. 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\": \"$FINAL_PANO_ID\" }, \"model\": \"echo-2\", \"title\": \"Edited world\" }" ``` `POST /v1/panoramas/edit` request shape: ```json { "source": { "type": "request_id", "request_id": "req_..." }, "prompt": "make the room warmer", "images": [ { "type": "url", "image_url": "https://example.com/reference.jpg" } ] } ``` `source` can be: * `{ "type": "request_id", "request_id": "req_..." }` — an API-created completed world request owned by the same user. * `{ "type": "world_id", "world_id": "" }` — an API-created completed world owned by the same user, even if created by a different API key. * `{ "type": "panorama_id", "panorama_id": "pano_..." }` — a previous edit artifact. Optional `images` accepts up to 3 references (`url`, `base64`, or `file_id`) for instructions like "add this sofa" or "merge this style". The edit prompt is passed through as the user's instruction. There is intentionally **no aspect-ratio field**; Spaitial preserves the panorama format so the result remains valid for world generation. Response: ```json { "panorama_id": "pano_...", "status": "READY", "panorama_url": "https://api.spaitial.ai/v1/panoramas/pano_.../download", "prompt": "make the room warmer", "source_request_id": "…", "source_world_id": "…", "parent_panorama_id": null, "consumed": false, "created_at": "2026-06-19T12:00:00Z", "expires_at": "2026-06-20T12:00:00Z" } ``` #### Full request shape[​](#full-request-shape "Direct link to Full request shape") ```json { "input": { "type": "url", "image_url": "https://..." }, "model": "echo-2", "title": "My world", "output_format": "spz", "validation": { "skip": true, "error_on_fail": false }, "visibility": { "is_public": false, "is_listed": false }, "webhook": { "url": "https://example.com/hooks/spaitial" } } ``` | Field | Default | Notes | | -------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `model` | required (no default) | `GET /v1/models` to list | | `title` | unset | User-facing world caption (≤200 chars) | | `output_format` | `spz` | Final splat artifact: `spz` (default), `sog` (PlayCanvas-optimized), or `ply`. `sog` adds roughly 25s to generation; `ply` is converted from the spz after generation. The `splat` download and `splat_format` reflect this format. Honored by every input type, including `capture_bundle`. | | `validation.skip` | `true` | Skip suitability check (saves cost + latency) | | `validation.error_on_fail` | `false` | When `skip:false`, reject (422) on flagged input instead of proceeding with warnings | | `visibility.is_public` | `false` | Anyone with `viewer_url` can view | | `visibility.is_listed` | `false` | Eligible for public gallery (requires `is_public:true`) | | `webhook.url` | unset | HTTPS callback on terminal state | #### Statuses[​](#statuses "Direct link to Statuses") ```text PENDING → PROCESSING → COMPLETED ↘ FAILED ↘ CANCELLED ``` `progress` (0–1) is coarse: it tracks the pipeline stage, or elapsed time against the ETA for long capture/multi-pano runs (never above 0.79 until done). `estimated_duration_seconds` is the expected total runtime and is `null` once terminal. For `FAILED`, `error: { code, message }` carries the reason (same as the `world.failed` webhook); otherwise `error` is `null`. #### Result envelope (`GET /v1/worlds/requests/:id`)[​](#result-envelope-get-v1worldsrequestsid "Direct link to result-envelope-get-v1worldsrequestsid") ```json { "request_id": "req_...", "model": "echo-2", "status": "COMPLETED", "created_at": "2026-05-14T12:00:00Z", "updated_at": "2026-05-14T12:09:48Z", "completed_at": "2026-05-14T12:09:48Z", "world": { "id": "", "title": "Cozy reading nook", "splat_url": "https://api.spaitial.ai/v1/worlds/requests/req_.../splat", "splat_format": "spz", "thumbnail_url": "https://img.spaitial.ai/.../thumbnail.webp", "panorama_url": "https://api.spaitial.ai/v1/worlds/requests/req_.../panorama", "viewer_url": "https://app.spaitial.ai/worlds/", "visibility": { "is_public": false, "is_listed": false }, "camera": { "hfov_deg": 103.93, "roll_deg": 1.34, "pitch_deg": -10.23 }, "created_at": "2026-05-14T12:00:01Z", "updated_at": "2026-05-14T12:09:48Z", "completed_at": "2026-05-14T12:09:48Z" }, "validation": { "passed": true, "issues": [] }, "input": { "type": "url", "image_url": "https://example.com/room.jpg" }, "error": null } ``` `input` is the request's `input` object. Query strings and fragments are stripped from every `url` / `*_url` field, so presigned links you sent are never stored or returned. `splat_url` and `panorama_url` are **stable API endpoints**, not signed URLs. Each `GET` to them returns a `302 Found` with a fresh 5-minute signed URL — safe to store the API URL in your DB forever. Auth on every download. `camera` is the estimated source-image pose from image-to-pano. `null` when that stage was skipped (pano upload, `panorama_id`, capture, multi-pano) or the estimate is missing. Same object is on `world.completed` webhook `data` and the PATCH response. #### Exports[​](#exports "Direct link to Exports") Exports are optional artifacts derived from a completed world. They are keyed by `type` so the API can grow beyond mesh without changing the route shape. Supported export types today: | Type | Description | | ----------------- | ---------------------------------------------------------------------------------------- | | `mesh` | Full-resolution reconstructed mesh (`.ply`) | | `mesh-simplified` | Simplified mesh optimized for real-time use | | `floorplan` | Top-down render of the splat (`.png`). A minimap-style image, not an architectural plan. | | `zip` | One archive bundling world assets: any of `spz`, `sog`, `ply`, `mesh`, `floorplan` | Start or retrieve an export: ```bash curl -sX POST "https://api.spaitial.ai/v1/worlds/requests/$REQ_ID/exports/mesh" \ -H "Authorization: Bearer $API_KEY" | jq ``` Poll export status using the same typed endpoint: ```bash curl -s "https://api.spaitial.ai/v1/worlds/requests/$REQ_ID/exports/mesh" \ -H "Authorization: Bearer $API_KEY" | jq ``` Not ready: ```json { "type": "mesh", "status": "PROCESSING" } ``` Ready: ```json { "type": "mesh", "status": "READY", "download_url": "https://api.spaitial.ai/v1/worlds/requests/req_.../exports/mesh?download=1", "created_at": "2026-05-21T09:00:00Z", "updated_at": "2026-05-21T09:00:00Z" } ``` `download_url` is a stable API proxy endpoint. Calling it redirects to a short-lived signed file URL, so store the API URL and fetch a fresh redirect when needed. Requesting either mesh type starts the shared mesh pipeline; both `mesh` and `mesh-simplified` become ready when processing completes. `floorplan` is generated automatically after completion for worlds that produce a voxel grid (Echo 3 / capture worlds), so check its status before starting it — it is often already `PROCESSING` or `READY`. For other worlds, `POST …/exports/floorplan` starts the render. ##### Zip exports[​](#zip-exports "Direct link to Zip exports") `POST .../exports/zip` takes an optional JSON body selecting the assets to bundle; omitting it bundles all five: ```bash curl -sX POST "https://api.spaitial.ai/v1/worlds/requests/$REQ_ID/exports/zip" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"include": ["spz", "floorplan"]}' | jq ``` A zip is addressed by its effective include set (deduplicated and sorted), so repeating a request for the same set returns `200 READY` with the cached archive instantly, while a new set starts a new build (`202`). Missing assets (e.g. an unrequested mesh) are generated first, so a fresh full zip can take several minutes. Poll with the same set: ```bash curl -s "https://api.spaitial.ai/v1/worlds/requests/$REQ_ID/exports/zip?include=spz,floorplan" \ -H "Authorization: Bearer $API_KEY" | jq ``` Omit `?include=` to address the default all-assets zip. Responses carry the effective `include` array, and the archive contains fixed entry names — `splat.spz`, `splat.sog`, `splat.ply`, `mesh.ply`, `floorplan.png` — plus a `manifest.json` listing each entry's source and byte size. When a webhook URL was set on the original request, completion also delivers a `world.export.completed` event with `export.type: "zip"`, the `include` set, and the `download_url`. #### Idempotency[​](#idempotency "Direct link to Idempotency") Send the same `Idempotency-Key` header to safely retry a POST without double-charging: ```bash KEY=$(uuidgen) curl -X POST https://api.spaitial.ai/v1/worlds \ -H "Authorization: Bearer $API_KEY" \ -H "Idempotency-Key: $KEY" \ -d '{...}' ``` * Same key + same body → cached 202 response (no new job) * Same key + different body → `409 IDEMPOTENCY_KEY_REUSED` * Keys are not retry tokens; to retry a `FAILED` job, submit a fresh POST with a new key (or no key). #### Webhooks[​](#webhooks "Direct link to Webhooks") Set `webhook.url` on the POST to receive a callback on terminal state. ##### Headers[​](#headers "Direct link to Headers") ```text Content-Type: application/json User-Agent: SpaitialWebhook/1.0 X-Spaitial-Event: world.completed | world.failed | world.cancelled | world.export.completed | world.export.failed X-Spaitial-Request-ID: req_... X-Spaitial-Delivery-ID: wd_... X-Spaitial-Delivery-Attempt: 1 X-Spaitial-Signature: sha256= ``` Verify with the `webhook_secret` from your API key's settings page. ##### Payload (flat envelope)[​](#payload-flat-envelope "Direct link to Payload (flat envelope)") ```json { "event": "world.completed", "delivery_id": "wd_", "timestamp": "2026-05-15T10:00:02Z", "request_id": "req_...", "status": "COMPLETED", "created_at": "2026-05-14T12:00:00Z", "updated_at": "2026-05-14T12:09:48Z", "completed_at": "2026-05-14T12:09:48Z", "validation": { "passed": true, "issues": [] }, "data": { /* same shape as `world` in the result envelope */ } } ``` For `world.failed`: `data` is `null` + top-level `error: { code, message }`. For `world.cancelled`: `data` is `null`, no `error`. Export webhooks use the same envelope with `data: null` and an `export` block: ```json { "event": "world.export.completed", "delivery_id": "wd_", "timestamp": "2026-05-15T10:00:02Z", "request_id": "req_...", "status": "COMPLETED", "created_at": "2026-05-14T12:00:00Z", "updated_at": "2026-05-14T12:09:48Z", "completed_at": "2026-05-14T12:09:48Z", "data": null, "export": { "type": "mesh", "status": "READY" } } ``` For `world.export.failed`, `export.status` is `FAILED` and both the top-level `error` and `export.error` include `{ code, message }`. ##### Delivery semantics[​](#delivery-semantics "Direct link to Delivery semantics") * HTTPS only. Private/loopback hostnames rejected (SSRF). * 30s timeout per attempt. * Up to 5 retries with backoff (≈10s / 60s / 600s / …) on non-2xx or timeout. * Idempotent on `X-Spaitial-Delivery-ID` — same delivery may arrive twice; dedupe on this header. ##### Signature verification (Node example)[​](#signature-verification-node-example "Direct link to Signature verification (Node example)") ```js import { createHmac } from "crypto"; function verify(rawBody, signature, secret) { const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex"); return signature === expected; } ``` #### Cancellation[​](#cancellation "Direct link to Cancellation") ```bash curl -X POST "https://api.spaitial.ai/v1/worlds/requests/$REQ_ID/cancel" \ -H "Authorization: Bearer $API_KEY" # → 200 { "success": true } intent recorded (or the request was already CANCELLED) # → 409 REQUEST_NOT_CANCELLABLE the request already COMPLETED or FAILED ``` Cancel is best-effort and safe to retry. The API reports `CANCELLED` immediately; in-flight processing stops within seconds. The `world.cancelled` webhook's `completed_at` is the cancel time, matching the status endpoint. #### Updating a World[​](#updating-a-world "Direct link to Updating a World") Update visibility or title of a completed world: ```bash curl -X PATCH "https://api.spaitial.ai/v1/worlds/requests/$REQ_ID" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "My updated world", "visibility": { "is_public": true, "is_listed": true } }' ``` All fields are optional, but at least one must be provided: | Field | Type | Description | | ---------------------- | ------------------- | -------------------------------------------------------- | | `title` | string (≤200 chars) | User-facing world caption | | `visibility.is_public` | boolean | Anyone with `viewer_url` can view | | `visibility.is_listed` | boolean | Eligible for public gallery (requires `is_public: true`) | Returns the updated world object on success. Returns `409 RESOURCE_NOT_READY` if the world is not yet completed. #### Error envelope[​](#error-envelope "Direct link to Error envelope") Every non-2xx response: ```json { "error": { "code": "VALIDATION_FAILED", "message": "Human-readable reason", "details": { /* optional structured context */ } } } ``` Stable codes: | Code | HTTP | Meaning | | -------------------------------------------------- | ---- | ---------------------------------------------------------------------------------------------- | | `UNAUTHORIZED` | 401 | Missing / invalid API key | | `FORBIDDEN` | 403 | API key lacks required scope | | `MODEL_NOT_FOUND` | 400 | Unknown `model` | | `MODEL_FORBIDDEN` | 403 | Restricted model | | `MODEL_UNAVAILABLE` | 503 | Model deployment unhealthy | | `INVALID_INPUT` | 400 | Malformed body / unsupported input; `details.issues[]` lists `{ path, message }` per bad field | | `INSUFFICIENT_CREDITS` | 402 | Choose a plan or purchase credits; `details` has `required_credits` + `available_credits` | | `MODERATION_REJECTED` | 403 | Content moderation blocked the input | | `VALIDATION_FAILED` | 422 | Suitability check rejected (with `details.validation.issues`) | | `FILE_NOT_FOUND` | 404 | `file_id` unknown or not owned by caller | | `FILE_EXPIRED` | 404 | `file_id` older than 24 hours or already consumed | | `REQUEST_NOT_FOUND` | 404 | Unknown `request_id` or not owned by caller | | `PANORAMA_NOT_FOUND` | 404 | Unknown `panorama_id` or not owned by caller | | `PANORAMA_EXPIRED` | 410 | Edited panorama is past its 24-hour TTL | | `EDIT_FAILED` | 503 | Panorama edit could not be completed; retry | | `RESOURCE_NOT_READY` | 409 | World not yet `COMPLETED` for artifact/export operations | | `IDEMPOTENCY_KEY_REUSED` | 409 | Same key used with different body | | `REQUEST_NOT_CANCELLABLE` | 409 | Cancel on a COMPLETED / FAILED request | | `RATE_LIMIT_EXCEEDED` | 429 | Back off; check `Retry-After` + `X-RateLimit-*` | | `WORLD_USER_LIMIT` | 429 | Too many active generations on the account; retry when one finishes | | `WORLD_INTAKE_PAUSED` / `WORLD_INTAKE_UNAVAILABLE` | 503 | Generation intake temporarily closed; retry later | | `INTERNAL_ERROR` | 500 | Retry with backoff | #### Rate limits[​](#rate-limits "Direct link to Rate limits") Each response carries: ```text X-RateLimit-Limit: 60 X-RateLimit-Remaining: 58 X-RateLimit-Reset: 1778836080 ``` Defaults per key: | Bucket | Routes | Limit | | ----------------- | ---------------------------------------------------------------- | ------- | | `v1-world-create` | `POST /v1/worlds` | 10/min | | `v1-status` | `GET /…/status` | 300/min | | `v1-download` | `GET /…/splat`, `/panorama` | 120/min | | `v1-files` | `POST /v1/files` | 20/min | | `v1-default` | `POST /v1/panoramas/edit`, `GET /v1/panoramas…`, everything else | 120/min | `429 RATE_LIMIT_EXCEEDED` includes `Retry-After` (seconds). On world-create routes the `X-RateLimit-*` headers describe the create bucket, and requests rejected with a 4xx (validation, credits, access) don't count against it. #### Models[​](#models "Direct link to Models") Public models are **Echo 2** (id `echo-2`, 160 credits, ~8 min) and **Echo HQ** (id `echo-hq`, 800 credits, ~60 min). Full details: ```bash curl https://api.spaitial.ai/v1/models -H "Authorization: Bearer $API_KEY" ``` `model` is required on `POST /v1/worlds`: pass one of the ids from `GET /v1/models` (e.g. `"echo-2"`, `"echo-hq"`). Ids are stable lowercase slugs; each entry also has a display `name`. There is no default; a missing or `"default"` model fails with `400 INVALID_INPUT`, and every model error lists the ids your key can use. Each list entry includes `docs_url` and `pricing`: `{ "unit": "world", "credits": 160 }`, or for capture models `{ "unit": "capture_minute", "credits_per_minute", "minimum_credits", "minimum_duration_seconds" }`. `pricing` is the authoritative price. For capture models `credit_cost` is the minimum charge; get the exact figure for a recorded scan from `GET /v1/captures/process-quote` (`required_credits`, `available_credits`, `has_enough`). Worlds created from a panorama (`panorama_id`, or a panorama image) skip image-to-pano and cost `credit_cost − 45` (115 for Echo 2). #### Conventions worth knowing[​](#conventions-worth-knowing "Direct link to Conventions worth knowing") * IDs are opaque UUIDs with type prefixes: `req_`, `file_`, `pano_`, `wd_` (delivery). World IDs are returned as raw UUIDs (in `world.id`). * Times are ISO-8601 UTC. `completed_at` is `null` until terminal. * `world` is the **artifact**; `request` is the **operation**. They have different IDs. * `GET /v1/worlds/requests` and the per-request reads are scoped to **worlds created through the API by any key on your account**, so a read-only key can monitor jobs a write key started. Worlds generated in the Spaitial web app (dashboard) are **not** listed here, even for the same account — generate through the API if you need to list/download the result programmatically. * `validation` is advisory by default. Issues are surfaced as warnings on the world unless you opt into `error_on_fail: true`. * Submitting the same body with a new `Idempotency-Key` makes a fresh job. Reuse the same key only for genuine network retries. * Splats and panoramas are served from a private bucket. Always go through `/v1/worlds/requests/:id/splat` or `/panorama`. * Export `download_url` values are backend proxy URLs returned by `GET /v1/worlds/requests/:id/exports/:type`; calling one redirects to a short-lived signed file URL. #### Reference[​](#reference "Direct link to Reference") * Spec: `https://api.spaitial.ai/v1/openapi.json` * Swagger UI: `https://api.spaitial.ai/v1/docs` * Developers portal (keys, usage, webhooks): `https://developers.spaitial.ai` --- ### MCP Server SpAItial hosts a remote [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server so AI agents — Cursor, Claude, VS Code, Cline, and anything else that speaks MCP — can generate and manage 3D worlds directly, without you writing any API glue. ```text https://mcp.spaitial.ai/mcp ``` It's a **stateless, streamable-HTTP** server. Every tool maps 1:1 onto the [Developer API](https://docs.spaitial.ai/api/getting-started.md), so an agent can prompt → generate → poll → download a splat or mesh entirely on its own. #### Discovery[​](#discovery "Direct link to Discovery") Agents that support MCP Server Cards can discover the hosted server from: ```text https://mcp.spaitial.ai/.well-known/mcp/server-card.json ``` The card advertises the streamable-HTTP endpoint, auth schemes, and supported MCP surfaces. A compatibility alias is also available at `https://mcp.spaitial.ai/.well-known/mcp.json`. Bring your own key (BYOK) The server holds no credentials. Each request must carry **your** Spaitial API key, and generations are billed to your account. Get a key at [developers.spaitial.ai](https://developers.spaitial.ai), then send it as the `X-Spaitial-Api-Key` header (or `Authorization: Bearer spt_...`). Requests without a key return `401`. #### Install[​](#install "Direct link to Install") Pick your agent below. Everywhere you see `spt_live_YOUR_KEY`, **replace it with your real key**. The server accepts the key as either `X-Spaitial-Api-Key: ` or `Authorization: Bearer `. * Cursor * VS Code * Claude Code * Codex * opencode * Claude Desktop / other [![Add spaitial MCP server to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=spaitial\&config=eyJ1cmwiOiJodHRwczovL21jcC5zcGFpdGlhbC5haS9tY3AiLCJoZWFkZXJzIjp7IlgtU3BhaXRpYWwtQXBpLUtleSI6InNwdF9saXZlX1lPVVJfS0VZIn19) Clicking the badge opens Cursor and pre-fills the server. After it's added, open **Settings → MCP**, edit the `spaitial` server, and replace the placeholder key with your own. [![Add spaitial MCP server to VS Code](https://img.shields.io/badge/VS_Code-Add_MCP_Server-0098FF?style=flat-square\&logo=visualstudiocode\&logoColor=white)](https://vscode.dev/redirect/mcp/install?name=spaitial\&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fmcp.spaitial.ai%2Fmcp%22%2C%22headers%22%3A%7B%22X-Spaitial-Api-Key%22%3A%22spt_live_YOUR_KEY%22%7D%7D) [![Add to VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Add_MCP_Server-24bfa5?style=flat-square\&logo=visualstudiocode\&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=spaitial\&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fmcp.spaitial.ai%2Fmcp%22%2C%22headers%22%3A%7B%22X-Spaitial-Api-Key%22%3A%22spt_live_YOUR_KEY%22%7D%7D) The badge opens a `vscode.dev` page that redirects to the VS Code desktop app — approve the **"Open Visual Studio Code?"** browser prompt. If the page just sits there (no desktop app registered, or the prompt was blocked), use the CLI instead — it always works: ```bash code --add-mcp '{"name":"spaitial","type":"http","url":"https://mcp.spaitial.ai/mcp","headers":{"X-Spaitial-Api-Key":"spt_live_YOUR_KEY"}}' ``` Then open the `spaitial` server via **MCP: Open User Configuration** and set your real key. Add the remote server with one command: ```bash claude mcp add --transport http spaitial https://mcp.spaitial.ai/mcp \ --header "X-Spaitial-Api-Key: spt_live_YOUR_KEY" ``` Use `--scope user` to make it available across all projects, or `--scope project` to commit it to a shared `.mcp.json`. Verify with `claude mcp list`. Codex configures remote (streamable-HTTP) servers in `~/.codex/config.toml`. Add: ```toml [mcp_servers.spaitial] url = "https://mcp.spaitial.ai/mcp" http_headers = { "X-Spaitial-Api-Key" = "spt_live_YOUR_KEY" } ``` To avoid committing the key, pull it from the environment instead: ```toml [mcp_servers.spaitial] url = "https://mcp.spaitial.ai/mcp" env_http_headers = { "X-Spaitial-Api-Key" = "SPAITIAL_API_KEY" } ``` Then set `SPAITIAL_API_KEY=spt_live_...` in your shell. Run `/mcp` in the Codex TUI to confirm it's active. Add a `remote` entry to your `opencode.json` (or `~/.config/opencode/opencode.json`). `oauth: false` tells opencode to use the API key instead of attempting an OAuth flow: ```json { "$schema": "https://opencode.ai/config.json", "mcp": { "spaitial": { "type": "remote", "url": "https://mcp.spaitial.ai/mcp", "enabled": true, "oauth": false, "headers": { "X-Spaitial-Api-Key": "{env:SPAITIAL_API_KEY}" } } } } ``` `{env:SPAITIAL_API_KEY}` reads the key from your environment; swap in the literal `spt_live_...` if you prefer. Clients with native remote-MCP + header support: point them at `https://mcp.spaitial.ai/mcp` and send `X-Spaitial-Api-Key: spt_live_YOUR_KEY`. For clients that only support local (stdio) servers — including **Claude Desktop** — bridge to the remote URL with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote): ```json { "mcpServers": { "spaitial": { "command": "npx", "args": [ "mcp-remote", "https://mcp.spaitial.ai/mcp", "--header", "X-Spaitial-Api-Key:${SPAITIAL_API_KEY}" ], "env": { "SPAITIAL_API_KEY": "spt_live_YOUR_KEY" } } } } ``` Keep your key out of shared config Where a client supports it (Codex `env_http_headers`, opencode `{env:...}`, `mcp-remote`'s `${SPAITIAL_API_KEY}` + `env` block), read the key from an environment variable instead of hardcoding it — especially for any config you commit or share. #### Tools[​](#tools "Direct link to Tools") | Tool | Maps to | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `list_models` | `GET /v1/models` | | `create_world` | `POST /v1/worlds` — text / image URL / base64 / `file_id` / `panorama_id`, `is_pano`, `output_format`, visibility, webhook, idempotency | | `edit_panorama` | `POST /v1/panoramas/edit` — edit a completed API-created world/request, or iterate on a `pano_...`, with an optional reference image set | | `list_panoramas` | `GET /v1/panoramas` | | `get_panorama` | `GET /v1/panoramas/:id` | | `get_edited_panorama_download_url` | `GET /v1/panoramas/:id/download` (resolved signed URL) | | `get_world_status` | `GET /v1/worlds/requests/:id/status` | | `get_world` | `GET /v1/worlds/requests/:id` | | `list_world_requests` | `GET /v1/worlds/requests` | | `get_splat_download_url` | `GET /v1/worlds/requests/:id/splat` (resolved signed URL) | | `get_panorama_download_url` | `GET /v1/worlds/requests/:id/panorama` (resolved signed URL) | | `update_world` | `PATCH /v1/worlds/requests/:id` | | `cancel_world` | `POST /v1/worlds/requests/:id/cancel` | | `start_mesh_export` | `POST /v1/worlds/requests/:id/exports/:type` | | `get_mesh_export` | `GET /v1/worlds/requests/:id/exports/:type` | Each tool returns the same JSON shapes documented in the [API reference](https://docs.spaitial.ai/api/getting-started.md), including the standard [error envelope](https://docs.spaitial.ai/api/errors.md). Visibility scope: connector vs. web app `list_world_requests` (and the individual `get_world*` lookups) only see worlds generated **through the API/connector with your API key**. Worlds you create in the Spaitial **web app (dashboard)** are **not** listed by the connector — even when the API key belongs to the same account. For an automation or agent flow, generate through the connector (`create_world`) so the pipeline can read the result back. If you kick off generation in the web app, the connector won't be able to discover or download that world. #### Example prompts[​](#example-prompts "Direct link to Example prompts") Once installed, just ask your agent in natural language: * "Generate a Spaitial world of an empty cozy reading nook and give me the splat download URL when it's done." * "Edit world ``: change the rug to yellow, then show me the edited panorama." * "Add this attached sofa to `pano_...`, then create a new world from the result." * "List my recent world generations and their statuses." * "Export a simplified collision mesh for request `req_...` and download it when ready." The agent calls `create_world`, polls `get_world_status`, then `get_splat_download_url` / `start_mesh_export` — the same flow you'd script against the [REST API](https://docs.spaitial.ai/api/getting-started.md#your-first-request), driven by the model. For app-style edits, the agent calls `edit_panorama`, lets you inspect the returned `pano_...` with `get_edited_panorama_download_url`, optionally iterates by passing that `pano_...` back to `edit_panorama`, then calls `create_world` with `panorama_id` when you're satisfied. #### Notes[​](#notes "Direct link to Notes") * **Authentication** is identical to the REST API — the same `spt_live_` / `spt_test_` keys and [scopes](https://docs.spaitial.ai/api/llm-skills.md) apply per tool. * **Generation time** is ~5–10 minutes (Echo 2 - Standard); agents should poll `get_world_status` rather than block. * **Panorama edits are synchronous** and return a `pano_...` artifact. There is no aspect-ratio option; edited panoramas stay valid for world generation. * **Downloads** return short-lived signed URLs (~5 min); resolve them just before fetching. * Prefer scripting instead? See the [LLM Skills](https://docs.spaitial.ai/api/llm-skills.md) page for a self-contained REST reference, or install the skill with `npx skills add spaitial-dev/spaitial-api-skill`. --- ### Moderation Moderation is the safety gate that runs before user-controlled content reaches the generation pipeline. It is separate from [Validation](https://docs.spaitial.ai/api/validation.md): moderation blocks unsafe content; validation checks whether an otherwise safe image is likely to produce a good 3D world. #### What is checked[​](#what-is-checked "Direct link to What is checked") | Input flow | What moderation checks | | ---------------------------------------------- | ------------------------------------------------------------------------------------------------- | | `POST /v1/files` | Uploaded image bytes before the file is stored. | | `POST /v1/worlds` with `input.type: "file_id"` | Reuses the moderation stamp from the uploaded file when it is still fresh. | | `POST /v1/worlds` with `input.type: "url"` | The fetched image before generation starts. | | `POST /v1/worlds` with `input.type: "base64"` | The decoded inline image before generation starts. | | `POST /v1/worlds` with `input.type: "text"` | The text prompt before prompt-to-image generation. | | `POST /v1/panoramas/edit` | The edit prompt, the source panorama, and up to 3 optional reference images before the edit runs. | Image and text moderation uses the NSFW classifier. If moderation fails, no world job is started and the input is not sent downstream for generation. Validation flags do not bypass moderation `validation.skip` only controls the quality/suitability VLM check. Content moderation still runs for API inputs. Panorama inputs with `is_pano: true` also skip suitability validation, but still follow the same moderation path as their `url`, `base64`, or `file_id` input type. Edited panoramas (`panorama_id`) are pre-moderated by `POST /v1/panoramas/edit` before they can be used to create a world. #### Rejection shape[​](#rejection-shape "Direct link to Rejection shape") Moderation failures return `403` with `MODERATION_REJECTED`: ```json { "error": { "code": "MODERATION_REJECTED", "message": "Image failed moderation" } } ``` Branch on `error.code`, not the message. The message is intentionally human-readable and may change. #### Example: uploaded file rejected[​](#example-uploaded-file-rejected "Direct link to Example: uploaded file rejected") ```bash curl -sX POST "https://api.spaitial.ai/v1/files" \ -H "Authorization: Bearer $API_KEY" \ -F "file=@unsafe-input.jpg" ``` ```json { "error": { "code": "MODERATION_REJECTED", "message": "Image failed moderation" } } ``` #### Example: text prompt rejected[​](#example-text-prompt-rejected "Direct link to Example: text prompt rejected") ```bash curl -sX POST "https://api.spaitial.ai/v1/worlds" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "type": "text", "prompt": "..." }, "model": "echo-2" }' ``` ```json { "error": { "code": "MODERATION_REJECTED", "message": "Prompt failed moderation" } } ``` #### Operational behavior[​](#operational-behavior "Direct link to Operational behavior") * Moderation runs before generation work and before credits are consumed for rejected content. * Uploaded files are moderated before the `file_id` is returned. * `file_id` world requests use the upload's moderation stamp instead of asking you to resubmit bytes. * Panorama edits are moderated before credits are committed. If moderation rejects an edit prompt or reference image, no panorama artifact is created. * Moderation metadata is kept server-side for auditing and freshness checks; the public API only exposes the stable rejection envelope. * Moderation provider failures are treated as server errors. Retry `5xx` responses with backoff and an `Idempotency-Key` on mutating endpoints like `POST /v1/worlds` and `POST /v1/panoramas/edit`. #### Related errors[​](#related-errors "Direct link to Related errors") | HTTP | `error.code` | Meaning | | ----- | --------------------- | ----------------------------------------------------------- | | `403` | `MODERATION_REJECTED` | Input content was blocked by moderation. | | `403` | `FORBIDDEN` | Your key is valid but lacks the endpoint's required scope. | | `500` | `INTERNAL_ERROR` | Moderation or another internal service failed unexpectedly. | If content is safe but structurally poor for 3D generation, use [Validation](https://docs.spaitial.ai/api/validation.md) instead of expecting a moderation error. --- ### 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. Panoramas are iterable edit artifacts Each edit returns a public `panorama_id` (`pano_...`). Feed it back into the edit endpoint to iterate, or pass it to `POST /v1/worlds` to generate a world. Panoramas are scoped to the API key's user and live for 24 hours. #### Endpoints[​](#endpoints "Direct link to 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[​](#the-edit-loop "Direct link to The edit loop") ```text 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" }, model: "echo-2" } -> { request_id: "req_...", status: "PENDING", ... } # normal world flow ``` #### Edit a panorama[​](#edit-a-panorama "Direct link to 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` — the `world.id` from 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). ```bash 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: ```json { "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[​](#optional-reference-images "Direct link to 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`: ```json { "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[​](#iterate "Direct link to 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. ```bash 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[​](#create-a-world-from-a-panorama "Direct link to 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. ```bash 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" }, "model": "echo-2", "title": "Edited world" }' ``` This returns the usual `202 Accepted` world job — poll its `status_url` or use a [webhook](https://docs.spaitial.ai/api/getting-started.md) 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[​](#idempotency "Direct link to 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[​](#errors "Direct link to 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. | | `503` | `EDIT_FAILED` | The edit could not be completed; retry. | See [Moderation](https://docs.spaitial.ai/api/moderation.md) for content screening and [Credits & Billing](https://docs.spaitial.ai/api/credits-billing.md) for edit pricing. --- ### Rate Limits The SpAItial Developer API enforces per-key rate limits using fixed-window counters. #### Defaults[​](#defaults "Direct link to Defaults") | Bucket | Default | Applies to | | ------------ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | General | **60 requests / minute** | Every authenticated request, regardless of method. | | World create | **10 requests / minute** | `POST /v1/worlds` only. Consumed in addition to the general bucket. | | Downloads | **120 requests / minute** | `GET /v1/worlds/requests/{id}/splat`, `GET /v1/worlds/requests/{id}/panorama`, and `GET /v1/panoramas/{id}/download`. Runs on its own bucket so polling a download does not lock out status/result/cancel. | A `POST /v1/worlds` (and `POST /v1/captures/{id}/process`) therefore decrements both the general and the world-create buckets in the same window, and its `X-RateLimit-*` headers describe the world-create bucket. A creation rejected with a `4xx` (invalid input, insufficient credits, concurrency limit) is refunded to the world-create bucket. Panorama edits (`POST /v1/panoramas/edit`) use the general bucket. Downloads decrement only the dedicated download bucket. Need higher limits? Contact us if you need a higher ceiling for a specific integration or project. #### Response headers[​](#response-headers "Direct link to Response headers") Every rate-limited response (success or 429) includes: | Header | Description | | ----------------------- | -------------------------------------------------------------- | | `X-RateLimit-Limit` | The bucket's ceiling for this request, in requests per minute. | | `X-RateLimit-Remaining` | Requests remaining in the current window. Clamped to `0`. | | `X-RateLimit-Reset` | Unix timestamp (seconds) at which the current window expires. | | `Retry-After` | Present on `429` responses; seconds to wait before retrying. | #### When you hit the limit[​](#when-you-hit-the-limit "Direct link to When you hit the limit") A blocked request returns `429 Too Many Requests` with the standard [error codes](https://docs.spaitial.ai/api/errors.md): ```json { "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded. Try again in 17 seconds." } } ``` Recommended client behavior: 1. Read `Retry-After` on `429` responses, or fall back to `X-RateLimit-Reset`. 2. Apply jitter so a fleet of clients does not synchronize against the same window boundary. 3. Pair retries on `POST /v1/worlds` and `POST /v1/panoramas/edit` with an `Idempotency-Key` so the retry does not create duplicate work. --- ### files File upload endpoints #### [📄️List files uploaded by this API key](https://docs.spaitial.ai/api/reference/v-1-files-list-files.md) [List files uploaded by this API key](https://docs.spaitial.ai/api/reference/v-1-files-list-files.md) #### [📄️Upload a file for later use in world generation](https://docs.spaitial.ai/api/reference/v-1-files-upload-file.md) [Upload a file for later use in world generation](https://docs.spaitial.ai/api/reference/v-1-files-upload-file.md) --- ### models Model discovery endpoints #### [📄️List available models](https://docs.spaitial.ai/api/reference/v-1-models-list-models.md) [Each model includes estimated\_duration\_seconds derived from its current per-stage time estimates. Pass input\_kind to list only aliases for that pipeline (iPhone capture uses perspective\_capture).](https://docs.spaitial.ai/api/reference/v-1-models-list-models.md) --- ### SpAItial Developer API Version: 1.0.0 Programmatic API for generating 3D worlds. See the [Getting Started guide](https://docs.spaitial.ai/api/getting-started) for authentication and usage examples. #### Authentication[​](#authentication "Direct link to Authentication") * HTTP: Bearer Auth API key issued by the developers site. Example: spt\_live\_abc123... | Security Scheme Type: | http | | -------------------------- | ------- | | HTTP Authorization Scheme: | bearer | | Bearer format: | API Key | --- ### List files uploaded by this API key List files uploaded by this API key #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 Paginated file list Invalid query parameters Unauthorized Forbidden Rate limit exceeded --- ### Upload a file for later use in world generation Upload a file for later use in world generation #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 201 * 400 * 401 * 403 * 413 * 429 File uploaded successfully Invalid file Unauthorized Moderation rejected File too large Rate limit exceeded --- ### List available models Each model includes estimated\_duration\_seconds derived from its current per-stage time estimates. Pass input\_kind to list only aliases for that pipeline (iPhone capture uses perspective\_capture). #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 401 List of available models for world generation Unauthorized --- ### Download a panorama image Redirects (302) to a short-lived signed download URL (~5 minutes). Re-hit this endpoint when it expires. #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 302 * 404 * 410 Redirect to signed download URL Panorama not found Panorama has expired --- ### Edit a panorama and return the updated panorama Fetches the panorama behind the source (a request\_id / world\_id you created, or a panorama\_id from a previous edit), applies the prompt + optional reference images, and returns the new panorama. Iterate by passing the returned panorama\_id back in as the source; create a world from the final panorama via POST /v1/worlds with input { type: "panorama\_id" }. #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 402 * 403 * 404 * 409 * 410 * 429 Edited panorama Invalid request Unauthorized Insufficient credits Moderation rejected Source not found Source world is not ready yet Source panorama has expired Rate limit exceeded --- ### Get a panorama Get a panorama #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 404 * 410 Panorama details Panorama not found Panorama has expired --- ### List panoramas created by this API key List panoramas created by this API key #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 Paginated panorama list --- ### Cancel a job Cancel a job #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 404 Cancel request processed Job not found --- ### Start or retrieve an export for a completed job Export artifacts from worlds: collision meshes (mesh, mesh-simplified), a top-down floorplan render (floorplan), and a zip bundle of world assets (zip). Zip exports accept an optional JSON body {"include": \["spz", "sog", "ply", "mesh", "floorplan"]} and default to all assets. Exports can take several minutes, so check the status before requesting the download URL. #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 202 * 404 * 409 Export is ready Export generation started Request not found World is not ready yet --- ### Create a new world generation job Create a new world generation job #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 202 * 400 * 401 * 402 * 409 * 429 Job accepted and queued for processing Invalid request Unauthorized Insufficient credits Idempotency key conflict Rate limit exceeded --- ### Get export status Returns export status. When ready, the response includes a backend proxy download\_url. Add ?download=1 to this same endpoint to redirect to a short-lived signed file URL. Zip exports accept ?include=spz,sog,... (comma-separated) to address the zip for that asset set; omit it for the default all-assets zip. #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 302 * 404 * 409 Export status and download link Redirect to a short-lived signed file URL when ?download=1 is provided Request not found World is not ready yet --- ### Get full job result Get full job result #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 404 Full job result with world details Job not found --- ### Get job status Progress is 0..1 when the pipeline has meaningful stage movement, otherwise null (never treat null as 0%). Long reconstruction jobs return the current model alias estimated\_duration\_seconds so clients can show ETA + elapsed instead of a percent. world\_id is set as soon as the world is created, before the job completes. #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 404 Job status Job not found --- ### Download the panorama artifact for a completed job Redirects (302) to a short-lived signed download URL. The URL is valid for ~5 minutes; re-hit this endpoint when it expires. #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 302 * 404 * 409 Redirect to a short-lived signed download URL Request not found Panorama is not ready yet --- ### Download the splat artifact for a completed job Redirects (302) to a short-lived signed download URL. The URL is valid for ~5 minutes; re-hit this endpoint when it expires. #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 302 * 404 * 409 Redirect to a short-lived signed download URL Request not found Splat is not ready yet --- ### List exports for a completed job List exports for a completed job #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 404 * 409 List of export artifacts Request not found World is not ready yet --- ### List API jobs on this account Returns worlds generated through the API by any key on your account. Worlds created in the Spaitial web app (dashboard) are not included, even for the same account. Generate through the API if you need the pipeline to list the result. `status` is optional; omitting it returns the same unfiltered page as before. #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 Paginated job list Invalid query parameters Unauthorized Forbidden --- ### Update world visibility or title Update world visibility or title #### Request[​](#request "Direct link to request") #### Responses[​](#responses "Direct link to Responses") * 200 * 400 * 404 * 409 Updated world details Invalid request Request not found World is not ready yet --- ### worlds World generation endpoints #### [📄️Create a new world generation job](https://docs.spaitial.ai/api/reference/v-1-worlds-create-job.md) [Create a new world generation job](https://docs.spaitial.ai/api/reference/v-1-worlds-create-job.md) #### [📄️List API jobs on this account](https://docs.spaitial.ai/api/reference/v-1-worlds-list-jobs.md) [Returns worlds generated through the API by any key on your account. Worlds created in the Spaitial web app (dashboard) are not included, even for the same account. Generate through the API if you need the pipeline to list the result. \`status\` is optional; omitting it returns the same unfiltered page as before.](https://docs.spaitial.ai/api/reference/v-1-worlds-list-jobs.md) #### [📄️Get job status](https://docs.spaitial.ai/api/reference/v-1-worlds-get-job-status.md) [Progress is 0..1 when the pipeline has meaningful stage movement, otherwise null (never treat null as 0%). Long reconstruction jobs return the current model alias estimated\_duration\_seconds so clients can show ETA + elapsed instead of a percent. world\_id is set as soon as the world is created, before the job completes.](https://docs.spaitial.ai/api/reference/v-1-worlds-get-job-status.md) #### [📄️Get full job result](https://docs.spaitial.ai/api/reference/v-1-worlds-get-job-result.md) [Get full job result](https://docs.spaitial.ai/api/reference/v-1-worlds-get-job-result.md) #### [📄️Update world visibility or title](https://docs.spaitial.ai/api/reference/v-1-worlds-update-world.md) [Update world visibility or title](https://docs.spaitial.ai/api/reference/v-1-worlds-update-world.md) #### [📄️Cancel a job](https://docs.spaitial.ai/api/reference/v-1-worlds-cancel-job.md) [Cancel a job](https://docs.spaitial.ai/api/reference/v-1-worlds-cancel-job.md) #### [📄️Start or retrieve an export for a completed job](https://docs.spaitial.ai/api/reference/v-1-worlds-create-export.md) [Export artifacts from worlds: collision meshes (mesh, mesh-simplified), a top-down floorplan render (floorplan), and a zip bundle of world assets (zip). Zip exports accept an optional JSON body {'include': \['spz', 'sog', 'ply', 'mesh', 'floorplan'\]} and default to all assets. Exports can take several minutes, so check the status before requesting the download URL.](https://docs.spaitial.ai/api/reference/v-1-worlds-create-export.md) #### [📄️Get export status](https://docs.spaitial.ai/api/reference/v-1-worlds-get-export.md) [Returns export status. When ready, the response includes a backend proxy download\_url. Add ?download=1 to this same endpoint to redirect to a short-lived signed file URL. Zip exports accept ?include=spz,sog,... (comma-separated) to address the zip for that asset set; omit it for the default all-assets zip.](https://docs.spaitial.ai/api/reference/v-1-worlds-get-export.md) #### [📄️List exports for a completed job](https://docs.spaitial.ai/api/reference/v-1-worlds-list-exports.md) [List exports for a completed job](https://docs.spaitial.ai/api/reference/v-1-worlds-list-exports.md) #### [📄️Download the splat artifact for a completed job](https://docs.spaitial.ai/api/reference/v-1-worlds-get-splat-download.md) [Redirects (302) to a short-lived signed download URL. The URL is valid for ~5 minutes; re-hit this endpoint when it expires.](https://docs.spaitial.ai/api/reference/v-1-worlds-get-splat-download.md) #### [📄️Download the panorama artifact for a completed job](https://docs.spaitial.ai/api/reference/v-1-worlds-get-panorama-download.md) [Redirects (302) to a short-lived signed download URL. The URL is valid for ~5 minutes; re-hit this endpoint when it expires.](https://docs.spaitial.ai/api/reference/v-1-worlds-get-panorama-download.md) --- ### SpAItial Developer API Service Description *Last updated: September 23, 2026* This page describes the customer-facing scope, principal features, and current limitations of the SpAItial Developer API. It is intended to help customers evaluate and operate integrations with the service. #### Service scope[​](#service-scope "Direct link to Service scope") The SpAItial Developer API is an authenticated REST API for programmatically creating and retrieving AI-generated 3D worlds. The production API is available at `https://api.spaitial.ai`, and customers manage API access through [developers.spaitial.ai](https://developers.spaitial.ai). Customers can submit text, supported images, equirectangular 360° panoramas, or edited panoramas to an asynchronous generation workflow. Completed jobs provide access to a 3D Gaussian Splat, panorama, thumbnail, and hosted viewer. Supported export operations can produce additional artifacts. This description covers the public Developer API and its published endpoints. The interactive web application is a separate service described in the [SpAItial App Service Description](https://docs.spaitial.ai/overview/app-service-description.md). #### Primary features[​](#primary-features "Direct link to Primary features") | Feature | Description | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | API authentication and scopes | Create bearer API keys with explicit scopes and revoke them through the developer portal. | | Model discovery | List the generation models available to an API key, including model descriptions, credit costs, and estimated durations. | | Input methods | Generate from text, an image URL, an inline base64 image, a temporary uploaded file, a 360° panorama, or a supported edited panorama. | | Asynchronous world generation | Submit a job, inspect its status and progress, cancel eligible jobs, and fetch completed results. | | Polling and webhooks | Poll job status or configure a webhook to receive terminal job events. | | Generated outputs | Retrieve authenticated download endpoints for splats and panoramas, along with thumbnail and viewer URLs. | | Panorama editing | Create and retrieve temporary edited panoramas for use in subsequent world-generation requests. | | File handling | Upload, list, and reference temporary private image inputs. | | Error and usage controls | Use stable error codes, idempotency keys, validation feedback, and rate-limit headers to operate an integration reliably. | The API reference documents the current request and response schemas for each published endpoint. #### Access and service requirements[​](#access-and-service-requirements "Direct link to Access and service requirements") Customers need: * A SpAItial developer account. * A server-side integration capable of protecting a bearer API key. * An API key with the scopes required by each requested operation. * Sufficient eligible credits for credit-consuming operations. * The ability to process asynchronous job states, errors, retries, and expiring resources. See [Authentication](https://docs.spaitial.ai/api/authentication.md), [Credits and Billing](https://docs.spaitial.ai/api/credits-billing.md), and the API Reference for current requirements. #### Limitations and customer considerations[​](#limitations-and-customer-considerations "Direct link to Limitations and customer considerations") * **Generated results vary.** Worlds and panorama edits are produced by generative AI. Results may not precisely reproduce an input or prompt and may contain visual, geometric, or semantic artifacts. * **Generation is asynchronous.** A successful submission accepts a job for processing; it does not mean generation is complete. Published durations are estimates, and jobs can fail or be cancelled. Integrations must inspect the terminal status before requesting results. * **Rate limits apply per API key.** The default limits are 60 general requests per minute, 10 world-creation requests per minute, and 120 download requests per minute. A world-creation request consumes both its world-create bucket and the general bucket. Account-specific limits may differ. * **Authentication and credits are required.** Requests can be rejected because a key is missing, invalid, revoked, expired, or insufficiently scoped. Credit-consuming requests can also be rejected when the account lacks enough eligible credits. * **Inputs are screened.** User-controlled inputs are subject to content moderation. Optional suitability validation can identify image-quality issues, but it is not a safety system and is skipped by default unless the customer enables it. * **Some resources are temporary.** Uploaded files and edited panoramas have a 24-hour lifetime. Download endpoints redirect to short-lived signed URLs, so integrations should resolve them shortly before downloading. * **Input and output constraints apply.** Supported file types, file sizes, input shapes, models, and output formats are limited to those documented in the current API schema. Model availability can vary by account. * **Failures require explicit handling.** Integrations should branch on stable error codes, honor `Retry-After`, use backoff with jitter for retryable failures, and use idempotency keys for retried mutating requests. * **API keys are server-side secrets.** The API is not designed for keys to be embedded in browser code, mobile applications, or other software distributed to end users. #### Customer responsibilities[​](#customer-responsibilities "Direct link to Customer responsibilities") Customers are responsible for: * Keeping API keys secret, assigning only necessary scopes, and revoking keys that may have been exposed. * Providing content they are authorized to process. * Reviewing generated results before relying on or distributing them. * Implementing appropriate timeout, retry, idempotency, error-handling, and webhook-handling behavior. * Consuming temporary input and panorama resources before they expire. * Monitoring credit consumption and applicable rate limits. Use of the service is also governed by the [Terms of Service](https://app.spaitial.ai/terms) and [Privacy Policy](https://app.spaitial.ai/privacy). #### Related documentation and support[​](#related-documentation-and-support "Direct link to Related documentation and support") * [Getting Started](https://docs.spaitial.ai/api/getting-started.md) * [Authentication](https://docs.spaitial.ai/api/authentication.md) * [Models](https://docs.spaitial.ai/api/generation-models.md) * [Credits and Billing](https://docs.spaitial.ai/api/credits-billing.md) * [Errors](https://docs.spaitial.ai/api/errors.md) * [Rate Limits](https://docs.spaitial.ai/api/rate-limits.md) * [Files](https://docs.spaitial.ai/api/files.md) * [Panorama Editing](https://docs.spaitial.ai/api/panorama-editing.md) * [Moderation](https://docs.spaitial.ai/api/moderation.md) * [Validation](https://docs.spaitial.ai/api/validation.md) * [Security](https://spaitial.ai/security) * [Subprocessors](https://spaitial.ai/subprocessors) For service questions or support, use the [Spaitial Support page](https://spaitial.ai/support) for troubleshooting and the contact form. This description is updated when material changes are made to the Developer API's scope, features, requirements, or limitations. --- ### Validation Validation is an optional suitability check for image inputs. It asks whether an image is likely to produce a good 3D world: level camera, enough scene context, clean image quality, no heavy overlays, and similar pipeline-specific concerns. It is not a safety system. Unsafe content is handled by [Moderation](https://docs.spaitial.ai/api/moderation.md), which runs independently. #### Options[​](#options "Direct link to Options") `POST /v1/worlds` accepts a `validation` object: ```json { "validation": { "skip": false, "error_on_fail": false } } ``` | Field | Default | Meaning | | --------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `skip` | `true` | Skip the suitability VLM check. This is the v1 default to save latency and cost. | | `error_on_fail` | `false` | When `skip: false`, return `422 VALIDATION_FAILED` for blocking issues instead of continuing with warnings. Ignored when `skip: true`. | #### Advisory mode[​](#advisory-mode "Direct link to Advisory mode") Set `skip: false` and leave `error_on_fail: false` when you want validation feedback without blocking generation: ```bash curl -sX POST "https://api.spaitial.ai/v1/worlds" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "type": "url", "image_url": "https://example.com/room.jpg" }, "model": "echo-2", "validation": { "skip": false, "error_on_fail": false } }' ``` The job proceeds when validation finds warnings. Completed job responses and webhook payloads may include: ```json { "validation": { "passed": true, "issues": [ { "code": "low_quality", "severity": "warning", "message": "Image quality looks borderline — sharper, higher-resolution photos usually produce better worlds." } ] } } ``` The `validation` field is omitted when there are no validation warnings. #### Strict mode[​](#strict-mode "Direct link to Strict mode") Set `error_on_fail: true` when you want blocking validation issues to reject the request before the job starts: ```bash curl -sX POST "https://api.spaitial.ai/v1/worlds" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "type": "file_id", "file_id": "file_abc123" }, "model": "echo-2", "validation": { "skip": false, "error_on_fail": true } }' ``` Blocking failures return `422`: ```json { "error": { "code": "VALIDATION_FAILED", "message": "This image is not suitable for 3D world generation.", "details": { "validation": { "passed": false, "issues": [ { "code": "watermark_or_label", "severity": "error", "message": "There appears to be a watermark, logo, or caption overlay in the image. A clean image you own (without any overlays) usually works best." } ] }, "fix_options": [ { "id": "remove_text_overlays", "issue_code": "watermark_or_label", "label": "Remove watermarks and overlays", "description": "Logos, captions, or UI chrome will be inpainted out." } ] } } } ``` #### Issue shape[​](#issue-shape "Direct link to Issue shape") Validation issues use the same shape in job results, webhook payloads, and strict-mode error details: ```json { "code": "extreme_viewpoint", "severity": "warning", "message": "The camera angle looks a bit unusual. Eye-level shots with a visible horizon usually produce the best results." } ``` `severity` is `warning` for advisory issues and `error` for blockers. #### Common issue codes[​](#common-issue-codes "Direct link to Common issue codes") | Code | What it means | | -------------------- | -------------------------------------------------------------------------------------------------------------- | | `extreme_viewpoint` | The camera is straight up/down, heavily tilted, aerial, fisheye, or already equirectangular. | | `watermark_or_label` | Text overlays, watermarks, logos, captions, UI chrome, or labels may get baked into the world. | | `tight_space` | The image has too little surrounding scene context for robust extrapolation. | | `low_quality` | The image is blurry, low-resolution, heavily compressed, noisy, or poorly exposed. | | `non_scene` | The input looks like a portrait, product shot, document, chart, UI screenshot, or other non-environment image. | | `people_present` | People are visible; the pipeline prefers empty static scenes. | | `other` | The validator found another suitability issue. | #### When validation is skipped[​](#when-validation-is-skipped "Direct link to When validation is skipped") Validation is skipped when `validation.skip` is `true` or omitted. Some internally generated sources, such as text-to-image outputs and already-fixed inputs, also skip the suitability check because they have already gone through a controlled flow. Panorama inputs also skip validation. Set `is_pano: true` on a `url`, `base64`, or `file_id` input only when you are providing an equirectangular 360 panorama: ```json { "input": { "type": "file_id", "file_id": "file_pano123", "is_pano": true }, "model": "echo-2", "validation": { "skip": false, "error_on_fail": true } } ``` The API trusts the pano flag and ignores the validation object for that request. Moderation still runs. A request can skip validation and still fail with `MODERATION_REJECTED`. --- ## Create With Ai Agents ### Create with AI Agents [](https://pub-fd1d4d4f8ef34c7e8597ee625f885bf9.r2.dev/final_final_cut_claude_plugin.mp4) SpAItial is built to be used directly from AI coding agents. You can connect an agent through our hosted MCP server, load a self-contained LLM skill, or install the Claude plugin and create worlds from chat. #### Choose Your Agent Path[​](#choose-your-agent-path "Direct link to Choose Your Agent Path") ##### Hosted MCP Server[​](#hosted-mcp-server "Direct link to Hosted MCP Server") Use the hosted [SpAItial MCP server](https://docs.spaitial.ai/api/mcp-server.md) when your agent supports MCP tools. It gives Cursor, Claude Code, VS Code, Codex, Cline, and other clients direct access to world creation, panorama editing, status checks, downloads, world updates, and mesh exports. ```text https://mcp.spaitial.ai/mcp ``` The MCP server is bring-your-own-key: send your SpAItial API key as `X-Spaitial-Api-Key` or `Authorization: Bearer spt_...`. ##### LLM Skills[​](#llm-skills "Direct link to LLM Skills") Use [LLM Skills](https://docs.spaitial.ai/api/llm-skills.md) when you want an agent to understand the REST API without browsing the docs. The skill is a single-page implementation reference with auth, endpoints, request shapes, webhooks, errors, rate limits, and code examples. ```bash npx skills add spaitial-dev/spaitial-api-skill ``` ##### Claude Plugin[​](#claude-plugin "Direct link to Claude Plugin") Use the [SpAItial Claude plugin](https://github.com/spaitial-ai/spaitial-ai-plugin) when you want chat-first world creation. The plugin connects Claude to the hosted SpAItial MCP server, so you can create worlds from text, images, or panoramas, edit generated panoramas, manage previous worlds, and export meshes from a Claude conversation. Install it from the Claude plugin marketplace: ```text /plugin marketplace add spaitial-ai/spaitial-ai-plugin /plugin install spaitial-ai@spaitial-ai ``` #### What Agents Can Do[​](#what-agents-can-do "Direct link to What Agents Can Do") * Create a world from a text prompt, image URL, uploaded file, or 360° panorama. * Edit an API-created world by generating a new panorama from a prompt and optional references. * Poll generation status and fetch the finished `viewer_url`, splat download URL, or panorama download URL. * Rename worlds, change visibility, cancel running jobs, and start mesh exports. #### Start Here[​](#start-here "Direct link to Start Here") * Need tool calls inside an IDE or CLI agent? Start with the [MCP Server](https://docs.spaitial.ai/api/mcp-server.md). * Need a compact reference for a coding assistant? Load [LLM Skills](https://docs.spaitial.ai/api/llm-skills.md). * Need Claude chat workflows? Install the [Claude plugin](https://github.com/spaitial-ai/spaitial-ai-plugin). * Need API keys, usage, and webhooks? Open the [Developer Portal](https://developers.spaitial.ai). ---