Skip to content

Семейство: protocols

norm_protocol_types

Типы встреч компании и подсказки к ним — весь справочник одним ответом, спрашивайте один раз за прогон. Тип распознавайте по aliases детерминированно: совпало написание в названии встречи — передавайте type_id и unit_id типа в запись и применяйте его подсказку. Не совпало — type_id не передавайте, подразделение определяйте по участникам, а подсказку берите у типа из fallback_type_id, если он указан. Режим replace — самостоятельная инструкция, не вкладывайте её в свой каркас. truncated: true у типа — подсказка обрезана ради предела размера ответа, НЕ применяйте её: отрезан конец, где обычно и лежат требования. Пустой массив — у компании нет типов, это не сбой. Доменные состояния: not_ready — типы не настроены; schema_unavailable — источник типов не синхронизирован.

Доступ: чтение · Списковый: нет · Версии: 1

Вход

ПолеОбязательноТипЗначение

Данные ответа

ПолеПри успехеТипЗначение
errorнетDomainErrorДоменное состояние
fallback_type_idнетstring
typesдасписок MeetingType
Пример: успешный ответ
json
{
  "source_refs": [
    {
      "source": "company-data",
      "ref": "protocol-types",
      "label": "Типы встреч"
    }
  ],
  "freshness": {
    "as_of": "2026-09-22T09:30:00.000Z",
    "ttl_sec": 3600,
    "stale": false
  },
  "data": {
    "types": [
      {
        "id": "mt-standup",
        "name": "Планёрка",
        "aliases": [
          "планёрка",
          "планерка",
          "standup",
          "дейли"
        ],
        "unit_id": "u-sales",
        "membership_kind": "unit",
        "hint": {
          "text": "В протоколе планёрки обязательны разделы «Решения» и «Поручения» с ответственным и сроком по каждому пункту.",
          "mode": "insert"
        },
        "description": "Короткая встреча отдела по текущим задачам."
      },
      {
        "id": "mt-client",
        "name": "Встреча с клиентом",
        "aliases": [
          "клиент",
          "встреча с клиентом",
          "презентация"
        ],
        "membership_kind": "pool",
        "hint": {
          "text": "Протокол встречи с клиентом оформляется письмом клиенту: обращение, договорённости, следующие шаги, подпись менеджера.",
          "mode": "replace"
        }
      },
      {
        "id": "mt-retro",
        "name": "Ретроспектива",
        "aliases": [
          "ретро",
          "retro"
        ]
      }
    ],
    "fallback_type_id": "mt-standup"
  }
}
Пример: случай «domain_error»
json
{
  "source_refs": [
    {
      "source": "company-data",
      "ref": "protocol-types"
    }
  ],
  "freshness": {
    "as_of": "2026-09-22T09:30:00.000Z",
    "ttl_sec": 0,
    "stale": false
  },
  "data": {
    "error": {
      "code": "not_ready",
      "message": "Типы встреч не настроены: заведите их в кабинете.",
      "retryable": false
    }
  }
}

norm_protocols_list

Протоколы компании страницами, от свежих к старым по дате встречи. Фильтры: from и to — дни встречи включительно; status — одно из значений контракта; unit_id — подразделение по прямому совпадению, без спуска в дочерние; meeting_ids — до 200 идентификаторов встреч: протокол есть в ответе — значит существует, так один вызов заменяет пакетную проверку «обработано ли». Без include_deleted удалённые человеком протоколы не отдаются. Тело протокола в списке не едет — оно велико, берите norm_protocol_get. total — сколько подошло под фильтр до применения страницы. Для следующей страницы передайте page.next_cursor как cursor. Доменное состояние not_ready — протоколы у компании не настроены.

Доступ: чтение · Списковый: да · Версии: 1

Вход

ПолеОбязательноТипЗначение
cursorнетstring
fromнетstring (date)
include_deletedнетboolean
limitнетinteger
meeting_idsнетсписок string
statusнетProtocolStatusСтатус протокола
toнетstring (date)
unit_idнетstring

Данные ответа

ПолеПри успехеТипЗначение
errorнетDomainErrorДоменное состояние
protocolsдасписок Protocol
totalдаinteger
Пример: успешный ответ
json
{
  "source_refs": [
    {
      "source": "company-data",
      "ref": "protocols:2026-09",
      "label": "Протоколы за сентябрь"
    }
  ],
  "freshness": {
    "as_of": "2026-09-22T09:30:00.000Z",
    "ttl_sec": 300,
    "stale": false
  },
  "page": {
    "has_more": true,
    "next_cursor": "eyJvZmZzZXQiOjJ9"
  },
  "data": {
    "protocols": [
      {
        "id": "prt-2031",
        "meeting_id": "01K9F2QH7T8ZRC4M0X5B2VNJ3D",
        "title": "Планёрка отдела продаж 2026-09-21",
        "status": "done",
        "participant_ids": [
          "p-1042",
          "p-1077"
        ],
        "deleted": false,
        "meeting_date": "2026-09-21",
        "organizer_id": "p-1042",
        "unit_id": "u-sales",
        "type_id": "mt-standup",
        "url": "https://records.example.com/prt-2031",
        "updated_at": "2026-09-21T11:02:00.000Z"
      },
      {
        "id": "prt-2028",
        "meeting_id": "01K9EZZC2P4NM6S1VB8H3TQXWR",
        "title": "Встреча с клиентом «Актив»",
        "status": "draft",
        "participant_ids": [
          "p-1042"
        ],
        "participant_emails": [
          "s.ilin@example.com"
        ],
        "deleted": false,
        "meeting_date": "2026-09-19",
        "url": "https://records.example.com/prt-2028"
      }
    ],
    "total": 7
  }
}
Пример: случай «domain_error»
json
{
  "source_refs": [
    {
      "source": "company-data",
      "ref": "protocols"
    }
  ],
  "freshness": {
    "as_of": "2026-09-22T09:30:00.000Z",
    "ttl_sec": 0,
    "stale": false
  },
  "page": {
    "has_more": false
  },
  "data": {
    "error": {
      "code": "not_ready",
      "message": "База протоколов не подключена: настройте её в кабинете.",
      "retryable": false
    }
  }
}

norm_protocol_get

Протокол одной встречи по meeting_id целиком: ядро, тело, дополнительные поля, ссылка. Удалённый человеком протокол отдаётся с deleted: true — воссоздавать его нельзя. Тело реализация обрезает так, чтобы ответ уложился в предел размера, и тогда ставит body_truncated: true: обрезанное тело не принимайте за полное. Доменные состояния: no_data — протокола по этой встрече нет, можно создавать (повтор ничего не изменит); not_ready — протоколы не настроены.

Доступ: чтение · Списковый: нет · Версии: 1

Вход

ПолеОбязательноТипЗначение
meeting_idдаstring

Данные ответа

ПолеПри успехеТипЗначение
body_truncatedдаboolean
errorнетDomainErrorДоменное состояние
protocolдаProtocolПротокол встречи
Пример: успешный ответ
json
{
  "source_refs": [
    {
      "source": "company-data",
      "ref": "protocol:01K9F2QH7T8ZRC4M0X5B2VNJ3D",
      "label": "Карточка протокола"
    }
  ],
  "freshness": {
    "as_of": "2026-09-22T09:30:00.000Z",
    "ttl_sec": 300,
    "stale": false
  },
  "data": {
    "protocol": {
      "id": "prt-2031",
      "meeting_id": "01K9F2QH7T8ZRC4M0X5B2VNJ3D",
      "title": "Планёрка отдела продаж 2026-09-21",
      "status": "done",
      "participant_ids": [
        "p-1042",
        "p-1077"
      ],
      "deleted": false,
      "meeting_date": "2026-09-21",
      "source_url": "https://meet.example.com/rec/01K9F2QH7T8ZRC4M0X5B2VNJ3D",
      "organizer_id": "p-1042",
      "agenda": "1. Воронка недели. 2. Блокеры по «Активу».",
      "unit_id": "u-sales",
      "type_id": "mt-standup",
      "body": "## Решения\n\n- Перенести демо для «Актива» на 25.09.\n\n## Поручения\n\n- Илин: подготовить смету до среды.",
      "url": "https://records.example.com/prt-2031",
      "updated_at": "2026-09-21T11:02:00.000Z",
      "extra": {
        "decision_owner": "p-1042"
      }
    },
    "body_truncated": false
  }
}
Пример: случай «deleted»
json
{
  "source_refs": [
    {
      "source": "company-data",
      "ref": "protocol:01K9EZZC2P4NM6S1VB8H3TQXWR",
      "label": "Карточка протокола"
    }
  ],
  "freshness": {
    "as_of": "2026-09-22T09:30:00.000Z",
    "ttl_sec": 300,
    "stale": false
  },
  "data": {
    "protocol": {
      "id": "prt-2028",
      "meeting_id": "01K9EZZC2P4NM6S1VB8H3TQXWR",
      "title": "Встреча с клиентом «Актив»",
      "status": "draft",
      "participant_ids": [
        "p-1042"
      ],
      "deleted": true,
      "meeting_date": "2026-09-19"
    },
    "body_truncated": false
  }
}
Пример: случай «domain_error»
json
{
  "source_refs": [
    {
      "source": "company-data",
      "ref": "protocol:01K9G000000000000000000000"
    }
  ],
  "freshness": {
    "as_of": "2026-09-22T09:30:00.000Z",
    "ttl_sec": 0,
    "stale": false
  },
  "data": {
    "error": {
      "code": "no_data",
      "message": "Протокола по этой встрече ещё нет: можно создавать.",
      "retryable": false
    }
  }
}

norm_protocol_upsert

Создать или переписать протокол встречи: ядро полей плюс тело в markdown. Состав полей задан контрактом — читать схему перед записью не нужно. Идемпотентно по meeting_id: повторный вызов переписывает ТУ ЖЕ карточку (created: false). Тело заменяется целиком, поля ядра объединяются: поля нет во входе — оно не трогается; переданный массив заменяет прежний набор; null у одиночного поля очищает его; внутри extra то же правило по каждому ключу. Статус, который человек поставил в системе компании вне значений контракта, не затирается — поле вернётся в kept_existing; реализация вправе защищать так и другие поля. Запись создаётся ДАЖЕ если обязательное для компании поле пустое — отражайте незавершённость статусом draft, а не отказом. Непринятое поле не отменяет операцию: оно приедет в rejected_fields успешного ответа, там же unresolved_people — почты участников и организатора без учётной записи в системе компании. Идентификаторы людей берите в norm_people_list; тип и подразделение — из norm_protocol_types. Доменные состояния: not_ready — запись протоколов не настроена; record_deleted — карточку удалил человек, воссоздавать нельзя; record_rejected — хранилище отвергло карточку целиком, повтор даст то же; unknown_field — компания не ведёт поле ядра; temporary и rate_limited — повторите позже. Реализация вправе разрешить запись только привилегированным ролям и отвечать unauthorized_scope.

Доступ: запись · Списковый: нет · Версии: 1

Вход

ПолеОбязательноТипЗначение
agendaнетstring | null
bodyдаstring
extraнетobject
meeting_dateнетstring (date)
meeting_idдаstring
organizer_emailнетstring
organizer_idнетstring | null
participant_emailsнетсписок string
participant_idsнетсписок string
source_urlнетstring (uri)
statusнетProtocolStatusСтатус протокола
titleнетstring
type_idнетstring | null
unit_idнетstring | null

Данные ответа

ПолеПри успехеТипЗначение
createdдаboolean
errorнетDomainErrorДоменное состояние
idдаstring
kept_existingдасписок string
noteнетstring
rejected_fieldsдасписок RejectedField
unresolved_peopleдасписок string
urlнетstring (uri)
Пример: успешный ответ
json
{
  "source_refs": [
    {
      "source": "company-data",
      "ref": "protocol:01K9F2QH7T8ZRC4M0X5B2VNJ3D",
      "label": "Карточка протокола"
    }
  ],
  "freshness": {
    "as_of": "2026-09-22T09:31:00.000Z",
    "ttl_sec": 0,
    "stale": false
  },
  "data": {
    "id": "prt-2031",
    "created": false,
    "url": "https://records.example.com/prt-2031",
    "rejected_fields": [
      {
        "key": "extra.duration",
        "reason": "вычисляемое свойство: значение задаёт хранилище"
      }
    ],
    "unresolved_people": [
      "s.ilin@example.com"
    ],
    "kept_existing": [
      "status"
    ]
  }
}
Пример: случай «created»
json
{
  "source_refs": [
    {
      "source": "company-data",
      "ref": "protocol:01K9EZZC2P4NM6S1VB8H3TQXWR",
      "label": "Карточка протокола"
    }
  ],
  "freshness": {
    "as_of": "2026-09-22T09:31:00.000Z",
    "ttl_sec": 0,
    "stale": false
  },
  "data": {
    "id": "prt-2032",
    "created": true,
    "url": "https://records.example.com/prt-2032",
    "rejected_fields": [],
    "unresolved_people": [],
    "kept_existing": []
  }
}
Пример: случай «domain_error»
json
{
  "source_refs": [
    {
      "source": "company-data",
      "ref": "protocol:01K9EZZC2P4NM6S1VB8H3TQXWR"
    }
  ],
  "freshness": {
    "as_of": "2026-09-22T09:31:00.000Z",
    "ttl_sec": 0,
    "stale": false
  },
  "data": {
    "error": {
      "code": "record_deleted",
      "message": "Протокол этой встречи удалил человек. Создавать его заново нельзя — это его решение.",
      "retryable": false
    }
  }
}