Afina HTTP API#
Afina HTTP API - це місток між антидетект-браузером Afina та вашим власним кодом. Усе, що ви зазвичай робите руками в інтерфейсі - створюєте профілі, запускаєте браузери, ганяєте RPA-скрипти, перемикаєте проксі, - те саме робиться звичайними HTTP-запитами. Найзручніше це тоді, коли Afina треба підключити до CRM, AI-агента чи CI-пайплайна й більше не повертатися до ручної роботи.
- Base URL:
http://127.0.0.1:50778(default port50778; якщо зайнятий - сервер бере наступний вільний). - Формат: усі відповіді
application/json, крім/api/tasks/logsі/api/scripts/run-logs(text/plain). - Покриття: документовано всі ~67 HTTP-ендпоінтів, які розбиті на 11 логічних модулів.
Будь-яка мова програмування#
API - це звичайні HTTP-запити, тож звертатися до нього можна з будь-якої мови програмування. Вам не потрібні спеціальні SDK чи бібліотеки Afina: достатньо відкрити улюблену IDE й написати короткий скрипт на Python, JavaScript, Go, PHP - будь-чому, що вміє робити HTTP-виклики. Нижче - максимально лаконічний приклад на Python (бібліотека requests), який отримує список профілів:
Цього достатньо, щоб переконатися, що сервер живий і відповідає. Далі ви просто міняєте URL ендпоінта й метод (GET/POST/DELETE) - решта довідника описує, які саме запити доступні.
Як працює API#
Усередині Afina живуть дві частини, які працюють пліч-о-пліч. Одна - звичний десктопний застосунок з кнопками, які ви тиснете мишкою. Друга - невеликий локальний веб-сервер, що тихо стартує разом із застосунком і слухає порт 50778 на вашій же машині (127.0.0.1). Ось до нього й ходить HTTP API.
Кілька речей, які варто тримати в голові:
- API живе локально. Сервер прив'язаний до
127.0.0.1, тож приймає запити тільки з вашого комп'ютера. Жодної хмари: назовні він не світиться, а поки застосунок Afina закритий - API просто недоступне. Якщо треба ходити з іншої машини в локальній мережі, це налаштовується окремо, але за замовчуванням усе сидить на localhost заради безпеки. - Застосунок - це «мозок», API - це «руки». Надсилаєте
POST /api/profiles/start- і сервер не чаклує сам по собі: він просить десктопний рушій Afina підняти справжній браузер з потрібним відбитком і проксі, а вам віддаєwsEndpoint, пряму адресу для підключення через CDP (Chrome DevTools Protocol). По суті API - це тонкий шар керування над тим самим рушієм, що крутить і GUI. - Один стан на двох. API та інтерфейс дивляться в одну базу даних (SQLite). Створили профіль через API - він тієї ж миті з'явиться у списку в застосунку, і навпаки. Після кожного запису, що міняє дані, сервер шле подію
reload-event, щоб GUI одразу підхопив зміни. - Передбачувані відповіді. Майже все повертається у JSON зі зрозумілою структурою (
status,data,message,error). Окремий випадок - логи: їх віддають простим текстом, щоб зручно було стрімити прямо в консоль.
Як користуватися API#
Уся робота з API тримається на трьох кроках: взяти ключ → надіслати з ним запит → прочитати JSON-відповідь. Розберемо кожен.
1. Авторизація за ключем. Оскільки сервер має доступ до ваших профілів, проксі та cookies, кожен запит до /api/* має містити заголовок x-api-key. Ключ ви берете один раз у застосунку: Налаштування → Основні налаштування Afina → API ключ. Думайте про нього як про пароль - не зашивайте його в публічні репозиторії, тримайте в змінних оточення чи секрет-менеджері. Якщо ключ невірний або відсутній, сервер відповість 401 і просто закриє з'єднання. Винятки, що не потребують ключа: GET /api/health, GET / і GET /oauth/callback.
2. Типові сценарії використання. API створене для того, щоб вбудувати Afina у ваші власні процеси. Найчастіше його використовують, щоб:
- Інтегрувати Afina з CRM чи внутрішніми системами - наприклад, автоматично заводити новий браузерний профіль під кожного клієнта або акаунт.
- Будувати AI-агентів, які самостійно відкривають сторінки, читають їх (
/api/profiles/eval), роблять скриншоти й ухвалюють рішення. - Масово створювати профілі з унікальними, реалістичними відбитками - десятками й сотнями, не клікаючи мишкою.
- Запускати RPA-скрипти за розкладом на групах акаунтів (мінти, прогріви, рутинні дії) і моніторити їхнє виконання через логи.
- Керувати проксі та cookies програмно - перевіряти, додавати, експортувати/імпортувати сесії між профілями.
3. Перший запит. Найшвидший спосіб переконатися, що все живе, - попросити в сервера список профілів:
Прийшов JSON зі списком акаунтів - усе, ви на зв'язку. Далі логіка майже завжди та сама: спершу дізнаєтесь потрібні id (через */list), потім робите дію (create, start, run…), а за довгими операціями на кшталт задач чи скриптів стежите через їхні логи. Нижче - повний довідник, розкладений по 11 тематичних модулях.
Аутентифікація#
Усі /api/* потребують заголовка x-api-key (Налаштування → Основні → API ключ). Без/з невірним ключем - 401 Unauthorized.
Без ключа доступні лише GET /api/health, GET /, GET /oauth/callback. CORS - лише localhost-origin (http://localhost, http://127.0.0.1, tauri://); методи GET, POST, DELETE, OPTIONS; headers Content-Type, X-API-Key.
Конвенції#
Кілька наскрізних правил, що працюють однаково в усіх модулях. Прочитайте раз - і далі ніде не спіткнетесь:
id= числовий PK (SQLite);accountId= UUID профілю. Більшість write-ендпоінтів приймають обидва - користуйтеся тим, що зручніше у вашому коді.- Boolean приймається як
true/falseабо0/1; datetime - у форматі ISO-8601 (defaultdatetime('now')). */delete= soft-delete (isDeleted=1, об'єкт у «кошику», відновлюваний);*/hard-delete= фізичне видалення з БД/диска без вороття;*/update= PATCH лише переданих полів (решта лишається як була).- Позначення обов'язковості в таблицях: ✅ - обов'язковий, ⬜ - опційний.
Health#
GET /api/health - найпростіший «пінг» сервера, не потребує API key. Зручно використовувати у скриптах як перевірку «чи живий застосунок» перед серією запитів. Відповідь: { "status": "ok", "running": 2 }, де running = кількість зараз запущених браузерів.
Модуль 1. Профілі / Акаунти#
Профілі (акаунти) - це фундамент усієї роботи в Afina. Кожен профіль - окремий ізольований браузер зі своїм унікальним «відбитком» (fingerprint), власними cookies, проксі та налаштуваннями. Завдяки цьому кожен сайт бачить профіль як окремого живого користувача з іншого пристрою.
Це ваш головний інструмент масштабування. Тут ви програмно ліпите десятки унікальних відбитків, оновлюєте й видаляєте їх, а заодно керуєте життям самого браузера - запускаєте, зупиняєте, створюєте одноразові (disposable) профілі під разові задачі. Тільки починаєте знайомство з API? Починайте саме звідси.
GET /api/profiles/list - список акаунтів; до кожного додаються isRunning, tags, groups. Query-фільтри (AND, опційні):
| Параметр | Тип | Обов. | Опис |
|---|---|---|---|
isRunning | bool | ⬜ | Запущені/зупинені |
groupId | int | ⬜ | Фільтр за групою |
tagId | int | ⬜ | Фільтр за тегом |
GET|POST /api/profiles/get - один акаунт. Query/body { accountId }. → { message, profile:{...} }.
POST /api/profiles/create - створює профіль. Fingerprint: не передано → генерується; повний → нормалізується; частковий → база + merge (incoming-wins).
| Поле | Тип | Обов. | Опис |
|---|---|---|---|
name | string | ⬜ | Назва |
accountId | string | ⬜ | Форсувати UUID |
os | macos/windows10/windows11 | ⬜ | Генерація FP (default macos) |
osArch | arm64/x86 | ⬜ | Арх macOS (default arm64) |
chip | m1-m4/intel | ⬜ | Mac-чип |
browserType | string | ⬜ | afina(деф.)/mimic/octo/vision/ads/dolphin |
proxyId | int | ⬜ | Saved proxy (взаємовикл. з proxyData) |
proxyType | saved/set/without_proxy | ⬜ | Тип проксі |
proxyData | object | ⬜ | Inline: host,port,type обов'язк.; опц. username,password,changeIpUrl,country… |
tagIds/tagNames | array | ⬜ | Теги за id/name (нові створюються) |
accountGroupIds/accountGroupNames | array | ⬜ | Групи за id/name |
language/timezone | string | ⬜ | Якщо *_from_ip=false |
languages | array | ⬜ | Override мов |
timezone_from_ip/language_from_ip/languages_from_ip | bool | ⬜ | Авто з IP (деф. true) |
screenSize | string | ⬜ | 1920x1080; avail авто |
availWidth/availHeight/colorDepth/pixelDepth | int | ⬜ | Екран |
blockedPorts | [int] | ⬜ | Захист портів |
blockOnProxyCountryChange | bool/null | ⬜ | null=global, true=block, false=allow |
localCacheMode | default/no_cache | ⬜ | no_cache→--disk-cache-size=0 |
startupUrls | [string] | ⬜ | URL першого запуску |
extraArgs | string | ⬜ | Chromium CLI args |
settings | object | ⬜ | Per-account KV (${key}) |
isNoiseCanvas/Audio/Rects/GLEnabled | bool | ⬜ | FP-шуми |
fingerprint | object | ⬜ | Повний/частковий FP |
note/teamUuid | string | ⬜ | Нотатка / команда ліцензії |
FP-нюанси. Завжди ігноруються (беруться з build):
userAgent,BrandFullVersion,deviceMemory(дляua<147максимум 8).uaбереться з{data_dir}/browser/UA*. Аліаси нормалізуються:hardwareConcurrency→CPUcores,webGLRenderer→WebGLRenderer,uaPlatformVersion→platformVersion,device_memory→deviceMemoryтощо.macChipвизначається зWebGLRenderer(Apple M2→m2).
Успіх: { "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 }. → { "message":"Account successfully deleted" }.
POST /api/profiles/hard-delete - безповоротно (account/profile/junction/proxy_usage/файли + DELETE /profiles/:uuid на сервері; закриває браузер). Body { id }/{ ids:[...] } або { accountId }/{ accountIds:[...] }. → { "status":"success","deleted":3 }.
POST /api/profiles/start - запускає браузер. Body { "profileId":"<UUID>" }. → містить wsEndpoint і data.port (для Puppeteer/Playwright/CDP); якщо вже запущено - alreadyRunning:true.
POST /api/profiles/stop - закриває браузер (CDP Browser.close → graceful kill). Body { "profileId":"<UUID>" }. 404, якщо не запущено.
POST /api/profiles/one-time - disposable-профіль: створює + одразу запускає + hard-delete після зупинки. Ідеально для разових перевірок чи scrape-задач, після яких не хочеться лишати «сміття». Body = поля create; name default one-time-<ts>. → { id, accountId, wsEndpoint, port, isOneTime:true }.
isOneTimeне виставляється черезcreate/update; hard-delete в exit-handler спрацьовує лише за SQL-перевіркоюaccount.isOneTime=1. Темп: ~1 виклик/сек.
Модуль 2. Змінні акаунтів і каталог ключів#
Разом із профілем часто доводиться тримати супутні дані: пароль від гаманця, токен, номер картки, логін на сайті. Цей модуль дозволяє «причепити» до акаунта набір змінних, а потім діставати їх прямо з RPA-скрипта через ${key} - і не зашивати секрети в сам код.
Сховищ для цього два. plain - звичайний JSON (account.settings) під несекретні значення. encrypted - захищене сховище (account_data_blob, sealed-box): воно вимагає введеного майстер-пароля й розшифровується лише в момент запуску скрипта. А каталог ключів (key_entity) - то навіть не значення, а просто реєстр імен усіх ключів, які колись засвітилися, щоб ви бачили, які змінні взагалі є в системі.
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 + реєстрація в каталозі; true → decrypt→merge→encrypt. value: string/number/bool/object/array.
POST /api/accounts/vars/delete - Body { accountId|accountUuid, key, encrypted?:bool }; відповідь містить removed.
GET /api/keys/list - каталог імен ключів. → { message, count, keys:[{ id, key, createdAt }] }.
POST /api/keys/delete - видаляє лише реєстр імен (не значення акаунтів). Body { ids:[...] } або { globalKeyIds:[...] }.
Модуль 3. Скрипти (RPA)#
RPA-скрипти - це серце автоматизації Afina. Скрипт - візуальна послідовність блоків (відкрити сторінку, клікнути, ввести текст, перевірити умову), яку рушій виконує всередині профілю замість вас. Через цей модуль ви дістаєте готові скрипти, створюєте нові програмно й запускаєте їх.
Для розробника тут дві швидкості. Прямий запуск (/api/scripts/run) стартує скрипт на одному профілі негайно, без зайвої бюрократії із задачами - те, що треба для розробки й відладки. А коли скрипт час прогнати на сотні акаунтів за розкладом, у гру вступають повноцінні задачі та групи з Модуля 7. І не забувайте про поле form: якщо скрипт чекає вхідні дані, ви передаєте їх через additionalData.
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) |
startElement | id єдиного елемента з start:true |
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). Body { profileId:"<UUID>", scriptId:<numeric|hash>, closeBrowserAfter?:bool }. → { status:"success", uuid:"<task-uuid>" }.
GET /api/scripts/run-logs?uuid=UUID (або ?taskUuid=UUID) - лог прямого запуску (text/plain).
POST /api/scripts/stop - зупинити running script. Body { uuid } або { taskUuid }; executor переривається на найближчому await.
Модуль 4. Модулі (RPA modules)#
Коли вбудованих RPA-блоків уже не вистачає, в гру заходять модулі - ваш власний JavaScript-код, який ви чіпляєте до скрипта блоком executeModule. Фактично це спосіб дотягнути Afina до чого завгодно: хитра логіка, робота з файлами, дзвінки до зовнішніх API через Node.js.
Робота з модулем - це короткий цикл, який варто закарбувати: створити (create, сервер сам згенерує папку зі скелетом і зробить npm install) → відредагувати файли на диску в moduleDirAbs (index.js, settings.json) → переписати підпис (resign). Ось на останньому кроці спіткнутися найлегше. З міркувань безпеки Afina запускає тільки модулі з валідним Ed25519-підписом, тож забули зробити resign після правки файлів - і 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:
| Поле | Тип | Обов. | Опис |
|---|---|---|---|
name | string | ✅ | Назва |
hash | string | ⬜ | Форсувати UUID |
code | string | ⬜ | UI-мірор index.js |
settings | object | ⬜ | { type:"module", fields:[{name,label,type,default,options?,groupId?}] } |
folderId | int/null | ⬜ | Папка |
tagIds | [int] | ⬜ | Теги |
allowedFunctions | [string] | ⬜ | Node API whitelist |
useCustomFolder/customFolder | bool/string | ⬜ | Використати наявну папку |
Успіх: { status:"success", data:{ id, hash, moduleDir, moduleDirAbs } }. Після create сервер у фоні робить npm install + підпис.
POST /api/modules/update - PATCH рядка БД (не файлів): id, name, code, moduleDir, settings, allowedFunctions, hashes, warnReason, warningFindings, flags isFavorite/isDirty/isMigrated/isWarn/requiresReview/isDeleted, folderId, tagIds + selection.
POST /api/modules/resign - перерахувати підпис папки. Body { id }. → { status:"success", sig:"<base64-ed25519>" }.
POST /api/modules/delete - soft-delete (файли лишаються). Body { id } або { ids:[...] }.
POST /api/modules/hard-delete - видаляє ряд module, module_tags_tag і папку. Body { id } або { ids:[...] }.
Модуль 5. Бази даних#
RPA-скрипти рідко живуть у вакуумі - їм треба звідкись брати дані (логіни, посилання, тексти) і кудись складати результати. Цей модуль чіпляє до Afina зовнішні бази даних, щоб потім ходити до них прямо зі скрипта через RPA-блок database.
Підтримуються і легкі файлові БД (SQLite - Afina може навіть створити .db-файл за вас), і дорослі мережеві сервери (PostgreSQL, MySQL, MSSQL, MongoDB, Redis). Тут ви керуєте лише підключеннями (рядок у таблиці 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:
| Поле | Тип | Обов. | Опис |
|---|---|---|---|
name | string | ✅ | Назва |
type | sqlite/postgres/mysql/mssql/mongodb/redis | ⬜ | Default sqlite |
filePath | string | ⬜ | .db для sqlite |
host/port/user/password/database | mixed | ⬜ | Мережеві БД |
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:[...] }.
Модуль 6. Глобальні змінні#
Буває, одне значення потрібне відразу купі скриптів: базовий URL вашого API, спільний ключ сервісу, назва кампанії. Замість того щоб копіювати його туди-сюди, винесіть у глобальну змінну й діставайте з будь-якого скрипта через ${name}.
На відміну від змінних акаунта (Модуль 2), що прив'язані до конкретного профілю, глобальні змінні спільні для всього робочого простору (таблиця settings, розділ Налаштування → Змінні середовища). Один нюанс тримайте в голові: і name, і value мають бути унікальними, дублікат сервер створити не дасть.
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/duplicate_value).
POST /api/global-vars/update - Body { id, name?, value? } (ті самі правила унікальності).
POST /api/global-vars/delete - Body { id } / { ids:[...] } / { settingIds:[...] }.
Модуль 7. Задачі та групи задач#
Це модуль для серйозної, масштабної автоматизації. Коли один скрипт треба прогнати на сотні акаунтів - за розкладом, з повторами, лімітом паралельності й контролем часу - на сцену виходять задачі та групи задач.
Логіка проста й ієрархічна. Група - це контейнер з правилами: коли запускати (часове вікно), скільки разів повторити, скільки браузерів тримати одночасно. Задача всередині групи - конкретна пара «цей скрипт × цей акаунт» зі своїм часом старту й вхідними даними. Робочий цикл рекомендую такий: створити групу з active:false → насипати в неї задач → викликати start. Так планувальник не вхопить порожню групу раніше часу. А за ходом виконання стежте через логи й статуси (waiting/working/finished/error/stop/stopWithError).
Групи задач#
Група = контейнер розкладу/повторів/таймауту/паралельності; active=1 запускає scheduler. Поля розкладу:
| Поле | Тип | Опис |
|---|---|---|
schedule + timeFrom/timeTo | bool + HH:MM | Вікно часу доби |
scheduleTime + startHour/endHour | bool + int 0-23 | Вікно за годинами |
isRepeatable + repeatCount | bool + 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?, name?, active?, ...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|DELETE /api/task-groups/delete - soft-delete групи та її задач. POST body { id } / DELETE ?id=N.
POST /api/task-groups/hard-delete - видаляє задачі групи й ряд групи. Body { id } або { ids:[...] }; → deletedGroups, deletedTasks, ids.
Задачі#
Задача = scriptId × accountId з executeAt, status, additionalData, sort.
POST /api/tasks/create - створює задачі в групі однією транзакцією. Body { groupId:int, tasks:[...] }. → { message, created, requested, errors }. Поле task:
| Поле | Тип | Обов. | Опис |
|---|---|---|---|
accountId | int | ✅ | account.id |
scriptId | int/string | ✅ | Скрипт |
additionalData | object | ⬜ | Form-поля скрипта (критично, якщо є форма) |
executeAt | ISO | ⬜ | Default now |
tag | string | ⬜ | Мітка |
sort | int | ⬜ | Порядок |
POST /api/tasks/update - PATCH за id/taskId: status, tag, description, executeAt, sort, additionalData.
GET /api/tasks/list - плоский список (фільтри AND). Query: status (CSV), groupId, accountId, scriptId, limit (деф. 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; closeBrowser:true додатково закриває браузер. Body { id } / { ids:[...] } / [{ id, uuid? }] + closeBrowser?.
Логи#
GET /api/tasks/logs?taskUuid=UUID (або ?taskId=UUID) - текстовий лог за task.uuid (text/plain); читається під час виконання. 404, якщо файл ще не створено. (Для прямих запусків - GET /api/scripts/run-logs, Модуль 3.)
Модуль 8. Проксі#
Саме проксі дає кожному профілю свою IP-адресу й географію. Без нормально налаштованого проксі весь сенс антидетекту просто випаровується. Цей модуль додає нові проксі та масово перевіряє ті, що вже є, перед важливим запуском.
Одна приємна деталь: Afina не просто зберігає проксі, а «прогріває» його - реально підключається, дивиться на видиму IP, країну, часовий пояс і (для socks5) підтримку UDP. Не відповів - навіть не збережеться. Це рятує від класики, коли масовий запуск падає через один мертвий проксі. Тож візьміть за звичку ганяти check/check-all перед стартом великої групи задач.
POST /api/proxies/check - перевіряє проксі акаунтів (checker ipapicom/ipinfoio; для socks5 ще UDP); оновлює proxy і proxy_usage, враховує country-block. Body (фільтри AND): accountIds:[int] / groupId / tagId / нічого = всі 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_usage усіх акаунтів. → { message, checked, results:[...] }.
POST /api/proxies/add - додає proxy після прогрівальної перевірки (при fail не зберігає). Body { host, port, type?, username?, password?, remark?, changeIpUrl? } (type деф. http). → { added:true, proxyId, result } або { added:false, result:{status:"error",message} }.
Модуль 9. Cookies та CDP-міграція#
Cookies - це збережені сесії: залогінені акаунти, кошики, налаштування сайтів. Цей модуль дозволяє програмно «заливати» cookies у профіль і вивантажувати їх назовні - скажімо, перенести робочу сесію з іншого браузера чи зробити бекап.
Головне тут - зрозуміти момент застосування. Імпортовані cookies не вставляються миттєво: вони лягають у чергу й інжектяться при наступному запуску браузера, тому під час cookies/set акаунт має бути зупинений. А якщо браузер уже працює і cookies треба підкласти «на гарячу», йдіть через пряме підключення по CDP (wsEndpoint) - про це трохи нижче. З експортом теж є нюанс: cookies лежать у зашифрованому сховищі, тож знадобиться розблокована сесія (введений майстер-пароль).
POST /api/profiles/cookies/set - кладе cookies у чергу {data_dir}/cookies/{uuid}/cookies_{ts}.json; інжектяться через CDP при наступному start. Body { accountId (int/UUID, alias id), cookies:[{domain,name,value,path?,expirationDate?,secure?,httpOnly?,sameSite?,…}] }. → { "status":"success", "data":{ "count":1 } }.
Акаунт має бути зупинений. Для гарячого вводу - CDP
Network.setCookiesчерез/api/profiles/eval.
POST /api/profiles/cookies/export - експорт розшифрованих cookies (папка профілю → .zip → .afbk; Chrome-extension формат). Body:
| Поле | Тип | Опис |
|---|---|---|
id / ids | int / [int] | Один / bulk account.id |
accountId / accountIds | UUID / [UUID] | Один / bulk UUID |
path | string | Файл (.json) або папка |
Поведінка: single без path → data.cookies; single + .json → один файл; bulk/папка → cookie_{accountName}_{ts}.json. Потрібна розблокована сесія (інакше 500 Cookie key not available).
CDP-міграція (пряме підключення). Окремого ендпоінта імпорту через CDP HTTP API не має; міграцію роблять так: POST /api/profiles/start → взяти wsEndpoint → під'єднатися Puppeteer/Playwright:
Далі Storage.getCookies / Network.setCookies через CDP, або /api/profiles/cookies/export + /api/profiles/cookies/set між акаунтами.
Модуль 10. Взаємодія з браузером#
Цей модуль перетворює API на «очі та руки» всередині запущеного браузера. Не обов'язково писати повноцінний RPA-скрипт: можна на льоту виконати довільний JavaScript у поточній вкладці або зробити скриншот. Для AI-агентів і відладки це безцінно.
Саме /api/profiles/eval робить API по-справжньому гнучким. Через нього ви читаєте зі сторінки будь-що, клікаєте елемент, заповнюєте форму, навіть смикаєте CDP-команди. А screenshot дає візуальний контекст - наприклад, щоб vision-модель буквально побачила сторінку. Одне «але»: браузер профілю має бути запущений (/api/profiles/start), інакше у відповідь прилетить 404.
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..." }.
Для пошуку елементів/тексту/URL (аналоги MCP
find_clickable,get_page_text,get_current_url) у HTTP API окремих ендпоінтів немає - виконуйте відповідний JS через/api/profiles/eval.
Модуль 11. Email (IMAP)#
Купа сценаріїв автоматизації рано чи пізно впирається в пошту: коди реєстрації, листи верифікації, OTP. Цей модуль керує тим, які поштові скриньки (через IMAP) Afina моніторить, щоб скрипт міг сам дочекатися потрібного листа й прочитати його.
З міркувань безпеки паролі скриньок через API не повертаються ніколи. Ви бачите тільки метадані підключення й можете вмикати чи вимикати моніторинг.
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 }.
Формат помилок#
Помилки сервера прозорі й передбачувані: HTTP-статус 4xx/5xx плюс тіло { "error": "Опис помилки" }. По коду одразу видно, що саме пішло не так:
| Код | Причина |
|---|---|
400 | Відсутні required-поля / невалідний JSON |
401 | Невірний/відсутній x-api-key (з'єднання закривається) |
404 | Ресурс не знайдено / браузер не запущено |
500 | DB / внутрішня помилка |
І один підступний момент, який легко проґавити: частина бізнес-помилок прилітає з HTTP 200, але з тілом { status:"error", code:"<i18n-key>", message:"..." } - скажімо, спроба завести дублікат глобальної змінної. Тому в серйозному коді дивіться не лише на HTTP-статус, а й на поле status усередині JSON.
Повний приклад (запуск за розкладом)#
Щоб усе вище склалося в одну картину, пройдемо наскрізний сценарій від першого запиту до моніторингу. Завдання: запустити скрипт MintNFT (scriptId=12) з полями walletPassword/mintCount на акаунтах 42/43/44, у часовому вікні 08:00-20:00, з повтором 2, паралельністю 5, починаючи з 2026-05-11T09:00:00.000Z.
Моніторинг: GET /api/task-groups/get?id=7 → GET /api/tasks/logs?taskUuid=<uuid>. Керування: GET /api/tasks/active; POST /api/tasks/stop {"ids":[100],"closeBrowser":true}; POST /api/task-groups/restart; POST /api/task-groups/hard-delete.