Предлагаемый контракт provisioning и оплаты
AGNT2-532: Бесплатно. Оплата пока не подключена. Для создания, активации и подключения организации не нужны договор, договорный лимит, карта или адреса возврата. Использование не создаёт задолженности или будущих платёжных обязательств. Технические лимиты, утверждённые политики, права операторов и явная активация платформой сохраняются. Будущая оплата подключается отдельно и явно; бесплатный период не оплачивается задним числом.
Исторический проект
Эта страница фиксирует прежний проект, а не текущий статус выпуска. Подготовка ресурсов, scoped authorization и trusted MCP уже реализованы в коммитах Agentfy; см. контракт серверного чата и границы доказательств. Браузерные/BONSITE протоколы и более широкие предложения по оплате/жизненному циклу сохраняют свои условия выпуска. Боевой запуск партнёра не подразумевается.
Атомарное создание и повторы
Параметр пути {orgId} обязан совпадать с организацией, определённой по проверенному ключу; он не выбирает другую организацию и не предоставляет к ней доступ. См. утверждённое соглашение об именовании.
Сначала проверяем сервисный ключ, из него определяем namespace/окружение; organizationId тела не является источником полномочий. До выделения ресурсов проверяем scope, subject/team mappings, профиль, тариф, лимиты и финансовое согласие. Одна транзакция БД фиксирует внешнюю идентичность/субъекта (если новый), команду, владельца, назначение плательщика, операцию и outbox. Существующий субъект используется только в своём namespace. Все локальные записи откатываются вместе; внешние вызовы агента/provider происходят после commit.
Ключ квитанции: (namespace, operationKind, idempotencyKey), независимо от ротации credential ID; реально использованный ключ записывается в аудит. Хешируем канонический версионированный запрос с нормализованными defaults, без транспортных заголовков. Тот же ключ/хеш возвращает ту же операцию, включая pending/failed; другой хеш — 409 idempotency_conflict. Рекомендованный срок квитанции 30 дней; постоянная привязка внешнего ID и исходный bootstrap fingerprint запрещают дубликаты после истечения. Потерянный ответ восстанавливается повтором или lookup операции.
Параллельные разные ключи сериализуются уникальностью namespace/team ID. Одинаковый fingerprint возвращает исходную операцию; другой создатель/профиль/плательщик даёт 409 external_id_conflict. Bootstrap не восстанавливает удалённое членство, не меняет плательщика и не активирует удалённую/приостановленную команду. Изменения — отдельные команды с ожидаемой версией. Чужая или отсутствующая ссылка даёт нераскрывающий 404, без данных прямого аккаунта.
Outbox использует idempotency operation + step, lease захвата и безопасные повторы после сбоя. Для агента постоянная уникальность team/external-agent. Сохраняем pending, ready, failed, шаг, безопасную причину и retryability; ready только после реальной подготовки агента. Неудачная подготовка не оставляет платную автоматику: запуск требует readiness, плательщика и бюджета. Provider setup имеет свой idempotency reference и reconciliation, не распределённую транзакцию и не повторный платёж.
Конкретные предлагаемые запросы
POST /v1/orgs/{orgId}/provisionings
Authorization: Bearer <provisioning-key>
Idempotency-Key: workspace-77-v1
Content-Type: application/json
{
"subject": { "externalUserId": "opaque-person-42" },
"team": { "externalId": "workspace-77", "name": "Workspace 77" },
"agent": {"externalId": "assistant", "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."}}},
"payer": { "mode": "organization" }
}{
"success": true,
"data": {
"operationId": "operation-7",
"userId": "user-partner-42",
"teamId": "team-77",
"status": "pending",
"billingStatus": "active",
"version": 1
}
}Ответ содержит только ID партнёрского субъекта, никогда linked direct ID/email/payment reference. Первое принятие — 202; повтор — та же операция, 202 пока pending и 200 в конечном состоянии. GET /v1/orgs/{orgId}/operations/operation-7 возвращает состояние и стабильные ID; ошибка — безопасные reason, retryable, correlation ID без секретов. Повторяемый сбой продолжает ту же операцию, неповторяемый конфликт требует явного исправления.
Для оплаты командой: {"mode":"team","consentId":"consent-9"} либо {"mode":"team"}. Одноразовый consent связан с namespace, внешним ID команды, условиями и независимо вошедшим прямым плательщиком; потребляется атомарно. Без него ответ billingStatus: "pending_acceptance": создатель записан как предполагаемый ответственный, бесплатные возможности сохранены, платная работа запрещена. Намерение не считается принятым личным мандатом. Действующее назначение — team-paid/pending; последующее согласие активирует его BillingAccount без новой команды и задним числом возникшего долга.
POST /v1/orgs/{orgId}/browser-authorizations
Authorization: Bearer <separate-identity-issuer-key>
Content-Type: application/json
{
"transactionId": "browser-transaction-8",
"externalUserId": "opaque-person-42",
"teamId": "team-77",
"nonce": "transaction-bound-nonce",
"purpose": "login"
}Дополнительно проверяются backchannel proof и browser-транзакция. Ответ — одноразовый callback code и уже зарегистрированный redirect, не access token. Произвольный redirect или прямую/глобальную сессию запросить нельзя. Redemption привязан к браузеру Agentfy, это не универсальный token endpoint.
| Предлагаемый endpoint | Полномочия и проверки |
|---|---|
POST /v1/orgs/{orgId}/provisionings | provision:create; свой namespace, профиль, согласованное покрытие/лимит организации; без browser scope |
GET /v1/orgs/{orgId}/operations/:id | provision:read; свой namespace, безопасный ответ |
POST /v1/orgs/{orgId}/operations/:id/retry | provision:create; retryable шаг, та же операция, текущее состояние/версия |
POST /v1/orgs/{orgId}/browser-authorizations | Отдельный identity:authorize; активные browser-транзакция, subject, membership |
POST /v1/orgs/{orgId}/subjects/:id/suspend | identity:manage; свой subject, отзыв контекстов/агентов |
POST /v1/teams/:id/invitations | Членство owner/admin, политика цели/роли, версия команды |
POST /invitations/:id/accept | Свежий proof точного получателя, одноразовость, актуальные права пригласившего |
POST /account-links / DELETE /account-links/:id | Независимый прямой вход; создание также требует свежего partner proof, согласия и блокировки link |
POST /contexts | Прямой proof + активная связь + политика/членство; без повышения partner→direct |
POST /v1/teams/:id/owner-transfers | Активный owner, свежий proof, получатель и будущая роль |
POST /owner-transfers/:id/accept | Указанный активный участник, свежий proof, версионированная транзакция |
POST /v1/teams/:id/payer-changes | Запрос owner или делегированного org billing; запрос не является согласием |
POST /payer-changes/:id/accept | Новый прямой плательщик входит независимо либо org billing принимает за организацию; provider готов |
PATCH /billing-accounts/:id/contact | Финансовые полномочия аккаунта, проверка контакта по необходимости; ответственность не меняется |
Мутация имеет CSRF-безопасный browser path либо scoped server path, idempotency и expected version при изменении состояния. Неизвестные scope запрещены. Provisioning-ключ не вызывает связь, credentials, личную оплату или platform admin; браузер не получает серверные секреты.
Платить и владеть — разные действия
Оплата → Кто платит показывает организацию, ответственного, контакт, готовность способа оплаты, даты, ожидающий запрос, исторический долг и бесплатное/покрытое состояние. Изменить контакт меняет уведомления, не ответственность и не payment method. Передать оплату называет нового плательщика и cutoff: «Предыдущие счета остаются у прежнего плательщика».
Новый плательщик выбирает Проверить и принять, входит независимо, видит команду/организацию, валюту, тариф/usage terms, бюджет, дату и разрешает provider mandate. Членство необязательно; финансовое приглашение показывает условия, не содержимое команды. Прежний плательщик сохраняет доступ только к своим счетам даже после удаления из команды. Рекомендованный срок приглашения 7 дней. Отказ/истечение/отмена сохраняет старое назначение. Принятие не даёт ролей. Partner key не может согласиться за прямой аккаунт, даже если это владелец.
По умолчанию переход — со следующего расчётного периода. После готовности provider и проверки актуальных условий создаётся запланированное назначение; на активации согласие/мандат проверяются снова. Блокируем billing timeline команды и сравниваем версию; один ожидающий переход, замена явно отменяет прежний. Устаревшее/конкурентное согласие получает 409 и новый экран проверки. Одной транзакцией закрываем [from, cutoff) и открываем [cutoff, to) без разрыва/пересечения. Если активация не удалась, прежнее назначение сохраняется только пока действует прежний мандат/договор; иначе статус неплатёжный и платная работа запрещена. Нельзя молча продлить прекращённую ответственность.
Только будущая платная модель, не текущее бесплатное подключение: Покрывается организацией требует разрешённой team/org policy, финансовых полномочий организации, договора и бюджета. Согласованный provisioning-договор BONSITE даёт исходное согласие в пределах лимита. Администратор платформы Agentfy может разрешить внутреннее спонсирование с причиной и cost center. Оно учитывает расходы, не выручку/полученные деньги, и не выставляет счёт Agentfy. Снятие покрытия требует согласия нового плательщика либо остановки платной работы; владелец не становится плательщиком автоматически.
Счёт организации объединяет только её внешне оплачиваемые назначения с разбивкой по командам/агентам. Team-paid команды получают отдельные счета ответственным аккаунтам, исключённые из счёта организации. Условные USD 30 + 70 = 100 только если обе команды в этот период оплачены организацией. Смешанный пример: org-paid A30 + B70 и team-paid C20 дают счёт BONSITE 100 и счёт C 20; отчёт организации может показывать 120, но это не её долг. Team-paid счета остаются отдельными по командам, даже если один direct user оплачивает несколько. Ключ grouping/dedup включает mode и teamId для team mode вместе с payer/currency/period/contract. Внутреннее спонсирование не добавляет выручку. Способ оплаты и provider subscription нельзя скопировать другому юридическому плательщику. Новый мандат/подписка и согласованная отмена старой обязательны; отсутствие поддержки provider блокирует такой переход.
Usage, границы периодов и бюджеты
Начисление фиксирует team, organization, payer-assignment ID, price version, валюту и время. Позднее событие относится по доверенному серверному occurredAt и историческому назначению, не времени доставки/сегодняшнему плательщику. На cutoff интервал делится либо ограниченные резервы завершаются с каждой стороны; старая авторизация не оплачивает новый период. До активации согласуются provider period и cutoff. Старый долг, возвраты и счета остаются у исходного плательщика; исправления — новые adjustment entries, не переписывание истории.
Usage дедуплицируется по event ID, callbacks по provider/event ID. Проверить подпись, долговечно сохранить перед acknowledgement, применять монотонные переходы, сверять переставленные события с provider. Повтор не создаёт второй счёт/платёж. Цены/агрегаты — целые pico-USD; округление до minor units валюты один раз на границе счёта с сохранённой поправкой, без несогласованного смешивания валют. Usage total/спонсирование не равно полученной выручке.
Платный dispatch заранее резервирует жёсткий бюджет атомарно, консервативно для ограниченной работы; после сверяет факт. Бюджет плательщика использует продажную стоимость в валюте счёта: лимит минус проведённые начисления и открытые резервы. Внутренний предел vendor costUsdPico — отдельная защита, не сумма расхода клиента. Два запроса не тратят последний остаток одновременно. Долгая работа проверяется по ограниченным шагам; резерв освобождается после определённого завершения/reconciliation, не просто timeout, пока provider может списать. Без плательщика, мандата, покрытия или бюджета — Настройте оплату / Лимит достигнут; существующий free plan остаётся в своих пределах. Все гарантии требуют будущей реализации и проверки provider.
Передать профиль в запросе
Платформа один раз одобряет политику организации. Передавайте 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.
Нарушение политики возвращает 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. Изменение содержимого живого агента относится к отдельному процессу обновления на месте, а не к снятию с учёта. Снятие предназначено для намеренного удаления или замены.
agent.profile обязателен. Если modelId отсутствует, используется первая модель одобренной политики. Удалённые поля templateId и templateVersion, как и другие неизвестные поля, возвращают 422. Каталога сохранённого содержимого и выбора по умолчанию нет.
Во вкладке «Политика» флажок Все инструменты сохраняет workerTools: ["*"]: политика допускает все текущие и будущие инструменты воркера. Список отдельных инструментов при этом отключён. Агент по-прежнему запрашивает конкретные инструменты; сетевые адреса и пределы развёртывания проверяются отдельно.
Админка → Организации → Созданные агенты показывает все принятые редакции профиля, время и ID ключа отправителя. PUT /v1/orgs/{orgId}/operations/{operationId}/profile принимает { expectedVersion, profile } с ключом provisioning. Полный профиль ready-операции заменяется с проверкой текущей политики; ID агента, беседы и исходный fingerprint POST сохраняются. Устаревшая версия возвращает 409 OPERATION_VERSION_CONFLICT. Изменения подключения требуют повторной проверки в pending; только содержимое меняется без выхода из ready. См. шаг 8 руководства.
Шаг 1 — одобрить политику организации
В приложении откройте Организации → нужная организация → Политика. Владелец или администратор задаёт модели, хосты MCP, доверенный контекст, инструменты и адреса воркера, максимум команд, сохраняет черновик и отправляет на одобрение. Администратор платформы открывает очередь политик в разделе организаций админки, проверяет изменения и одобряет или отклоняет с причиной. Только одобренная политика разрешает подготовку; предыдущая версия действует до одобрения новой.
Ошибка policy_violation: <field> указывает разрешение, которое нужно исправить. Расширьте политику, получите одобрение и повторите операцию с её текущей версией. Адреса воркера должны входить также в RUNTIME_WORKER_EGRESS_ALLOWLIST; проверка публичных адресов сохраняется.
Ограничение MCP-доменов необязательно: редактор новой политики отправляет mcpHostsRestricted: false, поэтому публичный MCP URL в профиле агента не требует отдельного домена в политике. В дополнительных настройках включите «Разрешать только указанные MCP-домены», чтобы проверять mcpHosts; включённое ограничение с пустым списком запрещает все MCP-подключения. Существующие политики и старые API-запросы без флага сохраняют ограничение, включая пустые списки. Режим применяется после отправки/одобрения политики. Остальные требования одобрения, проверка URL, защита частных адресов и сетевые ограничения платформы и воркера сохраняются.