Обновлено:

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*. Алиасы нормализуются: 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 вызов/сек.

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=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:[...] }.

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. Задачи и группы задач#

Это модуль для серьезной, масштабной автоматизации. Когда один скрипт нужно прогнать на сотни аккаунтов - по расписанию, с повторами, лимитом параллельности и контролем времени, - на сцену выходят задачи и группы задач.

Логика простая и иерархичная. Группа - это контейнер с правилами: когда запускать (окно времени), сколько раз повторить, сколько браузеров держать одновременно. Задача внутри группы - конкретная пара "этот скрипт x этот аккаунт" со своим временем старта и входными данными. Рабочий цикл рекомендую такой: создать группу с 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, 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 x 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 без 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:

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=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.

Связанные термины глоссария