MMeerPartners docs

API: Merchant (self-service)

Self-service API бизнеса MeerPartners: офферы, эскроу-баланс и депозит, ключи интеграции, аналитика, паблишеры и допуски, профиль, тест постбэка.

Группа /api/v1/merchant/* — кабинет бизнеса через API. Все эндпоинты требуют JWT мерчанта. tenant_id резолвится только из членства в БД (анти-IDOR), никогда из тела запроса.

Заголовок X-Tenant-Id

Если ваш аккаунт владеет несколькими бизнесами, при каждом запросе указывайте активный через заголовок X-Tenant-Id: <id>. Значение валидируется против ваших членств. Если бизнес один — заголовок не нужен. Если бизнесов несколько и заголовок не передан — 400 TENANT_HEADER_REQUIRED со списком ваших tenant в details.tenants.

Роли: merchant_owner — полный доступ (CRUD офферов, ключи, депозит, гранты); merchant_member — только чтение (GET). Reveal секрета ключа — только владелец. Недостаток прав → 403 FORBIDDEN. Списочные ответы здесь имеют форму { "data": [...], "total": N }.

Офферы

GET /merchant/offers

GET/api/v1/merchant/offers🔒 Bearer JWT

Список офферов бизнеса. Query: skip (0+), limit (1–200, по умолчанию 50). Ответ — { "data": [...], "total": N }.

GET /merchant/offers/{offer_id}

GET/api/v1/merchant/offers/{offer_id}🔒 Bearer JWT

Карточка одного оффера (см. форму ответа ниже).

POST /merchant/offers

POST/api/v1/merchant/offers🔒 Bearer JWT

Создать оффер. Новый оффер уходит в статус pending_review (модерация платформой). Только merchant_owner.

namestringrequired
Название (1–200 символов).
monetization_modelstringrequired
Модель: cpl, cpa, revshare или hybrid.
descriptionstringoptional
Описание (до 2000 символов).
destination_urlstringoptional
Целевой URL (лендинг), до 2048 символов.
geoarrayoptional
Список 2-буквенных кодов стран.
categorystringoptional
Категория (до 64 символов).
visibilitystringoptional
public или private. По умолчанию public.
currencystringoptional
Валюта оффера (ISO-код). По умолчанию RUB.
cpa_amountstringoptional
Ставка CPA (NUMERIC-строка). Для cpa/hybrid.
cpl_amountstringoptional
Ставка CPL (NUMERIC-строка). Для cpl.
revshare_percentstringoptional
Процент RevShare (NUMERIC-строка). Для revshare/hybrid.
hold_daysintegeroptional
Hold-период в днях (0–180, по умолчанию 14).
attribution_window_hoursintegeroptional
Окно атрибуции в часах (1–8760, по умолчанию 720).
multilevel_enabledbooleanoptional
Включить multi-level. По умолчанию false.
levels_enabledintegeroptional
Число уровней (1–7, по умолчанию 3).
level_ratesarrayoptional
Ставки по уровням (список NUMERIC-строк).
max_total_commission_percentstringoptional
CAP на суммарную комиссию в процентах.
max_total_commission_amountstringoptional
CAP на суммарную комиссию в абсолюте.
Ответ 201
{
  "id": 501,
  "tenant_id": 17,
  "name": "Acme Premium",
  "monetization_model": "cpa",
  "currency": "RUB",
  "visibility": "public",
  "status": "pending_review",
  "hold_days": 14,
  "geo": ["RU", "KZ"],
  "multilevel_enabled": true,
  "active_tariff": {
    "id": 700,
    "monetization_model": "cpa",
    "cpa_amount": "750.00",
    "cpl_amount": null,
    "revshare_percent": null,
    "currency": "RUB",
    "hold_days": 14,
    "max_levels": 3,
    "level_rates": { "1": "10", "2": "5", "3": "2" }
  },
  "rejection_reason": null,
  "terms": {},
  "created_at": "2026-06-14T12:00:00Z",
  "updated_at": null
}

PUT /merchant/offers/{offer_id}

PUT/api/v1/merchant/offers/{offer_id}🔒 Bearer JWT

Редактировать оффер. Только merchant_owner. Тело — те же поля, что в создании, но все опциональны (передавайте только изменяемые). Если оффер был отклонён, в ответе rejection_reason содержит причину.

Баланс и депозит

GET /merchant/balance

GET/api/v1/merchant/balance🔒 Bearer JWT

Эскроу-баланс бизнеса с леджером движений. Только merchant_owner. Query: currency (ISO-код, по умолчанию RUB).

currencystringrequired
Валюта баланса.
availablestringrequired
Доступно (из чего платятся начисления).
reservedstringrequired
Зарезервировано под hold-начисления.
total_depositedstringrequired
Всего пополнено.
total_spentstringrequired
Всего израсходовано.
accruals_pausedbooleanrequired
Приостановлены ли начисления (низкий баланс).
accruals_paused_reasonstringoptional
Причина приостановки начислений.
ledgerarrayoptional
Последние движения по балансу.
Ответ 200
{
  "currency": "RUB",
  "available": "84000.00",
  "reserved": "12000.00",
  "total_deposited": "200000.00",
  "total_spent": "104000.00",
  "accruals_paused": false,
  "accruals_paused_reason": null,
  "ledger": [
    {
      "id": 9100,
      "entry_type": "deposit",
      "amount": "100000.00",
      "balance_after": "184000.00",
      "currency": "RUB",
      "ref_type": "deposit_intent",
      "ref_id": 55,
      "created_at": "2026-06-10T10:00:00Z"
    }
  ]
}

POST /merchant/deposit

POST/api/v1/merchant/deposit🔒 Bearer JWT

Инициировать пополнение. Возвращает pending-интент — фактическое зачисление приходит по вебхуку провайдера. Только merchant_owner. Успех — 202 Accepted.

amountstringrequired
Сумма (NUMERIC-строка, > 0).
currencystringoptional
Валюта (ISO-код). По умолчанию RUB.
providerstringoptional
Провайдер: card / crypto / manual / stub. По умолчанию manual.
Ответ 202
{
  "deposit_intent_id": 55,
  "status": "pending",
  "amount": "100000.00",
  "currency": "RUB",
  "provider": "card",
  "provider_ref": "yk_2f9...",
  "redirect_url": "https://pay.example.com/...",
  "instructions": null
}

Зачисление — по вебхуку

POST /merchant/deposit создаёт намерение, но не зачисляет деньги. Реальное пополнение происходит, когда провайдер вызывает S2S-вебхук платформы (POST /api/v1/merchant/deposit/webhook, подпись X-Provider-Signature + X-Timestamp, дедуп по provider_tx_id). Этот вебхук вызывает провайдер, а не ваша интеграция.

Ключи интеграции

Ключ tenant_api_key нужен для S2S: подписи постбэка и token-exchange. Скоупы: postback, sso, readonly.

GET /merchant/integration/keys

GET/api/v1/merchant/integration/keys🔒 Bearer JWT

Список ключей. Только merchant_owner. Секрет маскирован — виден только хвост secret_tail.

Ответ 200
{
  "data": [
    {
      "id": 12,
      "key_id": "ak_live_8d3f1a",
      "name": "prod-postback",
      "scopes": ["postback"],
      "is_active": true,
      "secret_tail": "9c1e",
      "created_at": "2026-05-01T10:00:00Z",
      "last_used_at": "2026-06-14T11:55:00Z",
      "revoked_at": null
    }
  ]
}

POST /merchant/integration/keys

POST/api/v1/merchant/integration/keys🔒 Bearer JWT

Выпустить ключ. Секрет показывается один раз в ответе. Только merchant_owner.

namestringrequired
Название ключа (1–100 символов).
scopesarrayrequired
Скоупы: непустой список из postback, sso, readonly.
Ответ 201
{
  "id": 12,
  "key_id": "ak_live_8d3f1a",
  "name": "prod-postback",
  "scopes": ["postback"],
  "secret": "sk_live_a1b2c3d4e5f6...9c1e",
  "secret_tail": "9c1e",
  "warning": "Сохраните secret — он больше не будет показан",
  "created_at": "2026-06-14T12:00:00Z"
}

POST /merchant/integration/keys/{key_id}/reveal

POST/api/v1/merchant/integration/keys/{key_id}/reveal🔒 Bearer JWT

Показать секрет существующего ключа. Только merchant_owner.

Ответ 200
{ "key_id": "ak_live_8d3f1a", "secret": "sk_live_a1b2c3...9c1e", "secret_tail": "9c1e" }

POST /merchant/integration/keys/{key_id}/revoke

POST/api/v1/merchant/integration/keys/{key_id}/revoke🔒 Bearer JWT

Отозвать ключ. Только merchant_owner.

Ответ 200
{ "status": "revoked" }

POST /merchant/integration/test-postback

POST/api/v1/merchant/integration/test-postback🔒 Bearer JWT

Тестовый постбэк (dry-run через реальный флоу) — проверить интеграцию без боевой конверсии. Только merchant_owner.

key_idstringrequired
key_id ключа из integration/keys.
external_order_idstringrequired
Тестовый идентификатор заказа (1–120 символов).
offer_idintegerrequired
ID оффера.
amountstringoptional
Сумма. По умолчанию 1.00.
currencystringoptional
Валюта. По умолчанию RUB.
event_typestringoptional
sale или lead. По умолчанию sale.
Ответ 200
{
  "accepted": true,
  "duplicate": false,
  "conversion_id": 778,
  "error_code": null,
  "error_message": null
}

Аналитика

GET /merchant/analytics

GET/api/v1/merchant/analytics🔒 Bearer JWT

Аналитика бизнеса: клики, конверсии, расход — всего и по офферам. Query: date_from, date_to, currency (ISO-код, по умолчанию RUB).

total_clicksintegerrequired
Всего кликов.
total_conversionsintegerrequired
Всего конверсий.
total_spentstringrequired
Суммарный расход (NUMERIC).
currencystringrequired
Валюта.
by_offerarrayoptional
Разбивка по офферам (clicks, conversions, cr, spent, epc).
Ответ 200
{
  "date_from": "2026-06-01",
  "date_to": "2026-06-14",
  "total_clicks": 53200,
  "total_conversions": 1180,
  "total_spent": "884000.00",
  "currency": "RUB",
  "by_offer": [
    {
      "offer_id": 501,
      "offer_name": "Acme Premium",
      "clicks": 21000,
      "conversions": 540,
      "cr": "2.57",
      "spent": "405000.00",
      "epc": "19.29",
      "currency": "RUB"
    }
  ]
}

Паблишеры и допуски

GET /merchant/affiliates

GET/api/v1/merchant/affiliates🔒 Bearer JWT

Инфлюенсеры, работающие с офферами бизнеса. Query: skip, limit (1–200, по умолчанию 50).

Ответ 200
{
  "data": [
    {
      "affiliate_id": 88,
      "display_name": "Аня",
      "email": "creator@example.com",
      "referral_code": "ANYA7K",
      "private_access": [
        { "offer_id": 502, "offer_name": "Acme VIP", "status": "approved" }
      ],
      "total_conversions": 96,
      "total_clicks": 4210
    }
  ],
  "total": 34
}

POST /merchant/affiliates/{affiliate_id}/grant

POST/api/v1/merchant/affiliates/{affiliate_id}/grant🔒 Bearer JWT

Выдать инфлюенсеру допуск к приватному офферу. Только merchant_owner.

offer_idintegerrequired
ID приватного оффера.
Ответ 200
{ "status": "granted" }

POST /merchant/affiliates/{affiliate_id}/revoke

POST/api/v1/merchant/affiliates/{affiliate_id}/revoke🔒 Bearer JWT

Отозвать допуск к приватному офферу. Только merchant_owner. Тело — { "offer_id": <int> }.

Ответ 200
{ "status": "revoked" }

Профиль

GET /merchant/profile

GET/api/v1/merchant/profile🔒 Bearer JWT

Профиль бизнеса: компания и контакты.

company_namestringoptional
Название компании.
contact_emailstringoptional
Контактный email.
website_urlstringoptional
Сайт.
Ответ 200
{ "company_name": "Acme LLC", "contact_email": "ops@acme.com", "website_url": "https://acme.com" }

PUT /merchant/profile

PUT/api/v1/merchant/profile🔒 Bearer JWT

Обновить профиль бизнеса. Только merchant_owner.

company_namestringoptional
Название компании (1–255 символов).
contact_emailstringoptional
Контактный email (до 255 символов).
website_urlstringoptional
Сайт (до 2048 символов).

Возвращает обновлённый профиль.

Команда и авто-пополнение

В этой же группе есть управление командой (/merchant/team, инвайты /merchant/team/invites) и настройки авто-пополнения (/merchant/deposit/auto-settings). Авто-пополнение помечено как «скоро» и пока не запускает реальные платежи — см. дашборд кабинета. Управление командой описано в руководстве Профиль и команда.

Что дальше