26 мая 2026 · 1С, ODATA, HTTP · 8 мин

Типы эндпоинтов 1С ODATA

Зачем эта классификация

endpoints.md устроен «по операциям» — read, create, update, delete, actions. Это удобно искать «как сделать X», но скрывает закономерность: в OData форма URL (плюс метод) однозначно определяет, какие query-параметры и тела имеют смысл. Шесть семейств ниже — карта семантики, по которой потом легко предсказывать поведение нового, ещё не встречавшегося эндпоинта.

Зная семейство, отвечаешь сразу:

  • работает ли тут $filter / $expand / If-Match;
  • какой HTTP-метод осмыслен и что класть в тело;
  • чего ждать в ответе (json / xml / text);
  • какие коды ошибок типичны.

Карта семейств

СемействоФорма URLМетод(ы)Семантика
service-rootB/, B/$metadataGETdiscovery
entity-setB/<Prefix>_<Имя> (+ _<TP>, /$count)GET, POST¹коллекция: чтение / создание
single-entityB/<Set>(<key>) (+ _RowType, _RecordType, dot-traversal)GET, PATCH, PUT, DELETEэкземпляр: чтение / обновление / удаление
bound-functionB/<Set>/<Func>(<params>)GETвычисление, без побочного эффекта
bound-actionB/<Set>(<key>)/<Verb>POSTкоманда над экземпляром
service-operationB/<Verb>?<params>POSTкорневая операция (action import в терминах OData)

¹ Внутри entity-set GET и POST формально делят URL, но это семантически разные операции: GET читает существующие элементы и поддерживает все OData-$-параметры; POST добавляет элемент в коллекцию и возвращает один созданный экземпляр с Location. См. §2.


1. service-root

Discovery. Два эндпоинта корневого уровня, без query-параметров и тела, всегда XML.

URLB/, B/$metadata
МетодGET
Тело
Queryигнорируется ($format=json семантически = код 3, фактически 1С молча отдаёт XML)
Ответatom service document или CSDL XML
Ошибкиредко; 4XX/5XX без специфики

Что выделяет семейство: единственное место, где json не предусмотрен в принципе, и единственное, где URL описывает не доменную сущность, а сам сервис.

Подробнее: endpoints.md §4.


2. entity-set

Коллекция сущностей одного типа. По форме URL — одно семейство, но GET и POST на одном пути несут разные семантики. Внутри живут все ходовые $-параметры OData (только на GET) — больше нигде их нет.

2.1. GET — чтение коллекции

URLB/<Prefix>_<Имя>, B/<Prefix>_<Имя>_<TP>, B/<Prefix>_<Имя>/$count
МетодGET
Query$filter, $select, $expand, $orderby, $top, $skip, $inlinecount, $format, allowedOnly
Ответatom-feed или {"value": [...]} (+ m:count / odata.count при $inlinecount)
Ошибки8 (тип не найден), 14 ($filter/$orderby), 20/401 (RLS), 21 (нереализованные функции)

$count как часть URL (/<Set>/$count) — отдельная под-форма entity-set GET с ответом text/plain; из query доступен только $filter.

Подробнее: endpoints.md §5.

2.2. POST — создание (action over a set)

POST по URL коллекции — формально action on a set в терминах OData: команда «добавь элемент», направленная на множество, возвращающая один созданный объект. URL общий с GET, но контракт другой: query-параметры OData здесь бессмысленны, нужно тело, в ответ — единичная сущность с Location.

URLB/<Prefix>_<Имя> (без ключа в пути)
МетодPOST
HeadersContent-Type: application/json или application/atom+xml; (опц.) 1C_OData-DataLoadMode: true
Bodyобъект в json/atom; ссылочные поля через <Реквизит>_Key; составные — пара <Реквизит> + <Реквизит>_Type
Query$format (формат ответа). OData $-параметры бессмысленны.
Ответ201 Created; Location: с каноническим URL созданной сущности; тело — сама сущность с заполненными Ref_Key, DataVersion
Ошибки4, 5 (нет обязательного поля), 13 (POST на табличную часть), 15 (ключ уже занят), 16, 17 (DataLoadMode не поддерживается), 19, 20

Семантическая ловушка: эндпоинт _<TP> (табличная часть) — читаемая коллекция, но POST на него запрещён (код 13). Табличная часть создаётся/меняется только через PATCH родителя.

Подробнее: endpoints.md §7.


3. single-entity

Один экземпляр по ключу. Здесь живут DataVersion, If-Match и dot-traversal.

URLB/<Set>(<key>), B/<Set>_<TP>(Ref_Key=..., LineNumber=N), B/InformationRegister_<Имя>_RecordType(<dims>), dot-path B/<Set>(<key>)/Реквизит/Свойство
МетодыGET, PATCH, PUT, DELETE. RowType/RecordType — только GET.
Ключпростой (guid'...') или составной (Name=val, Name2=val2); для составного — все компоненты обязательны, иначе код 7
Body (PATCH)частичный объект; ссылочные поля через _Key
Body (PUT)все поля; ссылочные поля только через <Реквизит>@odata.bind
Query$select, $format. $expand тут не работает. $filter бессмыслен — выбран конкретный экземпляр. allowedOnly запрещён.
HeadersIf-Match: <DataVersion> для PATCH/DELETE
Ответ (PATCH/PUT)обновлённая сущность либо 204
Ответ (DELETE)204; объект удаляется физически
Ошибки7, 9, 10, 19, 20; 412 при несовпадении If-Match

Невидимая граница: _RowType и _RecordType синтаксически выглядят как single-entity (есть ключ, канонический GET), но семантическитолько чтение. PATCH/DELETE на строку табличной части или запись регистра напрямую невозможны; изменения идут через PATCH родителя или набора записей.

Dot-traversal — частный случай single-entity: B/<Set>(<key>)/Реквизит/Свойство возвращает значение свойства связанной сущности. Для стандартных реквизитов в цепочке используются только английские имена (Description, Code, Date, Posted).

Подробнее: endpoints.md §6, §8, §9.


4. bound-function

Параметризованное чтение, привязанное к ресурсу (обычно — регистру). Это не действие — побочных эффектов нет. По OData это формально function: GET-only, идемпотентно, можно кешировать.

URLB/<Set>/<Func>(<key=value>, ...)
МетодGET
Параметры функциив скобках, пары Key=Value через запятую; типизированные литералы (datetime'...', guid'...'); если параметр — отбор, синтаксис $filter в одинарных кавычках
Query$select, $expand*, $format
Ответколлекция строк (как у entity-set), но вычисленных, не хранимых
Ошибки11 (функции нет на этом ресурсе), 12 (нет обязательного аргумента), 14 (синтаксис Condition=), 20, 21

*$expand на bound-function отклоняется от OData v3: для измерений — раскрывается рядом с _Key; для составных типов результат уезжает в свойство с суффиксом _Expanded. Клиентский код должен это знать.

Концептуально важно: параметры функции — это не query-параметры. Их нельзя пропустить произвольно, нельзя заменить на $filter. Внутри Condition=... живёт второй уровень DSL — фильтр в одинарных кавычках с экранированием:

.../SliceLast(Period=datetime'2024-01-01T00:00:00',Condition='Валюта_Key eq guid''aaa-bbb-...''')

Документированные функции:

  • InformationRegister_<Имя>/SliceLast, SliceFirst
  • InformationRegister_<Имя>_RecordType/SliceLast (для подчинённых регистратору)
  • AccumulationRegister_<Имя>/Balance, Turnovers, BalanceAndTurnovers
  • AccountingRegister_<Имя>/Balance (c AccountCondition, Condition, ExtraDimensions)

Конкретный набор функций для CalculationRegister зависит от типа регистра — сверять с $metadata целевой конфигурации.

Подробнее: endpoints.md §11.


5. bound-action

Команда над конкретным экземпляром. POST с глаголом в пути, тело обычно пустое, параметры команды — в query. По OData это bound action: связана с ресурсом через сегмент пути.

URLB/<Set>(<key>)/<Verb> — у 1С это /Post, /Unpost
МетодPOST
Body— (тело не передаётся)
Queryпараметры команды (PostingModeOperational) — не OData $-параметры
Ответобновлённый экземпляр (после команды)
Ошибки9 (не найден), 11 (команда не определена на этом ресурсе), 19, 20

Семантика «команда, а не флаг»: Post нельзя заменить на PATCH ... {"Posted": true}. Поле Posted — состояние, его выставляет именно команда. PATCH Posted молча проигнорируется или вернёт ошибку 16. Аналогично с Unpost.

Подробнее: endpoints.md §10.


6. service-operation

Корневая операция, не привязанная к экземпляру через путь. В терминах OData v3 это action import — действие на уровне сервиса. У 1С таких ровно две: SelectChanges и NotifyChangesReceived, обе относятся к плану обмена.

URLB/<Verb>?<params> — корневой эндпоинт, без сущности в пути
МетодPOST
Body
Queryпараметры операции; для плана обмена — DataExchangePoint='<URL узла>', MessageNo=<N>
ОтветSelectChangesтолько atom-feed, $format=json игнорируется; NotifyChangesReceived200 без тела
Ошибки8 (узел не найден через DataExchangePoint), 9, 14 (синтаксис параметров), 19, 20

Чем отличается от bound-action: binding явный, не через путь — целью операции выступает значение query-параметра (DataExchangePoint), а не сегмент URL. Отсюда же отличается и формат ответа: bound-action возвращает обновлённый экземпляр (json в общем случае), SelectChanges — фиксированно atom-feed. Из-за этих различий имеет смысл держать категорию отдельно.

Подробнее: endpoints.md §12.


Как определить семейство по URL

Алгоритм:

  1. Путь пустой или $metadata? → service-root.
  2. Корневой эндпоинт-глагол (SelectChanges, NotifyChangesReceived)? → service-operation.
  3. Есть (<params>) после имени функции (не после имени набора с ключом)? → bound-function.
  4. Есть (<key>) в пути, а за ним /<Verb> без скобок (/Post, /Unpost)? → bound-action.
  5. Есть (<key>) в пути без последующего /<Verb>? → single-entity (включая _RowType, _RecordType, dot-path).
  6. Иначе — плоский путь к набору (с /$count или без)? → entity-set:
    • метод GET → чтение коллекции;
    • метод POST → создание (action over a set).

Что меняется между семействами

Признакservice-rootentity-set GETentity-set POSTsingle-entitybound-functionbound-actionservice-operation
$filter / $top / $skip— (через Condition=)
$select
$expand✓ (с оговоркой)
$inlinecount / $orderby
allowedOnly
If-Match✓ (PATCH/DELETE)
Location в ответе
Body запросаjson/atomjson/atom (PATCH/PUT)— (обычно)
Тело ответаXMLjson/atomjson/atom (created)json/atomjson/atomjson/atomatom-only (SelectChanges) или пусто

См. также: Префиксы объектов — какие семейства поддерживает каждый префикс; Подводные камни и рецепты — типовые ошибки в разрезе семейств; Ошибки — матрица семейство × типичные коды.