# Справочник коннектора

> Поля манифеста, подстановки, схемы ключа, форма кассеты, команды пакета, коды проверок и каталог глаголов — собраны из кода пакета проверки.

Справочник собран из кода пакета проверки: таблица полей здесь — та же, по которой проверка принимает ключи манифеста, а слова кодов — те же, что стоят за адресом под красной строкой. Пример целиком — [Быстрый старт](https://www.zengi.ai/developers/quickstart).

## Поля манифеста

Поля верхнего уровня манифеста объявленной среды. Ключ вне этого списка проверка отвергает (`unknown_key`), нет обязательного — `missing_key`.

- `manifest_version` · обязательное — версия формы манифеста — 1
- `connector_id` · обязательное — имя коннектора строчными словами через «_», до 64 знаков; с `version` — адрес ревизии `connector_id@version`
- `version` · обязательное — номер ревизии — целое от 1; правка содержимого — новый номер
- `author` · обязательное — автор — строка до 64 знаков
- `family` · обязательное — семейство — предмет, с которым работает коннектор (`shipment`, `booking`, `bonus`); от него зависит, где запись только глаголом платформы
- `provider` · обязательное — провайдер — служба, к API которой ходит коннектор, строчными словами через «_»
- `environment` · обязательное — среда: `declarative` — описание операциями и шагами; `code` — модуль с инструментами
- `auth` · обязательное — как ставится ключ: `scheme` (см. «Схемы ключа»), `fields` — поля кредов `{ name, secret, label? }`, `exchange` — обмен ключа на токен
- `recipients` · обязательное — хосты, к которым уходят запросы: точные имена или `*.домен`; первая запись — хост шагов без своего `host`
- `operations` · обязательное — операции: чтения (`read`) и эффекты (`effect`) с шагами запросов, привязкой к глаголу `binds` или блоком `tool`
- `contract_tests` · обязательное — кассеты — записанные ответы API: `{ cassette: "cassettes/<имя>.json", taken_at: "ГГГГ-ММ-ДД" }`
- `description` · необязательное — что коннектор делает — строка до 500 знаков
- `settings` · необязательное — несекретные настройки подключения, которые форма кабинета спросит у компании: `{ name, label, kind?, required? }`
- `probe` · необязательное — имя чтения, которым проверяется ключ при подключении; без него проверка `probe_declared` красная
- `inbound` · необязательное — точки входа — адреса, на которые служба шлёт события подключения

## Подстановки

Значение в фигурных скобках в пути, параметрах, заголовках и теле шага, в охране цены и в результате — подстановка: корень и путь через точку. Выражений нет; корень вне списка — `bad_template`. «?» в конце (`{input.comment?}`) — нет значения, ключ опускается; только целым значением.

- `input` — вход операции: `{input.<поле>}` — поля, которые глагол собирает из аргументов модели
- `key` — токен бизнес-ключа этой операции: `{key}` — короткий, для номера заявки или ключа повтора у службы
- `order` — токен ключа заказа: `{order}` — у чтений и отмены, чтобы найти созданное
- `step` — ответ прежнего шага: `{step.<id>.status}`, `{step.<id>.body.<путь>}`, `{step.<id>.headers.<имя>}`; сегмент пути в теле ответа чтения может выбрать элемент массива по полю — `{step.<id>.body.items[type=image].url}`
- `approved` — значения карточки, одобренные оператором: `{approved.<поле>}` — например, цена «не дороже одобренного»
- `item` — элемент массива внутри узла `each` результата: `{item.<путь>}` — только там
- `connection` — несекретная настройка подключения: ровно `{connection.settings.<имя>}` из `settings`, только в шагах
- `inbound` — адрес точки входа этого подключения: ровно `{inbound.<точка>.address}` из `inbound`, только в шагах

## Схемы ключа

`auth.scheme` — как дверь платформы кладёт ключ подключения в запрос. Значения ключа в манифесте нет: только поле кредов и его назначение.

- `bearer` — ключ уходит заголовком `Authorization: Bearer <ключ>`
- `header` — ключ уходит заголовком, имя которого называет `header_name`
- `query` — ключ уходит параметром запроса, имя которого называет `query_name`
- `basic` — значение поля кредов в base64 уходит заголовком `Authorization: Basic …`

Обмен ключа на токен — `auth.exchange` (вид `client_credentials`): поля кредов — ровно `client_id` и `client_secret`, токен из ответа обмена дверь ставит в `Authorization: Bearer` каждого шага.

## Кассета

Кассета — записанный контракт службы: вход операции, обмены «запрос → ответ» и ожидание. Ключ вне списка проверка отвергает. Сбои — обрыв ответа, повтор, задержку — пакет вносит сам, руками их в кассету не пишут.

- `name` · обязательное — имя сценария
- `operation` · обязательное — операция манифеста, которую проигрывает кассета
- `tenant_id` · обязательное — компания прогона — любой uuid
- `connection` · обязательное — подключение прогона: `{ id, state, endpoint? }`
- `secret` · обязательное — ключ прогона — фикстурная строка от 8 знаков (у обмена ключа на токен — объект «поле кредов → значение»); настоящий ключ не кладите
- `input` · необязательное — вход операции; нужен `input` или `args`
- `args` · необязательное — аргументы глагола: вход, бизнес-ключ и ключ заказа из них выведет сам глагол
- `now` · необязательное — момент прогона в ISO, если сценарию важно время
- `key` · необязательное — токен бизнес-ключа операции
- `order_key` · необязательное — токен ключа заказа
- `approved` · необязательное — значения, одобренные оператором (цена)
- `exchanges` · обязательное — обмены «запрос → ответ»: `{ method, path, query?, respond: { status, body?, headers? }, times? }`; `times` — сколько раз обмен выдаётся, `"*"` — без предела
- `expect` · обязательное — ожидание чистого прогона: `{ outcome: confirmed | rejected | unknown, result?, reason? }`
- `description` · необязательное — откуда пример: по документации службы или живой снимок

## Команды пакета

- `node dala-connector-kit.mjs conformance <манифест.json> [--json]` — проверить манифест; кассеты из contract_tests — пути от папки манифеста; красная строка — с адресом страницы кода
- `node dala-connector-kit.mjs bundle <манифест.json>` — напечатать тело публикации { manifest, cassettes, suite_version } (только при зелёном наборе)
- `node dala-connector-kit.mjs conformance <манифест.json> --executor <файл.mjs>` — среда code: прогнать кассеты вашим исполнителем (см. sandbox)
- `node dala-connector-kit.mjs sandbox` — напечатать песочницу шагов-кода (тот же исполнитель, что на сервере): сохраните как dala-code-sandbox.mjs, `npm i quickjs-emscripten@0.32.0`
- `node dala-connector-kit.mjs catalog` — каталог глаголов: схемы, потолок, правило «результат несёт ссылку»
- `node dala-connector-kit.mjs sample <образец>` — рабочий образец в форме тела публикации
- `node dala-connector-kit.mjs init <имя> [папка]` — шаблон пакета автора: агент, навык, знание, шаг-код с примером, кейс передачи
- `node dala-connector-kit.mjs check <папка> [--executor <файл.mjs>] [--json]` — правила двери пакета без базы; примеры шага — вашим исполнителем (см. sandbox)
- `node dala-connector-kit.mjs pack <папка> [-o <файл.zip>]` — архив для «Загрузить пакет» в кабинете песочницы (≤ 2 МБ)
- `node dala-connector-kit.mjs catalog --packages` — глаголы каталога и чтения шага, которые может назвать пакет
- `node dala-connector-kit.mjs case <документ.json> [--json] [--words]` — проверить документ дела (формат case@1): все ошибки разом; --words — дело простыми словами
- `node dala-connector-kit.mjs case --format` — справка по формату документа дела
- `node dala-connector-kit.mjs --help` — эта справка

## Коды проверок

Строка пакета на каждую проверку набора; красная печатает под собой адрес страницы кода.

- [schema](https://www.zengi.ai/developers/codes/schema) — Манифест не прошёл строгую грамматику: ключ вне формы, значение не той формы или ссылка в никуда. Остальные проверки пропущены — без годной схемы им нечего читать.
- [environment](https://www.zengi.ai/developers/codes/environment) — Среда манифеста вне тех, что проверяет пакет.
- [recipients_declared](https://www.zengi.ai/developers/codes/recipients_declared) — Список `recipients` называет внутреннюю сеть, адрес-литерал, имя без домена, путь, учётку или порт — либо у шагов без своего `host` первая запись не точное имя хоста.
- [auth_fields](https://www.zengi.ai/developers/codes/auth_fields) — Поля ключа `auth.fields` не сходятся со входом. У объявленной среды без обмена ключа на токен — ровно одно поле, с обменом — ровно `client_id` и `client_secret`; назначение поля — одно из access · hook · refresh · token · merchant · webhook · portal. У шагов-кода каждое поле стоит меткой `{{secret:<имя>}}` в модуле, и каждая метка называет объявленное поле.
- [forbidden](https://www.zengi.ai/developers/codes/forbidden) — В подстановках шагов — обращение к переменным окружения, файлу или выход из пути (`${`, `file:`, `data:`, `../`, `__proto__`, `eval`), либо путь шага начинается с `//`.
- [effect_bound](https://www.zengi.ai/developers/codes/effect_bound) — Право записи. Эффект (`class: "effect"`) объявлен без привязки к глаголу и без блока `tool`; запись-инструмент стоит в семействе, где у платформы свой процесс или писатель факта (домен каталога, семейство или провайдер коннектора ядра); или шаг с методом POST, PUT, PATCH или DELETE не объявлен ни эффектом, ни безопасным.
- [verb_declared](https://www.zengi.ai/developers/codes/verb_declared) — Адрес глагола в `binds` не объявлен в каталоге, глагол другого семейства, чем `family` манифеста, чтение привязано к команде (или эффект — к чтению), либо один глагол исполняют две операции.
- [idempotency](https://www.zengi.ai/developers/codes/idempotency) — У эффект-операции нет ключа повтора (`idempotency`), ключ не стоит на первом эффект-шаге или у глагола нет бизнес-ключа.
- [reconcile_path](https://www.zengi.ai/developers/codes/reconcile_path) — У эффект-шага нет сверки — потерянный ответ нечем установить; сверка ищет не по ключу; или сверка не возвращает поле, которое дальше читается из тела эффект-шага.
- [compensation_declared](https://www.zengi.ai/developers/codes/compensation_declared) — Компенсация `compensate` не совпала с глаголом: у необратимого глагола объявлена компенсация, у компенсируемого её нет или она не той операцией.
- [budget_timeout](https://www.zengi.ai/developers/codes/budget_timeout) — Бюджет операции меньше её работы: вызовов меньше, чем шагов и опросов, или время опроса больше `total_ms`. У шагов-кода — общий срок меньше срока одного запроса или у входа обменом меньше двух запросов.
- [status_mapping_total](https://www.zengi.ai/developers/codes/status_mapping_total) — Таблица значений результата `{ from, map }` выдаёт слово вне словаря глагола, не покрывает статусы службы из `provider_statuses` или перечня статусов нет вовсе.
- [hold_preview](https://www.zengi.ai/developers/codes/hold_preview) — Глагол удерживается для одобрения оператором, а у операции нет предпросмотра — карточку одобрения построить нечем.
- [contract_tests](https://www.zengi.ai/developers/codes/contract_tests) — Кассет нет или файла кассеты из `contract_tests` нет рядом с манифестом — годность не доказать.
- [effect_guarded](https://www.zengi.ai/developers/codes/effect_guarded) — Эффект значимого глагола достижим без охраны цены по одобренному значению `{approved.…}`.
- [cancel_free_only](https://www.zengi.ai/developers/codes/cancel_free_only) — Отмена проводит эффект не только при бесплатной отмене: предпросмотр не отображает условия отмены службы `cancel_terms` в `free`, или платная ветка доходит до эффекта.
- [probe_declared](https://www.zengi.ai/developers/codes/probe_declared) — У манифеста нет пробы ключа `probe` — ключ подключения нечем проверить, и подключение не станет рабочим.
- [reconcile_carries_ref](https://www.zengi.ai/developers/codes/reconcile_carries_ref) — Результат глагола создания несёт ссылку заявки службы, а `provider_ref` в `result` операции не объявлен, читает не тело эффект-шага или сверка не возвращает поле, из которого он читается.
- [cassette_run](https://www.zengi.ai/developers/codes/cassette_run) — Кассета не проиграна как ждали: форма кассеты, операция или инструмент не найдены, вход не прошёл схему, запросы ушли вне обменов кассеты, исход разошёлся с `expect`; или операция не покрыта кассетой (у эффекта — кассетой с исходом `confirmed`). У шагов-кода ещё — не подан исполнитель.
- [recipients_observed](https://www.zengi.ai/developers/codes/recipients_observed) — Прогон ушёл к хосту вне `recipients`, или точный получатель объявлен, а ни одна кассета к нему не ходила.
- [result_shape](https://www.zengi.ai/developers/codes/result_shape) — Подтверждённый результат не проходит схему глагола, контракт операции писателя или `output_schema` инструмента, либо `provider_ref` пуст.
- [retry_no_duplicate](https://www.zengi.ai/developers/codes/retry_no_duplicate) — Повтор тем же ключом отправил новый эффект-запрос — служба исполнила бы действие дважды.
- [unknown_on_lost_response](https://www.zengi.ai/developers/codes/unknown_on_lost_response) — Пакет терял ответ эффекта, и исход не установила сверка, запрос ушёл повторно или журнал остался в полёте.
- [timeout](https://www.zengi.ai/developers/codes/timeout) — Пакет задерживал ответ дольше таймаута операции, и запоздавший ответ принят как подтверждённый без сверки или назван отказом.
- [kind_matches_effect](https://www.zengi.ai/developers/codes/kind_matches_effect) — Вид операции не совпал с поведением: чтение совершило эффект-запрос или писало журнал, эффект не совершил ни одного.
- [token_exchange](https://www.zengi.ai/developers/codes/token_exchange) — Манифест объявил обмен ключа на токен `auth.exchange`, а кассета его не проиграла, креды ушли не только в query обмена или шаг ушёл без токена в `Authorization: Bearer`.
- [tools_classified](https://www.zengi.ai/developers/codes/tools_classified) — Инструменты манифеста не совпали с функциями модуля `module.exports.tools`, или форма модуля не прочиталась.
- [reconcile_declared](https://www.zengi.ai/developers/codes/reconcile_declared) — Есть инструмент-эффект `egress`, а в модуле нет функции `reconcile(keyToken, input, ctx)` — потерянный ответ эффекта нечем установить.
- [module_shape](https://www.zengi.ai/developers/codes/module_shape) — Модуль не годен: `module_hash` не равен sha256 текста, модуль не загрузился, в `module.exports` лишние ключи или в тексте запрещённые обращения (сеть мимо двери, таймеры, `require`, `import`, `process`, `eval`).
- [secret_never_visible](https://www.zengi.ai/developers/codes/secret_never_visible) — Значение ключа попало модулю, в результат или в журнал.

## Коды схемы

Красная `schema` называет узел и код: `$.operations[0].steps[1].method: bad_step`.

- [not_object](https://www.zengi.ai/developers/codes/not_object) — Манифест и каждый его узел — объект: массив, строка или пусто здесь не годятся. У шагов-кода: манифест и каждый его узел — объект.
- [unknown_key](https://www.zengi.ai/developers/codes/unknown_key) — Ключ вне закрытой грамматики: молча проигнорированный, он снял бы ограничение. У шагов-кода: ключ вне закрытой грамматики ревизии `code@1`: молча проигнорированный, он снял бы ограничение.
- [missing_key](https://www.zengi.ai/developers/codes/missing_key) — Обязательный ключ не объявлен.
- [bad_value](https://www.zengi.ai/developers/codes/bad_value) — Значение не той формы.
- [bad_environment](https://www.zengi.ai/developers/codes/bad_environment) — Среда вне перечня core · declarative · external · service · code. У шагов-кода: ревизия этой проверки — только среда «code».
- [bad_auth](https://www.zengi.ai/developers/codes/bad_auth) — Схема ключа вне перечня bearer · header · query · basic, поле кредов без имени и назначения либо обмен ключа на токен объявлен неверно. У шагов-кода: поля ключей: имя, слово назначения из семи, подпись у спрашиваемых в форме; вход — получатель, путь, указатель и поле-приёмник.
- [bad_recipient](https://www.zengi.ai/developers/codes/bad_recipient) — Получатель — точное имя хоста или `*.домен` (≥2 метки после `*.`), без схемы, пути, порта и `*` вне префикса. У шагов-кода: получатель — точное имя хоста или `*.домен` (≥2 метки после `*.`), без схемы, пути, порта и `*` вне префикса; список — от 1 до 8 записей.
- [bad_operation](https://www.zengi.ai/developers/codes/bad_operation) — Операция объявлена неверно: имя, класс, привязка или обязательства эффекта.
- [duplicate_name](https://www.zengi.ai/developers/codes/duplicate_name) — Имя повторено: операции и шаги различаются именем, второе такое же — какая из двух исполнится, неизвестно. У шагов-кода: имя или адрес повторены.
- [bad_step](https://www.zengi.ai/developers/codes/bad_step) — Шаг объявлен неверно: метод, путь, хост, тело или таблица статусов.
- [bad_template](https://www.zengi.ai/developers/codes/bad_template) — Подстановка вне закрытого набора: выражения, окружение и ссылки вперёд в декларации невозможны.
- [bad_reference](https://www.zengi.ai/developers/codes/bad_reference) — Ссылка на операцию или шаг, которого нет.
- [bad_binding](https://www.zengi.ai/developers/codes/bad_binding) — Привязка к глаголу — адрес `домен.существительное.глагол@ревизия`; у `effect` она обязательна. У шагов-кода: привязка к глаголу (IZ-S16): `binds` — адрес `id@rev` глагола каталога, один инструмент на адрес; `preview` — только у связанного эффекта и называет инструмент вида query этого манифеста.
- [bad_contract_tests](https://www.zengi.ai/developers/codes/bad_contract_tests) — Контракт-тесты: у `core` — файлы `*.test.mjs` модуля, у `declarative` — кассеты с датой снятия. У шагов-кода: контракт-тесты: кассеты `*.json` с датой снятия — те, что доказывают годность модуля.
- [bad_settings](https://www.zengi.ai/developers/codes/bad_settings) — Настройки подключения: непустой список полей «имя · подпись · вид · обязательность», только у объявленной среды. У шагов-кода: настройки подключения: список полей «имя · подпись · вид · обязательность».
- [bad_inbound](https://www.zengi.ai/developers/codes/bad_inbound) — Точка входа: имя · схема подлинности (hmac_sha256_body · header_token · path_token) · назначение ключа входа · приёмник.
- [bad_tool](https://www.zengi.ai/developers/codes/bad_tool) — Блок `tool` объявлен неверно: среда, адрес по форме реестра вне доменов каталога, описание либо сочетание с привязкой к глаголу. У шагов-кода: инструмент объявлен неверно: адрес по форме реестра вне доменов каталога, описание, вид query | egress, reconcile у egress, схемы.
- [schema_unreducible](https://www.zengi.ai/developers/codes/schema_unreducible) — Схема инструмента вне закрытой грамматики каталога: инструмент не допускается.
- [context_argument](https://www.zengi.ai/developers/codes/context_argument) — Инструмент просит контекст в аргументах (tenant, connection, secret…): его выбирает сервер, не модель (INV-031).
- [bad_probe](https://www.zengi.ai/developers/codes/bad_probe) — Проба — адрес инструмента вида query, которому не нужны обязательные аргументы.
- [bad_budget](https://www.zengi.ai/developers/codes/bad_budget) — Бюджет: число запросов, общий срок и срок одного запроса — в пределах платформы.
- [bad_module](https://www.zengi.ai/developers/codes/bad_module) — Модуль: имя файла, текст в пределах длины и sha256 текста.

## Каталог глаголов

Глаголы платформы, к которым привязывается операция (`binds`); схемы, потолок и обратимость — `node dala-connector-kit.mjs catalog`.

- `shipment.parcel.quote@1` · query — Варианты доставки посылки от адреса до адреса: цена в тенге, срок, класс услуги (курьер по требованию или посылочная сеть), как передать посылку и где получить. Цена и срок приходят от службы доставки — не называй клиенту цену или срок, которых нет в ответе. Название службы тебе не раскрывается: не выдумывай его.
- `shipment.parcel.create@1` · command — Создать отправление по выбранному варианту доставки: имя и телефон получателя. Цену и условия ты не задаёшь — их берёт сервер у службы доставки по ссылке варианта, и сотрудник одобряет карточку с этими цифрами. После вызова отправление считается созданным только когда результат confirmed; при unknown не обещай клиенту доставку и не создавай отправление повторно.
- `shipment.parcel.status@1` · query — Состояние отправления по ссылке: принято, в пути, ждёт в пункте выдачи, вручено, отменено или не доставлено, и чем подтверждено вручение, если служба это даёт. Не обещай срок, которого в ответе нет.
- `shipment.parcel.cancel@1` · command — Отменить отправление по ссылке, если служба доставки позволяет отменить бесплатно. Если отмена платная или недоступна — результат rejected с причиной, решение за сотрудником; клиенту не обещай возврат денег.
- `media.image.generate@1` · query — Картинка по текстовому описанию сервисом генерации компании. Контракт семейства для кода: модели не предлагается.
- `media.image.generate@2` · query — Картинка по текстовому описанию сервисом генерации компании — ссылкой или телом ответа. Контракт семейства для кода: модели не предлагается.
- `booking.availability.search@1` · query — Свободное время приёма по услуге: ближайшие варианты с врачом и часовым поясом клиники. Врача, календарь и точный момент резолвит сервер; код времени действителен только в этом ответе. Записать этот инструмент не умеет: запись — отдельной способностью записи на приём, если она предложена, иначе запись делает оператор.
- `booking.appointment.create@1` · command — Записать пациента на приём — ТОЛЬКО после его явного «да» на прочитанные время и врача. Сначала в этом же ответе найди время поиском свободного времени по ТОЙ ЖЕ услуге и возьми код нужного слота. До записи пациенту должно быть прочитано время и врач как в выдаче поиска, каждое время в форме «ДД.ММ ЧЧ:ММ, врач <имя>» (дата и время рядом), а его последнее сообщение — согласие. `consent_quote` — его слова согласия дословно из последнего сообщения (например «да»). Пациента, врача и точное время подставляет сервер. «Ну давайте», «наверное», встречный вопрос — не явное согласие: переспроси. Исход not_confirmed — прочитай пациенту `read_back` и спроси, записать ли.
- `booking.appointment.cancel@1` · command — Отменить запись пациента на приём по его просьбе — ТОЛЬКО после его явного «да» на прочитанные время и врача ЭТОЙ записи. Записи пациента сервер находит сам по номеру диалога: вызови без кода — у единственной записи исход not_confirmed вернёт `read_back` (время и врач), у нескольких — choose_appointment со списком `appointments` и их кодами: прочитай и спроси, какую отменить, затем передай её код `appointment_ref` в этом же ответе. Прочитай пациенту `read_back` дословно, в форме «<подпись времени>, врач <имя врача>», и спроси, отменить ли. `confirm_quote` — его слова согласия дословно из последнего сообщения (например «да»). «Ну, наверное», встречный вопрос — не явное согласие: переспроси. Чужие записи (другого человека или номера) не называй и не отменяй — на такую просьбу откажи и предложи связаться с клиникой. Переносом эта способность не занимается.
