Підключення організації: шлях
Це наскрізний маршрут партнера від «хочемо, щоб у наших користувачів був агент» до робочого чату з інструментами. Тут названо всі екрани, ключі та виклики в тому порядку, в якому вони потрібні. Детальні контракти на сусідніх сторінках: 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):
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-сервер, і передайте його:
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/list | POST …/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. Код у браузері не бачить ані ключа, ані гранту.
- Синхронізуйте членство за кожної зміни ролі користувача у вашому просторі:
PUT /v1/orgs/{orgId}/chat/membershipsзexternalUserId,externalTeamId,role,active,sourceRevision,validUntil. Відкликані учасники одразу втрачають права чату та provisioning. - Відкрийте бесіду на користувача та агента:
POST /v1/orgs/{orgId}/chat/conversations{externalUserId, externalTeamId, agentId}. У відповіді єmcp: {serverId, destination}, якщо в агента є MCP-сервер. - Прив’яжіть довірений контекст до першого повідомлення (і знову до закінчення): випустіть у себе короткоживучий грант для цієї людини та бесіди, потім
PUT …/conversations/{id}/contextзserverId,destination(саме ті, що повернула бесіда),grant,expiresAt(не більше п’яти хвилин уперед) таexpectedVersion. Agentfy зберігає грант зашифрованим і пред’являє його вашому MCP якX-SD-Context.GET …/contextчитає версію та строк без гранту. - Надішліть повідомлення:
POST …/conversations/{id}/messages{externalUserId, externalTeamId, message}зIdempotency-Key→202 {runId, status: accepted}. - Читайте ран потоком:
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 обирається перша дозволена модель.
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, команди чи суб’єкта зніміть агента з обліку та створіть заново:
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_ENVIRONMENTAPI (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 у репозиторії.