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
/api/v1/merchant/offers🔒 Bearer JWTСписок офферов бизнеса. Query: skip (0+), limit (1–200, по умолчанию 50). Ответ — { "data": [...], "total": N }.
GET /merchant/offers/{offer_id}
/api/v1/merchant/offers/{offer_id}🔒 Bearer JWTКарточка одного оффера (см. форму ответа ниже).
POST /merchant/offers
/api/v1/merchant/offers🔒 Bearer JWTСоздать оффер. Новый оффер уходит в статус pending_review (модерация платформой). Только merchant_owner.
namestringrequiredmonetization_modelstringrequiredcpl, cpa, revshare или hybrid.descriptionstringoptionaldestination_urlstringoptionalgeoarrayoptionalcategorystringoptionalvisibilitystringoptionalpublic или private. По умолчанию public.currencystringoptionalRUB.cpa_amountstringoptionalcpa/hybrid.cpl_amountstringoptionalcpl.revshare_percentstringoptionalrevshare/hybrid.hold_daysintegeroptionalattribution_window_hoursintegeroptionalmultilevel_enabledbooleanoptionalfalse.levels_enabledintegeroptionallevel_ratesarrayoptionalmax_total_commission_percentstringoptionalmax_total_commission_amountstringoptional{
"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}
/api/v1/merchant/offers/{offer_id}🔒 Bearer JWTРедактировать оффер. Только merchant_owner. Тело — те же поля, что в создании, но все опциональны (передавайте только изменяемые). Если оффер был отклонён, в ответе rejection_reason содержит причину.
Баланс и депозит
GET /merchant/balance
/api/v1/merchant/balance🔒 Bearer JWTЭскроу-баланс бизнеса с леджером движений. Только merchant_owner. Query: currency (ISO-код, по умолчанию RUB).
currencystringrequiredavailablestringrequiredreservedstringrequiredtotal_depositedstringrequiredtotal_spentstringrequiredaccruals_pausedbooleanrequiredaccruals_paused_reasonstringoptionalledgerarrayoptional{
"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
/api/v1/merchant/deposit🔒 Bearer JWTИнициировать пополнение. Возвращает pending-интент — фактическое зачисление приходит по вебхуку провайдера. Только merchant_owner. Успех — 202 Accepted.
amountstringrequiredcurrencystringoptionalRUB.providerstringoptionalcard / crypto / manual / stub. По умолчанию manual.{
"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
/api/v1/merchant/integration/keys🔒 Bearer JWTСписок ключей. Только merchant_owner. Секрет маскирован — виден только хвост secret_tail.
{
"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
/api/v1/merchant/integration/keys🔒 Bearer JWTВыпустить ключ. Секрет показывается один раз в ответе. Только merchant_owner.
namestringrequiredscopesarrayrequiredpostback, sso, readonly.{
"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
/api/v1/merchant/integration/keys/{key_id}/reveal🔒 Bearer JWTПоказать секрет существующего ключа. Только merchant_owner.
{ "key_id": "ak_live_8d3f1a", "secret": "sk_live_a1b2c3...9c1e", "secret_tail": "9c1e" }POST /merchant/integration/keys/{key_id}/revoke
/api/v1/merchant/integration/keys/{key_id}/revoke🔒 Bearer JWTОтозвать ключ. Только merchant_owner.
{ "status": "revoked" }POST /merchant/integration/test-postback
/api/v1/merchant/integration/test-postback🔒 Bearer JWTТестовый постбэк (dry-run через реальный флоу) — проверить интеграцию без боевой конверсии. Только merchant_owner.
key_idstringrequiredkey_id ключа из integration/keys.external_order_idstringrequiredoffer_idintegerrequiredamountstringoptional1.00.currencystringoptionalRUB.event_typestringoptionalsale или lead. По умолчанию sale.{
"accepted": true,
"duplicate": false,
"conversion_id": 778,
"error_code": null,
"error_message": null
}Аналитика
GET /merchant/analytics
/api/v1/merchant/analytics🔒 Bearer JWTАналитика бизнеса: клики, конверсии, расход — всего и по офферам. Query: date_from, date_to, currency (ISO-код, по умолчанию RUB).
total_clicksintegerrequiredtotal_conversionsintegerrequiredtotal_spentstringrequiredcurrencystringrequiredby_offerarrayoptional{
"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
/api/v1/merchant/affiliates🔒 Bearer JWTИнфлюенсеры, работающие с офферами бизнеса. Query: skip, limit (1–200, по умолчанию 50).
{
"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
/api/v1/merchant/affiliates/{affiliate_id}/grant🔒 Bearer JWTВыдать инфлюенсеру допуск к приватному офферу. Только merchant_owner.
offer_idintegerrequired{ "status": "granted" }POST /merchant/affiliates/{affiliate_id}/revoke
/api/v1/merchant/affiliates/{affiliate_id}/revoke🔒 Bearer JWTОтозвать допуск к приватному офферу. Только merchant_owner. Тело — { "offer_id": <int> }.
{ "status": "revoked" }Профиль
GET /merchant/profile
/api/v1/merchant/profile🔒 Bearer JWTПрофиль бизнеса: компания и контакты.
company_namestringoptionalcontact_emailstringoptionalwebsite_urlstringoptional{ "company_name": "Acme LLC", "contact_email": "ops@acme.com", "website_url": "https://acme.com" }PUT /merchant/profile
/api/v1/merchant/profile🔒 Bearer JWTОбновить профиль бизнеса. Только merchant_owner.
company_namestringoptionalcontact_emailstringoptionalwebsite_urlstringoptionalВозвращает обновлённый профиль.
Команда и авто-пополнение
В этой же группе есть управление командой (/merchant/team, инвайты /merchant/team/invites) и настройки авто-пополнения (/merchant/deposit/auto-settings). Авто-пополнение помечено как «скоро» и пока не запускает реальные платежи — см. дашборд кабинета. Управление командой описано в руководстве Профиль и команда.