TypeScript SDK
Official TypeScript / Node.js SDK for the Aholo Open API.
- Requirements: Node.js ≥ 18
- GitHub: manycoretech/aholo-spatial-sdk
Installation
Install only the packages you need:
npm install @manycore/aholo-sdk-asset # file upload
npm install @manycore/aholo-sdk-world # world reconstruction & generation
npm install @manycore/aholo-sdk-lux3d # Lux3D generation
Authentication
Recommended: set the environment variable; the SDK reads AHOLO_API_KEY:
export AHOLO_API_KEY=your_api_key_here
Or pass it in code:
import { createWorldClient } from '@manycore/aholo-sdk-world';
const world = createWorldClient({ apiKey: 'your_api_key_here', region: 'com' });
Never hardcode API keys in source code, packages, or public repositories.
Region
| Value | Description | API endpoint |
|---|---|---|
cn | China | https://api.aholo3d.cn |
com | Global | https://api.aholo3d.com |
Asset upload
import { createAssetClient } from '@manycore/aholo-sdk-asset';
const asset = createAssetClient({ region: 'com' });
Upload a file
const result = await asset.uploadFile('./video.mp4');
console.log(result.url); // public URL
Upload a Buffer
import { readFileSync } from 'node:fs';
const data = readFileSync('./image.jpg');
const result = await asset.uploadBuffer(data, { filename: 'image.jpg' });
Upload with progress
const result = await asset.uploadFile('./video.mp4', {
onProgress: (uploaded, total) => {
const pct = Math.round((uploaded / total) * 100);
process.stdout.write(`\rUploading: ${pct}%`);
},
});
UploadOptions
| Option | Type | Description |
|---|---|---|
filename | string | Override filename (defaults to basename) |
onProgress | (uploaded: number, total: number) => void | Progress callback (bytes) |
partTimeoutMs | number | Per-part timeout (default 120,000 ms) |
signal | AbortSignal | Cancellation signal |
UploadResult
| Field | Type | Description |
|---|---|---|
url | string | Public URL of the uploaded file |
md5 | string | File MD5 |
World
import { createWorldClient } from '@manycore/aholo-sdk-world';
const world = createWorldClient({ region: 'com' });
3DGS reconstruction (video / images)
const { worldId } = await world.reconstructions.create({
name: 'Living room',
resources: [{ url: 'https://cdn.example.com/room.mp4', type: 'video' }],
taskQuality: 'normal', // 'low' | 'normal' | 'high'
scene: 'model', // 'model' | 'space'
useMask: false, // optional: segment uploaded resources when true
});
const detail = await world.waitFor(worldId);
console.log(detail.assets?.splats?.urls?.plyPath); // PLY download URL
When using images, you need ≥ 20 image resources (type: 'image' or omit type).
3DGS generation (from prompt)
const { worldId } = await world.generations.create({
name: 'Forest cabin',
prompt: 'A modern cabin in the forest',
});
const detail = await world.waitFor(worldId);
Get world detail
const detail = await world.retrieve(worldId);
console.log(detail.status);
Task status & polling
| Phase | Status | Description |
|---|---|---|
| In progress | PENDING | Queued |
| In progress | PREPROCESSING | Preprocessing |
| In progress | RUNNING | Running |
| Success | SUCCEEDED | Success |
| Failed | FAILED | Failed |
| Failed | CANCELED | Canceled |
| Failed | TIMEOUT | Timed out |
| Failed | REJECTED | Rejected |
world.waitFor(worldId) polls until SUCCEEDED and returns WorldDetail. Terminal failures throw PollingFailedError.
List worlds
const list = await world.list({ pageNum: 1, pageSize: 20 });
list.result?.forEach((w) => console.log(w.worldId, w.status));
WorldDetail fields
| Field | Type | Description |
|---|---|---|
worldId | string | World ID |
name | string? | Name |
status | string | See task status table above |
assets.splats.urls.plyPath | string? | PLY download URL |
assets.splats.urls.spzPath | string? | SPZ download URL |
assets.splats.urls.lodMetaPath | string? | LOD metadata URL (if generated) |
assets.imagery.panoUrl | string? | AI panorama URL (Spatial Gen only, after pano subtask succeeds) |
assets.semanticsMetadata.upAxis | "Y" | "Z"? | World up axis (Y = glTF/USD; Z = 3DGS convention) |
createTime | number? | Created at (Unix ms) |
updateTime | number? | Updated at (Unix ms) |
Lux3D
import { createLux3dClient } from '@manycore/aholo-sdk-lux3d';
const lux3d = createLux3dClient({ region: 'com' });
Image to 3D
// From URL
const taskId = await lux3d.imgTo3d.create({
img: 'https://example.com/object.jpg',
version: 'v2.0-preview', // 'v1.0-pro' | 'v2.0-preview' (default)
});
// From local file
const taskId2 = await lux3d.imgTo3d.createFromFile('./object.jpg');
const result = await lux3d.tasks.waitFor(taskId);
console.log(result.outputs[0]?.content); // download URL
Text to 3D
const taskId = await lux3d.textTo3d.create({
prompt: 'A wooden chair with carved legs',
// style: 'photorealistic', // see styles below
});
const result = await lux3d.tasks.waitFor(taskId);
Text-to-3D styles:
photorealistic (default) | cartoon | anime | hand_painted | cyberpunk | fantasy | glass
Material transfer
const taskId = await lux3d.materialTransfer.create({
img: 'https://example.com/material.jpg',
meshUrl: 'https://example.com/model.glb',
});
const result = await lux3d.tasks.waitFor(taskId);
Version differences
| Version | Default | Output formats | Notes |
|---|---|---|---|
v2.0-preview | Yes | .zip + .glb + .usdz (3 files) | Enhanced text & texture detail |
v1.0-pro | .lux3d (1 file) | Full PBR, supports transparent materials |
Lux3dTaskResult
| Field | Type | Description |
|---|---|---|
taskId | number | Task ID |
status | 0 | 1 | 3 | 4 | 0 init; 1 running; 3 success; 4 failed |
outputs | TaskOutput[] | Output files (outputs[n].content is download URL, ~2 h TTL after success) |
lux3d.tasks.waitFor(taskId) returns when status === 3; throws PollingFailedError on 4. Poll every 10–15 seconds.
Error handling
import {
AuthenticationError,
RateLimitError,
BusinessError,
PollingTimeoutError,
PollingFailedError,
} from '@manycore/aholo-sdk-core';
try {
const detail = await world.waitFor(worldId);
} catch (e) {
if (e instanceof AuthenticationError) {
console.error('Invalid or missing API Key');
} else if (e instanceof RateLimitError) {
console.error('Rate limit exceeded');
} else if (e instanceof BusinessError) {
console.error('API error:', e.code, e.message);
} else if (e instanceof PollingTimeoutError) {
console.error('Polling timed out');
} else if (e instanceof PollingFailedError) {
console.error('Task failed:', e.message);
}
}
| Error | Description |
|---|---|
AuthenticationError | Invalid or missing API Key |
RateLimitError | Rate limit exceeded |
BusinessError | API business error (includes code) |
PollingTimeoutError | Polling timed out |
PollingFailedError | Task execution failed |
More examples
See GitHub examples:
upload-file.mts— upload a local fileworld-reconstruct.mts— full 3DGS reconstruction flowlux3d-img-to-3d.mts— image to 3D
GitHub README is for installation only. If it conflicts with this page, this page wins. Source and runnable examples: GitHub.