Как пользоваться развёрнутой системой — через браузер и через API/агентов.
granata005/outreach-platform (auto-deploy при push в main)Жёсткое ядро (state machine + валидируемые операции + модель данных + интеграционные адаптеры), которым и люди (браузер/REST), и AI-агенты (MCP/worker) управляют через один аудируемый command bus. Агент действует «суждением» (какое копи, каких лидов), но не может сломать структуру: единственный способ менять состояние — зарегистрированная команда, проходящая authN → RBAC → human-only → gate → effect → audit.
Полный бизнес-процесс кампании:
build_list → dedupe_suppress → verify_emails → submit_copy → run_qa → assign_inboxes → upload_to_smartlead → approve_launch (человек) → mark_running → pull_stats → pull_replies → score_replies → handoff
Прод работает fail-closed: аутентификация только по workspace-scoped bearer-токену (header-stub выключен).
Лежит в env-переменной Coolify BOOTSTRAP_ADMIN_TOKEN (workspace ws1, роль admin):
coolify app env get j60tka03nr5ics2cljpw0fll BOOTSTRAP_ADMIN_TOKEN -s --format json | jq -r .real_value
Дальше во всех запросах — заголовок Authorization: Bearer <token>.
Admin'ом выпускаешь scoped-токены (плейнтекст показывается один раз, хранится только хэш):
# токен для агента (роль agent)
curl -s -X POST $URL/commands/issue_agent_token \
-H "Authorization: Bearer $ADMIN" -H 'Content-Type: application/json' \
-d '{"principal_id":"openclaw-1"}'
# -> {"result":{"id":"tok-...","token":"<СЕКРЕТ — сохрани>","role":"agent",...}}
# токен для менеджера-человека
curl ... -d '{"principal_id":"manager-anna","role":"manager","is_agent":false}'
Отозвать: POST /commands/revoke_token {"token_id":"tok-..."}.
Открой консоль → кнопка Identity → вставь bearer-токен в поле «Bearer token» → Save.
Что умеет:
valid_ops), pending-этапы и audit-trail;approve_launch) помечен ⛨ и доступен только людям.(workspace, role, is_agent). Кросс-workspace доступ запрещён.admin / manager / agent / client. Каждая команда несёт список допустимых ролей.draft → list_ready → verified → copy_ready → qa_passed → uploaded → pending_approval → launched → running → done (плюс qa_failed — петля назад на copy, и paused).GET /campaigns/{id}/state отдаёт {status, valid_ops[], pending[]}: что можно сделать сейчас и что ещё впереди. Сначала смотришь его — потом выбираешь команду.Для команд mgr/agent подойдёт токен менеджера/агента; approve_launch — только человек. Helper, чтобы команды копипастились:
URL=https://outreach.apps.verticality.co
TOKEN=<твой bearer-токен>
oc() { curl -s -X POST "$URL/commands/$1" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d "$2"; echo; }
# 0. Таргетинг
oc create_offer '{"id":"o1","name":"SEO Audit","description":"Free teardown"}'
oc define_icp '{"id":"i1","name":"US dentists","filters":{"hard":["country=US","industry=dental"],"soft":["size<50"]}}'
oc approve_icp '{"icp_id":"i1"}'
# 1. Кампания (gate: ICP должен быть approved)
oc create_campaign '{"id":"c1","offer_id":"o1","icp_id":"i1"}'
# 2. База: сбор → дедуп/suppression → верификация (порог по умолчанию 0.8)
oc build_list '{"campaign_id":"c1","source":"apollo","query":{"title":"owner"},"count":50}'
oc dedupe_suppress '{"campaign_id":"c1"}'
oc verify_emails '{"campaign_id":"c1","threshold":0.8}' # -> verified, если % валидных >= порога
# 3. Копи (gate: непустые steps с subject+body) → QA
oc submit_copy '{"campaign_id":"c1","steps":[
{"n":1,"delay_days":0,"subject":"Quick question","body":"Hi {{first_name}}, ..."},
{"n":2,"delay_days":3,"subject":"Following up","body":"Bumping this..."}]}'
oc run_qa '{"campaign_id":"c1","passed":true}' # passed:false -> qa_failed (вернётся на submit_copy)
# 4. Инбоксы → загрузка в Smartlead (gate: verified+qa+inboxes) → запрос аппрува (создаёт Task человеку)
oc assign_inboxes '{"campaign_id":"c1","inbox_ids":["acc-101","acc-102"]}'
oc upload_to_smartlead '{"campaign_id":"c1","schedule":{"timezone":"US/Eastern","days_of_the_week":[1,2,3,4,5],"start_hour":"08:00","end_hour":"17:00"}}'
oc request_launch_approval '{"campaign_id":"c1"}'
# 5. ЗАПУСК — только человек
oc approve_launch '{"campaign_id":"c1"}' # human-only; агенту вернёт 403
# 6. Мониторинг и ответы
oc mark_running '{"campaign_id":"c1"}' # send_monitor: снимает первый stats-snapshot
oc pull_stats '{"campaign_id":"c1"}' # read-on-demand из Smartlead
oc score_replies '{"campaign_id":"c1"}' # классифицирует ответы -> ReplyVerdict[]
oc handoff '{"verdict_id":"c1-rv-0"}' # позитив -> в sales/CRM
oc complete_campaign '{"campaign_id":"c1"}'
INTEGRATIONS_MODE=fake — провайдеры детерминированно мокаются, реальные письма не уходят. Это догфуд/демо-режим. Боевая отправка — см. §11.Подсмотреть, что делать дальше:
curl $URL/campaigns/c1/state -H "Authorization: Bearer $TOKEN"
# {"status":"list_ready","valid_ops":["dedupe_suppress","verify_emails",...],"pending":["verify_emails","copywriting",...]}
approve_launch, provision_domains, provision_inboxes (траты денег) — агентам недоступны (403).oc из §5):
oc set_operation_guard '{"command":"verify_emails","human_only":true}'
# снять: oc set_operation_guard '{"command":"verify_emails","human_only":false}'
После этого агент не увидит команду в valid_ops и получит 403 при попытке.denied.app.mcp.server) — для OpenClaw/Claude. Токен агента — в OUTREACH_AGENT_TOKEN; инструменты: get_campaign_state, list_operations, run_operation, run_command.POST /commands/{command} — тот же gateway, те же RBAC+gate+audit.app.worker, Arq+Redis) исполняет команды из очереди вне процесса под scoped-токеном. Сейчас стадии идут синхронно in-process (worker не поднят, и так работает).Жёсткое = каталог типов этапов (данные, app/core/catalog.py, 30 типов). Гибкое = какие этапы включены и с какими параметрами.
oc create_pipeline_template '{"id":"tpl1","name":"no-dedupe","is_default":true,
"stages":[{"stage_type":"dedupe_suppress","enabled":false}]}'
Выключенный этап исчезает из pending.oc set_worker_binding '{"stage_type":"verify_emails","skill_ref":"v-verify","params":{"threshold":0.95}}'Все 30 типов каталога имеют executor. Помимо core-пути:
| Группа | Команды |
|---|---|
| Стратегия | campaign_strategy, lead_magnet |
| Квалификация списка | qualify_companies, enrich_contacts, score_list_quality |
| Инфраструктура 💰 human-only | provision_domains, provision_inboxes; check_dns_auth, start_warmup |
| Контент | personalization_research, spintax, set_ab_variants |
| QA | placement_test, pre_launch_audit |
| После отправки | snapshot_stats, pull_replies, incident_response, optimize, generate_report |
| Identity / config | create_user, create_agent_identity, add_membership, update_workspace_settings, create_integration_credential |
| Lifecycle | advance, pause_campaign, resume_campaign |
Полный список — app/core/commands.py (реестр COMMANDS) и app/core/catalog.py.
| Код | Значение |
|---|---|
| 401 | нет/невалидный токен (или header-stub выключен) |
| 403 | роль не разрешена / human-only / кросс-workspace |
| 404 | неизвестная команда |
| 409 | gate/precondition не выполнен (напр. upload без verified+qa+inboxes; пропуск этапа) |
Любая попытка (включая отказы и неизвестные команды) пишется в audit: GET /campaigns/{id}/audit.
Деплой: push в main → Coolify auto-build (Dockerfile, target web) → авто-деплой за 1–2 мин. Ручной: coolify deploy uuid j60tka03nr5ics2cljpw0fll.
Сервисы Coolify: app outreach-web (j60tka03nr5ics2cljpw0fll), БД outreach-postgres (csg3b5pp1jkvue1918f4l53q).
| Переменная | Сейчас | Назначение |
|---|---|---|
DATABASE_URL | Postgres | каноническая БД |
ALLOW_HEADER_PRINCIPAL | false | header-stub (dev). В проде держать false |
BOOTSTRAP_ADMIN_TOKEN | задан | seed admin-токена на старте (§2) |
AUTO_CREATE_SCHEMA | true | создание схемы на старте (идемпотентно) |
INTEGRATIONS_MODE | fake | fake (моки) или live (реальные API) |
SMARTLEAD_API_KEY, APOLLO_API_KEY, MILLIONVERIFIER_API_KEY (Smartlead есть; Apollo и MillionVerifier нужно получить — без них build_list/verify_emails в live упадут) → coolify app env update j60... INTEGRATIONS_MODE --value live → redeploy.Worker (опц.): второе приложение из того же репо с target worker + Redis-сервис, задать REDIS_URL. Миграции: вместо AUTO_CREATE_SCHEMA — Alembic (alembic revision --autogenerate -m baseline && alembic upgrade head).
create_offer → define_icp → approve_icp.create_campaign (с offer_id/icp_id).build_list → verify_emails (дойти до verified).submit_copy → run_qa.assign_inboxes → upload_to_smartlead.request_launch_approval → человек делает approve_launch.mark_running → периодически pull_stats / score_replies → handoff позитивов.