Оновлено:

Afina HTTP API#

Afina HTTP API - це місток між антидетект-браузером Afina та вашим власним кодом. Усе, що ви зазвичай робите руками в інтерфейсі - створюєте профілі, запускаєте браузери, ганяєте RPA-скрипти, перемикаєте проксі, - те саме робиться звичайними HTTP-запитами. Найзручніше це тоді, коли Afina треба підключити до CRM, AI-агента чи CI-пайплайна й більше не повертатися до ручної роботи.

  • Base URL: http://127.0.0.1:50778 (default port 50778; якщо зайнятий - сервер бере наступний вільний).
  • Формат: усі відповіді 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), який отримує список профілів:

python

Цього достатньо, щоб переконатися, що сервер живий і відповідає. Далі ви просто міняєте 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.

Як користуватися API

2. Типові сценарії використання. API створене для того, щоб вбудувати Afina у ваші власні процеси. Найчастіше його використовують, щоб:

  • Інтегрувати Afina з CRM чи внутрішніми системами - наприклад, автоматично заводити новий браузерний профіль під кожного клієнта або акаунт.
  • Будувати AI-агентів, які самостійно відкривають сторінки, читають їх (/api/profiles/eval), роблять скриншоти й ухвалюють рішення.
  • Масово створювати профілі з унікальними, реалістичними відбитками - десятками й сотнями, не клікаючи мишкою.
  • Запускати RPA-скрипти за розкладом на групах акаунтів (мінти, прогріви, рутинні дії) і моніторити їхнє виконання через логи.
  • Керувати проксі та cookies програмно - перевіряти, додавати, експортувати/імпортувати сесії між профілями.

3. Перший запит. Найшвидший спосіб переконатися, що все живе, - попросити в сервера список профілів:

bash

Прийшов JSON зі списком акаунтів - усе, ви на зв'язку. Далі логіка майже завжди та сама: спершу дізнаєтесь потрібні id (через */list), потім робите дію (create, start, run…), а за довгими операціями на кшталт задач чи скриптів стежите через їхні логи. Нижче - повний довідник, розкладений по 11 тематичних модулях.

Аутентифікація#

Усі /api/* потребують заголовка x-api-key (Налаштування → Основні → API ключ). Без/з невірним ключем - 401 Unauthorized.

bash

Без ключа доступні лише 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 (default datetime('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, опційні):

ПараметрТипОбов.Опис
isRunningboolЗапущені/зупинені
groupIdintФільтр за групою
tagIdintФільтр за тегом
json

GET|POST /api/profiles/get - один акаунт. Query/body { accountId }. → { message, profile:{...} }.

POST /api/profiles/create - створює профіль. Fingerprint: не передано → генерується; повний → нормалізується; частковий → база + merge (incoming-wins).

ПолеТипОбов.Опис
namestringНазва
accountIdstringФорсувати UUID
osmacos/windows10/windows11Генерація FP (default macos)
osArcharm64/x86Арх macOS (default arm64)
chipm1-m4/intelMac-чип
browserTypestringafina(деф.)/mimic/octo/vision/ads/dolphin
proxyIdintSaved proxy (взаємовикл. з proxyData)
proxyTypesaved/set/without_proxyТип проксі
proxyDataobjectInline: host,port,type обов'язк.; опц. username,password,changeIpUrl,country
tagIds/tagNamesarrayТеги за id/name (нові створюються)
accountGroupIds/accountGroupNamesarrayГрупи за id/name
language/timezonestringЯкщо *_from_ip=false
languagesarrayOverride мов
timezone_from_ip/language_from_ip/languages_from_ipboolАвто з IP (деф. true)
screenSizestring1920x1080; avail авто
availWidth/availHeight/colorDepth/pixelDepthintЕкран
blockedPorts[int]Захист портів
blockOnProxyCountryChangebool/nullnull=global, true=block, false=allow
localCacheModedefault/no_cacheno_cache--disk-cache-size=0
startupUrls[string]URL першого запуску
extraArgsstringChromium CLI args
settingsobjectPer-account KV (${key})
isNoiseCanvas/Audio/Rects/GLEnabledboolFP-шуми
fingerprintobjectПовний/частковий FP
note/teamUuidstringНотатка / команда ліцензії

FP-нюанси. Завжди ігноруються (беруться з build): userAgent, BrandFullVersion, deviceMemory (для ua<147 максимум 8). ua береться з {data_dir}/browser/UA*. Аліаси нормалізуються: hardwareConcurrencyCPUcores, webGLRendererWebGLRenderer, uaPlatformVersionplatformVersion, device_memorydeviceMemory тощо. macChip визначається з WebGLRenderer (Apple M2m2).

Успіх: { "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 виклик/сек.

bash

Модуль 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=falseaccount.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:[...] }.

bash

Модуль 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)
startElementid єдиного елемента з 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:

ПолеТипОбов.Опис
namestringНазва
hashstringФорсувати UUID
codestringUI-мірор index.js
settingsobject{ type:"module", fields:[{name,label,type,default,options?,groupId?}] }
folderIdint/nullПапка
tagIds[int]Теги
allowedFunctions[string]Node API whitelist
useCustomFolder/customFolderbool/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:

ПолеТипОбов.Опис
namestringНазва
typesqlite/postgres/mysql/mssql/mongodb/redisDefault sqlite
filePathstring.db для sqlite
host/port/user/password/databasemixedМережеві БД
sslboolTLS
uristringConnection URI override
folderIdint/nullUI-папка
isCreateFileboolsqlite: створити .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/timeTobool + HH:MMВікно часу доби
scheduleTime + startHour/endHourbool + int 0-23Вікно за годинами
isRepeatable + repeatCountbool + intПовтори групи
timeoutint sec0 = немає
activeSessionintПаралельність (0 = unlimited)
waitForOtherTaskCompletionboolЧекати інші групи
folderIdint/nullUI-папка

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, workingstopWithError; браузери не закриває.

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:

ПолеТипОбов.Опис
accountIdintaccount.id
scriptIdint/stringСкрипт
additionalDataobjectForm-поля скрипта (критично, якщо є форма)
executeAtISODefault now
tagstringМітка
sortintПорядок

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 / idsint / [int]Один / bulk account.id
accountId / accountIdsUUID / [UUID]Один / bulk UUID
pathstringФайл (.json) або папка

Поведінка: single без pathdata.cookies; single + .json → один файл; bulk/папка → cookie_{accountName}_{ts}.json. Потрібна розблокована сесія (інакше 500 Cookie key not available).

CDP-міграція (пряме підключення). Окремого ендпоінта імпорту через CDP HTTP API не має; міграцію роблять так: POST /api/profiles/start → взяти wsEndpoint → під'єднатися Puppeteer/Playwright:

js

Далі 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..." }.

bash

Для пошуку елементів/тексту/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Ресурс не знайдено / браузер не запущено
500DB / внутрішня помилка

І один підступний момент, який легко проґавити: частина бізнес-помилок прилітає з 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.

bash

Моніторинг: GET /api/task-groups/get?id=7GET /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.

Пов'язані терміни глосарія