メインコンテンツまでスキップ

REST API

Last Updated:27.01.2026Version2.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 ログイン(ネイティブアプリ向け)

ネイティブアプリケーションは OAuth 2.0 Authorization Code flow with PKCE を使用して、ユーザーが 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));

    ユーザーには、Google サインインとメール/パスワードオプションを含む Arrival.Space ログインページが表示されます。

  3. コールバックを処理 — ログイン後、ブラウザは ?code=...&state=... 付きで redirect_uri にリダイレクトされます:

    // 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、場合によっては message または error を含む共通のエンベロープを使用します
  • タイムスタンプは ISO 8601 文字列です(例:2025-01-15T10:30:00.000Z
  • 一部のオペレーションは非同期で、job_id を返します。job_statuscompleted または failed になるまで GET /jobs/:jobId でポーリングしてください
  • 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/jsonはいJSONリクエストボディ

ボディパラメーター:

フィールド必須説明
file_namestringはいファイル名
file_sizenumberはいファイルサイズ(バイト)
content_typestringはいMIMEタイプ
レスポンス
{
"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/jsonはいJSONリクエストボディ

ボディパラメーター:

フィールド必須説明
statusstringはいアップロード完了後に "success" を使用
extra_info.file_urlstringはいS3にアップロードされたファイルの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パラメーター:

パラメーター必須説明
jobIdstringはいupload-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 に新しいスペースを作成します。アップロードの確認後にこのエンドポイントを呼び出してください。

スペースタイプ

スペースには2つのタイプがあります:

タイプ説明
"infinite"デフォルト。 ルームアーキテクチャのないオープンスペース。3Dコンテンツはオープンな環境に浮遊します。静的ゲートフレームは作成されません。個々の3Dモデルを展示するのに最適です。
"hub"アーキテクチャ付きのルーム — 壁、床、天井、およびルーム周囲に配置された 7つの静的ゲートフレーム。他のスペースへのポータルを含むホームスペースの作成に最適です。
リクエスト

ヘッダー:

ヘッダー必須説明
AuthorizationBearer <api_key>はいユーザーのAPIキー
Content-Typeapplication/jsonはいJSONリクエストボディ

ボディパラメーター:

パラメーター必須説明
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/jsonはいJSONリクエストボディ

ボディパラメーター:

パラメーター必須説明
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/jsonはいJSONリクエストボディ

ボディパラメーター:

パラメーター必須説明
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/jsonはいJSONリクエストボディ

ボディパラメーター:

フィールド必須説明
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
});
}

ゲート

ゲートはスペースを相互にリンクするポータルです。2種類あります:

  • 静的ゲートhub ルーム周囲に配置された7つの固定ポータルフレーム。hub スペースが作成されると(POST /user/create-spacespace_type: "hub")自動的に作成されます。コンテンツを設定したり空のままにしたりできますが、追加や削除はできません。静的ゲートは infinite スペースには存在しません — infinite スペースにはルームアーキテクチャがないため、ゲートフレームを表示する場所がありません。

  • 動的ゲート — 3D空間内の任意の場所に自由に配置できるポータル。hubinfinite の両方のスペースで任意の数を作成できます。

両方のゲートタイプは、上記のエンティティエンドポイントを通じて管理されます。

Hub vs. 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またはWebページURL。外部リンクの場合、ゲート上にページをレンダリングするには link と同じURLを設定してください
logoURLstringロゴ画像URL(ゲート上の小さいアイコン)
logoLinkURLstringロゴクリック時に開くURL
content360Enabledboolean360度コンテンツモードを有効にする
contentSynchronizedbooleanユーザー間でコンテンツを同期する
openAsTabbooleanナビゲーションの代わりに新しいブラウザタブでリンクを開く
enableWebStreambooleanWebストリーミングを有効にする
enableWebProxybooleanWebプロキシを有効にする
embeddedEnabledboolean埋め込みコンテンツを有効にする
screenSelectnumberスクリーン選択インデックス(デフォルト:0
ヒント

マージセマンティクスを活用してください — 変更したいフィールドのみを送信するだけで済みます。ブーリアンフラグやその他のフィールドはデフォルト値を保持します。

動的ゲート(タイプ:DynamicGate

動的ゲートは静的ゲートと同じコンテンツフィールドに加えて、3D位置、回転、スケールを定義する dynamicGate オブジェクトを持ちます。任意の数を作成できます。

動的ゲートは 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キーを安全に保管し、クライアントサイドのコードに公開しないでください