MMeerPartners docs

API: Affiliate (self-service)

MeerPartners influencer self-service API: profile and KYC, offer catalog, tracking links, balance, statistics and payout methods.

The /api/v1/affiliate/* group is the influencer cabinet via API. All endpoints require an influencer JWT (Authorization: Bearer <access_token>). The affiliate_id is always taken from the token, never from the request (anti-IDOR). Monetary amounts are NUMERIC strings.

Embed token (scoped)

A scoped embed token (scope: affiliate:embed, issued via token-exchange) works in this group read-only within its own affiliate_id. Mutations — creating/deleting payout methods, changing the profile and submitting KYC — require a full token; otherwise 403 EMBED_SCOPE_DENIED.

List responses here have the shape { "items": [...], "total": N }.

Profile

GET /affiliate/profile

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

The influencer profile: contacts, language, KYC status, referral code.

user_idintegerrequired
User ID.
affiliate_idintegerrequired
Partner profile ID.
emailstringoptional
Email (may be absent).
display_namestringoptional
Display name.
language_codestringoptional
Interface language (ru / en).
statusstringoptional
Profile status.
kyc_statusstringoptional
KYC status: none / pending / verified / rejected.
payout_currencystringoptional
Default payout currency.
referral_codestringoptional
The influencer's referral code for inviting sub-partners.
created_atstringoptional
Creation date (ISO 8601).
200 response
{
  "user_id": 1042,
  "affiliate_id": 88,
  "email": "creator@example.com",
  "display_name": "Anya",
  "language_code": "ru",
  "status": "active",
  "kyc_status": "verified",
  "payout_currency": "RUB",
  "referral_code": "ANYA7K",
  "created_at": "2026-05-01T10:00:00Z"
}

PUT /affiliate/profile

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

Update the display name and/or language. Requires a full token.

display_namestringoptional
New display name (up to 160 characters).
language_codestringoptional
Language: ru or en.

Returns the updated profile object (as in GET /affiliate/profile).

POST /affiliate/profile/kyc

POST/api/v1/affiliate/profile/kyc🔒 Bearer JWT

Submit documents for a KYC check. Moves the status from none/rejected to pending. Requires a full token.

document_typestringrequired
Document type (for example passport, national_id, driver_license), 1–64 characters.
document_refstringoptional
Reference/identifier of the uploaded document (up to 512 characters).
200 response
{ "kyc_status": "pending" }

Errors: KYC_ALREADY_SUBMITTED (409 — already pending/verified), PROFILE_NOT_FOUND (404), EMBED_SCOPE_DENIED (403).

Offers

GET /affiliate/offers

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

The catalog of available offers: all public ones plus private ones you have been granted access to.

Query parameters: tenant_id (filter by business), geo (2-letter country code), model (cpl/cpa/revshare/hybrid), skip (0+), limit (1–200, default 50).

200 response
{
  "items": [
    {
      "id": 501,
      "tenant_id": 17,
      "name": "Acme Premium",
      "slug": "acme-premium",
      "visibility": "public",
      "status": "active",
      "monetization_model": "cpa",
      "currency": "RUB",
      "hold_days": 14,
      "geo": ["RU", "KZ"],
      "cpa_amount": "750.00",
      "cpl_amount": null,
      "revshare_percent": null,
      "multilevel_enabled": true,
      "affiliate_access_status": null
    }
  ],
  "total": 12
}

affiliate_access_status — access to a private offer (approved / requested / null for public ones).

GET /affiliate/offers/{offer_id}

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

The detailed offer card: everything from the list plus terms and multi-level parameters.

termsobjectoptional
Offer terms (rules, restrictions).
attribution_window_hoursintegeroptional
Attribution window in hours (default 720 = 30 days).
max_levelsintegeroptional
Maximum number of referral-network levels (if multi-level is enabled).
level_ratesobjectoptional
Per-level rates L0…L7.

If status = paused, this is reflected in the field, and the UI shows the reason (OFFER_PAUSED).

GET/api/v1/affiliate/links🔒 Bearer JWT

A list of your tracking links. Query: skip (0+), limit (1–500, default 100).

POST /affiliate/links

POST/api/v1/affiliate/links🔒 Bearer JWT

Create a link to an offer. Returns a short code for the /r/{code} path.

offer_idintegerrequired
ID of the offer the link is created for.
sub_id1stringoptional
Analytics label (up to 128 characters).
sub_id2stringoptional
Analytics label (up to 128 characters).
sub_id3stringoptional
Analytics label (up to 128 characters).
201 response
{
  "id": 9001,
  "offer_id": 501,
  "tenant_id": 17,
  "code": "a1B2c3",
  "is_active": true,
  "deep_link_params": {},
  "created_at": "2026-06-14T12:00:00Z"
}

Errors: LINK_LIMIT_REACHED (422), OFFER_PAUSED (422), OFFER_NOT_FOUND (404).

GET/api/v1/affiliate/links/{link_id}🔒 Bearer JWT

A link together with its statistics.

clicksintegeroptional
Total clicks.
unique_clicksintegeroptional
Unique clicks.
conversionsintegeroptional
Conversions.
epcstringoptional
Earnings per click (NUMERIC string).
200 response
{
  "id": 9001,
  "offer_id": 501,
  "tenant_id": 17,
  "code": "a1B2c3",
  "is_active": true,
  "deep_link_params": {},
  "created_at": "2026-06-14T12:00:00Z",
  "clicks": 4210,
  "unique_clicks": 3880,
  "conversions": 96,
  "epc": "17.05"
}

How to build a link with labels and macros is on the Tracking links page.

Balance

GET /affiliate/balance

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

Balance by currency: payable for withdrawal, in hold, and locked under an active request. Currencies with zero amounts do not appear in the response.

balancesarrayrequired
List of aggregates by currency.
balances[].currencystringrequired
Currency (ISO code).
balances[].payablestringrequired
Payable for withdrawal.
balances[].holdstringrequired
In hold — accrued, still "maturing".
balances[].lockedstringrequired
Locked under an active payout request.
200 response
{
  "balances": [
    { "currency": "RUB", "payable": "12400.00", "hold": "3100.00", "locked": "0.00" },
    { "currency": "USD", "payable": "85.00", "hold": "0.00", "locked": "10.00" }
  ]
}

Statistics

GET /affiliate/stats

GET/api/v1/affiliate/stats🔒 Bearer JWT

Aggregates of clicks, conversions and commissions: by day, by offer and by network level. Query: date_from, date_to (format YYYY-MM-DD).

period_fromstringoptional
Start of the period (date).
period_tostringoptional
End of the period (date).
total_clicksintegerrequired
Total clicks for the period.
total_conversionsintegerrequired
Total conversions.
total_commissionstringrequired
Total commission (NUMERIC).
by_dayarrayrequired
Breakdown by day (day × offer).
by_offerarrayrequired
Breakdown by offer.
by_levelarrayrequired
Breakdown of commissions by level (L0 — direct, L1+ — network).
200 response (excerpt)
{
  "period_from": "2026-06-01",
  "period_to": "2026-06-14",
  "total_clicks": 18240,
  "total_conversions": 412,
  "total_commission": "215300.00",
  "by_day": [
    {
      "day": "2026-06-14",
      "offer_id": 501,
      "offer_name": "Acme Premium",
      "tenant_id": 17,
      "clicks": 1320,
      "unique_clicks": 1190,
      "conversions": 31,
      "revenue": "0.00",
      "commission_total": "23250.00",
      "currency": "RUB"
    }
  ],
  "by_level": [
    { "level": 0, "currency": "RUB", "commission_total": "190000.00", "conversions": 380 },
    { "level": 1, "currency": "RUB", "commission_total": "25300.00", "conversions": 32 }
  ]
}

Payout methods

GET /affiliate/payout-methods

GET/api/v1/affiliate/payout-methods🔒 Bearer JWT

A list of saved payout methods. The details are encrypted and are not exposed — only the masked tail display_last4.

200 response
{
  "items": [
    {
      "id": 301,
      "method_type": "card",
      "display_last4": "4242",
      "is_default": true,
      "is_verified": true,
      "created_at": "2026-05-10T09:00:00Z"
    }
  ]
}

POST /affiliate/payout-methods

POST/api/v1/affiliate/payout-methods🔒 Bearer JWT

Add a payout method. The details are encrypted with AES-256-GCM before being stored. Requires a full token (embed → 403 EMBED_SCOPE_DENIED).

method_typestringrequired
Method type (card, crypto, etc.), 1–24 characters.
detailsobjectrequired
Details in plaintext (for example card_number, wallet_address). Encrypted on the server.
is_defaultbooleanoptional
Make it the default method. Defaults to false.

In the response (201) — the method object with display_last4; the full details are not returned.

DELETE /affiliate/payout-methods/{method_id}

DELETE/api/v1/affiliate/payout-methods/{method_id}🔒 Bearer JWT

Delete your own payout method. Requires a full token. Success — 204 No Content with no body.

What's next