跳到主要内容

REST API

最后更新:27.01.2026版本2.1

概述

Arrival Space REST API 允许第三方应用程序验证用户身份、通过预签名 URL 上传资源、跟踪异步处理任务、创建和管理空间、管理实体以及配置传送门。

功能

  • 使用预签名 S3 URL 上传资源并确认处理
  • 通过实时进度跟踪异步处理任务
  • 从上传创建空间并更新隐私设置
  • 创建、读取、更新和删除空间中的实体
  • 使用实体端点配置静态和动态传送门
警告

在开始上传之前,请确保您有足够的可用存储空间。否则,上传可能会失败。


基础 URL 与版本

const API_BASE_URL = "https://api-staging.arrival.space/api";
const VERSION = "v1";

所有端点应以 ${API_BASE_URL}/${VERSION} 为前缀。

备注

API 端点将来可能会更改。如果您遇到任何连接问题,请联系我们的支持团队。


认证

所有 API 请求需要在 Authorization 标头中携带 Bearer 令牌:

Authorization: Bearer <api_key>

选项 1:用户账户 API 密钥

  1. 登录 Arrival Space
  2. 导航到您的账户设置
  3. 生成或复制您的 API 密钥

选项 2:OAuth 2.0 登录(用于原生应用)

原生应用程序可以使用带 PKCE 的 OAuth 2.0 授权码流程让用户通过其 Arrival.Space 账户(Google 登录或邮箱/密码)登录。这是推荐的第三方集成方法,用户无需手动复制 API 密钥。

OAuth 流程使用标准端点,并返回 API 密钥作为访问令牌 — 与用户账户设置中显示的密钥相同。这意味着令牌在服务器重启后仍然有效,并在所有 API 端点中以相同方式工作。

OAuth 端点:

端点描述
GET /.well-known/oauth-authorization-serverOAuth 2.0 服务器元数据(自动发现)
POST /register动态客户端注册(RFC 7591
GET /authorize授权端点 — 重定向到登录页面
POST /token令牌交换 — 返回访问令牌
POST /revoke令牌撤销

流程概述:

  1. 注册您的客户端(一次性,或在首次启动时):

    const regResponse = await fetch(`${API_BASE_URL}/register`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
    client_name: "My App",
    redirect_uris: ["https://myapp.example.com/callback"],
    grant_types: ["authorization_code"],
    response_types: ["code"],
    token_endpoint_auth_method: "none"
    })
    });
    const client = await regResponse.json();
    // Save client.client_id for future use
  2. 生成 PKCE 挑战引导用户进行授权

    // Generate PKCE code verifier + challenge
    const codeVerifier = generateRandomString(64);
    const codeChallenge = base64url(sha256(codeVerifier));

    // Open browser to:
    const authUrl = new URL(`${API_BASE_URL}/authorize`);
    authUrl.searchParams.set("client_id", client.client_id);
    authUrl.searchParams.set("redirect_uri", "https://myapp.example.com/callback");
    authUrl.searchParams.set("response_type", "code");
    authUrl.searchParams.set("code_challenge", codeChallenge);
    authUrl.searchParams.set("code_challenge_method", "S256");
    authUrl.searchParams.set("state", generateRandomString(32));

    用户将看到 Arrival.Space 登录页面,包含 Google 登录和邮箱/密码选项。

  3. 处理回调 — 登录后,浏览器会重定向到您的 redirect_uri,带有 ?code=...&state=...

    // Your callback handler receives: code, state
    const tokenResponse = await fetch(`${API_BASE_URL}/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
    grant_type: "authorization_code",
    code: code,
    redirect_uri: "https://myapp.example.com/callback",
    client_id: client.client_id,
    code_verifier: codeVerifier
    })
    });
    const tokens = await tokenResponse.json();
    const apiKey = tokens.access_token;
  4. 使用令牌 — 它是标准 API 密钥,使用方式与其他密钥相同:

    const response = await fetch(`${API_BASE_URL}/${VERSION}/spaces`, {
    headers: { Authorization: `Bearer ${apiKey}` }
    });
令牌详情
  • OAuth 流程返回的 access_token 是用户的实际 API 密钥 — 与其账户设置中显示的相同
  • 令牌不会过期(没有 expires_in 限制)— 在用户撤销密钥之前持续有效
  • 由于访问令牌不会过期,因此不需要刷新令牌
  • 如果用户已有 API 密钥,将返回现有密钥(不会创建重复项)
反向代理配置

如果您的服务器位于反向代理(Apache、nginx)之后,请确保 /.well-known/oauth-authorization-server 被转发到 Node.js 后端。如果发现功能不可用,客户端可以直接使用端点:/authorize/token/register

MCP 协议支持

OAuth 端点也兼容 Model Context Protocol (MCP)。Claude Desktop 等 MCP 客户端可以使用 https://api-dev.arrival.space/api/v1/mcp 直接连接 — OAuth 流程由 MCP 客户端自动处理。


约定

  • 响应使用通用封装格式,包含 statusdata,有时还有 messageerror
  • 时间戳为 ISO 8601 字符串(例如 2025-01-15T10:30:00.000Z
  • 某些操作是异步的,返回一个 job_id,您需要通过 GET /jobs/:jobId 轮询,直到 job_statuscompletedfailed
  • resource_key 标识已上传的资源,用于创建空间或实体
  • spaceId 是空间 URL 中的唯一标识符(例如 https://arrival.space/12345678_9012
  • entity_id 标识空间中的特定实体

速率限制

限制按 API 密钥执行(如果未提供密钥则回退到 IP)。429 响应包含 Retry-After 标头和响应体中的 retry_after_seconds 字段。

范围限制
所有 API 端点300 次请求/分钟
POST /files/upload20 次请求/分钟
POST /files/upload-complete20 次请求/分钟
GET /jobs/:jobId120 次请求/分钟
POST /user/create-space60 次请求/分钟
POST /spaces/update-privacy60 次请求/分钟
实体 CRUD(/spaces/:spaceId/entities*300 次请求/分钟

快速入门

流程概要

  1. 获取 API 密钥(用户账户密钥或 OAuth 登录)
  2. 请求预签名上传 URL
  3. 将文件上传到 S3
  4. 确认上传,如需要则轮询异步处理完成状态
  5. 创建空间并管理实体和传送门

完整集成示例

const API_BASE_URL = "https://api-staging.arrival.space/api";
const VERSION = "v1";

/**
* Poll job status until completion or failure
* Used for async operations like zip file processing or large file copying
* Provides real-time progress updates during processing
*/
async function pollJobStatus(apiKey, jobId, onProgress = null) {
const pollUrl = `${API_BASE_URL}/${VERSION}/jobs/${jobId}`;

return new Promise((resolve, reject) => {
const poll = async () => {
try {
const res = await fetch(pollUrl, {
headers: { Authorization: `Bearer ${apiKey}` }
});
const data = await res.json();
const job = data.data;

// Optional progress callback
if (onProgress) {
onProgress(job.progress, job.message);
}

if (job.job_status === "completed") {
resolve(job.result.resource_key);
} else if (job.job_status === "failed") {
reject(new Error(job.error || "Job failed"));
} else {
// Still processing, poll again in 2 seconds
setTimeout(poll, 2000);
}
} catch (e) {
reject(e);
}
};
poll();
});
}

/**
* Upload a file to Arrival Space and create a space
*/
async function uploadFileToArrivalSpace(apiKey, file, spaceTitle, spaceDescription = "") {
// Step 1: Request presigned upload URL
const uploadRequest = await fetch(`${API_BASE_URL}/${VERSION}/files/upload`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
file_name: file.name,
file_size: file.size,
content_type: file.type || "application/octet-stream"
})
});

if (!uploadRequest.ok) {
throw new Error("Failed to get upload URL");
}

const { data } = await uploadRequest.json();
const { params } = data;

// Step 2: Upload file to S3
const s3Upload = await fetch(params.url, {
method: params.method,
body: file,
headers: params.headers
});

if (!s3Upload.ok) {
throw new Error("Failed to upload file to S3");
}

// Step 3: Confirm upload
const fileUrl = params.url.split("?")[0]; // Remove query params
const confirmRequest = await fetch(`${API_BASE_URL}/${VERSION}/files/upload-complete`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
status: "success",
extra_info: {
file_url: fileUrl
}
})
});

if (!confirmRequest.ok) {
throw new Error("Failed to confirm upload");
}

const confirmData = await confirmRequest.json();
let resource_key;

// Step 3b: Handle async processing for large files (zip files or files over 1.5GB)
if (confirmData.status === "processing" && confirmData.data.job_id) {
console.log("Large file detected - polling for completion...");
resource_key = await pollJobStatus(apiKey, confirmData.data.job_id, (progress, message) => {
console.log(`Processing: ${progress}% - ${message}`);
});
} else {
// Immediate response for small files (under 1.5GB, non-zip)
resource_key = confirmData.data.resource_key;
}

// Step 4: Create space with uploaded file
const createSpaceRequest = await fetch(`${API_BASE_URL}/${VERSION}/user/create-space`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
space_data: {
title: spaceTitle,
description: spaceDescription,
resource_key: resource_key,
}
})
});

if (!createSpaceRequest.ok) {
throw new Error("Failed to create space");
}

return await createSpaceRequest.json();
}

// Usage
const apiKey = "your_api_key_here";
const file = document.getElementById("fileInput").files[0];

uploadFileToArrivalSpace(apiKey, file, "My New Space", "Optional description")
.then(result => console.log("Space created:", result.data))
.catch(error => console.error("Upload failed:", error));

API 参考

上传

请求上传 URL

POST /files/upload

生成预签名 S3 URL 用于文件上传。

请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥
Content-Typeapplication/jsonJSON 请求体

请求体参数:

字段类型必需描述
file_namestring文件名
file_sizenumber文件大小(字节)
content_typestringMIME 类型
响应
{
"status": "ok",
"data": {
"upload_type": "presigned_url",
"params": {
"url": "https://s3.amazonaws.com/bucket/...?X-Amz-Signature=...",
"method": "PUT",
"headers": { "Content-Type": "application/zip" }
}
}
}
请求示例
const response = await fetch(`${API_BASE_URL}/${VERSION}/files/upload`, {
method: "POST",
headers: {
Authorization: `Bearer ${api_key}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
file_name: "example.zip",
file_size: 1048576000,
content_type: "application/zip"
})
});
上传到 S3

收到预签名 URL 后,使用返回的参数将文件直接上传到 S3:

fetch(params.url, {
method: params.method,
headers: params.headers,
body: file,
});

确认上传

POST /files/upload-complete

在文件成功上传到 S3 后调用此端点。确认上传完成并返回资源密钥,您可以稍后用于创建空间或其他实体。

大文件异步处理

对于 .zip 文件或超过 1.5GB 的文件,此端点返回任务 ID 而非直接返回资源密钥。您必须轮询任务状态端点以在处理完成后获取最终的 resource_key。详见获取任务状态

请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥
Content-Typeapplication/jsonJSON 请求体

请求体参数:

字段类型必需描述
statusstring上传完成后使用 "success"
extra_info.file_urlstringS3 中已上传文件的 URL
响应(小文件)

对于 1.5GB 以下的非 zip 文件,立即返回资源密钥:

{
"status": "confirmed",
"data": {
"resource_key": "api-uploads/...ply"
}
}
响应(大文件 - 异步处理)

对于 .zip 文件或超过 1.5GB 的文件,返回用于轮询的任务 ID:

{
"status": "processing",
"message": "Large file queued for processing",
"data": {
"job_id": "a1b2c3d4e5f6...",
"poll_url": "/api/v1/jobs/a1b2c3d4e5f6..."
}
}
字段描述
status"processing" 表示异步任务已启动
job_id用于轮询任务状态的唯一标识符
poll_url便捷轮询 URL
请求示例
const response = await fetch(`${API_BASE_URL}/${VERSION}/files/upload-complete`, {
method: "POST",
headers: {
Authorization: `Bearer ${api_key}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
status: "success",
extra_info: {
file_url: "https://s3.amazonaws.com/bucket/uploads/example.zip"
}
})
});

任务

获取任务状态

GET /jobs/:jobId

轮询此端点以检查异步任务的状态(例如 zip 文件处理或大文件复制)。该端点提供实时进度更新。持续轮询直到 job_status"completed""failed"

请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥

URL 参数:

参数类型必需描述
jobIdstringupload-complete 返回的任务 ID
响应
{
"status": "ok",
"data": {
"job_id": "a1b2c3d4e5f6...",
"job_status": "processing",
"progress": 45,
"message": "Copying file... 45% (2.3 GB / 5.0 GB)",
"result": null,
"error": null,
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:31:30.000Z"
}
}

已完成任务示例:

{
"status": "ok",
"data": {
"job_id": "a1b2c3d4e5f6...",
"job_status": "completed",
"progress": 100,
"message": "File copied successfully",
"result": {
"resource_key": "api-uploads/..."
},
"error": null,
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:32:15.000Z"
}
}
任务状态值
状态描述
queued任务等待处理中
processing任务正在处理中
completed任务成功完成 — result.resource_key 可用
failed任务失败 — 检查 error 字段获取详情
响应字段
字段类型描述
job_idstring唯一任务标识符
job_statusstring任务当前状态
progressnumber进度百分比(0-100),处理期间实时更新
messagestring人类可读的状态消息,包含进度详情(例如 "Copying file... 45% (2.3 GB / 5.0 GB)")
resultobject完成时的结果数据(包含 resource_key
errorstring任务失败时的错误消息
created_atstring任务创建的 ISO 时间戳
updated_atstring最后状态更新的 ISO 时间戳
请求示例
const response = await fetch(`${API_BASE_URL}/${VERSION}/jobs/${jobId}`, {
headers: { Authorization: `Bearer ${api_key}` }
});
进度更新

对于 zip 文件和大文件(超过 1.5GB),progressmessage 字段在文件处理过程中实时更新。您可以每 1-2 秒轮询此端点以获取实时进度更新,显示:

  • 完成百分比
  • 已处理数据量(MB 或 GB)
  • 当前操作(例如 "Copying file..."、"Extracting zip..."、"Uploading files...")
示例:轮询任务完成状态
async function pollJobStatus(apiKey, jobId) {
const pollUrl = `${API_BASE_URL}/${VERSION}/jobs/${jobId}`;

return new Promise((resolve, reject) => {
const poll = async () => {
try {
const res = await fetch(pollUrl, {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
const data = await res.json();
const job = data.data;

console.log(`Progress: ${job.progress}% - ${job.message}`);

if (job.job_status === 'completed') {
resolve(job.result.resource_key);
} else if (job.job_status === 'failed') {
reject(new Error(job.error));
} else {
// Still processing, poll again in 2 seconds
setTimeout(poll, 2000);
}
} catch (e) {
reject(e);
}
};
poll();
});
}

列出任务

GET /jobs

列出已认证用户的所有任务。适用于跟踪多个异步操作。

请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥
响应
{
"status": "ok",
"data": {
"jobs": [
{
"job_id": "a1b2c3d4e5f6...",
"type": "zip-processing",
"job_status": "completed",
"progress": 100,
"message": "Zip file processed successfully",
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:32:15.000Z"
},
{
"job_id": "b2c3d4e5f6a1...",
"type": "file-processing",
"job_status": "processing",
"progress": 65,
"message": "Copying file... 65% (3.25 GB / 5.0 GB)",
"created_at": "2025-01-15T11:00:00.000Z",
"updated_at": "2025-01-15T11:01:30.000Z"
}
],
"count": 1
}
}
请求示例
const response = await fetch(`${API_BASE_URL}/${VERSION}/jobs`, {
headers: { Authorization: `Bearer ${api_key}` }
});
备注

任务会在 1 小时后自动清理。


空间

创建空间

POST /user/create-space

使用已上传文件的密钥在 Arrival Space 中创建新空间。在确认上传后调用此端点。

空间类型

空间有两种类型:

类型描述
"infinite"默认。 没有房间架构的开放空间。3D 内容悬浮在开放环境中。不创建静态传送门框架。最适合展示单个 3D 模型。
"hub"带有架构的房间 — 墙壁、地板、天花板,以及房间周围排列的 7 个静态传送门框架。最适合创建带有通往其他空间传送门的主空间。
请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥
Content-Typeapplication/jsonJSON 请求体

请求体参数:

参数类型必需描述
titlestring空间标题(省略时从文件名自动检测)
descriptionstring空间描述
resource_keystring来自 upload-complete 的资源密钥。省略时创建空空间
space_typestring"hub""infinite"。默认:"infinite"
响应
{
"status": "ok",
"message": "Space created successfully",
"data": {
"space_url": "https://arrival.space/12345678_9012",
"title": "My New Space",
"space_type": "hub"
}
}
请求示例
const response = await fetch(`${API_BASE_URL}/${VERSION}/user/create-space`, {
method: "POST",
headers: {
Authorization: `Bearer ${api_key}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
space_data: {
title: "My New Space",
description: "Optional description",
resource_key: "api-uploads/...ply",
space_type: "hub"
}
})
});

更新空间隐私设置

POST /spaces/update-privacy

更新现有空间的隐私设置。需要空间所有权。

请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥
Content-Typeapplication/jsonJSON 请求体

请求体参数:

参数类型必需描述
spaceIdstring空间的唯一标识符(在空间 URL 中找到)
roomPrivacystring隐私设置。可选值:"Public""Private""Link Only"
informFollowersboolean如果为 true,当空间设为"Public"时会通知关注者
隐私选项
选项描述
Public空间公开可见且可被发现。可以通知关注者。
Private空间私有且不可被发现。仅所有者可通过直接链接访问。
Link Only空间可通过直接链接访问但不会公开列出。需要 Pro 订阅。
响应

成功:

{
"status": "ok",
"message": "Space privacy updated successfully"
}

错误响应:

// 404 - Space not found
{
"status": "error",
"message": "Space not found"
}

// 403 - Not authorized
{
"status": "error",
"message": "You are not authorized to update the privacy of this space"
}

// 400 - Invalid privacy option
{
"status": "error",
"message": "Invalid privacy option"
}

// 400 - Pro required for Link Only
{
"status": "error",
"message": "You need to be a Pro user to set a space to Link Only"
}
请求示例
async function updateSpacePrivacy(apiKey, spaceId, privacy, informFollowers = false) {
const response = await fetch(`${API_BASE_URL}/${VERSION}/spaces/update-privacy`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
spaceId: spaceId,
roomPrivacy: privacy,
informFollowers: informFollowers
})
});

if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}

return await response.json();
}

// Usage
updateSpacePrivacy("your_api_key", "12345678_9012", "Public", true)
.then(result => console.log("Privacy updated:", result))
.catch(error => console.error("Update failed:", error));

实体

实体是空间中的对象(3D 模型、传送门、媒体等)。这些端点允许您在自己拥有的空间中创建、读取、更新和删除实体。

所有实体端点需要 Authorization: Bearer <api_key> 标头和空间所有权。

创建实体

POST /spaces/:spaceId/entities

在空间中创建新实体。提供 resource_key 时,实体从已上传文件创建,并自动检测默认值(根据文件类型确定缩放、旋转)。省略时创建空白实体。

请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥
Content-Typeapplication/jsonJSON 请求体

请求体参数:

参数类型必需描述
resource_keystring来自 upload-complete 的 S3 密钥。触发基于文件的实体创建,自动检测缩放/旋转
entity_idstring自定义实体 ID。省略时自动生成
entity_typestring实体类型(例如 "UserModelEntity""Simple""Gate"
entity_dataobject实体数据。提供 resource_key 时与自动检测的默认值合并

响应 (201):

{
"status": "ok",
"data": {
"entity_id": "user-model-91de72809185c9fa",
"entity_type": "UserModelEntity",
"entity_data": {
"glbUrl": "https://s3.amazonaws.com/...",
"scale": [2, 2, 2],
"rotation": { "x": 0, "y": 0, "z": 0 },
"position": [0, 1, 0]
}
}
}
请求示例
const response = await fetch(`${API_BASE_URL}/${VERSION}/spaces/${spaceId}/entities`, {
method: "POST",
headers: {
Authorization: `Bearer ${api_key}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
resource_key: "api-uploads/...glb",
entity_id: "my-custom-id",
entity_type: "UserModelEntity",
entity_data: {
scale: [2, 2, 2],
position: [0, 1, 0]
}
})
});

列出实体

GET /spaces/:spaceId/entities

返回空间中的所有实体(不包括内部 RoomInfo 实体)。

请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥

查询参数:

参数类型必需描述
limitnumber返回实体最大数量(默认 50,最大 200)
cursorstring来自 nextCursor 的分页游标

响应:

{
"status": "ok",
"data": {
"entities": [
{
"entity_id": "user-model-91de72809185c9fa",
"entity_type": "UserModelEntity",
"entity_data": { "glbUrl": "...", "scale": 1, "rotation": { "x": 0, "y": 0, "z": 0 } },
"entity_state": {},
"created_date": "2026-01-27T12:00:00.000Z",
"change_date": "2026-01-27T12:00:00.000Z"
}
],
"count": 1,
"total": 1,
"nextCursor": null,
"hasMore": false
}
}
请求示例
const response = await fetch(`${API_BASE_URL}/${VERSION}/spaces/${spaceId}/entities?limit=50`, {
headers: { Authorization: `Bearer ${api_key}` }
});

获取实体

GET /spaces/:spaceId/entities/:entityId

按 ID 返回单个实体。

请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥

路径参数:

参数类型必需描述
spaceIdstring空间 ID(来自空间 URL)
entityIdstring实体 ID

响应:

{
"status": "ok",
"data": {
"entity_id": "user-model-91de72809185c9fa",
"entity_type": "UserModelEntity",
"entity_data": { "glbUrl": "...", "scale": 1, "rotation": { "x": 0, "y": 0, "z": 0 } },
"entity_state": {},
"created_date": "2026-01-27T12:00:00.000Z",
"change_date": "2026-01-27T12:00:00.000Z"
}
}

错误 (404):

{ "status": "error", "message": "Entity not found" }
请求示例
const response = await fetch(`${API_BASE_URL}/${VERSION}/spaces/${spaceId}/entities/${entityId}`, {
headers: { Authorization: `Bearer ${api_key}` }
});

更新实体

PUT /spaces/:spaceId/entities/:entityId

使用合并语义更新实体:提供的字段覆盖现有值,未设置的字段保持不变。实体的 state 也会被保留。

请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥
Content-Typeapplication/jsonJSON 请求体

请求体参数:

字段类型必需描述
entity_dataobject要合并到现有实体的字段

响应:

{
"status": "ok",
"data": {
"entity_id": "user-model-91de72809185c9fa",
"entity_type": "UserModelEntity",
"entity_data": {
"glbUrl": "https://s3.amazonaws.com/...",
"scale": [3, 3, 3],
"rotation": { "x": 0, "y": 0, "z": 0 },
"label": "My Label"
}
}
}
请求示例
const response = await fetch(`${API_BASE_URL}/${VERSION}/spaces/${spaceId}/entities/${entityId}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${api_key}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
entity_data: {
scale: [3, 3, 3],
label: "My Label"
}
})
});

删除实体

DELETE /spaces/:spaceId/entities/:entityId

永久删除空间中的实体。

请求

标头:

标头必需描述
AuthorizationBearer <api_key>用户的 API 密钥

路径参数:

参数类型必需描述
spaceIdstring空间 ID(来自空间 URL)
entityIdstring实体 ID

响应:

{
"status": "ok",
"data": { "message": "Entity deleted successfully" }
}
请求示例
const response = await fetch(`${API_BASE_URL}/${VERSION}/spaces/${spaceId}/entities/${entityId}`, {
method: "DELETE",
headers: { Authorization: `Bearer ${api_key}` }
});

实体管理示例

async function manageEntities(apiKey, spaceId, resourceKey) {
const headers = {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
};
const base = `${API_BASE_URL}/${VERSION}/spaces/${spaceId}/entities`;

// Create entity from uploaded file
const createRes = await fetch(base, {
method: "POST",
headers,
body: JSON.stringify({
resource_key: resourceKey,
entity_data: { scale: [2, 2, 2] }
})
});
const { data: created } = await createRes.json();
console.log("Created:", created.entity_id);

// List all entities
const listRes = await fetch(base, { headers });
const { data: list } = await listRes.json();
console.log("Entities in space:", list.count);

// Update entity
await fetch(`${base}/${created.entity_id}`, {
method: "PUT",
headers,
body: JSON.stringify({ entity_data: { label: "Updated" } })
});

// Delete entity
await fetch(`${base}/${created.entity_id}`, {
method: "DELETE",
headers
});
}

传送门

传送门是连接空间的传送入口。有两种类型:

  • 静态传送门 — 围绕 hub 房间排列的 7 个固定传送门框架。创建 hub 空间时自动创建(在 POST /user/create-space 中使用 space_type: "hub")。您可以为它们填充内容或留空,但不能添加或删除。静态传送门在 infinite 空间中不存在 — 因为 infinite 空间没有房间架构,所以没有传送门框架可以显示。

  • 动态传送门 — 自由定位的传送门,可放置在 3D 空间的任何位置。您可以在 hubinfinite 空间中创建任意数量。

两种传送门类型都通过上述实体端点进行管理。

Hub 与 Infinite

静态传送门仅在 hub 空间中有意义(其中 hideArchitecturefalse)。当您使用 space_type: "infinite"(默认值)创建空间时,不会创建静态传送门,因为房间架构被隐藏了。如果您需要在 infinite 空间中使用传送门,请使用动态传送门

静态传送门(类型:Gate

静态传送门在 hub 空间中自动创建。它们的 3D 位置由客户端固定 — 您只能控制其内容(标题、链接、描述、缩略图等)。

每个 hub 空间都有相同的 7 个传送门实体 ID。使用这些固定 UUID 通过 PUT /spaces/:spaceId/entities/:entityId 直接操作传送门:

传送门实体 ID
09769e4b4-e5d9-4286-9353-c2c66b158347
11aef8fd4-6447-4e04-969a-4669c11dd52e
275f16b87-5314-4cdf-a8d5-bb2a8292b687
3608f2483-802b-4fa7-95bb-920bed1431ad
4fcc35518-309c-47e1-8d93-d939d6bca475
5404c7fe3-26f1-4fb6-bac7-a50bf1f3cb1a
6b9a67fa0-00ce-401d-94c2-4045c4aaec35

示例 — 将传送门 3 设为链接到另一个空间:

const GATE_IDS = [
"9769e4b4-e5d9-4286-9353-c2c66b158347", // Gate 0
"1aef8fd4-6447-4e04-969a-4669c11dd52e", // Gate 1
"75f16b87-5314-4cdf-a8d5-bb2a8292b687", // Gate 2
"608f2483-802b-4fa7-95bb-920bed1431ad", // Gate 3
"fcc35518-309c-47e1-8d93-d939d6bca475", // Gate 4
"404c7fe3-26f1-4fb6-bac7-a50bf1f3cb1a", // Gate 5
"b9a67fa0-00ce-401d-94c2-4045c4aaec35", // Gate 6
];

// Internal link — point gate 3 to another space
await fetch(`${API_BASE_URL}/${VERSION}/spaces/${spaceId}/entities/${GATE_IDS[3]}`, {
method: "PUT",
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
body: JSON.stringify({
entity_data: {
title: "My Portal",
link: "42485456_5076",
platform: "arrival.space",
description: "Step through to see my gallery"
}
})
});

// External link — point gate 0 to a web page
// Set videoURL to the same URL so the page renders on the gate surface
await fetch(`${API_BASE_URL}/${VERSION}/spaces/${spaceId}/entities/${GATE_IDS[0]}`, {
method: "PUT",
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
body: JSON.stringify({
entity_data: {
title: "Visit Our Website",
link: "https://example.com",
platform: "<auto-detect>",
videoURL: "https://example.com"
}
})
});

传送门内容字段(entity_data):

字段类型描述
titlestring显示在传送门框架上的标题
linkstring内部链接的空间 ID(例如 "42485456_5076"外部链接的完整 URL(例如 "https://example.com")。留空表示无链接
platformstring内部空间链接使用 "arrival.space",外部 URL 使用 "<auto-detect>"。必须与 link 类型匹配
descriptionstring描述文本(支持 HTML)
videoURLstring显示在传送门表面的图片 URL 或网页 URL。对于外部链接,将其设置为与 link 相同的 URL 以在传送门上渲染页面
logoURLstringLogo 图片 URL(传送门上的小图标)
logoLinkURLstring点击 logo 时打开的 URL
content360Enabledboolean启用 360° 内容模式
contentSynchronizedboolean在用户间同步内容
openAsTabboolean在新浏览器标签页中打开链接,而非导航跳转
enableWebStreamboolean启用网页流式传输
enableWebProxyboolean启用网页代理
embeddedEnabledboolean启用嵌入内容
screenSelectnumber屏幕选择索引(默认:0
提示

利用合并语义的优势 — 您只需发送要更改的字段。布尔标志和其他字段将保持默认值。

动态传送门(类型:DynamicGate

动态传送门与静态传送门具有相同的内容字段,另外还有一个 dynamicGate 对象,定义它们的 3D 位置、旋转和缩放。您可以创建任意数量。

使用 POST /spaces/:spaceId/entities 创建动态传送门。

附加字段:

字段类型描述
dynamicGateobject动态传送门必需。包含 3D 变换数据
dynamicGate.position{x, y, z}世界位置
dynamicGate.rotation{x, y, z}欧拉旋转角度(度)
dynamicGate.scale{w, h}宽度和高度缩放(默认:{w: 1, h: 1}
dynamicGate.framelessboolean隐藏传送门框架(false = 显示框架)
dynamicGate.hiddenboolean完全隐藏传送门

示例 — 创建动态传送门:

await fetch(`${API_BASE_URL}/${VERSION}/spaces/${spaceId}/entities`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
entity_id: "my-dynamic-gate",
entity_type: "DynamicGate",
entity_data: {
dynamicGate: {
position: { x: 5.0, y: 0.0, z: -3.0 },
rotation: { x: 0, y: 45, z: 0 },
frameless: false,
hidden: false,
scale: { w: 1, h: 1 }
},
title: "Side Gallery",
link: "42485456_5076",
platform: "arrival.space",
description: "A freely-placed portal"
}
})
});

清除传送门

要清除静态传送门(移除内容但保留槽位),使用空值更新它:

await fetch(`${API_BASE_URL}/${VERSION}/spaces/${spaceId}/entities/${gateId}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
entity_data: {
title: "",
link: "",
description: "",
videoURL: "",
logoURL: "",
logoLinkURL: ""
}
})
});

要完全移除动态传送门,使用 DELETE /spaces/:spaceId/entities/:entityId

警告

不要删除静态传送门 — 它们是房间结构的一部分。只需清除它们的内容。删除静态传送门将留下无法通过 API 恢复的空槽位。


错误处理

状态码描述
200OK — 请求成功
201Created — 资源创建成功(例如实体)
202Accepted — 异步任务已启动(轮询完成状态)
204No Content — 异步任务更新标头
400Bad Request — 参数无效
401Unauthorized — API 密钥无效或已过期
403Forbidden — 权限不足
404Not Found — 资源或任务未找到
413Payload Too Large — 文件超过大小限制
500Internal Server Error — 服务器内部错误
507Insufficient Storage — 用户存储配额已超出

注意事项

  • API 密钥与单个用户账户绑定
  • 上传的文件创建的空间归 API 密钥持有者所有
  • 预签名 URL 会在有限时间后过期 — 请及时上传
  • 妥善保管您的 API 密钥,切勿在客户端代码中暴露