FSP / Манифест
Манифест и согласование
Сторона FSP не договаривается о формате в переписке. Она публикует манифест по адресу /.well-known/fsp, где машиночитаемо сказано: какие модули поддерживаю, каких версий, на каком уровне и с какими расширениями. Первый шаг любого обмена — забрать манифест другой стороны и свести его со своим.
Схема манифеста — fsp-profile.schema.json. Правила согласования нормативны и лежат в FSP Core под кодами PROFILE-001…PROFILE-008 (modules/core/rules.csv).
Зачем
Без манифеста участие в обмене означает «быть заведённым в чужой админке». С манифестом оператор становится участником, отдав ссылку: его прайс и мощности читает любая площадка, а не одна. Это же снимает вопрос «почему не сработало»: если модуль или версия не совпали, это видно сразу.
Кто что объявляет
| Роль | Обычно объявляет | Смысл |
|---|---|---|
operator |
core, pricing, capacity |
Отдаёт свой прайс и свободные мощности |
requester |
core, quote |
Заявитель или его агент: присылает ТЗ и объём, читает сметы |
aggregator |
core, pricing (read_only), capacity (read_only), quote |
Площадка: читает операторов, отдаёт сметы заявителям |
read_only: true означает «умею читать данные модуля, но своих не отдаю». Площадке не нужен собственный прайс, чтобы считать по чужому.
Как сводятся два манифеста
Считает принимающая сторона, её результат окончателен (PROFILE-005).
- Кандидаты — только модули, объявленные обеими сторонами (
PROFILE-003). - Для каждого кандидата берётся пересечение
versions, выбирается наибольшая. Пустое пересечение исключает модуль. - Модуль исключается, если исключена любая его зависимость. Расширение исключается вместе с родительским модулем. Пересчёт повторяется, пока набор не перестанет меняться (
PROFILE-004). - У модуля с уровнями соответствия согласованный уровень — наименьший общий (
PROFILE-006). - Незнакомые поля, модули и расширения игнорируются, а не роняют обмен (
PROFILE-008).
Пример
Площадка МПФИТ (core 1.0, pricing 1.0 L3 read_only, capacity 1.0 read_only, quote 1.0) сводится с оператором «Склад 24» (core 1.0, pricing 1.0 L1, capacity 1.0):
core 1.0 активен
pricing 1.0 L1 активен, уровень понижен до L1 — у оператора плоский прайс
capacity 1.0 активен
quote — неактивен: оператор его не объявлял, сметы считает площадка
Дальше запрос с pricing_level_min: "L2" не выкинет «Склад 24» из выдачи. Он попадёт туда с fit: partial и кодом pricing_level_insufficient, а в кабинете оператора это превратится в понятную задачу: описать условия и подняться до L2.
Расширения
Своя операция, которой нет в стандарте, не повод ждать новой версии. Расширение объявляется в манифесте под именем x-<vendor>:<code> с указанием родительского модуля:
"x-sklad:route-cutoff-fee": {
"module": "pricing",
"versions": ["1.0"],
"spec": "https://sklad.example/fsp/route-cutoff-fee",
"candidate_for_standard": true
}
Расширение только добавляет поля и никогда не переопределяет стандартные (PROFILE-007). Флаг candidate_for_standard — заявка автора на включение в стандарт: такие расширения разбираются при подготовке следующей версии модуля.
Примеры
Оператор с глубоким прайсом, оператор с плоским прайсом из Excel и площадка.
aggregator-mpfit.json
{
"fsp": "1.0",
"party": {
"role": "aggregator",
"id": "mpfit",
"name": "МПФИТ — биржа мощностей",
"contact_channel": "https://mpfit.ru"
},
"modules": {
"core": { "versions": ["1.0"] },
"pricing": { "versions": ["1.0"], "level": "L3", "read_only": true },
"capacity": { "versions": ["1.0"], "read_only": true },
"quote": { "versions": ["1.0"] }
},
"endpoints": {
"quote": "https://api.mpfit.ru/fsp/v1/quote"
},
"updated_at": "2026-07-25T10:00:00+03:00"
} operator-excel-price.json
{
"fsp": "1.0",
"party": {
"role": "operator",
"id": "sklad-24",
"name": "Склад 24",
"contact_channel": "mailto:sales@sklad24.example"
},
"modules": {
"core": { "versions": ["1.0"] },
"pricing": { "versions": ["1.0"], "level": "L1" },
"capacity": { "versions": ["1.0"] }
},
"updated_at": "2026-07-25T10:00:00+03:00"
} operator-full-price.json
{
"fsp": "1.0",
"party": {
"role": "operator",
"id": "sklad",
"name": "Складсервис",
"contact_channel": "tg:@sklad_ff"
},
"modules": {
"core": { "versions": ["1.0"] },
"pricing": { "versions": ["1.0"], "level": "L3" },
"capacity": { "versions": ["1.0"] }
},
"extensions": {
"x-sklad:route-cutoff-fee": {
"module": "pricing",
"versions": ["1.0"],
"spec": "https://sklad.example/fsp/route-cutoff-fee",
"candidate_for_standard": true
}
},
"endpoints": {
"capacity": "https://sklad.example/fsp/capacity"
},
"updated_at": "2026-07-25T10:00:00+03:00"
}