Skip to main content
Documentation

Panorama to VR

Package multiple panoramas, submit an overseas Pano-to-3D task, and poll for a browsable VR URL.

This guide adapts the Domestic panorama-to-VR tutorial to the separately provisioned overseas Pano-to-3D contract. It uploads a ZIP containing multiple panoramas, creates a Realsee project, and returns a browsable VR URL.

Two reconstruction contracts

The overseas gateway exposes two different capabilities:

  • Argus/VGGT uses /open/saas/v1/vggt/* for one input_image_id and returns reconstruction output through result_url.
  • Pano-to-3D uses /open/v1/pano/* for a multi-panorama ZIP and returns a Realsee project plus vr_url.

This page follows the Pano-to-3D contract in open-pano-to-3d.openapi.json. Do not mix its task_code, ZIP location, or status fields into Argus/VGGT requests.

Before you start

  • Submit an App request that explicitly describes the multi-panorama-to-VR workflow and expected monthly volume. Access to Argus/VGGT does not by itself document access to Pano-to-3D.
  • Keep the approved app key, app secret, gateway access token, upload credentials, and task orchestration on your backend.
  • Prepare equirectangular JPG panoramas and the ZIP manifest described below.
  • Install the verified uploader and its COS adaptor peer:
npm install @realsee/universal-uploader@0.1.1 cos-js-sdk-v5@1.8.3

All requests in this guide use the overseas production gateway:

https://app-gateway.realsee.ai

ZIP package format

Create one ZIP with manifest.json at its root and the panorama files under images/:

my-project.zip
├── manifest.json
└── images/
    ├── entrance.jpg
    ├── entrance-02.jpg
    ├── end-of-nave.jpg
    ├── presbyterium.jpg
    └── high-altar.jpg

Example manifest.json:

{
  "version": "1.0",
  "floor_map": {
    "0": 1,
    "1": 2
  },
  "project_name": "my-pano-project",
  "scan_list": [
    { "id": "entrance", "floor": 0 },
    { "id": "entrance-02", "floor": 0 },
    { "id": "end-of-nave", "floor": 0 },
    { "id": "presbyterium", "floor": 1 },
    { "id": "high-altar", "floor": 1 }
  ]
}
FieldTypeDescription
versionstringPackage version. Use "1.0" for this workflow.
floor_mapobjectMaps each scan_list[].floor key to the displayed floor number.
project_namestringName recorded in the package. Use the same stable project name when submitting the task.
scan_listarrayOrdered panorama records.
scan_list[].idstringMust match the corresponding JPG filename without its extension.
scan_list[].floornumberFloor key represented in floor_map.

Every file under images/ must have a matching scan_list entry. For a single-floor project, one mapping such as { "0": 0 } is sufficient.

Workflow

Get access_token → get upload credentials → upload ZIP → submit task → poll task_code → read vr_url

Get an access token

Call POST /auth/access_token with application/x-www-form-urlencoded data:

app_key=<reviewed_app_key>&app_secret=<reviewed_app_secret>

A successful response contains data.access_token and data.expire_at. Send the raw token in the Authorization header on the remaining requests; do not add a Bearer prefix.

Get upload credentials

Call GET /open/v1/pano/file/token with the gateway token:

GET /open/v1/pano/file/token
Authorization: <raw_gateway_access_token>

Unlike the Argus/VGGT upload-token endpoint, this request has no input_image_id and returns the UploadToken fields directly under data.

Key response fields include:

FieldPurpose
tmpSecretId, tmpSecretKey, sessionTokenTemporary object-storage credentials. Never log them.
bucket, region, hostUpload destination.
prefixPrefix that must be prepended to the uploaded ZIP key.
ttl, expireCredential lifetime in seconds. Here expire is a TTL, not the expire_at Unix timestamp returned by authentication.
download_type, download_hostDetermine which file-location field to send when submitting the task.
custom_domain, custom_schemeProvider endpoint values used by the upload adaptor.

If the gateway returns code: -3, refresh the access token before requesting a new upload token.

Upload the ZIP

Create the uploader with the COS adaptor and pass the complete data object from the previous response as its UploadToken:

import {
  Uploader,
  type ProviderAdaptor,
  type UploadToken,
} from '@realsee/universal-uploader'

const cosAdaptor: ProviderAdaptor = () => import('@realsee/universal-uploader/adaptors/cos')

export async function uploadPanoramaZip(zipFile: File, token: UploadToken) {
  const uploader = new Uploader(cosAdaptor, {
    getToken: async () => token,
  })
  const uploadKey = 'my-project.zip'

  await uploader.upload(uploadKey, zipFile, {
    parallel: Uploader.defaultUploadHandler.parallel,
    partSize: Uploader.defaultUploadHandler.partSize,
    retry: Uploader.defaultUploadHandler.retry,
  })

  return `${token.prefix}${uploadKey}`
}

The returned string is the full object path used as private_cos_key. If download_host is non-empty and your upload adaptor returns a download_url, retain that URL for zip_cos_url instead.

Submit the processing task

Call POST /open/v1/pano/task/submit. project_name is required, and the runtime requires one ZIP location:

  • Use private_cos_key when download_host is empty, which represents the private-bucket presign flow.
  • Use zip_cos_url when the upload result provides a downloadable URL.

Private-bucket example:

{
  "project_name": "my-pano-project",
  "private_cos_key": "vrfile/release/open_task_original/T-XXXXXXXXXX/pano/1700000000/my-project.zip"
}

Successful data contains both identifiers:

type PanoTaskSubmission = {
  project_id: string
  task_code: string
}

Retain task_code for status polling. It is not interchangeable with the Argus/VGGT input_image_id or alg_task_id fields.

Poll for completion

Call GET /open/v1/pano/task/status with the returned task code:

GET /open/v1/pano/task/status?task_code=<task_code>
Authorization: <raw_gateway_access_token>

The response data follows this schema:

type PanoTaskStatus = {
  project_id: string
  status: 'pending' | 'processing' | 'success' | 'failed'
  vr_url: string
}

Continue polling while the status is pending or processing. On success, vr_url contains the complete browsable Realsee URL. Stop and surface a retry or support path on failed.

The access token can expire during a long-running task. If the envelope returns code: -3 and status: "access_token expired", refresh the token and continue polling with the same task_code.

Response envelope

Pano-to-3D responses use the gateway envelope:

FieldDescription
request_id, trace_idIdentifiers to retain in server-side diagnostics.
business_codeString business result or error code.
code0 for success, -1 for a business failure, and -3 for an expired access token.
statusSuccess or error description.
dataEndpoint-specific data; it can be null on failure.
costServer processing time in milliseconds.

Do not expose the app secret, access token, temporary storage credentials, object path, or raw gateway diagnostics in browser logs or analytics.

For the single-image Argus/VGGT workflow, use the Argus Quickstart and Argus API Reference instead.