Licenze
Gestisci la convalida delle licenze, l'attivazione e il tracciamento dei dispositivi.
429 Too Many Requests con un'intestazione Retry-After: 60 finché la finestra non si azzera.Convalida licenza
Convalida e attiva una chiave di licenza. Questo endpoint è limitato a 5 richieste al minuto per indirizzo IP.
Endpoint: POST /api/licenses/validate
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
license_key | string | Sì | Chiave di licenza da convalidare |
device_id | string | Sì | Identificatore stabile e univoco del dispositivo da attivare. Invia sempre lo stesso valore per un determinato dispositivo: è ciò che assegna e riassegna a quel dispositivo la sua postazione |
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?;
Risposta (successo):
{
"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
}
}
Risposta (errore):
{
"statusCode": 400,
"statusMessage": "Invalid licence key or validation failed"
}
| Stato | Quando |
|---|---|
400 | license_key o device_id è mancante, vuoto o troppo lungo, oppure la chiave è sconosciuta, inattiva o scaduta |
409 | Tutte le postazioni della licenza sono già occupate da altri dispositivi e nessuna è rimasta inattiva abbastanza a lungo da poter essere recuperata |
429 | Limite di richieste superato, vedi Limitazione delle richieste |
400 quindi non indica mai quale campo ha causato l'errore: verifica che license_key e device_id siano entrambi presenti e non vuoti prima di riprovare.Disattiva licenza
Autenticazione richiestaRichiede il cookie di sessione session_token del proprietario della licenza (vedi Autenticazione): puoi disattivare solo una licenza di tua proprietà.
Disattiva una licenza su un dispositivo specifico o su tutti i dispositivi.
Endpoint: POST /api/licenses/deactivate
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
license_key | string | Sì | Chiave di licenza da disattivare |
device_id | string | No | Dispositivo specifico da disattivare (omettilo per tutti i dispositivi) |
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?;
Risposta:
{
"success": true,
"message": "Device deactivated successfully",
"deactivated_count": 1,
"remaining_activations": 4
}
remaining_activations è il numero di postazioni ancora in uso dopo la disattivazione, ovvero il conteggio dei dispositivi distinti rimasti attivi sulla licenza. Non è il numero di postazioni libere. Disattivando tutti i dispositivi si ottiene quindi 0. Calcola le postazioni libere come activation_limit - remaining_activations, usando activation_limit di GET /api/licenses/status. Senza device_id il messaggio è «All devices deactivated successfully»; deactivated_count è il numero di postazioni liberate da questa chiamata.Stato della licenza
Autenticazione richiestaRichiede il cookie di sessione session_token del proprietario della licenza (vedi Autenticazione): puoi consultare solo una licenza di tua proprietà.
Controlla lo stato attuale di una licenza. Limitato a 30 richieste al minuto per IP.
Endpoint: GET /api/licenses/status
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
license_key | query | Sì | Chiave di licenza da controllare |
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?;
Risposta:
{
"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"
}
]
}
Ciclo di vita della licenza
Limitazione delle richieste
Gli endpoint delle licenze applicano un limite di richieste per IP su una finestra fissa di 60 secondi. In caso di 429, viene restituita un'intestazione:
| Intestazione | Descrizione |
|---|---|
Retry-After | Secondi mancanti all'azzeramento della finestra (sempre 60) |
Limiti di richieste per endpoint
| Endpoint | Richieste / 60s | Livello |
|---|---|---|
/api/licenses/validate | 5 | Rigoroso |
/api/licenses/status | 30 | Standard |
/api/licenses/deactivate | 30 | Standard |
POST /api/licenses/validate applica il limite Rigoroso due volte: una per IP e una in base alla license_key inviata, così una singola chiave non può essere bersagliata da molti IP.
429 Too Many Requests per il resto della finestra di 60 secondi. Non esiste una durata di blocco progressiva o crescente.Tipi di licenza
Una licenza corrisponde a uno dei livelli di piano attuali. L'insieme di funzionalità e limiti di riferimento per ciascun livello è quello mostrato nella pagina dei prezzi, vedi Confronta i piani.
| Livello | A chi è rivolto | Principali aggiunte rispetto al livello inferiore |
|---|---|---|
free | Piano gratuito permanente, senza carta, senza scadenza | Un semplice telecomando per presentazioni: una stanza, un relatore, condivisione dello schermo con filigrana e Stream Deck; tutte le altre funzionalità sono bloccate |
presenter | Relatori | Timer Pro, chat, disegno, multi-host e NDI |
pro (Studio HD) | Produttori singoli | Chiamanti, stanze personalizzate, condivisione video e gli elementi essenziali di Studio |
ultra (Studio 4K) | Utenti esperti e streamer | Stanze illimitate, temi di colore del brand salvati, livelli, registrazione ISO, replay e il set di strumenti Studio più ampio |
studio (Studio 8K) | Team di produzione | Designer del layout delle stanze, branding whitelabel, la Client API, chiavi API, domini personalizzati, licenze di team condivise per postazione |
enterprise | Grandi organizzazioni e uffici acquisti | SSO, SCIM e ruoli di team personalizzati, contratti annuali per postazione |