ライセンス
ライセンスの検証、有効化、デバイスの追跡を管理します。
Retry-After: 60 ヘッダー付きで 429 Too Many Requests が返されます。ライセンスの検証
ライセンスキーを検証して有効化します。このエンドポイントは、IP アドレスごとに 1 分あたり 5 リクエストにレート制限されています。
エンドポイント: POST /api/licenses/validate
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
license_key | string | はい | 検証するライセンスキー |
device_id | string | はい | 有効化するデバイスの、変わらない一意の識別子。同じデバイスには毎回同じ値を送信してください。この値によって、そのデバイスのシートが確保され、再確保されます |
curl -X POST "$BASE_URL/api/licenses/validate" \
-H "Content-Type: application/json" \
-d '{"license_key":"XXXX-XXXX-XXXX-XXXX","device_id":"device_abc123"}'
interface LicenseValidation {
valid: boolean
license: {
id: string
status: string
product: object
activation_date: string
expiration_date: string
current_activations: number
}
}
const result: LicenseValidation = await $fetch('/api/licenses/validate', {
method: 'POST',
body: {
license_key: 'XXXX-XXXX-XXXX-XXXX',
device_id: 'device_abc123'
}
})
import requests
response = requests.post(
"$BASE_URL/api/licenses/validate",
json={
"license_key": "XXXX-XXXX-XXXX-XXXX",
"device_id": "device_abc123"
}
)
result = response.json()
package main
import (
"bytes"
"encoding/json"
"net/http"
)
func validateLicense() {
payload := map[string]string{
"license_key": "XXXX-XXXX-XXXX-XXXX",
"device_id": "device_abc123",
}
body, _ := json.Marshal(payload)
resp, _ := http.Post(
"$BASE_URL/api/licenses/validate",
"application/json",
bytes.NewBuffer(body),
)
defer resp.Body.Close()
}
<?php
$response = file_get_contents(
'$BASE_URL/api/licenses/validate',
false,
stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/json',
'content' => json_encode([
'license_key' => 'XXXX-XXXX-XXXX-XXXX',
'device_id' => 'device_abc123'
])
]
])
);
$result = json_decode($response, true);
use reqwest::Client;
use serde_json::json;
let client = Client::new();
let response = client
.post("$BASE_URL/api/licenses/validate")
.json(&json!({
"license_key": "XXXX-XXXX-XXXX-XXXX",
"device_id": "device_abc123"
}))
.send()
.await?;
レスポンス(成功):
{
"valid": true,
"license": {
"id": "uuid",
"status": "active",
"product": { "id": "uuid", "name": "studio", "features": ["client_api", "api_keys"] },
"activation_date": "2024-01-01T00:00:00Z",
"expiration_date": "2025-12-31T23:59:59Z",
"current_activations": 1
}
}
レスポンス(エラー):
{
"statusCode": 400,
"statusMessage": "Invalid licence key or validation failed"
}
| ステータス | 条件 |
|---|---|
400 | license_key または device_id が欠落している、空である、長すぎる、あるいはキーが不明、無効、期限切れである |
409 | ライセンスのすべてのシートが既に他のデバイスで使用されており、再確保できるほど長くアイドル状態のシートがない |
429 | レート制限を超過した。レート制限を参照 |
400 では、どのフィールドに問題があったかは示されません。再試行する前に、license_key と device_id の両方が存在し、空でないことを確認してください。ライセンスの無効化
認証が必要ライセンス所有者の session_token セッション Cookie が必要です(認証を参照)。無効化できるのは、自分が所有するライセンスのみです。
特定のデバイスまたはすべてのデバイスからライセンスを無効化します。
エンドポイント: POST /api/licenses/deactivate
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
license_key | string | はい | 無効化するライセンスキー |
device_id | string | いいえ | 無効化する特定のデバイス(すべてのデバイスを対象にする場合は省略) |
curl -X POST "$BASE_URL/api/licenses/deactivate" \
-b cookies.txt \
-H "Content-Type: application/json" \
-d '{"license_key":"XXXX-XXXX-XXXX-XXXX","device_id":"device_abc123"}'
const result = await $fetch('/api/licenses/deactivate', {
method: 'POST',
// session cookie (session_token) is sent automatically same-origin
body: {
license_key: 'XXXX-XXXX-XXXX-XXXX',
device_id: 'device_abc123'
}
})
import requests
response = requests.post(
"$BASE_URL/api/licenses/deactivate",
headers={"Cookie": f"session_token={token}"},
json={
"license_key": "XXXX-XXXX-XXXX-XXXX",
"device_id": "device_abc123"
}
)
req, _ := http.NewRequest("POST",
"$BASE_URL/api/licenses/deactivate",
bytes.NewBuffer(body))
req.Header.Set("Cookie", "session_token="+token)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
<?php
$response = file_get_contents(
'$BASE_URL/api/licenses/deactivate',
false,
stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n" .
"Cookie: session_token=$token",
'content' => json_encode([
'license_key' => 'XXXX-XXXX-XXXX-XXXX',
'device_id' => 'device_abc123'
])
]
])
);
let response = client
.post("$BASE_URL/api/licenses/deactivate")
.header("Cookie", format!("session_token={}", token))
.json(&json!({
"license_key": "XXXX-XXXX-XXXX-XXXX",
"device_id": "device_abc123"
}))
.send()
.await?;
レスポンス:
{
"success": true,
"message": "Device deactivated successfully",
"deactivated_count": 1,
"remaining_activations": 4
}
remaining_activations は無効化後も引き続き使用中のシート数、つまりライセンス上で有効なまま残っている個別のデバイスの数です。空きシート数ではありません。そのため、すべてのデバイスを無効化すると 0 が返されます。空きシート数は、GET /api/licenses/status から取得した activation_limit を使って、activation_limit - remaining_activations として計算してください。device_id を指定しない場合、メッセージは「All devices deactivated successfully」となります。deactivated_count は、この呼び出しで解放されたシート数です。ライセンスのステータス
認証が必要ライセンス所有者の session_token セッション Cookie が必要です(認証を参照)。確認できるのは、自分が所有するライセンスのみです。
ライセンスの現在のステータスを確認します。IP ごとに 1 分あたり 30 リクエストにレート制限されています。
エンドポイント: GET /api/licenses/status
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
license_key | query | はい | 確認するライセンスキー |
curl -X GET "$BASE_URL/api/licenses/status?license_key=XXXX-XXXX-XXXX-XXXX" \
-b cookies.txt
const status = await $fetch('/api/licenses/status', {
// session cookie (session_token) is sent automatically same-origin
query: { license_key: 'XXXX-XXXX-XXXX-XXXX' }
})
import requests
response = requests.get(
"$BASE_URL/api/licenses/status",
headers={"Cookie": f"session_token={token}"},
params={"license_key": "XXXX-XXXX-XXXX-XXXX"}
)
req, _ := http.NewRequest("GET",
"$BASE_URL/api/licenses/status?license_key=XXXX-XXXX-XXXX-XXXX",
nil)
req.Header.Set("Cookie", "session_token="+token)
resp, _ := http.DefaultClient.Do(req)
<?php
$response = file_get_contents(
'$BASE_URL/api/licenses/status?license_key=XXXX-XXXX-XXXX-XXXX',
false,
stream_context_create([
'http' => [
'header' => "Cookie: session_token=$token"
]
])
);
let response = client
.get("$BASE_URL/api/licenses/status")
.header("Cookie", format!("session_token={}", token))
.query(&[("license_key", "XXXX-XXXX-XXXX-XXXX")])
.send()
.await?;
レスポンス:
{
"license": {
"id": "uuid",
"license_key": "XXXX-XXXX-XXXX-XXXX",
"status": "active",
"purchase_date": "2024-01-01T00:00:00Z",
"activation_date": "2024-01-01T00:00:00Z",
"expiration_date": "2025-12-31T23:59:59Z",
"days_until_expiration": 365,
"activation_limit": 5,
"current_activations": 2,
"available_activations": 3,
"is_expired": false,
"is_active": true,
"product": { "id": "uuid", "name": "Studio 8K", "description": "...", "features": [] },
"user": { "id": "uuid", "first_name": "Jane", "last_name": "Doe", "email": "user@example.com" }
},
"active_devices": [
{
"id": "uuid",
"device_id": "device_abc123",
"device_name": "Studio Mac",
"platform": "macos",
"app_version": "3.1.2",
"activated_at": "2024-01-01T00:00:00Z",
"last_seen_at": "2024-01-02T09:00:00Z",
"ip_address": "192.168.1.1"
}
]
}
ライセンスのライフサイクル
レート制限
ライセンスのエンドポイントには、IP ごとに固定の 60 秒ウィンドウでレート制限が適用されます。429 の場合は、次の 1 つのヘッダーが返されます。
| ヘッダー | 説明 |
|---|---|
Retry-After | ウィンドウがリセットされるまでの秒数(常に 60) |
エンドポイント別のレート制限
| エンドポイント | リクエスト数 / 60 秒 | ティア |
|---|---|---|
/api/licenses/validate | 5 | 厳格 |
/api/licenses/status | 30 | 標準 |
/api/licenses/deactivate | 30 | 標準 |
POST /api/licenses/validate には「厳格」の制限が 2 回適用されます。1 回は IP ごと、もう 1 回は送信された license_key をキーとして適用されるため、1 つのキーに多数の IP から集中的にリクエストを送ることはできません。
429 Too Many Requests が返されます。ブロック時間が段階的に延びたり、エスカレートしたりすることはありません。ライセンスの種類
ライセンスは、現在のプランのティアのいずれかに対応します。各ティアの正式な機能と上限は、料金ページに表示されている内容です。プランを比較をご覧ください。
| ティア | 対象 | 下位ティアからの主な追加機能 |
|---|---|---|
free | 永続的な無料プラン、カード不要、有効期限なし | シンプルなプレゼンリモコン:1 ルーム、1 人の発表者、透かし入りの画面共有、Stream Deck。その他の機能はすべてロックされています |
presenter | 発表者 | Timer Pro、チャット、描画、マルチホスト、NDI |
pro(Studio HD) | 個人のプロデューサー | コーラー、カスタムルーム、動画共有、Studio の基本機能 |
ultra(Studio 4K) | パワーユーザーと配信者 | ルーム数無制限、保存できるブランドカラーテーマ、レイヤー、ISO 収録、リプレイ、より幅広い Studio ツールセット |
studio(Studio 8K) | 制作チーム | ルームレイアウトデザイナー、ホワイトラベルブランディング、Client API、API キー、カスタムドメイン、シート単位でプールされるチームライセンス |
enterprise | 大規模組織と調達部門 | SSO、SCIM、カスタムチームロール、シート単位の年間契約 |