API pro vývojáře doplňků
Tento průvodce vysvětluje, jak vytvořit doplněk pro tržiště {productName}. Váš doplněk je v systému registrován se sadou URL adres, které platforma volá ve specifických momentech životního cyklu – aktivace, deaktivace, pozastavení/obnovení – a URL adres, které může uživatel otevřít pro konfiguraci a další zobrazení.
Přehled
Záznam verze doplňku obsahuje následující pole URL adres. Každé pole je volitelné; uveďte pouze ty, které váš doplněk vyžaduje.
| Pole | Kdy se volá | Metoda |
|---|---|---|
activationUrl | Uživatel nainstaluje doplněk | POST |
deactivationUrl | Uživatel odinstaluje doplněk | POST |
pauseUnpauseUrl | Uživatel pozastaví nebo obnoví doplněk | POST |
configUrl | Uživatel klikne na “Konfigurovat” | Otevře se v prohlížeči (iframe nebo odkaz) |
statusUrl | Platforma se dotazuje na aktuální stav doplňku | GET |
Pole urls navíc obsahuje další odkazy, které se zobrazují v postranním panelu s podrobnostmi o doplňku (např. protokoly, sestavy). Viz sekce Seznam URL adres níže.
API Token
Když platforma aktivuje váš doplněk, vygeneruje nosný token JWT spojený s touto konkrétní instalací. Tento token je předán vaší adrese activationUrl jako apiToken v aktivační datové části a měl by být bezpečně uložen vaší službou.
Použijte jej v následných voláních zpět do {productName} (např. čtení nebo zápis dat produktu prostřednictvím Query API) jako standardní hlavičku Authorization: Bearer <token>.
Token je vymezen na organizaci, která doplněk nainstalovala. Má oprávnění pro čtení/zápis a je platný neomezeně dlouho. Když je doplněk odinstalován, token automaticky vyprší do 15 minut.
tab: TypeScript
async function fetchProducts(apiToken: string, hostname: string): Promise<any[]> {
const response = await fetch(`{HOSTNAME}/api/query`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ query: 'products { id, name, price }' }),
});
if (!response.ok) throw new Error(`Query failed: ${response.status}`);
const data = await response.json();
return data.products ?? [];
}
---
tab: PHP
function fetchProducts(string $apiToken): array {
$ch = curl_init("{hostname}/api/query");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $apiToken",
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['query' => 'products { id, name, price }']),
]);
$body = curl_exec($ch);
curl_close($ch);
$data = json_decode($body, true);
return $data['products'] ?? [];
}
URL adresy životního cyklu
activationUrl – Doplněk byl nainstalován
Volá se jednou, když uživatel nainstaluje váš doplněk. Použijte jej k zajištění prostředků (např. vytvoření tenanta/pracovního prostoru ve vaší službě), uložení apiToken a přípravě jakékoli počáteční konfigurace.
Metoda: POST
Očekávaná odpověď: { "success": true } – jakákoli jiná než úspěšná odpověď nebo chyba HTTP způsobí selhání instalace.
Do odpovědi můžete zahrnout volitelné pole `data`. Jeho hodnota (jakýkoli objekt serializovatelný do JSON) bude předána beze změny do front-endové odpovědi, což je užitečné pro ladění problémů s aktivací z vývojářských nástrojů prohlížeče.
```json
{ "success": true, "data": { "workspaceId": "ws-123", "plan": "trial" } }
**Datová část požadavku:**
```typescript
interface ActivationPayload {
myAddon_id: string; // Jedinečné ID této instalace
addon_id: string; // ID definice doplňku
tenant_id: string; // ID tenanta {productName}
organisation_id: string; // Organizace, která nainstalovala doplněk
// API token – uložte si jej pro ověření budoucích volání zpět do {productName}
apiToken: string; // Nosný token JWT
access_token: string; // Stejné jako apiToken (alias pro kompatibilitu)
api_token_id: string; // ID záznamu tokenu v databázi
// Podpisový klíč – uložte si jej pro ověření JWT ps_token u příchozích požadavků configUrl / urls[] / statusUrl
psSigningSecret: string;
organisation: {
id: string;
code: string;
name: string;
email: string | null;
vatId: string | null;
registrationNumber: string | null;
taxId: string | null;
defaultLanguage: string; // např. "en", "cs"
defaultTimezone: string; // např. CET
flavours: Record<string, any> | null;
country: string | null; // ISO kód země
};
tenant: {
id: string;
code: string;
name: string;
};
user: {
id: string;
username: string;
firstname: string | null;
surname: string | null;
email: string | null;
telephone: string | null;
jobTitle: string | null;
};
}
tab: TypeScript
import express, { Request, Response } from 'express';
const app = express();
app.use(express.json());
// In-memory store (use a real database in production)
const installations = new Map<string, {
apiToken: string;
psSigningSecret: string;
organisationId: string;
}>();
app.post('/addon/activate', (req: Request, res: Response) => {
const { myAddon_id, apiToken, psSigningSecret, organisation } = req.body;
if (!myAddon_id || !apiToken || !psSigningSecret) {
return res.status(400).json({ success: false, error: 'Missing required fields' });
}
// Store both the API token and the signing secret
installations.set(myAddon_id, {
apiToken,
psSigningSecret,
organisationId: organisation.id,
});
console.log(`Addon installed for org: ${organisation.name} (${organisation.id})`);
res.json({ success: true });
});
---
tab: PHP
// activate.php
$payload = json_decode(file_get_contents('php://input'), true);
$myAddonId = $payload['myAddon_id'] ?? null;
$apiToken = $payload['apiToken'] ?? null;
$psSigningSecret = $payload['psSigningSecret'] ?? null;
$organisation = $payload['organisation'] ?? null;
if (!$myAddonId || !$apiToken || !$psSigningSecret) {
http_response_code(400);
echo json_encode(['success' => false, 'error' => 'Missing required fields']);
exit;
}
// Persist installation (use a real DB in production)
file_put_contents(
__DIR__ . "/installations/$myAddonId.json",
json_encode([
'apiToken' => $apiToken,
'psSigningSecret' => $psSigningSecret,
'organisationId' => $organisation['id'],
])
);
echo json_encode(['success' => true]);
deactivationUrl – Doplněk byl odinstalován
Volá se, když uživatel odinstaluje doplněk. Vyčistěte zajištěné prostředky a zneplatněte všechny uložené tokeny.
Metoda: POST
Očekávaná odpověď: { "success": true } – můžete zahrnout volitelné pole data (jakýkoli objekt JSON), které bude předáno do front-endové odpovědi a je užitečné pro ladění (viditelné ve vývojářských nástrojích prohlížeče).
Odpověď 404 z vašeho koncového bodu je považována za úspěch (idempotentní odinstalace). Pokud vrátíte jinou než úspěšnou odpověď, platforma zaprotokoluje chybu, ale stále označí doplněk jako odinstalovaný.
Datová část požadavku:
interface DeactivationPayload {
myAddon_id: string;
organisation_id: string;
}
tab: TypeScript
app.post('/addon/deactivate', (req: Request, res: Response) => {
const { myAddon_id } = req.body;
installations.delete(myAddon_id);
console.log(`Addon uninstalled: ${myAddon_id}`);
res.json({ success: true });
});
---
tab: PHP
// deactivate.php
$payload = json_decode(file_get_contents('php://input'), true);
$myAddonId = $payload['myAddon_id'] ?? null;
if ($myAddonId) {
@unlink(__DIR__ . "/installations/$myAddonId.json");
}
echo json_encode(['success' => true]);
pauseUnpauseUrl – Doplněk byl pozastaven / obnoven
Volá se, když uživatel pozastaví nebo obnoví doplněk. Pozastavení/obnovení používá stejnou URL adresu – rozlišujte akci pomocí pole action v datové části.
Metoda: POST
Očekávaná odpověď: { "success": true } – můžete zahrnout volitelné pole data (jakýkoli objekt JSON), které bude předáno do front-endové odpovědi a je užitečné pro ladění (viditelné ve vývojářských nástrojích prohlížeče).
Datová část požadavku:
interface PauseUnpausePayload {
myAddon_id: string;
addon_id: string;
tenant_id: string;
organisation_id: string;
action: 'pause' | 'unpause';
organisation: { /* stejný tvar jako v aktivaci */ };
tenant: { /* stejný tvar jako v aktivaci */ };
// Poznámka: uživatel NENÍ zahrnut v datových částech pozastavení/obnovení
}
tab: TypeScript
app.post('/addon/pause-unpause', (req: Request, res: Response) => {
const { myAddon_id, action } = req.body;
if (action !== 'pause' && action !== 'unpause') {
return res.status(400).json({ success: false, error: 'Invalid action' });
}
const installation = installations.get(myAddon_id);
if (!installation) {
return res.status(404).json({ success: false, error: 'Installation not found' });
}
// Pause or resume your background jobs / sync processes here
if (action === 'pause') {
console.log(`Pausing addon ${myAddon_id}`);
} else {
console.log(`Resuming addon ${myAddon_id}`);
}
res.json({ success: true });
});
---
tab: PHP
// pause-unpause.php
$payload = json_decode(file_get_contents('php://input'), true);
$myAddonId = $payload['myAddon_id'] ?? null;
$action = $payload['action'] ?? null;
if (!in_array($action, ['pause', 'unpause'], true)) {
http_response_code(400);
echo json_encode(['success' => false, 'error' => 'Invalid action']);
exit;
}
// Store desired state, adjust background jobs, etc.
$state = $action === 'pause' ? 'paused' : 'active';
// ... your business logic here ...
echo json_encode(['success' => true]);
configUrl – Konfigurační UI
Konfigurační URL adresa se otevře, když uživatel klikne na tlačítko Konfigurovat na stránce s podrobnostmi o doplňku. Nejedná se o webhook – vaše URL adresa se otevře přímo v prohlížeči. Můžete si vybrat, jak ji prezentovat, pomocí configUrlType.
configUrlType: "link" vs configUrlType: "iframe"
| Typ | Chování |
|---|---|
link | URL adresa se otevře v nové kartě prohlížeče. Uživatel opustí UI {productName}. Vhodné pro složité více stránkové průvodce konfigurací. |
iframe | URL adresa je vložena do inline panelu uvnitř UI {productName}. Uživatel zůstává v kontextu. Vhodné pro jednoduché formuláře nastavení. |
Při použití `iframe` se ujistěte, že vaše stránka nastavuje příslušné hlavičky `Content-Security-Policy` a `X-Frame-Options` (nebo je vynechává), aby bylo možné ji vložit. Restriktivní `X-Frame-Options: DENY` zabrání načtení iframe.
Před otevřením vaší configUrl v prohlížeči platforma připojí následující parametry dotazu:
| Parametr | Popis |
|---|---|
ps_token | Krátkodobý JWT (10 min) podepsaný pomocí psSigningSecret z aktivace. Použijte jej k ověření, že požadavek pochází z platformy. |
myAddon_id | ID instalace doplňku. |
organisation_id | ID organizace. |
tenant_id | ID tenanta. |
user_id | ID uživatele, který spustil akci. |
language | Aktuální jazyk UI uživatele (např. en, cs). |
JWT `ps_token` obsahuje dvě deklarace: `sub` (`myAddon_id`) a `org` (`organisation_id`). Prosté parametry dotazu jsou poskytovány pro usnadnění – vždy ověřte podpis JWT předtím, než jim budete důvěřovat.
Příklad polí verze doplňku:
{
"configUrl": "https://your-addon.example.com/config",
"configUrlType": "iframe"
}
tab: TypeScript
import jwt from 'jsonwebtoken';
app.get('/config', (req: Request, res: Response) => {
const psConfig = req.query.ps_token as string | undefined;
const myAddonId = req.query.myAddon_id as string | undefined;
if (!psConfig || !myAddonId) {
return res.status(401).send('<p>Missing ps_token</p>');
}
const installation = installations.get(myAddonId);
if (!installation) {
return res.status(403).send('<p>Unknown installation</p>');
}
// Verify signature and expiry using the stored psSigningSecret
try {
jwt.verify(psConfig, installation.psSigningSecret);
} catch {
return res.status(401).send('<p>Invalid or expired ps_token</p>');
}
res.send(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Addon Configuration</title>
<style>body { font-family: sans-serif; padding: 1rem; }</style>
</head>
<body>
<h2>Configure Addon</h2>
<form method="POST" action="/config/save">
<input type="hidden" name="myAddon_id" value="${myAddonId}">
<label>Sync interval (minutes):
<input type="number" name="interval" value="30" min="5">
</label>
<button type="submit">Save</button>
</form>
</body>
</html>
`);
});
---
tab: PHP
// config.php
// Requires firebase/php-jwt: composer require firebase/php-jwt
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$psConfig = $_GET['ps_token'] ?? null;
$myAddonId = $_GET['myAddon_id'] ?? null;
if (!$psConfig || !$myAddonId) {
http_response_code(401);
echo '<p>Missing ps_token</p>';
exit;
}
$installationFile = __DIR__ . "/installations/$myAddonId.json";
if (!file_exists($installationFile)) {
http_response_code(403);
echo '<p>Unknown installation</p>';
exit;
}
$installation = json_decode(file_get_contents($installationFile), true);
$psSigningSecret = $installation['psSigningSecret'] ?? null;
// Verify signature and expiry
try {
JWT::decode($psConfig, new Key($psSigningSecret, 'HS256'));
} catch (\Exception $e) {
http_response_code(401);
echo '<p>Invalid or expired ps_token: ' . htmlspecialchars($e->getMessage()) . '</p>';
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Addon Configuration</title>
<style>body { font-family: sans-serif; padding: 1rem; }</style>
</head>
<body>
<h2>Configure Addon</h2>
<form method="POST" action="/config/save.php">
<input type="hidden" name="myAddon_id" value="<?= htmlspecialchars($myAddonId) ?>">
<label>Sync interval (minutes):
<input type="number" name="interval" value="30" min="5">
</label>
<button type="submit">Save</button>
</form>
</body>
</html>
statusUrl – Stav doplňku
Pokud zadáte statusUrl, platforma ji zavolá, aby uživateli zobrazila aktuální stav vašeho doplňku (zobrazený v postranním panelu s podrobnostmi o doplňku s barevně odlišenou ikonou).
Metoda: GET
Ověření: Před voláním vaší statusUrl platforma připojí následující parametry dotazu:
| Parametr | Popis |
|---|---|
ps_token | Krátkodobý JWT (10 min) podepsaný pomocí psSigningSecret z aktivace. Použijte jej k ověření, že požadavek pochází z platformy. |
myAddon_id | ID instalace doplňku. |
organisation_id | ID organizace. |
tenant_id | ID tenanta. |
user_id | ID uživatele, který spustil akci. |
language | Aktuální jazyk UI uživatele (např. en, cs). |
JWT `ps_token` obsahuje dvě deklarace: `sub` (`myAddon_id`) a `org` (`organisation_id`). Prosté parametry dotazu jsou poskytovány pro usnadnění – vždy ověřte podpis JWT předtím, než jim budete důvěřovat.
Formát odpovědi:
interface AddonStatusResponse {
/** Aktuální stav doplňku */
status: 'ok' | 'paused' | 'blocked' | 'invalid-auth' | 'error';
/** Časové razítko ISO 8601 poslední úspěšné operace nebo null */
lastSuccessAt: string | null;
/** Časové razítko ISO 8601 posledního selhání nebo null */
lastFailureAt: string | null;
/**
* Volitelná zpráva čitelná pro člověka popisující aktuální stav.
* Může to být prostý řetězec nebo objekt TranslatableString (mapa jazyk → zpráva).
*/
statusMessage: string | Record<string, string> | null;
/**
* Volitelná libovolná data předaná do front-endové odpovědi beze změny.
* Nezobrazuje se v UI – viditelné pouze ve vývojářských nástrojích prohlížeče (karta Síť).
* Užitečné pro zobrazení diagnostických informací bez samostatného ladicího koncového bodu.
*/
data?: Record<string, any>;
}
Hodnoty stavu a jejich význam:
| Stav | Barva | Význam |
|---|---|---|
ok | Zelená | Doplněk běží normálně |
paused | Žlutá | Doplněk je pozastaven uživatelem |
blocked | Oranžová | Zpracování je blokováno (např. omezení rychlosti, problém s upstreamem) |
invalid-auth | Červená | Ověření externí služby selhalo |
error | Červená | Došlo k neočekávané chybě |
tab: TypeScript
app.get('/addon/status', (req: Request, res: Response) => {
const psConfig = req.query.ps_token as string | undefined;
const myAddonId = req.query.myAddon_id as string | undefined;
if (!psConfig || !myAddonId) {
return res.status(401).json({ error: 'Missing ps_token' });
}
const installation = installations.get(myAddonId);
if (!installation) {
return res.status(403).json({ error: 'Unknown installation' });
}
// Verify signature and expiry using the stored psSigningSecret
try {
jwt.verify(psConfig, installation.psSigningSecret);
} catch {
return res.status(401).json({ error: 'Invalid or expired ps_token' });
}
// Determine your actual status (query your DB, check job queue, etc.)
const isHealthy = checkHealth(myAddonId);
res.json({
status: isHealthy ? 'ok' : 'error',
lastSuccessAt: isHealthy ? new Date().toISOString() : null,
lastFailureAt: isHealthy ? null : new Date().toISOString(),
statusMessage: isHealthy
? { en: 'Running normally', cs: 'Běží normálně' }
: { en: 'Sync failed — check credentials', cs: 'Synchronizace selhala — zkontrolujte přihlašovací údaje' },
});
});
function checkHealth(_myAddonId: string): boolean {
// Your actual health check logic here
return true;
}
---
tab: PHP
// status.php
// Requires firebase/php-jwt: composer require firebase/php-jwt
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$psConfig = $_GET['ps_token'] ?? null;
$myAddonId = $_GET['myAddon_id'] ?? null;
if (!$psConfig || !$myAddonId) {
http_response_code(401);
echo json_encode(['error' => 'Missing ps_token']);
exit;
}
$installationFile = __DIR__ . "/installations/$myAddonId.json";
if (!file_exists($installationFile)) {
http_response_code(403);
echo json_encode(['error' => 'Unknown installation']);
exit;
}
$installation = json_decode(file_get_contents($installationFile), true);
$psSigningSecret = $installation['psSigningSecret'] ?? null;
// Verify signature and expiry
try {
JWT::decode($psConfig, new Key($psSigningSecret, 'HS256'));
} catch (\Exception $e) {
http_response_code(401);
echo json_encode(['error' => 'Invalid or expired ps_token: ' . $e->getMessage()]);
exit;
}
// Return status
header('Content-Type: application/json');
echo json_encode([
'status' => 'ok',
'lastSuccessAt' => date('c'),
'lastFailureAt' => null,
'statusMessage' => [
'en' => 'Running normally',
'cs' => 'Běží normálně',
],
]);
Seznam URL adres
Kromě configUrl můžete definovat pole dalších odkazů (urls), které se zobrazí v postranním panelu stránky s podrobnostmi o doplňku. Ty jsou užitečné pro hluboké odkazy do vaší služby, jako jsou prohlížeče protokolů, sestavy nebo řídicí panely.
{
"urls": [
{
"sortOrder": 1,
"name": {
"en": "Sync Logs",
"cs": "Logy synchronizace"
},
"icon": "list",
"url": "https://your-addon.example.com/logs?addon=${myAddon.id}",
"urlType": "iframe"
},
{
"sortOrder": 2,
"name": {
"en": "Open Dashboard",
"cs": "Otevřít přehled"
},
"icon": "external-link",
"url": "https://your-addon.example.com/dashboard",
"urlType": "link"
}
]
}
Pole:
| Pole | Typ | Popis |
|---|---|---|
sortOrder | number | Pořadí zobrazení v postranním panelu (vzestupně) |
name | TranslatableString | Popisek tlačítka – objekt s kódy jazyků jako klíči |
icon | string | Název ikony (identifikátor ikony Lucide, např. list, history, settings) |
url | string | Cílová URL adresa. Podporuje proměnné šablony (viz níže). |
urlType | "link" | "iframe" | Jak se URL adresa otevírá (viz níže) |
Proměnné šablony URL adres
Pole url podporuje následující proměnné šablony, které jsou nahrazeny na straně serveru před odesláním URL adresy do prohlížeče:
| Proměnná | Nahrazeno |
|---|---|
${HOSTNAME} | Veřejná základní URL adresa platformy (např. https://app.example.com) |
${myAddon.id} | myAddon_id aktuální instalace |
Platforma také připojí následující parametry dotazu ke každé URL adrese před jejím otevřením:
| Parametr | Popis |
|---|---|
ps_token | Krátkodobý JWT (10 min) podepsaný pomocí psSigningSecret z aktivace. Použijte jej k ověření, že požadavek pochází z platformy. |
myAddon_id | ID instalace doplňku. |
organisation_id | ID organizace. |
tenant_id | ID tenanta. |
user_id | ID uživatele, který spustil akci. |
language | Aktuální jazyk UI uživatele (např. en, cs). |
JWT `ps_token` obsahuje dvě deklarace: `sub` (`myAddon_id`) a `org` (`organisation_id`). Prosté parametry dotazu jsou poskytovány pro usnadnění – vždy ověřte podpis JWT předtím, než jim budete důvěřovat.
urlType: "link" vs urlType: "iframe"
| Typ | Chování |
|---|---|
link | Kliknutím na tlačítko se URL adresa otevře v nové kartě prohlížeče. Uživatel opustí {productName}. |
iframe | Kliknutím na tlačítko se URL adresa otevře inline jako vložený panel uvnitř stránky s podrobnostmi o doplňku. Uživatel zůstává v kontextu. |
Použijte iframe pro zobrazení, která mají být spotřebována bez opuštění platformy (např. protokol volání nebo řídicí panel stavu). Použijte link, když je cílem plnohodnotná externí aplikace, kterou nemá smysl vkládat (např. váš administrační panel).
TranslatableString
Několik polí (name, shortDescription atd.) podporuje více jazyků. Zadejte objekt JSON s jazykovými značkami IETF jako klíči:
{
"en": "Sync Logs",
"cs": "Logy synchronizace",
"de": "Synchronisierungsprotokolle",
"sk": "Logy synchronizácie",
"pl": "Dzienniki synchronizacji"
}
Platforma vybere řetězec odpovídající jazyku uživatele a v případě, že požadovaný jazyk není k dispozici, použije en.
Podporované jazyky: en, cs, de, sk, pl, hu, fr, it, es, ro, hr, ua, pt, sl
Formát odpovědi
Všechny koncové body životního cyklu (activationUrl, deactivationUrl, pauseUnpauseUrl) musí vracet JSON s minimálně booleovskou hodnotou success:
{ "success": true }
V případě selhání uveďte chybovou zprávu error:
{ "success": false, "error": "Failed to provision workspace: quota exceeded" }
Vraťte příslušný stavový kód HTTP:
200– úspěch400– chybný požadavek (neplatná nebo chybějící pole datové části)500– neočekávaná chyba serveru
Platforma považuje jakýkoli stav HTTP jiný než 2xx nebo odpověď { "success": false } za selhání. Pro activationUrl to způsobí selhání instalace a vrácení zpět. Pro deactivationUrl je chyba zaprotokolována, ale odinstalace se stále dokončí. Pro pauseUnpauseUrl je chyba zaprotokolována a aktualizace stavu se neprojeví.
Ladění pomocí data
Jakákoli odpověď JSON z koncového bodu životního cyklu (activationUrl, deactivationUrl, pauseUnpauseUrl, statusUrl) může obsahovat volitelné pole data. Platforma tuto hodnotu předává beze změny do front-endové odpovědi, takže je viditelná ve vývojářských nástrojích prohlížeče (karta Síť). To je záměrné a je to pohodlný způsob, jak zobrazit interní stav nebo diagnostické informace bez nutnosti samostatného ladicího koncového bodu.
// Příklad: aktivační odpověď s ladicími daty
{
"success": false,
"error": "Quota exceeded",
"data": {
"currentUsage": 42,
"limit": 40,
"resetAt": "2026-03-01T00:00:00Z"
}
}
Pole data se nikdy nezobrazuje v UI {productName} – je přítomno pouze v nezpracovaných odpovědích API. Neumísťujte sem citlivá uživatelská data; zacházejte s ním jako s diagnostickým kanálem pouze pro vývojáře.