Быстрый старт
Каталог и заказы по HTTP. Аутентификация — по ключу.
- Откройте b2b.phonifi.ru, раздел API.
- Нажмите «Выпустить ключ». Ключ показывается один раз.
- Передавайте его в заголовке каждого запроса.
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) |
Формат данных
| Поле | Тип | Значение |
|---|---|---|
price | integer | цена в копейках |
total | integer | сумма в копейках |
qty | integer | количество, штук |
createdAt | string | дата и время, ISO 8601, UTC |
sku | string | артикул, уникален в каталоге |
5200000 — это 52 000 ₽. Дробных значений в ответах не бывает;
чтобы получить рубли, разделите на 100.
price: null — цена не установлена. Запросить её можно в личном кабинете.
Ответы в UTF-8. Списки постраничные: параметры page (с нуля) и limit, в ответе total.
Проверка доступности
Работает без ключа — так удобно проверить связь до его получения:
{ "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.
Каталог
Область catalog:read.
| Параметр | Тип | По умолчанию | Значение |
|---|---|---|---|
limit | integer | 500 | размер страницы, максимум 1000 |
page | integer | 0 | номер страницы |
priced | 0 / 1 | 1 | 1 — только позиции с ценой, 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
}
]
}
Тот же прайс файлом Excel: артикул, бренд, категория, название, цена в рублях. Только позиции с ценой.
Уценка
Область catalog:read. Тот же товар, но не новый: активирован, восстановлен, повреждена упаковка и т. п.
/v1/catalog и /v1/catalog.xlsx отдают только новые товары. Позиция уценки — артикул и
состояние: у одного артикула может быть несколько предложений с разным состоянием. condition — метки
через « · », conditions — они же списком. Цена — по ценовой политике вашей компании, в копейках.
| Параметр | Тип | По умолчанию | Значение |
|---|---|---|---|
limit | integer | 500 | размер страницы, максимум 1000 |
page | integer | 0 | номер страницы |
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
}
]
}
Заказы
Область orders:read. Сортировка по дате создания, новые первыми.
| Параметр | Тип | По умолчанию | Значение |
|---|---|---|---|
limit | integer | 100 | размер страницы, максимум 200 |
page | integer | 0 | номер страницы |
{
"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 в позиции заказа — цена, по которой заказ оформлен.
Область 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": []
}
Область 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": "…" } } }
Область orders:write. Ответ на правки менеджера: { "decision": "approve" } — одобрить, { "decision": "cancel" } — отменить заказ (деньги возвращаются). Если правок, ожидающих ответа, нет — 409.
Область 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.
Область 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 или серийному номеру (все бренды) |
fmi | Find My (Локатор): включён или выключен |
icloud-status | Статус iCloud: чистый или потерян |
blacklist | Чёрный список GSMA |
blacklist-pro | Чёрный список GSMA с историей |
carrier-simlock | Оператор и SIM-lock (Apple) |
simlock | SIM-lock Apple (только IMEI) |
warranty | Гарантия, активация и замена (Apple) |
warranty-sn | Гарантия и активация по серийному номеру (Apple) |
warranty-pro | Гарантия Pro и активация (Apple) |
apple-basic | Apple: базовая информация (PRO) |
apple-full | Apple: полная информация с оператором |
apple-full-mdm | Apple: полная информация с оператором, MDM и Chimaera |
apple-full-nocarrier | Apple: полная информация без оператора |
apple-ultimate | Apple ULTIMATE: продавец, Chimaera, MDM, чёрный список |
apple-custom | Apple: расширенная информация с оператором (V2) |
mdm | MDM (корпоративная блокировка), 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-fmi | Find My на iMac и MacBook по серийному номеру |
imei-to-sn | IMEI в серийный номер (Apple) |
imei-pair | IMEI1 и IMEI2 устройства Apple |
eid | EID по IMEI |
serial-info | Информация по серийному номеру Apple |
demo-unit | Демо-устройство Apple |
chimaera | Chimaera: внутренний чёрный список Apple |
gsx-activation | GSX: дата активации, Next Tether, последнее восстановление |
verizon-esn | Verizon (США): ESN чистый или потерян |
tmobile-esn | T-Mobile (США): проверка ESN |
samsung | Samsung: информация |
samsung-knox | Samsung: информация и Knox Guard |
samsung-knox-s2 | Samsung: информация и Knox Guard (сервер 2) |
samsung-blacklist | Samsung: информация и чёрный список |
huawei | Huawei: информация |
xiaomi | Xiaomi: Mi-блокировка и информация |
oneplus | OnePlus: информация |
motorola | Motorola: информация |
honor | Honor: информация |
pixel | Google Pixel: информация и гарантия |
Область 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 — структурные поля, набор зависит от услуги.
Область 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 }
Область imei:read. Одна проверка; незавершённая дотягивается у сервиса прямо в этом запросе.
Область imei:read. Список проверок компании, новые первыми, постранично.
Финансы
Область finance:read. Только чтение — пополнения и бонусы проводятся через кабинет.
{ "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 — назначения, на которые есть что тратить.
Выписка по балансу, новые первыми: kind (deposit, charge, refund, adjustment), amount, balanceAfter, title, orderNumber, at.
Те же поля по бонусам плюс 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, /products | catalog:read | каталог с фильтрами кабинета: бренд, категория, модель, вариант, поиск; state=used — уценка, state=deals — «Выгодно!»: позиции дневной рассылки «Выгодные позиции!» с полем wasKopecks (вчерашняя цена); brands=A,B — несколько брендов сразу (режим «По категориям»: /facets?category=… без бренда отдаёт и brands — бренды категории) |
GET /v1/cart | orders:read | корзина компании (одна на компанию, общая с кабинетом и ботом) |
POST /v1/cart {sku, qty}, DELETE /v1/cart | orders:write | положить позицию (qty 0 — убрать), очистить корзину |
GET /v1/cart/template.xlsx, POST /v1/cart/import | orders: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}/comments | orders: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, /count | prices:read | запросы цены и ответы менеджера |
POST /v1/price-requests {sku, qty} | prices:write | запросить цену позиции без прайса |
POST /v1/imei/checks/{id}/refresh | imei:write | перечитать результат проверки |
GET /v1/bonus, /v1/bonus/ledger | finance:read | бонусы: начисления с остатками и движение |
POST /v1/finance/topups {amountRub} | finance:write | заявка на пополнение баланса |
GET /v1/subscriptions | finance:read | платные модули компании |
POST /v1/subscriptions/{module}/activate, /auto-renew | finance:write | подключить модуль, автопродление |
GET /v1/company/people, GET /v1/company/invites | company:read | сотрудники и приглашения — ключ владельца |
PUT /v1/company/people/{tg}/permissions, POST/DELETE /v1/company/invites | company:write | права сотрудника, пригласить и отозвать приглашение — ключ владельца |
GET/PUT /v1/webhook, POST /v1/webhook/test | webhooks: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 | оформлен заказ — в кабинете, в боте, файлом или через API | orderNumber, 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 | Причина |
|---|---|---|
| 400 | bad_request | Тело запроса не разобрано |
| 401 | unauthorized | Ключ отсутствует, отозван или недействителен |
| 403 | forbidden | Метод вне выданных областей либо доступ закрыт |
| 402 | subscription_required | Подписка на API не активна |
| 402 | rejected | Недостаточно средств для операции |
| 404 | not_found | Метод или объект не существует |
| 409 | rejected | Операция невозможна в текущем состоянии |
| 409 | unavailable | Проверка IMEI сейчас недоступна |
| 502 | upstream | Внешний сервис проверок недоступен |
| 429 | rate_limited | Превышен лимит запросов |
| 503 | starting | Сервис перезапускается |
| 500 | internal | Внутренняя ошибка |
Ключи
Ключи выпускаются и отзываются в личном кабинете, раздел API. Там же указано время последнего использования каждого ключа.
Отзыв действует немедленно. Восстановить ключ нельзя — вместо этого выпускается новый.
На компанию можно держать до десяти действующих ключей — по одному на каждую интеграцию.