Skip to content

Подключение организации: путь ​

Это сквозной маршрут партнёра от «хотим, чтобы у наших пользователей был агент» до работающего чата с инструментами. Здесь названы все экраны, ключи и вызовы в том порядке, в котором они нужны. Подробные контракты на соседних страницах: provisioning, серверный чат, идентичность.

Эталонная интеграция: Store Deputy. Одна команда Store Deputy = одна команда Agentfy, один подключённый магазин = один агент, каталог магазина = MCP-сервер. Inline-профили проверены также с изолированным локальным MCP; см. отчёт задачи.

Кто есть кто ​

ТерминЧто значит
ОрганизацияПартнёр внутри Agentfy. У неё неизменяемые namespace и environment (test или production), операторы, ключи и одобренная политика.
ОператорПрямой пользователь Agentfy, который управляет организацией: owner, admin, developer, billing, support. Операторы не видят команды клиентов и их беседы.
Администратор платформыСотрудник Agentfy. Активирует организации и утверждает политику организации в админке.
Внешний акторПользователь продукта партнёра. Agentfy знает его только по externalUserId внутри namespace; у него нет пароля и прямого входа.
КомандаКоманда Agentfy, созданная под рабочее пространство партнёра (team.externalId). Ей принадлежат агенты, беседы и счета.
ПрофильЧто такое агент: имя, инструкции, skill, модель, необязательный MCP-сервер и необязательные worker-инструменты. Передаётся с запросом и фиксируется в операции.
ОперацияОдин запрос provisioning и его автомат состояний: pending → ready, либо failed, либо retired.
MCP-серверСобственный HTTP-эндпоинт партнёра, который отдаёт агенту инструменты. Agentfy ходит к нему с per-agent ключом.
Продукт партнёра              Бэкенд партнёра                        Agentfy
────────────────              ───────────────                        ───────
пользователь: «создать агента» → POST /provisionings (ключ provisioning) → операция, команда, агент
                                 PUT  …/configuration {ключ MCP}         → tools/list на вашем MCP
                                                                         ← ready
пользователь открывает чат     → POST /chat/conversations (ключ chat:*)
                                 PUT  …/context {grant}                  → доверенный MCP-контекст
                                 POST …/messages                         → ран, SSE-события
                                                                         → вызовы инструментов на ваш MCP

Шаг 1. Создать организацию ​

В приложении Организации (/orgs) → Создать организацию. Создатель становится владельцем и подтверждает эту ответственность на форме.

  • namespace и environment постоянны. Для test и production заводите отдельные организации; у них нет общих команд, ключей и профилей.
  • Бесплатно. Оплата пока не подключена. Для создания, активации и подключения организации не нужны договор, договорный лимит, карта или адреса возврата. Использование не создаёт задолженности или будущих платёжных обязательств. Технические лимиты, утверждённые политики, права операторов и явная активация платформой сохраняются. Будущая оплата подключается отдельно и явно; бесплатный период не оплачивается задним числом.
  • Операторов добавляют на том же экране. Членство оператора не связано с командами клиентов и не даёт платформенной роли.

Организация создаётся в статусе pending. Администратор платформы проверяет её в админке (/orgs) и активирует. Пока статус не active, ничего ниже не работает.

Утвердить политику организации ​

В Организации → ваша организация → Политика владелец или администратор задаёт разрешённые модели, MCP-хосты, доверенный контекст, worker-инструменты, сетевые назначения worker и максимум команд, затем сохраняет и отправляет черновик. Администратор платформы открывает очередь политик в разделе Организации в админке, проверяет diff и утверждает или отклоняет с причиной. Provisioning разрешает только утверждённая политика; пока новая версия на рассмотрении, действует предыдущая.

Шаг 2. Выпустить серверные ключи ​

Организации → Интеграция · Серверные ключи. Создайте один ключ и выберите разрешения сервера. Можно совмещать подготовку (provision:create, provision:read) и чат (chat:membership, chat:create, chat:context, chat:run, chat:read). Для отдельных сервисов можно выпускать отдельные ключи. Права чата и identity требуют владельца организации или администратора платформы и явного подтверждения; admin/developer могут выпускать ключи только с правами подготовки.

Секрет показывается один раз и хранится на сервере. По умолчанию ключ без срока действия; можно указать будущую дату без ограничения в 90 дней. Старые даты сохраняются. Замена сохраняет права и срок; старый ключ отключается сразу или после выбранного периода совместной работы (до 24 часов, не позднее его прежнего срока). Отзыв доступен всегда. Ограничения организации, namespace, окружения и статуса сохраняются.

Права identity зарезервированы: рабочих API выпуска и управления идентификацией пока нет. Старое поле kind необязательно, поддерживается для совместимости и не даёт прав.

Шаг 3. Передать профиль в запросе ​

Платформа один раз одобряет политику организации. Передавайте agent.profile в запросе создания: {name, instructions, skill: {name, description, body}, modelId?, mcp?: {url, requiredTools, trustedContext?}, worker?: {tools, egress?}}. Содержимое в рамках политики не требует отдельного одобрения. Без mcp агент только разговаривает.

requiredTools — обязательный минимум, а не список разрешённых инструментов: доступны все обнаруженные инструменты с учётом прав вызывающего и правил совпадающих имён. Каждый успешный PUT …/profile перечитывает неизменённое MCP-подключение после сохранения профиля; изменение подключения по-прежнему обрабатывает worker настройки. Изменение только requiredTools также запускает перечитывание, не останавливая работающего агента.

Для всех подключённых MCP-серверов, включая добавленные вручную, первый допустимый ход после MCP_TOOLS_REFRESH_SECONDS (по умолчанию 300 секунд) запускает фоновое обновление. Этот ход сохраняет прежний список; новые инструменты доступны со следующего хода после завершения обновления. У неактивного агента опроса нет. После ошибки повтор происходит через тот же интервал; сохранение профиля или кнопка обновления не ждут истечения срока. Фоновое обнаружение ограничено 5 секундами на HTTP-обмен и не задерживает начавший его ход.

Метаданные с переданным секретом и список без обязательного инструмента отклоняются. Последний успешный список и время его обнаружения сохраняются; lastError виден в настройках MCP агента и очищается после успешного обновления. Клиент не держит соединение между запросами и не подписывается на notifications/tools/list_changed; обновление обеспечивают сохранение профиля и проверка срока.

Модель должна входить в allowedModels, хост MCP — проходить проверку публичного адреса и входить в mcpHosts, если включён mcpHostsRestricted. Доверенный контекст требует trustedContextAllowed; инструменты и сеть воркера ограничены workerTools и workerEgress, а также потолком развёртывания. Лимиты символов: имя 120, инструкции 16 000, имя skill 64, описание 1 024, тело 32 000. MCP-инструменты: 1–64, worker-инструменты: 1–16, сетевые адреса: до 64.

agent.profile обязателен. Если modelId отсутствует, используется первая модель одобренной политики. Удалённые поля templateId и templateVersion, как и другие неизвестные поля, возвращают 422. Каталога сохранённого содержимого и выбора по умолчанию нет.

Во вкладке «Политика» флажок Все инструменты сохраняет workerTools: ["*"]: политика допускает все текущие и будущие инструменты воркера. Список отдельных инструментов при этом отключён. Агент по-прежнему запрашивает конкретные инструменты; сетевые адреса и пределы развёртывания проверяются отдельно.

Админка → Организации → Созданные агенты показывает журнал только для чтения: внешний ID, команду, субъект, статус, дату и раскрываемый зафиксированный профиль. Существующие снимки и одобренные политики не меняются. Обновление содержимого живого агента — отдельная задача AGNT2-477.

Шаг 4. Открыть свой MCP-сервер ​

Если агент должен что-то делать с вашими данными, вы поднимаете MCP-сервер по HTTP и вписываете его адрес в профиль.

  • Адрес должен быть публичным HTTPS. Agentfy отказывает loopback, частным сетям и именам .internal/.local раньше любых проверок (пол адресов) и резолвит имя, проверяя каждый адрес за ним. Локально MCP_DEV_LOOPBACK=true при NODE_ENV=dev допускает localhost по обычному HTTP.
  • Аутентификация: Agentfy шлёт Authorization: Bearer <ключ> в каждом вызове. Ключ тот, что вы передадите на шаге 5: вы его выпускаете, вы его проверяете.
  • Обнаружение: при provisioning Agentfy вызывает initialize, notifications/initialized и tools/list; каждое имя из requiredTools должно присутствовать, иначе операция падает с mcp_tools_missing.
  • Доверенный контекст (trustedContext: true): в каждом вызове инструмента Agentfy добавляет X-Agentfy-Agent-Id, X-Agentfy-Thread-Id, X-Agentfy-Turn-Id, X-Agentfy-Tool-Call-Id, X-Agentfy-Initiated-By: human и грант беседы, привязанный на шаге 6, как X-SD-Context. По ним ваш сервер решает, что этому агенту можно читать для этого человека прямо сейчас.
  • Ход с done.stopReason: completed получает completed, даже после ошибок отдельных инструментов. Только завершение из-за tool_error даёт failed / TOOL_REFUSED; max_iterations остаётся limited. Итог определяют run_status или polling запуска. Оба содержат toolErrors: [{toolCallId, name, errorCode}]; без ошибок это пустой массив. errorCode — безопасный TOOL_FAILED, без сырых аргументов и результатов. Строгий клиент может считать любой запуск с непустым toolErrors неудачным.

Шаг 5. Provisioning: команда и её агент ​

Все вызовы идут с ключом provisioning и Idempotency-Key; повтор с тем же ключом и телом возвращает ту же операцию.

Первая команда рабочего пространства (bootstrap):

http
POST /v1/orgs/{orgId}/provisionings
Authorization: Bearer <ключ provisioning>
Idempotency-Key: workspace-77-v1

{
  "subject": { "externalUserId": "person-42" },
  "team":    { "externalId": "workspace-77", "name": "Workspace 77" },
  "agent": {
  "externalId": "store-9",
  "profile": {
    "name": "Store assistant",
    "instructions": "Help the authorized user with the store.",
    "skill": {
      "name": "store-help",
      "description": "Store assistance",
      "body": "Use the attached MCP for authorized store requests."
    },
    "modelId": "<allowed model>",
    "mcp": {
      "url": "https://mcp.example.com/mcp",
      "requiredTools": [
        "store_describe"
      ]
    }
  }
},
  "payer":   { "mode": "organization" }
}

subject становится владельцем команды. Ответ (202, пока pending) содержит operationId, userId (id внешнего актора в Agentfy), teamId, agentId, status, step, reason, retryable, version и configurationVersion. Сохраните id операции, команды и агента: других ручек у вас нет.

Ещё один агент в существующей команде: POST /v1/orgs/{orgId}/teams/{teamId}/agents/provisionings с subject (он уже должен быть owner или admin этой команды, см. членства на шаге 6) и agent.

Передать MCP-ключ. С MCP-профилем операция останавливается на pending / configuration_required. Выпустите ключ, который примет ваш MCP-сервер, и передайте его:

http
PUT /v1/orgs/{orgId}/operations/{operationId}/configuration
{ "expectedVersion": 1, "mcp": { "credential": "<секрет, который проверяет ваш MCP>" } }

Agentfy хранит его зашифрованным как секрет агента и никогда не возвращает. Ротация позже, это тот же вызов с текущим expectedVersion.

Дождаться готовности. Опрашивайте GET /v1/orgs/{orgId}/operations/{operationId}. Шаги идут profile → mcp_discovery → complete; status: ready значит, что агент активен со своим skill, MCP-сервером и обнаруженными инструментами.

reasonЧто значитПовтор
configuration_requiredждёт ключ из PUT вышепосле PUT
policy_violation: <field>профиль выходит за утверждённую политику организации (или утверждённой политики нет)расширить и утвердить политику, затем повторить ту же операцию с текущим expectedVersion
model_unavailableмодели профиля нет в каталоге или она выше планапосле правки настроек платформы
egress_refusedадрес MCP в частной сети или вне платформенной политики MCP-хостовнет, правьте профиль
mcp_discovery_failedваш MCP не ответил на initialize/tools/listPOST …/retry { "expectedVersion" }
mcp_tools_missingв tools/list нет части requiredToolsпосле правки вашего сервера
setup_profile_missingоперация создана до появления профилейнет

Нарушение политики возвращает HTTP 422 до создания ресурсов: code: policy_violation, message: policy_violation: <field>. Поля: policy, modelId, mcpHosts, mcp.url, trustedContextAllowed, workerTools, workerEgress. Исправьте профиль или получите одобрение изменения политики и отправьте запрос снова. Каталог моделей и план команды проверяются при подготовке.

Тот же agent.profile принимает ручка дополнительного агента; в ней нет team и payer. Без MCP не нужны конфигурация и обнаружение инструментов. С MCP передайте ключ ниже и дождитесь обнаружения.

Принятое содержимое замораживается в снимке операции и fingerprint запроса. Повтор сохраняет снимок, даже если политика изменилась. Другое содержимое с тем же ключом даёт IDEMPOTENCY_CONFLICT, с той же живой внешней резервацией — EXTERNAL_ID_CONFLICT. Изменение содержимого живого агента относится к отдельному процессу обновления на месте, а не к снятию с учёта. Снятие предназначено для намеренного удаления или замены.

Шаг 6. Чат через ваш бэкенд ​

Чат проверяет конкретные права chat:* для каждой операции; можно использовать тот же ключ, что и для подготовки. Интерфейс вашего продукта говорит с вашим бэкендом, бэкенд говорит с Agentfy. Код в браузере не видит ни ключа, ни гранта.

  1. Синхронизируйте членство при каждом изменении роли пользователя в вашем пространстве: PUT /v1/orgs/{orgId}/chat/memberships с externalUserId, externalTeamId, role, active, sourceRevision, validUntil. Отозванные участники сразу теряют права чата и provisioning.
  2. Откройте беседу на пользователя и агента: POST /v1/orgs/{orgId}/chat/conversations {externalUserId, externalTeamId, agentId}. В ответе есть mcp: {serverId, destination}, если у агента есть MCP-сервер.
  3. Привяжите доверенный контекст до первого сообщения (и снова до истечения): выпустите у себя короткоживущий грант для этого человека и беседы, затем PUT …/conversations/{id}/context с serverId, destination (ровно те, что вернула беседа), grant, expiresAt (не больше пяти минут вперёд) и expectedVersion. Agentfy хранит грант зашифрованным и предъявляет его вашему MCP как X-SD-Context. GET …/context читает версию и срок без гранта.
  4. Отправьте сообщение: POST …/conversations/{id}/messages {externalUserId, externalTeamId, message} с Idempotency-Key → 202 {runId, status: accepted}.
  5. Читайте ран потоком: GET …/runs/{runId}/events, это SSE с событиями turn, text (дельты), tool_call, tool_result {ok}, done и итоговым run_status {status, reason, toolErrors, timings}. Last-Event-ID возобновляет поток. GET …/runs/{runId} даёт ту же истину без потока; GET …/messages даёт сохранённую историю. thinking скрыт и не передаётся. Имена и поля JSON описаны в контракте событий. Без Last-Event-ID или с {runId}:0 возвращается весь запуск; {runId}:N возвращает хвост после N, в том числе после завершения.

Ход с done.stopReason: completed получает completed, даже после ошибок отдельных инструментов. Только завершение из-за tool_error даёт failed / TOOL_REFUSED; max_iterations остаётся limited. Итог определяют run_status или polling запуска. Оба содержат toolErrors: [{toolCallId, name, errorCode}]; без ошибок это пустой массив. errorCode — безопасный TOOL_FAILED, без сырых аргументов и результатов. Строгий клиент может считать любой запуск с непустым toolErrors неудачным.

Шаг 7. Дать агенту руки: worker-инструменты ​

С worker.tools в профиле в рамках политики Agentfy по требованию поднимает агенту изолированный под воркера и предлагает перечисленные инструменты (exec выполняет команды в рабочей директории, file читает и пишет её, http и web_fetch ходят в сеть, browser* управляют браузером). Воркер останавливается через idleSeconds без вызовов.

Сетевые инструменты ограничены дважды: потолком инсталляции (RUNTIME_WORKER_EGRESS_ALLOWLIST) и собственным запросом агента. Пока профиль не несёт worker.egress (AGNT2-472), у партнёрских агентов запрос пуст: http и web_fetch отказывают любому адресу, exec при этом работает.

Политика организации также ограничивает сетевые назначения worker: запрошенные адреса должны укладываться и в неё, и в RUNTIME_WORKER_EGRESS_ALLOWLIST. Утверждение политики не отменяет проверку публичности адресов. Сужение политики ограничивает новый или повторный provisioning, но не отзывает уже готовых агентов.

Шаг 8. Обновить содержимое на месте или удалить агента ​

Обновить содержимое на месте ​

Используйте ключ provisioning и текущую version из GET …/operations/{operationId}. Передайте полный inline-профиль с теми же полями и проверкой политики, что в шаге 5. Пропущенные необязательные поля удаляются; без modelId выбирается первая разрешённая модель.

http
PUT /v1/orgs/{orgId}/operations/{operationId}/profile
Authorization: Bearer <provisioning-key>
Content-Type: application/json

{
  "expectedVersion": 3,
  "profile": {
    "name": "Store assistant",
    "instructions": "Use the revised store procedures.",
    "skill": { "name": "store-guide", "description": "Store procedures", "body": "Follow the revised guide." }
  }
}

Инструкции, имя, навык, модель и права воркера меняются у того же агента. Обновление только содержимого сохраняет ready без discovery. Новый MCP URL или набор обязательных инструментов переводит операцию в pending; discovery использует сохранённый ключ и завершает её в ready либо failed с существующей причиной и признаком retryable. Добавление MCP без сохранённого ключа ожидает configuration. Исправьте удалённый endpoint и вызовите POST …/retry с актуальным expectedVersion; перед следующим PUT профиля операция должна вернуться в ready. Удаление MCP из профиля отключает привязку.

После изменения MCP URL дождитесь ready, затем заново привяжите контекст каждой существующей беседы до следующего сообщения. Гранты контекста привязаны к (serverId, destination) и не переносятся на новый URL. С ключом chat:* и теми же полями пользователя выполните GET …/conversations/{id}/context, чтобы получить текущую версию для нового адреса (0, если привязки ещё нет), затем PUT по тому же пути: expectedVersion из GET, прежний serverId, новый канонический destination, свежий грант, разрешённый для этого адреса, и expiresAt. Не используйте версию старого адреса. Поля запроса описаны в шаге 6.

Устаревшая версия возвращает 409 OPERATION_VERSION_CONFLICT, нарушение политики — 422. Обновляются только ready-операции; нужно право provision:create, даже если у ключа есть права чата. ID агента, команда, субъект и беседы сохраняются: текущий ход заканчивается с загруженными инструкциями, следующий получает новые. Повтор исходного POST использует исходный fingerprint. Admin → организация → Созданные агенты показывает все редакции, время и ключ отправителя.

Удалить агента ​

Для намеренного удаления или изменения externalId, команды либо субъекта снимите агента с учёта и создайте заново:

http
DELETE /v1/orgs/{orgId}/teams/{teamId}/agents/{externalAgentId}
Authorization: Bearer <ключ provisioning>

204 архивирует агента, отзывает его секреты и MCP-привязку, закрывает беседы и ставит tombstone на резервацию; старая операция читается как retired. Тот же externalId можно провизионировать снова: он получит нового агента, новую операцию и выбранный вами профиль. Вызов идемпотентен; неизвестные id отвечают непрозрачным 404. Считайте 404 успехом только после того, как GET …/operations/{id} подтвердил retired.

Окружения и локальная разработка ​

  • Каждый ключ принадлежит одному окружению. PARTNER_API_ENVIRONMENT API (test или production) должен совпадать; он не зависит от NODE_ENV.
  • Локальный Agentfy с MCP_DEV_LOOPBACK=true в api/.env.dev принимает MCP-адреса http://localhost:… в профилях, обнаружении и контексте чата. Вне NODE_ENV=dev ничего не меняется.
  • Публичный туннель к ноутбуку (serveo, cloudflared) годится для демо против размещённого Agentfy, но новый адрес можно передать в inline-профиле, если его хост разрешён политикой. Для разработки предпочитайте loopback-переключатель.

Что должен хранить ваш бэкенд ​

ЗначениеОткуда
id организации, окружение, ключи с правами подготовки и чаташаг 2, серверная конфигурация
профиль, переданный в запросешаг 3
на пространство: externalTeamId, teamId Agentfyответ bootstrap на шаге 5
на агента: externalAgentId, operationId, agentId, ваш MCP-ключ, configurationVersionшаг 5
на пользователя: externalUserId, sourceRevision членствашаг 6
на беседу: conversationId, serverId, destination, текущий грант и версия контексташаг 6
на сообщение: ключ идемпотентности и runIdшаг 6

Store Deputy хранит всё это в OrganizationWorkspace, OrganizationProvision, OrganizationChatMember и OrganizationConversation; его слайсы agent/orgProvision и agent/orgChat, рабочий образец всего пути.

Ограничение MCP-доменов необязательно: редактор новой политики отправляет mcpHostsRestricted: false, поэтому публичный MCP URL в профиле агента не требует отдельного домена в политике. В дополнительных настройках включите «Разрешать только указанные MCP-домены», чтобы проверять mcpHosts; включённое ограничение с пустым списком запрещает все MCP-подключения. Существующие политики и старые API-запросы без флага сохраняют ограничение, включая пустые списки. Режим применяется после отправки/одобрения политики. Остальные требования одобрения, проверка URL, защита частных адресов и сетевые ограничения платформы и воркера сохраняются.

Расход организации по ключу и локальное демо ​

В форме серверных ключей выпустите analytics:read (owner/admin или администратор платформы; developer не может выдать финансовый доступ). Scope можно добавить к provisioning-ключу либо выпустить отдельный ключ. GET /v1/orgs/{orgId}/analytics?period=7d|30d|month принимает его как Bearer и возвращает ту же агрегацию, что отчёт owner/admin по JWT. Только своя организация и окружение API: чужая организация — 404, нет scope — 403, истёкший/отозванный ключ или неверное окружение — 401. JWT без изменений; /analytics/live и /analytics/year доступны только по JWT.

priceSide: "cost" — сохранённая себестоимость поставщика, не списание кредитов и не перепродажная цена. costUsdPico — точная целочисленная строка в 10⁻¹² USD. unpricedEventsExcluded: true: неоценённые события исключены из суммы, а не оценены нулём; проверяйте unpricedEvents. usage: null у ответа тоже не означает ноль.

GET /v1/orgs/{orgId}/credentials/current показывает организацию, окружение и scope текущего действующего ключа без секрета. Работает и без analytics:read; не даёт прав человека.

Локальное демо /organization: подключение и диагностика scope → сохранённая команда → агент → разговор со стоимостью и частями хода → расход и сверка. Языки ru/en, светлая/тёмная тема, телефон; ключи остаются на сервере. Настройка — demo/README.md в репозитории.