Afina HTTP API#
Локальний HTTP-сервер для зовнішніх скриптів, автоматизації та AI-агентів через MCP. Запускається разом із десктопним застосунком Afina.
- Base URL:
http://127.0.0.1:50778 - Default port:
50778; якщо зайнятий, сервер пробує наступні порти. - JSON: усі відповіді мають
application/json, крім/api/tasks/logsі/api/scripts/run-logs(text/plain). - Source of truth:
src-tauri/src/browser/server.rs.
Аутентифікація#
Усі /api/* потребують x-api-key із Налаштування → Основні → API ключ. Якщо ключ не задано або він неправильний: 401 Unauthorized / тихе закриття з'єднання.
Без ключа доступні лише GET /api/health, GET /, GET /oauth/callback?code=.... CORS дозволено тільки для localhost-origin (http://localhost, http://127.0.0.1, tauri://); методи GET, POST, DELETE, OPTIONS; headers Content-Type, X-API-Key.
Конвенції#
id= numeric PK у SQLite;accountId= UUID профілю. Більшість write-endpoints приймають обидва.- Boolean:
true/falseабо0/1; datetime: ISO-8601, defaultdatetime('now'). */delete= soft delete (isDeleted=1);*/hard-delete= фізичне видалення з БД/диска.*/update= PATCH лише переданих полів.- Write-endpoints емітять
reload-eventдля UI refresh.
Health#
GET /api/health
Heartbeat без API key. Відповідь: { "status": "ok", "running": 2 }, де running = кількість запущених браузерів.
Profiles - акаунти (CRUD)#
GET /api/profiles/list
Список акаунтів; до кожного додаються isRunning, tags:[{id,name,color}], groups:[{id,name}].
Query params (опційні, AND):
| Параметр | Тип | Опис |
|---|---|---|
isRunning | true/false | Запущені/зупинені |
groupId | int | Фільтр за групою |
tagId | int | Фільтр за тегом |
Відповідь: { message, count, accounts:[{ id, accountId, name, ua, proxyId, os, macChip, isRunning, tags, groups }] }.
GET /api/profiles/get / POST /api/profiles/get
Один акаунт за UUID: query/body { accountId }. Відповідь: { message, profile:{...} }.
POST /api/profiles/create
Створює профіль. fingerprint: не передано → генерується; повний → береться як є з нормалізацією; частковий → генерується база й мерджиться за правилом incoming-wins.
Канонічні поля fingerprint: userAgent, platform, platformVersion, vendor, productSub, CPUcores, deviceMemory, language, languages, AcceptLanguage, Brand, BrandVersion, BrandFullVersion, WebGLRenderer, WebGLVendor, Arch, Bitness, wow64, плюс NoiseCanvas/Audio/Rects/GL.
Завжди ігноруються:
| Поле | Чому |
|---|---|
userAgent | Має збігатися з Afina-Chromium build |
BrandFullVersion | Константа build, пов'язана з UA |
deviceMemory | Для ua<147 браузер віддає максимум 8; 16/32 дозволені з ua>=147 |
Аліаси fingerprint:
| Передано | Стає |
|---|---|
uaPlatformVersion, platform_version | platformVersion |
uaArchitecture, architecture | Arch |
uaBitness | Bitness |
webGLVendor, webglVendor, unmasked_vendor | WebGLVendor |
webGLRenderer, webglRenderer, unmasked_renderer | WebGLRenderer |
hardwareConcurrency, hardware_concurrency, cpuCores | CPUcores |
device_memory | deviceMemory |
user_agent | userAgent |
product_sub | productSub |
accept_language, acceptLanguage | AcceptLanguage |
brandVersion / brandFullVersion / uaFullVersion | BrandVersion / BrandFullVersion |
ua_full_version | BrandFullVersion |
uaбереться з локального{data_dir}/browser/UA*; клієнтське значення ігнорується.osдля генерації: payload →fingerprint.platform→macos; допустимоmacos,windows10,windows11.osу БД:Windows10/Windows11/x86/arm64; Intel WebGL даєos="x86",macChip="",platform="x86",Arch="".macChip: WebGLRenderer (Apple M2→m2) →payload.chip/macChip→fp.chip→"".osArch:arm64/x86для macOS; Windows ігнорує.
Інші параметри (name, proxy*, tags*, settings, шуми, screenSize тощо) наведені нижче.
Body (усе опційно, крім вимог конкретного сценарію):
| Поле | Тип | Опис |
|---|---|---|
name | string | Назва |
accountId | string | Форсувати UUID |
os | macos/windows10 | Генерація FP; default macos |
osArch | arm64/x86 | Арх macOS; default arm64 |
chip | string | m1/m2/m3/m4/intel |
ua | string | Ігнорується; береться локальний UA |
browserType | string | afina/mimic/octo/vision/ads/dolphin; default afina |
note | string | Нотатка |
proxyId | int | Saved proxy; взаємовиключно з proxyData |
proxyType | saved/set/without_proxy | Тип проксі |
proxyData | object | Inline proxy. Обов'язкові: host, port, type.Опційні: username, password, changeIpUrl, remark, country, countryCode, timezone, visible_ip, isActive, isUDPSupported |
tagIds / tagNames | array | Теги за id/name; нові створюються |
accountGroupIds / accountGroupNames | array | Групи за id/name |
languages | array | Override мов |
language / timezone | string | Якщо *_from_ip=false |
timezone_from_ip / language_from_ip / languages_from_ip | bool | Авто з IP; default true |
languageInterfaceType | from_language/real_value | Default real_value |
screenSize | string | 1920x1080; avail auto |
useScreenDefault | bool | Дефолтний екран |
availWidth/availHeight/colorDepth/pixelDepth | int | Екран |
blockedPorts | [int] | `--afina-fp |
blockOnProxyCountryChange | bool/null | null=global, true=block, false=allow |
localCacheMode | default/no_cache | no_cache → --disk-cache-size=0 |
startupUrls | [string] | Відкрити під час першого запуску |
extraArgs | string | Chromium CLI args |
settings | object | Per-account KV ${key} |
isNoiseCanvasEnabled / isNoiseAudioEnabled / isNoiseRectsEnabled / isNoiseGLEnabled | bool | FP noise |
fingerprint | object | Повний/частковий FP |
teamUuid | string | Команда ліцензії |
Успіх: { "status": "success", "id": 50, "accountId": "<uuid>", "account": {...} }.
POST /api/profiles/update
PATCH за { id } або { accountId }. Приймає поля create, isDeleted, skipServerSync, теги tagIds + selection(replace|append|delete|clear), групи accountGroupIds + selectionGroups(replaceGroup|appendGroup|deleteGroup|clearGroup).
POST /api/profiles/delete
Soft-delete. Body { id } або { accountId } (UUID-string). Успіх: { "message": "Account successfully deleted" }.
POST /api/profiles/hard-delete
Безповоротно видаляє account/profile/junction, proxy_usage і файли профілю; синхронізує Afina server DELETE /profiles/:uuid; перед видаленням закриває браузер. Body { id } / { ids:[...] } або { accountId } / { accountIds:[...] }. Успіх: { "status":"success","deleted":3 }.
Browser control - керування браузером#
POST /api/profiles/start
Запускає браузер. Body { "profileId": "<UUID>" }. Відповідь містить wsEndpoint і data.port; якщо вже запущений: alreadyRunning:true. wsEndpoint підходить для Puppeteer/Playwright/CDP.
POST /api/profiles/stop
Закриває запущений браузер через CDP Browser.close, потім graceful kill після таймауту. Body { "profileId": "<UUID>" }. 404, якщо профіль не запущено.
One-time profiles - одноразові профілі#
POST /api/profiles/one-time
Створює disposable profile, одразу запускає й hard-delete після зупинки. Body = поля /api/profiles/create; name default one-time-<ts>.
- Створення йде через
/api/profiles/createз partial-fingerprint merge і внутрішнім__oneTime_internal__. - Повертає
id,accountId,wsEndpoint,port,isOneTime:true. - Зупинка:
POST /api/profiles/stop,browser.close()або crash; після цього профіль зникає з/api/profiles/listі Trash. - Якщо
start_browserвпав, створений one-time профіль видаляється синхронно. - RPM/RPH: один виклик = create + start + stop + delete; не частіше приблизно 1/сек.
isOneTime не можна виставити через /api/profiles/create або /api/profiles/update; hard-delete в exit-handler спрацьовує лише після SQL-перевірки account.isOneTime=1.
Browser introspection - eval / screenshot#
POST /api/profiles/eval
Виконує JS у поточній видимій вкладці запущеного профілю. Body { profileId, code }; код обгортається в IIFE, promises await, результат returnByValue. 404, якщо браузер не запущено. Приклад відповіді: { "value": "https://example.com" }.
POST /api/profiles/screenshot
Скриншот поточної видимої вкладки. Body { profileId, format:"png" }; відповідь { mimeType:"image/png", data:"iVBORw0..." }.
Cookies - імпорт/експорт cookies#
POST /api/profiles/cookies/set
Кладе cookies у чергу імпорту {data_dir}/cookies/{uuid}/cookies_{ts}.json; під час наступного старту браузера сервер інжектить їх через CDP. Неприйнятий cookies_*.json перезаписується.
Body:
| Поле | Тип | Опис |
|---|---|---|
accountId | int/UUID | account.id або UUID; alias id |
cookies | array | Cookie objects: domain/name/value/path/expirationDate/secure/httpOnly/sameSite/... |
Успіх: { "status":"success", "data": { "count": 1 } }.
Акаунт не має бути запущений: cookies застосуються під час наступного /api/profiles/start. Для гарячого вводу використовуйте CDP Network.setCookies через /api/profiles/eval або wsEndpoint.
POST /api/profiles/cookies/export
Експортує розшифровані cookies із відкритої папки профілю, потім .zip, потім .afbk. Формат Chrome extensions: domain/name/value/path/expirationDate/hostOnly/httpOnly/sameSite/secure/session/storeId.
Body:
| Поле | Тип | Опис |
|---|---|---|
id | int | Один account.id |
ids | [int] | Bulk ids |
accountId | UUID | Один UUID |
accountIds | [UUID] | Bulk UUID |
path | string | Файл або папка |
- Single без
path→ cookies уdata.cookies. - Single +
pathна.json→ один файл. - Bulk або
pathбез.json→ папка; файлиcookie_{accountName}_{ts}.json. - Відповідь:
{ status:"success", data:{ id?, accountName?, count?, cookies?, path?, exported?, errors? } }.
Потрібна розблокована сесія: cookie key береться з vault. Інакше 500 Cookie key not available - decrypt keys first.
Account vars - змінні акаунта#
Змінні доступні в RPA як ${key}:
plain=account.settings, JSON без шифрування.encrypted=account_data_blob, sealed-box; потребує master-password, executor розшифровує перед запуском.
GET /api/accounts/vars?accountId=N (або ?accountUuid=UUID)
Повертає { accountId, plain:{...}, encrypted:{...} }.
POST /api/accounts/vars/set
Один ключ. Body { accountId | accountUuid, key, value, encrypted?:bool }; encrypted=false пише в account.settings і реєструє ключ у catalog; encrypted=true decrypt → merge → encrypt. value може бути string/number/bool/object/array.
POST /api/accounts/vars/delete
Видаляє один ключ. Body { accountId | accountUuid, key, encrypted?:bool }; відповідь містить removed.
Proxies - проксі#
POST /api/proxies/check
Перевіряє проксі акаунтів тим самим checker (ipapicom/ipinfoio), для socks5 додатково UDP. У разі успіху оновлює proxy (visible_ip, country, timezone, UDP, active) і per-account proxy_usage; враховує country-block.
Body (фільтри AND):
| Поле | Тип | Опис |
|---|---|---|
accountIds | [int] | account.id |
groupId | int | Акаунти групи |
tagId | int | Акаунти з тегом |
| (нічого) | - | Усі non-deleted з proxy |
Відповідь: { message, checked, results:[{ accountId, accountName, accountUuid, proxyId, host, port, type, result }] }; result.status = success / error / no_proxy.
POST /api/proxies/check-all
Перевіряє всі записи proxy; оновлює proxy і proxy_usage усіх акаунтів, які використовують кожну проксі. Відповідь: { message, checked, results:[...] }.
POST /api/proxies/add
Додає proxy після прогрівальної перевірки; при fail не зберігає. Body { host, port, type?, username?, password?, remark?, changeIpUrl? }, type default http. Відповідь success { added:true, proxyId, result } або fail { added:false, result:{status:"error",message} }.
Databases - підключення до БД#
Підключення для RPA-блока database; таблиця connections.
GET /api/databases/list
Список БД: { message, count, databases:[{ id, name, type, filePath?|host?|port?|user?|database?|ssl? }] }.
GET /api/databases/get?id=N
Одна БД: { message, database:{...} }.
POST /api/databases/create
Body:
| Поле | Тип | Опис |
|---|---|---|
name | string | Назва |
type | sqlite/postgres/mysql/mssql/mongodb/redis | Default sqlite |
filePath | string | .db для sqlite |
host/port/user/password/database | - | Мережеві БД |
ssl | bool | TLS |
uri | string | Connection URI override |
folderId | int/null | UI-папка |
isCreateFile | bool | Для sqlite створити .db у <dataDir>/databases/ |
Успіх: { "status":"success", "data": { "id":3, "name":"scratch", "type":"sqlite", "filePath":"..." } }.
POST /api/databases/update
PATCH за id; поля create + isFavorite, isDeleted, tagIds + selection.
POST /api/databases/delete
Soft-delete. Body { id } або { ids:[...] }.
POST /api/databases/hard-delete
Видаляє connections, connections_tags_tag і файл із диска, якщо є filePath. Body { id } або { ids:[...] }.
Global vars - глобальні змінні#
Name/value з Налаштування → Змінні середовища, доступні в RPA як ${name}; таблиця settings.
GET /api/global-vars/list
Відповідь: { message, count, vars:[{ id, name, value, enable, isRestricted, owner }] }.
POST /api/global-vars/create
Body { name, value }; обидва мають бути унікальні. Дублікати: setting.error.duplicate_name / setting.error.duplicate_value.
POST /api/global-vars/update
Body { id, name?, value? }; ті самі правила унікальності.
POST /api/global-vars/delete
Body { id } / { ids:[...] } / { settingIds:[...] }.
Key catalog - каталог ключів#
key_entity зберігає імена ключів з account.settings і account_data_blob; значення лежать у /api/accounts/vars.
GET /api/keys/list
Відповідь: { message, count, keys:[{ id, key, createdAt }] }.
POST /api/keys/delete
Видаляє лише реєстр імен, не значення акаунтів. Body { ids:[...] } або { globalKeyIds:[...] }.
Scripts - RPA-скрипти#
GET /api/scripts/list
Усі non-deleted скрипти з деревом settings і form. Відповідь: { message, count, scripts:[{ id, name, hash, form, settings, isFavorite }] }. form = поля input/select/checkbox, що передаються в задачі через additionalData.
GET /api/scripts/get?id=N
Повна структура одного скрипта: { message, script:{ id, name, settings, form } }.
POST /api/scripts/create
Створює скрипт. Body { name, settings, form?, tab?, browser?, headlessMode?, noBrowser?, extraArgs? }.
Критичний формат settings:
elements[]:{ id, start, type, left, top, label:"", hash:"", note:"", settings:{} };left/topна елементі, неposition.- Рівно один
start:true;settings.startElement= його id. connections[]:{ sourceId, targetId, sourcePosition:"bottom|right|left", targetPosition:"top|left|right" };targetPositionобов'язковий.visualGroups:[]опційно.
Успіх: { status:"success", data:{ id, hash, name, settings } }.
POST /api/scripts/update
PATCH за id: name, settings (повна заміна), form, tab, browser, headlessMode, noBrowser, isFavorite, folderId, tagIds, extraArgs.
POST /api/scripts/run
Прямий запуск скрипта на профілі без task-group/tasks. Body { profileId:"<UUID>", scriptId:<numeric|hash>, closeBrowserAfter?:bool }; відповідь { status:"success", uuid:"<task-uuid>" }.
GET /api/scripts/run-logs?uuid=UUID (або ?taskUuid=UUID)
Лог прямого запуску (text/plain), аналог /api/tasks/logs для /api/scripts/run.
POST /api/scripts/stop
Зупинити running script за task uuid. Body { uuid:"<task-uuid>" } або { taskUuid }; executor переривається на найближчому await.
Modules - RPA-модулі (executeModule)#
Модуль = JS-код для блока executeModule: ряд module + папка (index.js, utils_<id>.js, package.json, settings.json).
Workflow: POST /api/modules/create → редагувати файли в moduleDirAbs (index.js без IPC-блока process.on('message',...) і process.send({status:'ready'}), settings.json, package.json) → POST /api/modules/resign. Без свіжого Ed25519-підпису executor поверне modules.error.signature_invalid.
GET /api/modules/list
Відповідь: { message, count, modules:[{ id, hash, name, sig, moduleDir, moduleDirAbs }] }.
GET /api/modules/get?id=N (або ?hash=UUID)
Повертає ряд + moduleDirAbs + top-level files.
POST /api/modules/create
Body:
| Поле | Тип | Опис |
|---|---|---|
name | string | required |
hash | string | Форсувати UUID |
code | string | UI-мірор index.js |
settings | object | { type:"module", fields:[{name,label,type:"text"|"number"|"checkbox"|"select",default,options?,groupId?}] } |
folderId | int/null | Папка |
tagIds | [int] | Теги |
allowedFunctions | [string] | Node API whitelist |
useCustomFolder | bool | Використати customFolder |
customFolder | string | Наявна папка |
Успіх: { status:"success", data:{ id, hash, moduleDir, moduleDirAbs } }.
Після create сервер у фоні запускає npm install і підписує модуль. Після редагування файлів завжди викликайте /api/modules/resign.
POST /api/modules/update
PATCH рядка БД, не файлів: id, name, code, moduleDir, warnReason, settings, allowedFunctions, hashes, warningFindings, flags isFavorite/isDirty/isMigrated/isWarn/requiresReview/isDeleted, folderId, tagIds + selection.
POST /api/modules/resign
Перерахувати підпис папки. Body { id }; успіх { status:"success", sig:"base64-ed25519-signature..." }.
POST /api/modules/delete
Soft-delete. Body { id } або { ids:[...] }; файли залишаються.
POST /api/modules/hard-delete
Видаляє ряд module, module_tags_tag і папку. Body { id } або { ids:[...] }.
Task Groups - групи задач#
Група задач = контейнер розкладу/повторів/таймауту/паралельності. active=1 запускає scheduler.
| Поле | Тип | Опис |
|---|---|---|
schedule | bool | Вікно timeFrom/timeTo |
timeFrom / timeTo | string HH:MM | Потребує schedule=true |
scheduleTime | bool | Вікно startHour/endHour |
startHour / endHour | int 0-23 | Потребує scheduleTime=true |
isRepeatable | bool | Повторювати групу |
repeatCount | int | Кількість повторів |
timeout | int sec | 0 = немає |
activeSession | int | Паралельність; 0 = unlimited |
waitForOtherTaskCompletion | bool | Чекати інші групи |
folderId | int/null | UI-папка |
GET /api/task-groups/list
Відповідь: { message, count, groups:[{ id, tag, active, schedule, timeFrom, timeTo, isRepeatable, timeout, activeSession }] }.
GET /api/task-groups/get?id=N
Група + задачі: { message, group:{...}, tasks:[{ id, uuid, scriptId, accountId, status, executeAt, additionalData }], tasksCount }.
GET /api/task-groups/tasks?groupId=N
Лише задачі групи. Статуси: waiting, working, finished, error, stop, stopWithError.
POST /api/task-groups/create
Створює порожню групу. Body { tag?:string, name?:string, active?:bool, ...scheduleFields }. Рекомендовано: active:false, потім створити задачі й викликати start.
POST /api/task-groups/update
PATCH за id: schedule-поля, tag, active, isFavorite, isDeleted, isRescheduled.
POST /api/task-groups/start
Body { id } або { groupId }. Ставить active=1; scheduler підхоплює waiting-задачі. Ідемпотентно, завершені не ресетить.
POST /api/task-groups/restart
Body { id }. Ставить active=1 і переводить finished/error/stop/stopWithError назад у waiting з executeAt=now().
POST /api/task-groups/stop
Body { id }. Ставить active=0, переводить working у stopWithError; браузери не закриває.
POST /api/task-groups/delete або DELETE /api/task-groups/delete?id=N
Soft-delete групи та її задач (isDeleted=1, UI deleteGroup). POST body { id }.
POST /api/task-groups/hard-delete
Видаляє задачі групи й ряд групи. Body { id } або { ids:[...] }; відповідь містить deletedGroups, deletedTasks, ids.
Tasks - задачі#
Задача = scriptId × accountId з executeAt, status, additionalData, sort.
POST /api/tasks/create
Створює одну/кілька задач у групі однією транзакцією. Body { groupId:int, tasks:[...] }; відповідь { message, created, requested, errors }.
Поле task:
| Поле | Тип | Опис |
|---|---|---|
accountId | int | required, account.id |
scriptId | int/string | required |
additionalData | object | form-поля скрипта; інакше defaultValue |
executeAt | ISO string | default now |
tag | string | Мітка |
sort | int | Порядок |
additionalData критично важливий, якщо скрипт має form-поля.
POST /api/tasks/update
PATCH за id або taskId: status, tag, description, executeAt, sort, additionalData.
GET /api/tasks/list
Плоский список задач; фільтри AND.
Query params:
| Параметр | Тип | Опис |
|---|---|---|
status | string | CSV: working,waiting,finished,error,stop,stopWithError |
groupId | int | Група |
accountId | int | account.id |
scriptId | int/string | Скрипт |
limit | int | Default 500, max 5000 |
Відповідь: { message, count, tasks:[{ id, uuid, status, groupId, accountId, scriptId, executeAt }] }.
GET /api/tasks/active
Усі working задачі з account:{id,name,accountId} і script:{id,name}.
POST /api/tasks/delete
Безповоротно видаляє задачі. Body { id } або { ids:[...] }.
POST /api/tasks/stop
Зупиняє working/waiting: status → stop + abort executor за uuid; closeBrowser:true додатково закриває браузер акаунта. Body { id } / { ids:[...] } / [{ id, uuid? }, ...], плюс closeBrowser?:bool.
Logs#
GET /api/tasks/logs?taskUuid=UUID (або ?taskId=UUID)
Текстовий лог за task.uuid (не numeric task.id), Content-Type: text/plain; charset=utf-8; можна читати під час виконання. 404, якщо файл ще не створено або видалено.
GET /api/scripts/run-logs?uuid=UUID
Те саме для прямих запусків через /api/scripts/run.
Emails - IMAP-облікові дані#
GET /api/emails/list
Список IMAP-облікових даних; паролі не повертаються. Відповідь: { message, count, emails:[{ id, email, imapServer, port, isActive, mailboxes }] }.
POST /api/emails/toggle
Вмикає/вимикає IMAP-моніторинг: оновлює isActive і синхронно відкриває/закриває з'єднання. Body { email:"user@gmail.com", isActive:true }.
Error format#
Помилка: HTTP 4xx/5xx + { "error": "Описание ошибки" }.
- 400 - required-поля / JSON.
- 401 - неправильний/відсутній
x-api-key(з'єднання закривається). - 404 - ресурс не знайдено.
- 500 - DB/внутрішня помилка.
- Деякі бізнес-помилки йдуть HTTP 200 з
{ status:"error", code:"<i18n-key>", message:"..." }, наприклад duplicate global vars.
Повний приклад: запустити скрипт на акаунтах за розкладом#
Сценарій: MintNFT (scriptId=12) з walletPassword, mintCount на акаунтах 42/43/44, вікно 08:00-20:00, repeat 2, паралельність 5, старт 2026-05-11T09:00:00.000Z.
- Знайти IDs:
GET /api/scripts/list;GET /api/profiles/list?groupId=5. - Перевірити/прогріти проксі:
POST /api/proxies/check, body{ accountIds:[42,43,44] }. - Створити групу:
POST /api/task-groups/create, body{ tag:"MintNFT run", active:false, schedule:true, timeFrom:"08:00", timeTo:"20:00", isRepeatable:true, repeatCount:2, activeSession:5 }; зберегтиgroup.id. - Створити задачі:
POST /api/tasks/create, body{ groupId:7, tasks:[{accountId:42,scriptId:12,additionalData:{walletPassword:"pwd1",mintCount:"1"},executeAt:"2026-05-11T09:00:00.000Z"}, ...] }. - Активувати:
POST /api/task-groups/start, body{ id:7 }. - Моніторити:
GET /api/task-groups/get?id=7, потімGET /api/tasks/logs?taskUuid=<uuid>. - Керування:
GET /api/tasks/active;POST /api/tasks/stopwith{ ids:[100], closeBrowser:true };POST /api/task-groups/restart;POST /api/task-groups/hard-delete.
Пряме підключення до браузера через CDP#
Після POST /api/profiles/start використовуйте wsEndpoint.
Python pyppeteer: browser = await connect(browserWSEndpoint=ws_endpoint). Без CDP-клієнта використовуйте POST /api/profiles/eval і POST /api/profiles/screenshot.
OAuth callback#
GET / або GET /oauth/callback?code=AUTH_CODE не потребує api-key; використовується для Google Drive OAuth flow, передає code у внутрішній канал і закриває вікно.
Source of truth#
Реалізація: src-tauri/src/browser/server.rs. Якщо поведінка endpoint-а відрізняється від документації, source code is source of truth.