FC Partner API RU · EN · Swagger · openapi.yaml

Инструкция по партнёрскому API

Каталог с вашими ценами и остатками и, если разрешено, заказы. Полное описание полей и ответов — в файле openapi.yaml (OpenAPI 3.0, открывается в Swagger Editor, Postman, Insomnia). Можно сразу попробовать запросы на странице Swagger — она отправляет настоящие запросы с вашим токеном.

1. Быстрый старт

  1. Откройте мини-приложение бота → Профиль → API и создайте токен: имя, срок действия, права («только каталог» или «каталог и заказы»). Раздел виден, только если менеджер включил вам API.
  2. Токен показывается один раз. Сохраните его в секретном хранилище; в репозиторий и клиентский код его класть нельзя. При утечке отзовите токен там же и выпустите новый.
  3. Передавайте токен в каждом запросе: Authorization: Bearer fcp_….
export FC_API="https://<хост>/api/partner"
curl -H "Authorization: Bearer $FC_TOKEN" "$FC_API/me"

Базовый адрес ($FC_API в примерах) вам сообщит менеджер. API серверное: браузерные запросы (CORS) не поддерживаются, вызывайте его с вашего сервера.

2. Общие правила

3. Формат ошибки

HTTP 403
{ "error": { "code": "scope_missing", "message": "…" } }

Ориентируйтесь на code, а не на текст message. Основные группы:

HTTPКодыЧто делать
400bad_request, idempotency_key_required, idempotency_key_invalidИсправить запрос
401token_missing, token_invalid, token_revoked, token_expiredПроверить или перевыпустить токен
403api_disabled, client_not_allowed, scope_missing, orders_disabled, forbidden, ip_not_allowedНужного права нет: обратитесь к менеджеру
404not_foundНет такой позиции или заказа
409request_in_progress, order_closed, line_changed, last_line_use_cancelСм. раздел «Заказы»
422order_rejected, onec_error, qty_limit_exceeded, qty_increase_unavailable, serial_line_qty_fixed, idempotency_key_reusedЗапрос понятен, но не принят; причина в message
423bot_closedБот закрыт по расписанию; повторить в рабочее время
429rate_limitedЗамедлиться и повторить
503order_busy, onec_unavailableВременный сбой; повторить с тем же Idempotency-Key

4. Каталог (право catalog:read)

ЗапросЧто отдаёт
GET /meВаши права после применения политики: склады, валюта, лимиты, права токена
GET /brandsБренды в наличии
GET /categories?brand=…Категории бренда
GET /catalogПозиции с ценами. Фильтры: brand, category, warehouse; страницы: limit (до 1000, по умолчанию 200) и cursor
GET /catalog/{itemCode}Одна позиция, в том числе шкала цен по количеству (tiers)

Данные витрины обновляются раз в 30–60 секунд, чаще опрашивать смысла нет. Во всех примерах ниже $FC_API и $FC_TOKEN заданы, как в разделе «Быстрый старт».

Параметры GET /catalog

ПараметрЗначениеПример
brandБренд из GET /brands. Одно значение, регистр не важенApple
categoryКатегория из GET /categories?brand=…. Одно значение, регистр не важенiPhone
warehouseКод склада из GET /me; по умолчанию все ваши склады23
limitРазмер страницы: от 1 до 1000, по умолчанию 200100
cursorЗначение nextCursor из предыдущего ответа—

Фильтры сочетаются по «И»: бренд и категория вместе дают позиции этой категории этого бренда. Названия с пробелами и кириллицей передавайте через --data-urlencode (с ключом -G curl подставит их в адрес сам). Если значение не найдено, ответ будет пустым списком "items": [], а не ошибкой.

Профиль и права: GET /me

curl -sS -H "Authorization: Bearer $FC_TOKEN" "$FC_API/me"
{
  "client": {
    "botKey": "dubai",
    "currency": "USD",
    "warehouses": [ { "code": "23" } ]
  },
  "token": {
    "prefix": "Ab3dE9xQ",
    "name": "my-erp",
    "scopes": ["catalog:read", "orders:write"],
    "createdAt": "2026-11-03T09:12:41.000Z",
    "expiresAt": "2027-11-03T09:12:41.000Z",
    "lastUsedAt": "2026-11-03T10:02:07.000Z"
  },
  "policy": {
    "includeTiers": true,
    "includeMarkdown": false,
    "respectSchedule": true,
    "rate": { "perMinute": 60, "perDay": 20000 },
    "orders": {
      "enabled": true,
      "allowModify": true,
      "allowDelete": false,
      "allowCancel": true,
      "maxLineQty": null
    }
  }
}

Из policy видно, что вам разрешено: показываются ли остатки (см. «Остатки» ниже), есть ли шкала цен, какие действия с заказами доступны.

Бренды: GET /brands

curl -sS -H "Authorization: Bearer $FC_TOKEN" "$FC_API/brands"
{ "brands": ["Apple", "Dyson", "Samsung", "Sony"] }

Категории бренда: GET /categories

curl -sS -G -H "Authorization: Bearer $FC_TOKEN" \
  --data-urlencode "brand=Apple" \
  "$FC_API/categories"
{ "categories": ["AirPods", "iPad", "iPhone", "MacBook"] }

Параметр brand обязателен, иначе 400 bad_request.

Каталог одного бренда

curl -sS -G -H "Authorization: Bearer $FC_TOKEN" \
  --data-urlencode "brand=Apple" \
  --data-urlencode "limit=2" \
  "$FC_API/catalog"
{
  "items": [
    {
      "itemCode": "24014",
      "name": "iPhone 15 128GB Black",
      "brand": "Apple",
      "category": "iPhone",
      "warehouse": "23",
      "currency": "USD",
      "price": 345,
      "available": 40
    },
    {
      "itemCode": "24159",
      "name": "Apple AirPods Max 2 (USB-C) Blue 2026 MHWM4",
      "brand": "Apple",
      "category": "AirPods",
      "warehouse": "23",
      "currency": "USD",
      "price": 100,
      "available": 20
    }
  ],
  "nextCursor": "eyJrIjoiMjQxNTl8MjN8IiwibyI6Mn0"
}

nextCursor не null — значит есть следующая страница (см. «Постраничная выгрузка» ниже). В списке у позиции одна цена; шкала цен по количеству приходит только в карточке позиции.

Каталог бренда и категории

curl -sS -G -H "Authorization: Bearer $FC_TOKEN" \
  --data-urlencode "brand=Apple" \
  --data-urlencode "category=iPhone" \
  --data-urlencode "limit=100" \
  "$FC_API/catalog"
{
  "items": [
    {
      "itemCode": "24014",
      "name": "iPhone 15 128GB Black",
      "brand": "Apple",
      "category": "iPhone",
      "warehouse": "23",
      "currency": "USD",
      "price": 345,
      "available": 40
    }
  ],
  "nextCursor": null
}

Только один склад из нескольких — добавьте --data-urlencode "warehouse=23".

Несколько категорий

Параметр category принимает одно значение, поэтому для нескольких категорий делайте по запросу на каждую и объединяйте ответы у себя. Пример: позиции Apple из категорий iPhone, iPad и AirPods одним списком (нужен jq):

for CAT in iPhone iPad AirPods; do
  curl -sS -G -H "Authorization: Bearer $FC_TOKEN" \
    --data-urlencode "brand=Apple" \
    --data-urlencode "category=$CAT" \
    --data-urlencode "limit=1000" \
    "$FC_API/catalog"
done | jq -s '[ .[].items[] ]'
[
  { "itemCode": "24014", "name": "iPhone 15 128GB Black", "category": "iPhone", "price": 345, "available": 40, "…": "…" },
  { "itemCode": "25101", "name": "iPad 10th gen 64GB Silver", "category": "iPad", "price": 289, "available": 15, "…": "…" },
  { "itemCode": "24159", "name": "Apple AirPods Max 2 (USB-C) Blue 2026 MHWM4", "category": "AirPods", "price": 100, "available": 20, "…": "…" }
]

Если в категории больше 1000 позиций, листайте каждую категорию по nextCursor (ниже). Все категории бренда без перебора — запрос без category (каталог бренда).

Постраничная выгрузка всего каталога

Подставляйте nextCursor из ответа в cursor, пока он не станет null:

CURSOR=""
while : ; do
  RESP=$(curl -sS -G -H "Authorization: Bearer $FC_TOKEN" \
    --data-urlencode "limit=500" \
    ${CURSOR:+--data-urlencode "cursor=$CURSOR"} \
    "$FC_API/catalog")
  echo "$RESP" | jq -c '.items[]'
  CURSOR=$(echo "$RESP" | jq -r '.nextCursor // empty')
  [ -z "$CURSOR" ] && break
done

Курсор — непрозрачная строка: не разбирайте и не меняйте её. Если витрина обновилась между страницами, выгрузка продолжится с ближайшей позиции без ошибки; для строгой сверки повторите выгрузку целиком.

Карточка позиции со шкалой цен: GET /catalog/{itemCode}

curl -sS -H "Authorization: Bearer $FC_TOKEN" "$FC_API/catalog/24014"
{
  "items": [
    {
      "itemCode": "24014",
      "name": "iPhone 15 128GB Black",
      "brand": "Apple",
      "category": "iPhone",
      "warehouse": "23",
      "currency": "USD",
      "price": 345,
      "available": 40,
      "tiers": [
        { "qty": 1,  "price": 345 },
        { "qty": 5,  "price": 343 },
        { "qty": 10, "price": 341 }
      ]
    }
  ]
}

items — массив. Шкала tiers читается так: «начиная с qty штук цена за штуку — price». Ступени выше доступного остатка скрыты. У уценённых позиций шкалы нет. Если позиции нет или она вам недоступна — 404 not_found.

Остатки

Остаток приходит в поле available в ответах GET /catalog и GET /catalog/{itemCode}. Отдельного запроса для остатков нет.

Право на остатки. Показ остатков включает менеджер. Если право не выдано, поля available в ответах нет, а каталог и заказы работают как обычно. Чтобы получать остатки, обратитесь к менеджеру.

Остаток одной позиции:

curl -sS -H "Authorization: Bearer $FC_TOKEN" "$FC_API/catalog/24014" \
  | jq '.items[] | {itemCode, warehouse, available}'
{ "itemCode": "24014", "warehouse": "23", "available": 40 }

Остатки по бренду и категории (код, склад, остаток):

curl -sS -G -H "Authorization: Bearer $FC_TOKEN" \
  --data-urlencode "brand=Apple" \
  --data-urlencode "category=iPhone" \
  --data-urlencode "limit=100" \
  "$FC_API/catalog" \
  | jq '.items[] | {itemCode, warehouse, available}'
{ "itemCode": "24014", "warehouse": "23", "available": 40 }
{ "itemCode": "24020", "warehouse": "23", "available": 12 }

Остатки меняются с каждым заказом. При добавлении в заказ сервер проверяет количество заново: если остатка не хватило, в ответе придут addedQty и capped: true (см. раздел «Заказы»).

Поля позиции каталога

ПолеЧто означает
itemCodeКод позиции (передаётся в заказ)
name, brand, categoryНазвание, бренд, категория
warehouseКод склада
currency, priceВалюта и ваша цена за штуку (с наценкой из карточки клиента)
availableОстаток. Поля нет, если остатки вам не показываются
tiersШкала цен: только в карточке и только если включена
markdown, serial, commentУценка: признак, серийный номер, комментарий (только если уценка вам доступна; такая позиция продаётся по одной штуке)
warehouseNameНазвание склада (только если разрешено)

5. Заказы (право orders:write)

Доступны, только если менеджер включил вам заказы, а токен выпущен с правом «каталог и заказы». Заказ — тот же документ, что корзина в боте и мини-приложении.

ЗапросДействиеНужно разрешение
POST /orders/linesДобавить позицию в заказзаказы включены
GET /orders/currentОткрытый заказ в текущей торговой сессиизаказы включены
GET /orders, GET /orders/{id}История и один заказ со статусомзаказы включены
PATCH /orders/{id}/lines/{lineId}Изменить количество строки (больше или меньше)allowModify
DELETE /orders/{id}/lines/{lineId}Убрать строкуallowDelete
POST /orders/{id}/cancelОтменить заказallowCancel

Добавить позицию

curl -X POST "$FC_API/orders/lines" \
  -H "Authorization: Bearer $FC_TOKEN" \
  -H "Idempotency-Key: 3f0c2a52-7b8e-4a43-9d1b-2f4a1c9e6b10" \
  -H "Content-Type: application/json" \
  -d '{"itemCode":"24014","warehouse":"23","qty":5}'

Ответ 200:

{
  "line": {
    "lineId": "ln_Qx3v9KpL2mZ1aBcD",
    "itemCode": "24014",
    "name": "iPhone 15 128GB Black",
    "brand": "Apple",
    "qty": 5,
    "price": 343,
    "sum": 1715,
    "currency": "USD",
    "addedAt": "2026-11-03T10:15:22.000Z"
  },
  "order": {
    "id": "6ae9aefdd12dd7057d4bf68e",
    "number": "",
    "status": "created",
    "currency": "USD",
    "warehouse": "23",
    "total": 1715,
    "cancelled": false,
    "readyToShip": false,
    "editable": true,
    "createdAt": "2026-11-03T10:15:22.000Z",
    "updatedAt": "2026-11-03T10:15:22.000Z",
    "lines": [ { "lineId": "ln_Qx3v9KpL2mZ1aBcD", "itemCode": "24014", "qty": 5, "price": 343, "sum": 1715, "…": "…" } ]
  },
  "created": true,
  "requestedQty": 5,
  "addedQty": 5,
  "capped": false
}

Здесь цена 343 — это цена ступени шкалы для 5 штук (как в примере карточки выше). Если остатка или лимита «в одни руки» хватило не на всё, придёт, например, "requestedQty": 5, "addedQty": 3, "capped": true: в заказ добавлено то, что доступно. Если не добавить ничего — 422 order_rejected.

Править и отменять

Текущий заказ: GET /orders/current

curl -sS -H "Authorization: Bearer $FC_TOKEN" "$FC_API/orders/current"
{
  "order": {
    "id": "6ae9aefdd12dd7057d4bf68e",
    "number": "",
    "status": "created",
    "currency": "USD",
    "warehouse": "23",
    "total": 502,
    "cancelled": false,
    "readyToShip": false,
    "editable": true,
    "createdAt": "2026-11-03T09:01:10.000Z",
    "updatedAt": "2026-11-03T09:14:55.000Z",
    "lines": [
      { "lineId": "ln_g_I2gvqZXVc9Y7DK", "itemCode": "24159", "name": "Apple AirPods Max 2 (USB-C) Blue 2026 MHWM4",
        "brand": "Apple", "qty": 3, "price": 100, "sum": 300, "currency": "USD" },
      { "lineId": "ln_eDECuLIGBiTGCs9x", "itemCode": "23972", "name": "Apple AirPods Max 2 (USB-C) Midnight 2026 MHWK4",
        "brand": "Apple", "qty": 2, "price": 101, "sum": 202, "currency": "USD" }
    ]
  }
}

Если открытого заказа в текущей торговой сессии нет, придёт { "order": null }.

Список и один заказ: GET /orders, GET /orders/{id}

curl -sS -G -H "Authorization: Bearer $FC_TOKEN" --data-urlencode "limit=20" "$FC_API/orders"
curl -sS -H "Authorization: Bearer $FC_TOKEN" "$FC_API/orders/6ae9aefdd12dd7057d4bf68e"

Список отдаёт { "items": [ …заказы… ], "nextCursor": "…" | null } (листается так же, как каталог, limit до 100), один заказ — объект в том же виде, что order выше. Чужой или несуществующий заказ — 404 not_found.

Поля заказа

ПолеЧто означает
id, numberИдентификатор заказа; номер документа в учётной системе (пустая строка, пока не присвоен)
statusСтатус заказа (например, created; у отменённого — «Отменён»)
currency, warehouse, totalВалюта, код склада, итог по строкам
cancelled, readyToShipОтменён; готов к отгрузке
editable, closedReasonМожно ли менять заказ через API; если нельзя — причина (cancelled, shipped, ready_to_ship, collecting, closed, unsupported)
lines[]Строки: lineId, itemCode, name, brand, qty, price, sum, currency, serial (для уценки), addedAt

Изменить количество строки: PATCH

Увеличить строку 23972 с 2 до 5 штук (ORDER — id заказа, LINE — lineId из GET /orders/current):

curl -X PATCH "$FC_API/orders/$ORDER/lines/$LINE" \
  -H "Authorization: Bearer $FC_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"qty":5}'
{
  "order": {
    "id": "6ae9aefdd12dd7057d4bf68e",
    "total": 800,
    "editable": true,
    "lines": [
      { "lineId": "ln_g_I2gvqZXVc9Y7DK", "itemCode": "24159", "qty": 3, "price": 100, "sum": 300, "…": "…" },
      { "lineId": "ln_xLW2qtzgku-2nn6t", "itemCode": "23972", "qty": 5, "price": 100, "sum": 500, "…": "…" }
    ],
    "…": "…"
  },
  "changed": true
}

В примере цена строки изменилась со 101 до 100: для 5 штук действует другая ступень шкалы, и по ней пересчитана вся строка. Поэтому изменился и lineId — для следующих запросов берите его из ответа. Строка остаётся одной: добавленные штуки склеиваются с ней, а не создают вторую строку.

Если остатка или лимита «в одни руки» хватило не на всё (запросили 500, можно 10), строка вырастет до доступного количества, а в ответе будет "capped": true:

{ "order": { "…": "…", "lines": [ { "itemCode": "23972", "qty": 10, "price": 99, "…": "…" } ] }, "changed": true, "capped": true }

Уменьшение работает так же ({"qty":3}), а резерв остатка возвращается. Запрос с тем же количеством вернёт "changed": false.

Убрать строку: DELETE

curl -X DELETE "$FC_API/orders/$ORDER/lines/$LINE" \
  -H "Authorization: Bearer $FC_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
{ "order": { "id": "6ae9aefdd12dd7057d4bf68e", "total": 500, "lines": [ { "itemCode": "23972", "qty": 5, "…": "…" } ], "…": "…" }, "changed": true }

Отменить заказ: POST /orders/{id}/cancel

curl -X POST "$FC_API/orders/$ORDER/cancel" \
  -H "Authorization: Bearer $FC_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
{ "order": { "id": "6ae9aefdd12dd7057d4bf68e", "status": "Отменён", "cancelled": true, "editable": false, "closedReason": "cancelled", "…": "…" }, "changed": true }

Примеры ошибок при работе с заказом

Устаревший lineId (строку уже изменили или удалили): 409, в ответе актуальный заказ — возьмите из него новый lineId и повторите с новым ключом.

HTTP 409
{
  "error": { "code": "line_changed", "message": "Строка заказа изменилась, перечитайте заказ" },
  "order": { "id": "6ae9aefdd12dd7057d4bf68e", "lines": [ "…" ], "…": "…" }
}

Нет остатка или исчерпан лимит (запрошенное количество недоступно совсем): 422, заказ не меняется.

HTTP 422
{ "error": { "code": "order_rejected", "message": "Товар недоступен в нужном количестве" } }

Действие не разрешено вам (например, удаление строк выключено менеджером): 403.

HTTP 403
{ "error": { "code": "forbidden", "message": "Удаление строк заказа через API отключено для этого клиента" } }

Последняя строка: 409 last_line_use_cancel. Нет заголовка Idempotency-Key: 400 idempotency_key_required. qty меньше 1: 400 bad_request (чтобы убрать строку, используйте DELETE).

Числа, коды товаров, цены и идентификаторы в примерах условные: ваши значения зависят от вашего каталога и настроек клиента.

6. Idempotency-Key

Все изменяющие запросы (POST, PATCH, DELETE) требуют заголовок Idempotency-Key. Без него — 400 idempotency_key_required.

7. Частые вопросы