API Phonifi B2B

v1 https://b2b-api.phonifi.ru/v1

Быстрый старт

Каталог и заказы по HTTP. Аутентификация — по ключу.

  1. Откройте b2b.phonifi.ru, раздел API.
  2. Нажмите «Выпустить ключ». Ключ показывается один раз.
  3. Передавайте его в заголовке каждого запроса.
curl -H "Authorization: Bearer b2b_xxxxxxxx_…" \
     https://b2b-api.phonifi.ru/v1/catalog?limit=5

Аутентификация

Ключ передаётся одним из двух заголовков — они равнозначны:

Authorization: Bearer b2b_xxxxxxxx_…
X-Api-Key: b2b_xxxxxxxx_…

Ключ обязателен для всех методов. Запрос без ключа или с недействительным ключом — 401.

У ключа есть области доступа: catalog:read, orders:read, orders:write, imei:read, imei:write, finance:read. Области выбираются при выпуске ключа в кабинете (раздел API) — отметьте только то, что нужно вашему сервису. Выдать ключу можно лишь те области, к которым есть доступ у сотрудника, выпускающего ключ. Обращение к методу вне выданных областей — 403.

Подписка

API — платный модуль с помесячной оплатой; подключается владельцем компании в личном кабинете (вкладка «Интеграции», раздел API). Без активной подписки все методы, кроме /health, отвечают 402 subscription_required. Каталог и заказы в самом кабинете подписки не требуют.

Ограничения

60 запросов в минуту на ключ. В каждом ответе:

ЗаголовокЗначение
X-RateLimit-Limitлимит запросов в минуту
X-RateLimit-Remainingостаток в текущей минуте
Retry-Afterсекунд до сброса лимита (при 429)

Формат данных

ПолеТипЗначение
priceintegerцена в копейках
totalintegerсумма в копейках
qtyintegerколичество, штук
createdAtstringдата и время, ISO 8601, UTC
skustringартикул, уникален в каталоге
Денежные поля — целые числа в копейках. 5200000 — это 52 000 ₽. Дробных значений в ответах не бывает; чтобы получить рубли, разделите на 100.

price: null — цена не установлена. Запросить её можно в личном кабинете.

Ответы в UTF-8. Списки постраничные: параметры page (с нуля) и limit, в ответе total.

Проверка доступности

GET/v1/health

Работает без ключа — так удобно проверить связь до его получения:

{ "ok": true, "service": "b2b-api", "version": 1 }

С ключом в заголовке Authorization ответ дополняется блоком key — рабочий ли ключ:

{ "ok": true, "service": "b2b-api", "version": 1,
  "key": { "valid": true, "works": true, "message": "Ключ рабочий",
           "scopes": ["catalog:read", "orders:read"], "rateLimitPerMinute": 60,
           "subscription": { "active": true, "paidUntil": "2026-10-26T00:00:00.000Z", "autoRenew": true } } }

valid: false — ключ неверный, отозван или его автор больше не в компании. works: false при верном ключе — не подключён модуль API.

Каталог

GET/v1/catalog

Область catalog:read.

ПараметрТипПо умолчаниюЗначение
limitinteger500размер страницы, максимум 1000
pageinteger0номер страницы
priced0 / 111 — только позиции с ценой, 0 — весь каталог

Сортировка по sku, порядок стабильный. model — модель без памяти и размера («iPhone 15»), variant — вариант внутри модели («128GB», «46mm»); у товаров без вариантов — пустая строка.

curl -H "X-Api-Key: $KEY" \
  "https://b2b-api.phonifi.ru/v1/catalog?limit=2"

{
  "total": 1061,
  "page": 0,
  "limit": 2,
  "items": [
    {
      "sku": "ip15-128black-EU",
      "brand": "Apple",
      "category": "iPhone",
      "model": "iPhone 15",
      "variant": "128GB",
      "name": "iPhone 15 128GB Black EU",
      "price": 5260000
    },
    {
      "sku": "ip15-128blue-EU",
      "brand": "Apple",
      "category": "iPhone",
      "model": "iPhone 15",
      "variant": "128GB",
      "name": "iPhone 15 128GB Blue EU",
      "price": 5220000
    }
  ]
}
GET/v1/catalog.xlsx

Тот же прайс файлом Excel: артикул, бренд, категория, название, цена в рублях. Только позиции с ценой.

Уценка

GET/v1/catalog/used

Область catalog:read. Тот же товар, но не новый: активирован, восстановлен, повреждена упаковка и т. п. /v1/catalog и /v1/catalog.xlsx отдают только новые товары. Позиция уценки — артикул и состояние: у одного артикула может быть несколько предложений с разным состоянием. condition — метки через « · », conditions — они же списком. Цена — по ценовой политике вашей компании, в копейках.

ПараметрТипПо умолчаниюЗначение
limitinteger500размер страницы, максимум 1000
pageinteger0номер страницы
curl -H "X-Api-Key: $KEY" \
  "https://b2b-api.phonifi.ru/v1/catalog/used?limit=1"

{
  "total": 214,
  "page": 0,
  "limit": 1,
  "items": [
    {
      "sku": "ip15-128black-US",
      "brand": "Apple",
      "category": "iPhone",
      "model": "iPhone 15",
      "variant": "128GB",
      "name": "iPhone 15 128GB Black US",
      "condition": "Активирован · Повреждена упаковка",
      "conditions": ["Активирован", "Повреждена упаковка"],
      "price": 4780000
    }
  ]
}

Заказы

GET/v1/orders

Область orders:read. Сортировка по дате создания, новые первыми.

ПараметрТипПо умолчаниюЗначение
limitinteger100размер страницы, максимум 200
pageinteger0номер страницы
{
  "total": 4,
  "page": 0,
  "limit": 100,
  "orders": [
    {
      "number": "TG000004",
      "status": "new",
      "total": 22100000,
      "comment": "",
      "createdAt": "2026-08-31T15:19:42.117Z",
      "items": [
        { "sku": "airpodsmax2-orange-US", "name": "AirPods Max 2 Orange US",
          "price": 3880000, "qty": 5 }
      ]
    }
  ]
}
statusЗначение
newНовый — ждёт проверки менеджером
approvedУтверждён — ждёт оплаты клиентом
processСобирается — оплачен
readyСобран
shippedОтгружен
finishedЗавершён
cancelledОтменён

price в позиции заказа — цена, по которой заказ оформлен.

POST/v1/orders

Область orders:write. Оформляет заказ из списка позиций. Цены — те же, что отдаёт /v1/catalog на момент запроса. Позиции без цены в заказ не попадают и возвращаются в badSkus; если таких все — 400. Деньги при оформлении не списываются: менеджер проверяет заказ и утверждает его (status: approved), после чего заказ оплачивается вызовом POST /v1/orders/{number}/pay или клиентом в кабинете. О смене статуса приходит вебхук order.status_changed. В ответах paidFrom (bonus, balance, mixed или null, пока не оплачен) и paid — сколько списано с каждой кассы, в копейках.

POST /v1/orders
{ "items": [ { "sku": "ip15-128black-EU", "qty": 2 } ], "comment": "склад №2" }

201
{
  "number": "TG000012",
  "items": [ { "sku": "ip15-128black-EU", "name": "iPhone 15 128GB Black EU",
               "price": 5260000, "qty": 2 } ],
  "paidFrom": null, "paid": { "bonus": 0, "money": 0 },
  "badSkus": []
}
GET/v1/orders/{number}

Область orders:read. Состав, статус и состояние согласования. review.state = awaiting_client означает, что менеджер изменил заказ при сборке и ждёт вашего ответа — без него заказ не соберут.

{ "order": { "number": "TG000012", "status": "process", "total": 10520000,
             "comment": "склад №2", "createdAt": "…", "items": [ … ],
             "paidFrom": "mixed", "paid": { "bonus": 300000, "money": 10220000 },
             "review": { "state": "awaiting_client", "at": "…" } } }
POST/v1/orders/{number}/review

Область orders:write. Ответ на правки менеджера: { "decision": "approve" } — одобрить, { "decision": "cancel" } — отменить заказ (деньги возвращаются). Если правок, ожидающих ответа, нет — 409.

POST/v1/orders/{number}/pay

Область orders:write. Оплата утверждённого заказа (или доплата разницы после одобренных правок): сначала бонусы, которым разрешены товары, остаток — с баланса, в пределах кредитного лимита, если он есть. Успех переводит заказ в process. Не хватает средств — 402 insufficient_funds, ничего не списывается и заказ не меняется; пока заказ не утверждён или ждёт вашего ответа на правки — 409.

200
{ "ok": true, "number": "TG000012", "status": "process",
  "paidFrom": "mixed", "paid": { "bonus": 300000, "money": 10220000 }, "amount": 10520000 }

Проверки IMEI

Идентификаторы услуг — наши и постоянные: какой бы сервис проверок ни работал на нашей стороне, id не меняется. Зашивайте нужные id из таблицы ниже. Если проверка временно недоступна, в списке у неё available: false, а запуск отвечает 409 unavailable.

GET/v1/imei/services

Область imei:read. Услуги с ценами (в копейках), доступные — по возрастанию цены, недоступные — в конце с price: null. У большинства есть description — что проверяется и ограничения (только IMEI, только Apple и т.п.).

{ "services": [
  { "id": "brand-model", "title": "Бренд и модель по IMEI", "price": 200, "available": true },
  { "id": "fmi", "title": "Find My (Локатор): включён или выключен", "price": 200,
    "available": true, "description": "Включён ли «Локатор» (Find My) на iPhone или iPad…" }
] }
idУслуга
brand-modelБренд и модель по IMEI
model-lookupМодель по IMEI или серийному номеру (все бренды)
fmiFind My (Локатор): включён или выключен
icloud-statusСтатус iCloud: чистый или потерян
blacklistЧёрный список GSMA
blacklist-proЧёрный список GSMA с историей
carrier-simlockОператор и SIM-lock (Apple)
simlockSIM-lock Apple (только IMEI)
warrantyГарантия, активация и замена (Apple)
warranty-snГарантия и активация по серийному номеру (Apple)
warranty-proГарантия Pro и активация (Apple)
apple-basicApple: базовая информация (PRO)
apple-fullApple: полная информация с оператором
apple-full-mdmApple: полная информация с оператором, MDM и Chimaera
apple-full-nocarrierApple: полная информация без оператора
apple-ultimateApple ULTIMATE: продавец, Chimaera, MDM, чёрный список
apple-customApple: расширенная информация с оператором (V2)
mdmMDM (корпоративная блокировка), Find My и описание модели
sold-by-gsxПродавец Apple и данные GSX
sold-byПродавец Apple (без GSX)
model-color-storageМодель, цвет, память и Find My (Apple)
model-descriptionОписание модели: модель, цвет, память (Apple)
model-description-mpnОписание модели, партномер и Find My (Apple)
replacement-activeСтатус замены: активное устройство (Apple)
replaced-originalСтатус замены: исходное устройство (Apple)
imac-fmiFind My на iMac и MacBook по серийному номеру
imei-to-snIMEI в серийный номер (Apple)
imei-pairIMEI1 и IMEI2 устройства Apple
eidEID по IMEI
serial-infoИнформация по серийному номеру Apple
demo-unitДемо-устройство Apple
chimaeraChimaera: внутренний чёрный список Apple
gsx-activationGSX: дата активации, Next Tether, последнее восстановление
verizon-esnVerizon (США): ESN чистый или потерян
tmobile-esnT-Mobile (США): проверка ESN
samsungSamsung: информация
samsung-knoxSamsung: информация и Knox Guard
samsung-knox-s2Samsung: информация и Knox Guard (сервер 2)
samsung-blacklistSamsung: информация и чёрный список
huaweiHuawei: информация
xiaomiXiaomi: Mi-блокировка и информация
oneplusOnePlus: информация
motorolaMotorola: информация
honorHonor: информация
pixelGoogle Pixel: информация и гарантия
POST/v1/imei/checks

Область imei:write. Одна проверка: { "imei": "355948398738345", "serviceId": "brand-model" }. Номер — 8–17 букв и цифр (IMEI или серийный номер, если услуга его принимает). Списание происходит до запроса: сначала бонусы (если им разрешены проверки), остаток — с баланса; в ответе paidFrom и paid — чем и на сколько оплачено. Если сервис не смог проверить номер (status: failed), деньги возвращаются автоматически.

201
{ "id": 318, "status": "done", "price": 200,
  "paidFrom": "balance", "paid": { "bonus": 0, "money": 200 },
  "result": { "imei": "355948398738345", "status": "success",
              "object": { "brand": "Apple Inc", "model": "Apple iPhone 17 Pro Max (A3526)",
                          "name": "iPhone 17 Pro Max (A3526)" },
              "result": "IMEI: 355948398738345<br>Brand: Apple Inc<br>…" } }

status: done — результат в result; pending — сервис ещё работает, опрашивайте GET /v1/imei/checks/{id} или подключите вебхук; failed — проверить не удалось, деньги возвращены. Поле result.result — готовый текст для показа, строки разделены <br>; result.object — структурные поля, набор зависит от услуги.

POST/v1/imei/checks/bulk

Область imei:write. До 100 номеров одной услугой: { "imeis": [ … ], "serviceId": "brand-model" }. Списание за каждый номер — сразу, сами проверки выполняются в фоне; результаты забирайте по id или получайте вебхуком. Повторы и мусор отклоняются с причиной (duplicate, bad_format), при нехватке средств остаток — payment_failed.

201
{ "accepted": [ { "id": 319, "imei": "355948398738346" } ],
  "rejected": [ { "imei": "35594", "reason": "bad_format" } ],
  "price": 200 }
GET/v1/imei/checks/{id}

Область imei:read. Одна проверка; незавершённая дотягивается у сервиса прямо в этом запросе.

GET/v1/imei/checks

Область imei:read. Список проверок компании, новые первыми, постранично.

Финансы

Область finance:read. Только чтение — пополнения и бонусы проводятся через кабинет.

GET/v1/finance
{ "balance": 1250000, "available": 3250000,
  "creditLimit": 2000000,
  "bonus": 50000, "bonusByPurpose": { "orders": 50000, "imei": 5000, "subscriptions": 0 },
  "bonusScopes": ["orders", "imei"] }

available — баланс плюс кредитный лимит (на сколько баланс может уйти в минус; задаёт администратор). Бонусы начисляются отдельными начислениями, у каждого свои назначения: bonusByPurpose — сколько сейчас можно потратить на orders (товары), imei (проверки IMEI), subscriptions (подписки); одно начисление может входить в несколько назначений. bonusScopes — назначения, на которые есть что тратить.

GET/v1/finance/operations

Выписка по балансу, новые первыми: kind (deposit, charge, refund, adjustment), amount, balanceAfter, title, orderNumber, at.

GET/v1/finance/bonus-operations

Те же поля по бонусам плюс grantId — начисление, к которому относится операция: kind — grant (начисление), spend (оплата), refund (возврат в то же начисление), revoke (начисление отозвано или его остаток обнулён администратором).

Порядок оплаты один для заказов, проверок и подписок: сначала бонусы (если назначение разрешает), остаток — с баланса, в пределах кредитного лимита, если он есть. Смешанная оплата видна как две операции с одним orderNumber: бонусная часть в /v1/finance/bonus-operations, денежная — в /v1/finance/operations; подписи операций называют обе части. Возврат идёт в ту же кассу, откуда списано.

Всё из кабинета

Всё, что компания делает в личном кабинете, доступно и через API — теми же правилами и с теми же проверками прав. Запрос выполняется от имени автора ключа с его текущими правами: урезали сотруднику права — ключ сузился; сотрудника заблокировали — ключ не работает. Ответы в формате кабинета: ошибка — { "error": "текст" }, деньги — в копейках (…Kopecks).

МетодОбластьЧто делает
GET /v1/meлюбаякто автор ключа, компания, права и доступные области
GET /v1/catalog/brands, /facets, /productscatalog:readкаталог с фильтрами кабинета: бренд, категория, модель, вариант, поиск; state=used — уценка, state=deals — «Выгодно!»: позиции дневной рассылки «Выгодные позиции!» с полем wasKopecks (вчерашняя цена); brands=A,B — несколько брендов сразу (режим «По категориям»: /facets?category=… без бренда отдаёт и brands — бренды категории)
GET /v1/cartorders:readкорзина компании (одна на компанию, общая с кабинетом и ботом)
POST /v1/cart {sku, qty}, DELETE /v1/cartorders:writeположить позицию (qty 0 — убрать), очистить корзину
GET /v1/cart/template.xlsx, POST /v1/cart/importorders:writeбланк заказа Excel и его загрузка — заменяет корзину
POST /v1/cart/checkout {comment}orders:writeоформить заказ из корзины
PATCH /v1/orders/{number} {items: [{sku, qty}]}orders:writeизменить состав заказа целиком (цену меняет только менеджер); правка уходит менеджеру
POST /v1/orders/{number}/status {status: "cancelled"}orders:writeотменить заказ, пока он не ушёл со склада
GET /v1/orders/{number}/commentsorders:readкомментарии к заказу лентой: первый — при оформлении, дальше клиент и менеджер (side: client/staff, author, text, at)
POST /v1/orders/{number}/comments {text}orders:writeдобавить комментарий (до 1000 символов), только пока заказ можно изменить или клиент отвечает на правки менеджера; прежние не меняются. Комментарий можно передать и полем comment в PATCH /v1/orders/{number}
GET /v1/orders/{number}/export.xlsx (.pdf)orders:readзаказ файлом
GET /v1/price-requests, /countprices:readзапросы цены и ответы менеджера
POST /v1/price-requests {sku, qty}prices:writeзапросить цену позиции без прайса
POST /v1/imei/checks/{id}/refreshimei:writeперечитать результат проверки
GET /v1/bonus, /v1/bonus/ledgerfinance:readбонусы: начисления с остатками и движение
POST /v1/finance/topups {amountRub}finance:writeзаявка на пополнение баланса
GET /v1/subscriptionsfinance:readплатные модули компании
POST /v1/subscriptions/{module}/activate, /auto-renewfinance:writeподключить модуль, автопродление
GET /v1/company/people, GET /v1/company/invitescompany:readсотрудники и приглашения — ключ владельца
PUT /v1/company/people/{tg}/permissions, POST/DELETE /v1/company/invitescompany:writeправа сотрудника, пригласить и отозвать приглашение — ключ владельца
GET/PUT /v1/webhook, POST /v1/webhook/testwebhooks:writeвебхук компании — ключ владельца
GET /v1/support, /unread; POST /v1/support {text}support:read / support:writeчат с менеджером
/v1/integrations/avito…integrations:read / integrations:writeинтеграция с Авито — те же методы, что в кабинете: подключение, связки, синхронизация, заказы

Через ключ нельзя: выпускать и отзывать сами ключи, управлять сессиями входа и личными настройками уведомлений — это управление доступом и личное, только в кабинете.

Вебхуки

Чтобы не опрашивать API, задайте адрес в кабинете (раздел API, «Вебхук») или методом PUT /v1/webhook — это делает владелец компании. Мы отправляем POST с JSON при событиях:

eventКогдаdata
order.createdоформлен заказ — в кабинете, в боте, файлом или через APIorderNumber, status, source, items[] (sku, name, qty, priceKopecks), totalKopecks
order.status_changedсменился статус заказаorderNumber, status
order.changedменеджер поправил заказ при сборке — нужен ваш ответorderNumber, totalKopecks, changes[] (sku, kind: added/removed/changed, was, now)
price.changedизменились ваши цены: новый прайс поставщиков или ваша наценкаchanges[] (sku, name, oldPriceKopecks, newPriceKopecks; null — цены нет), part, parts — пачками по 500
imei.check_completedпроверка завершиласьcheckId, imei, serviceId, status
pingкнопка «Проверить» в кабинетеmessage
POST https://ваш-адрес
X-B2B-Signature: 3f1a…   (HMAC-SHA256 тела, ключ — секрет из кабинета, hex)
X-B2B-Event: order.status_changed
X-B2B-Delivery: 7d0c…    (id события — тот же при повторе)
X-B2B-Attempt: 1

{ "id": "7d0c…", "event": "order.status_changed", "at": "2026-09-03T08:12:41.000Z",
  "data": { "orderNumber": "TG000012", "status": "shipped" } }

Проверяйте подпись: посчитайте HMAC-SHA256 от сырого тела запроса секретом из кабинета и сравните с заголовком. Отвечайте любым кодом 2xx в течение 10 секунд.

Повторы. Не дошло — повторяем через 1, 5, 30 минут, 2, 6, 12 и 24 часа: до 8 попыток за двое суток. Очередь переживает перезапуски сервиса. Повтор несёт тот же id — по нему отсекайте дубли. Порядок событий не гарантирован: сверяйтесь с полем at. После всех попыток событие считается недоставленным, причина видна в кабинете. Событие — ускорение, а не единственный источник истины: при сомнении перечитайте объект через API. Адрес должен быть внешним http(s); редиректы не выполняются.

Ошибки

Ошибка — это JSON с полем error и пояснением.

{ "error": "unauthorized", "message": "Ключ недействителен" }
КодerrorПричина
400bad_requestТело запроса не разобрано
401unauthorizedКлюч отсутствует, отозван или недействителен
403forbiddenМетод вне выданных областей либо доступ закрыт
402subscription_requiredПодписка на API не активна
402rejectedНедостаточно средств для операции
404not_foundМетод или объект не существует
409rejectedОперация невозможна в текущем состоянии
409unavailableПроверка IMEI сейчас недоступна
502upstreamВнешний сервис проверок недоступен
429rate_limitedПревышен лимит запросов
503startingСервис перезапускается
500internalВнутренняя ошибка

Ключи

Ключи выпускаются и отзываются в личном кабинете, раздел API. Там же указано время последнего использования каждого ключа.

Отзыв действует немедленно. Восстановить ключ нельзя — вместо этого выпускается новый.

Ключ даёт доступ к вашим данным. Не кладите его в репозиторий и не передавайте в параметрах адреса: они оседают в логах и в истории браузера. Только заголовок. Если ключ мог утечь — отзовите и выпустите новый.

На компанию можно держать до десяти действующих ключей — по одному на каждую интеграцию.