как опубликовать инструмент на histrio

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

карта шагов

что именно вы настраиваете

Первый шаг задаёт тип инструмента, а дальше сценарий расходится: для формы появляются настройка результата и проверка публикации, для чата остаются только шаги, связанные с диалогом и подключением сервера.

Шаг 1

Карточка

Обязательны только название, описание и категория. Здесь же выбирается interaction mode: Form или Chat.

Шаг 2

Форма / Чат

Для Form вы собираете input schema. Для Chat вы задаете context preset и callback timeout.

Шаг 3

Биллинг

Для Form доступны разовая оплата, usage-based и подписка. Для Chat остаются usage-based и подписка с token-priced метрикой.

Шаг 4

Endpoint

Секреты, auth, execute URL, callback secret и весь async callback contract, который реально ожидает backend.

Сценарий с формой

  • Шаг 1: Карточка
  • Шаг 2: Форма ввода
  • Шаг 3: Биллинг
  • Шаг 4: Endpoint
  • Шаг 5: Ответ
  • Шаг 6: Публикация с живой асинхронной проверкой

Сценарий с чатом

  • Шаг 1: Карточка
  • Шаг 2: Чат
  • Шаг 3: Биллинг
  • Шаг 4: Endpoint
  • Шаг 5: Публикация без отдельного test request

Коротко

  • Обязательные поля step 1: название, описание, категория.
  • Step 2 блокируется любым пустым `key` или `label`.
  • Для Form публикация невозможна без успешного async handshake.
  • Callback URL появляется после первого сохранения draft.

подготовка

что стоит сделать до первого клика

Эти ограничения не всегда видны в UI сразу, но именно они чаще всего объясняют, почему инструмент не уходит на публикацию.

  • У аккаунта должно быть право на публикацию. Если публикация недоступна для аккаунта или достигнут лимит опубликованных инструментов, submit будет заблокирован.
  • Первое сохранение draft полезно сделать рано: только после этого платформа знает `toolId` и может сгенерировать реальный callback URL.
  • Self-service путь ориентирован на публичный HTTPS endpoint. Режим `private_service` существует, но это отдельный admin-managed сценарий, а не обычная настройка с `/dashboard/tools/new`.
  • В API-режиме редактор жёстко ведёт вас в `http_async_callback`. Синхронный HTTP контракт в этом wizard не настраивается.
  • Если версия уже находится в `in_review`, повторный submit блокируется до решения модерации.

шаг 1

карточка инструмента

Это единственный шаг, который одновременно влияет и на каталог, и на остальной wizard.

Валидность первого шага проверяется очень просто: должны быть заполнены название,описание и выбранакатегория. Подкатегории, теги и текст CTA полезны, но не блокируют переход дальше.

Самый важный контрол на этом шаге interaction mode. Если выбрать Form, вы публикуете one-shot инструмент с input schema, отдельным шагом Ответ и live handshake перед модерацией. Если выбрать Chat, wizard превращается в developer-hosted conversational runtime: step 2 меняется на контекст чата, биллинг становится token-aware, а отдельный response step исчезает.

Что здесь редактируется

  • Название и описание для каталога.
  • Категория и подкатегории для фильтров и карточки.
  • Теги (`skills`) для дополнительного поиска.
  • Необязательный `customCtaLabel` для кнопки запуска.

Что видно только у Form

  • Примеры результата: изображения и видео для публичной карточки.
  • Инструкция после покупки: отдельное сообщение с тем, что делать клиенту после оплаты.
  • Эти поля не блокируют step 1, но помогают продаже и онбордингу.

шаг 2

форма ввода или chat runtime

Здесь wizard расходится сильнее всего. Для Form этот шаг строит input schema, для Chat задаёт правила контекста и timeout callback.

Form: что делает шаг валидным

  • Есть хотя бы одно поле.
  • У всех полей и вложенных полей заполнены `label` и `key`.
  • Нет повторяющихся `key`, включая repeatable group.
  • Скрытые служебные поля заполнены так, чтобы реально могли попасть в payload.

Form: типы полей

  • Text: одна строка текста
  • Textarea: длинный бриф или prompt
  • Number: числовой ввод
  • Boolean: да / нет
  • Select / Radio / Multiselect: варианты выбора
  • File / Document / Image / Audio: файлы и медиа
  • Date / URL / JSON / Secret: специальные типы
  • Section: информационный блок без ввода
  • Repeatable group: массив вложенных объектов

Form: важные свойства каждого поля

  • У каждого поля должны быть заполнены `label` и `key`.
  • `key` должен быть уникален не только среди верхнеуровневых полей, но и внутри вложенных repeatable group.
  • Если поле помечено как `hidden`, оно не показывается клиенту, но уходит в payload автоматически.
  • Для скрытого поля нужен `defaultValue`, иначе схема технически неполная.
  • Опции можно задавать по строкам: `value`, `value => Label` или `1:1`.
  • Repeatable group полезен для массивов референсов, контактов, файлов и любых списков объектов.

пример валидной input schema

показать
[
  {
    "key": "prompt",
    "label": "Что нужно сделать",
    "type": "textarea",
    "required": true,
    "placeholder": "Опишите задачу и желаемый результат"
  },
  {
    "key": "ratio",
    "label": "Формат",
    "type": "select",
    "options": [
      { "value": "1:1", "label": "1:1" },
      { "value": "16:9", "label": "16:9" }
    ]
  },
  {
    "key": "model",
    "label": "Модель",
    "type": "select",
    "hidden": true,
    "defaultValue": "gpt-image-1.5",
    "options": [
      { "value": "gpt-image-1.5", "label": "GPT Image 1.5" }
    ]
  },
  {
    "key": "references",
    "label": "Референсы",
    "type": "repeatable_group",
    "minItems": 0,
    "maxItems": 5,
    "fields": [
      {
        "key": "url",
        "label": "Ссылка",
        "type": "url",
        "required": true
      },
      {
        "key": "note",
        "label": "Комментарий",
        "type": "text"
      }
    ]
  }
]

Chat: context mode

  • `message_only`: только текущее сообщение пользователя, лимит 32 KB.
  • `recent_window`: текущее сообщение плюс до 20 предыдущих committed сообщений, лимит 128 KB.
  • `full_session_truncated`: до 100 committed сообщений и лимит 256 KB; если история не влезает, платформа отрежет самые старые сообщения.
  • В outbound context попадают только committed user/assistant messages. Pending, failed и служебные состояния не отправляются.
  • Callback timeout для developer-hosted chat clamp-ится в диапазон 30–3600 секунд; дефолт 300.

Chat: что реально получает backend

  • Committed transcript по выбранному preset.
  • Текущее сообщение пользователя и metadata по вложениям.
  • Callback deadline, чтобы backend понимал, сколько времени осталось.
  • При token-priced биллинге ещё и billing context с текущими ставками и authorized ceiling.

шаг 3

биллинг

Здесь важно понимать не только labels в UI, но и кодовую логику `isPrimaryPlanBillingConfigured`, потому что именно она решает, готов ли шаг к публикации.

Для Form-инструментов primary offer считается готовым, если у него есть валидная цена для выбранной модели. Для usage_based достаточно unitPrice или displayPrice, а если включена динамическая цена, наличие базового pricing rule уже делает модель валидной. Для one_time_purchase нужна фиксированная displayPrice. Для subscription нужны цена и период.

Разовая оплата

Клиент платит один раз. В access rules можно продавать либо один запуск, либо постоянный доступ к инструменту.

Usage-based

Цена может быть фиксированной за единицу или динамической: базовая стоимость плюс множители по полям формы.

Подписка

Нужны цена периода и период. Доступ управляется как subscription entitlement, а автопродление задается прямо в offer.

Dynamic pricing у Form

  • Работает только для `usage_based`.
  • База: `unitPrice` как цена за единицу.
  • Дальше можно умножать цену по visible полям формы.
  • Практически полезны поля типов `number`, `select`, `radio`, `checkbox`, `toggle`.
  • Публичный label цены может быть автоматическим или переопределенным вручную.

Пакеты

  • Пакеты продают заранее оплаченный объём кредитов.
  • Они появляются только после того, как у offer уже есть сохранённый id.
  • Пакет нельзя построить без хотя бы одной сохранённой разовой или usage-based модели оплаты.

Chat: usage-based

  • Разовая покупка у chat tool не поддерживается: wizard принудительно оставляет usage-based или subscription.
  • Для готовности нужны `inputPricePer1M`, `outputPricePer1M` и `maxAttemptCharge`.
  • Эти ставки участвуют в funding authorization до dispatch и в финальном расчете после callback.

Chat: subscription

  • Нужны цена периода и сам период.
  • Дополнительно нужны `includedBudgetPerPeriod`, `inputPricePer1M`, `outputPricePer1M`, `maxAttemptCharge`.
  • Если в callback не придёт usageReport с `inputTokens` и `outputTokens`, token-priced chat billing не сможет посчитать итог.

шаг 4

endpoint, auth и callback contract

Это самый технический шаг wizard. Здесь чаще всего ломается публикация: неправильный execute URL, невыбранный callback secret, неверный handshake или неподписанный callback.

Секреты и auth

В верхней части шага вы храните API secrets, которые платформа потом подставляет в запросы к вашему backend. Клиент эти значения не видит. Ниже выбирается способ авторизации:

  • `none`: без авторизации.
  • `bearer_token`: платформа отправляет `Authorization: Bearer <secret>`.
  • `header_secret`: платформа кладет секрет в ваш кастомный header, например `X-API-Key`.
  • `query_token`: секрет добавляется в query string по заданному имени параметра.
  • `basic_auth`: username и password хранятся как два отдельных секрета и уходят в `Authorization: Basic ...`.

Для обычного developer-hosted сценария нужен публичный https://... execute URL. В production backend отдельно валидирует, что и executeUrl, и callbackUrl используют HTTPS и публичный host. Исключение только для admin-managed private_service, который живет вне этого self-service гайда.

Callback URL платформа генерирует сама на основе toolId, поэтому в wizard вы начинаете видеть его только после первого сохранения черновика. Для Form и Chat это разные маршруты:

автогенерируемые callback endpoints

показать
Form tool:
https://histrio.ru/backend/tool-executions/callbacks/:toolId

Chat tool:
https://histrio.ru/backend/tool-chat-executions/callbacks/:toolId

что wizard отправляет в execute endpoint при test request или реальном form-run

показать
{
  "toolOrderId": "4dc6f4c6-1ec2-4c7e-b8fd-1f2bc5f94211",
  "toolId": "tool_123",
  "toolOfferId": "offer_123",
  "endUserRef": "test_end_user_ref",
  "input": {
    "prompt": "Нарисуй упаковку молока на белом фоне",
    "ratio": "1:1"
  },
  "callbackUrl": "https://histrio.ru/backend/tool-executions/callbacks/tool_123",
  "testMode": true
}

Какие headers платформа добавляет

  • `content-type: application/json` всегда.
  • `x-tool-id: <toolId>` всегда.
  • `x-tool-test-request: true` во время live проверки перед публикацией.
  • Плюс всё, что вы задали через auth: bearer, custom header, query token или basic auth.

Для успешного live handshake async endpoint не должен возвращать финальный результат сразу. При включенном `requireExplicitAsyncHandshake` backend ждёт JSON-объект со статусом queued или awaiting_callback. Если вернуть `result` сразу в ответе, wizard сочтет интеграцию невалидной.

минимальный успешный handshake response

показать
{
  "status": "queued",
  "jobId": "job_123"
}

подпись callback в x-tool-signature

показать
import crypto from "node:crypto";

const rawBody = JSON.stringify(payload);
const signature = crypto
  .createHmac("sha256", CALLBACK_SECRET)
  .update(rawBody)
  .digest("hex");

await fetch(callbackUrl, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-tool-signature": signature,
  },
  body: rawBody,
});

form callback: success

показать
{
  "toolOrderId": "order_123",
  "jobId": "job_123",
  "status": "completed",
  "result": {
    "imageUrl": "https://cdn.example.com/result.png"
  },
  "artifacts": [
    {
      "label": "Rendered image",
      "url": "https://cdn.example.com/result.png",
      "mimeType": "image/png",
      "sizeBytes": 2048
    }
  ]
}

form callback: failure

показать
{
  "toolOrderId": "order_123",
  "status": "failed",
  "error": "Provider timeout"
}

Form callback: на что смотрит backend

  • `toolOrderId` обязателен.
  • Успешный callback должен нести объект `result` или объект целиком, если `result` не выделен отдельным ключом.
  • Статус по умолчанию считается `completed`, но допустимы только `completed` и `failed`.
  • Callback принимается только пока order находится в `awaiting_developer_callback`.
  • Для callback artifacts нужны публичные HTTPS URL.
  • Безопасные типы сводятся к изображениям, аудио, видео и документам; опасные mime types backend отвергнет.
  • Даже безопасные файлы могут уйти в quarantine или rejection, если не проходят scanner / acceptance checks.

chat dispatch request к developer backend

показать
{
  "toolId": "tool_chat_123",
  "sessionId": "session_123",
  "attemptId": "attempt_123",
  "userMessageId": "user_msg_123",
  "assistantMessageId": "assistant_msg_pending_123",
  "endUserRef": "tool_chat_123:user_123",
  "callbackUrl": "https://histrio.ru/backend/tool-chat-executions/callbacks/tool_chat_123",
  "callbackDeadlineAt": "2026-04-07T10:08:00.000Z",
  "contextPreset": "recent_window",
  "context": {
    "preset": "recent_window",
    "messages": [
      { "role": "user", "text": "older completed user" },
      { "role": "assistant", "text": "older completed assistant" },
      { "role": "user", "text": "Current request" }
    ]
  },
  "currentUserMessage": {
    "id": "user_msg_123",
    "text": "Current request"
  },
  "assistantMessage": {
    "id": "assistant_msg_pending_123",
    "status": "pending"
  },
  "billing": {
    "billingModel": "usage_based",
    "currency": "RUB",
    "inputPricePer1M": "17.9",
    "outputPricePer1M": "111.87",
    "authorizedCeilingAmount": "25"
  }
}

chat callback: success

показать
{
  "sessionId": "session_123",
  "attemptId": "attempt_123",
  "userMessageId": "user_msg_123",
  "status": "completed",
  "assistantText": "Готово. Ниже результат и вложения.",
  "artifacts": [
    {
      "url": "https://example.com/out.png",
      "mimeType": "image/png",
      "sizeBytes": 2048
    }
  ],
  "usageReport": {
    "inputTokens": 120,
    "outputTokens": 45,
    "totalTokens": 165,
    "provider": "openai",
    "model": "gpt-4.1-mini"
  }
}

Chat callback: ограничения

  • `sessionId`, `attemptId` и `userMessageId` обязательны.
  • Успешный callback обязан содержать `assistantText`.
  • Допустимые статусы только `completed` и `failed`.
  • Если tool использует token-priced billing, в `usageReport` должны быть `inputTokens` и `outputTokens`.
  • В одном chat callback можно прислать максимум 5 artifacts.
  • Поддерживаются только `image/jpeg`, `image/png`, `image/webp`, `application/pdf`, `text/plain`, `text/csv`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document`.
  • Размерные лимиты разные: изображения до 10 MB, документы до 25 MB, общий callback budget до 50 MB.
  • Chat backend обязан прислать `assistantText` при успешном callback; только artifacts без текста считаются невалидным callback.

Advanced settings

  • `healthcheckUrl` опционален и полезен, если вы хотите быстро отсечь мёртвый runtime до основной нагрузки.
  • `timeoutMs` и `retryCount` сохраняются в execution config. Для live test редактор поднимает timeout минимум до 120000 ms.
  • В UI это звучит как 'оставьте по умолчанию, если не уверены' — и это хороший совет. Чаще всего достаточно дефолтных 120000 ms и 1 retry.

шаг 5/6

ответ, live handshake и отправка на модерацию

Финальные шаги различаются у Form и Chat, но их цель одна: показать платформе, что ваш runtime не только заполнен в UI, но и реально исполним.

Form: шаг 5 `Ответ`

  • Нужно выбрать `primaryResultType`: текст, JSON, изображение, файл или отчет.
  • Нужно выбрать `deliveryType`: inline, file, download или внешняя ссылка/доступ.
  • Можно задать `postPurchaseMessage` и ограничения, но они не блокируют готовность шага.

Form: шаг 6 `Публикация`

  • Wizard собирает test values из вашей формы и отправляет реальный HTTP request в execute endpoint.
  • Если endpoint не ответит queued/awaiting_callback, публикация останется заблокированной.
  • Если у инструмента нет visible input fields, в test request уйдут только hidden defaults.
  • Private runtime из админки — единственный сценарий, где live handshake можно не требовать.

Chat: шаг 5 `Публикация`

  • Отдельной кнопки test request нет.
  • Готовность считается по checklist: карточка, chat runtime, биллинг, execute URL, auth и callback secret.
  • Если всё заполнено и аккаунт может публиковать, инструмент можно сразу отправлять на модерацию.

финал

чеклист перед submit

Если пройти эти пункты слева направо, wizard обычно доходит до публикации без повторных кругов.

  • Выбрали правильный interaction mode на step 1 и больше не путаете Form с Chat.
  • У карточки есть понятные название, описание, категория и хотя бы минимально осмысленный CTA.
  • Все form fields имеют уникальные `key`, а hidden поля реально могут отправиться без участия пользователя.
  • Primary offer проходит правила готовности для своей billing model.
  • Execute endpoint доступен по публичному HTTPS, а auth собран из сохранённых secrets.
  • Callback secret выбран, callback URL уже сгенерирован после сохранения draft.
  • Form endpoint возвращает `queued` / `awaiting_callback`, а не финальный `result` в ответ на handshake.
  • Backend умеет подписывать raw callback body через `x-tool-signature`.
  • Для chat token-priced биллинга callback отправляет `usageReport` с `inputTokens` и `outputTokens`.
  • После live test request или publish checklist у аккаунта нет review gate: лимит публикаций не достигнут и версия не висит в `in_review`.

руководство прочитано, теперь проверь всё на реальном черновике