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
/api/v1/affiliate/profile🔒 Bearer JWTThe influencer profile: contacts, language, KYC status, referral code.
user_idintegerrequiredaffiliate_idintegerrequiredemailstringoptionaldisplay_namestringoptionallanguage_codestringoptionalru / en).statusstringoptionalkyc_statusstringoptionalnone / pending / verified / rejected.payout_currencystringoptionalreferral_codestringoptionalcreated_atstringoptional{
"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
/api/v1/affiliate/profile🔒 Bearer JWTUpdate the display name and/or language. Requires a full token.
display_namestringoptionallanguage_codestringoptionalru or en.Returns the updated profile object (as in GET /affiliate/profile).
POST /affiliate/profile/kyc
/api/v1/affiliate/profile/kyc🔒 Bearer JWTSubmit documents for a KYC check. Moves the status from none/rejected to pending. Requires a full token.
document_typestringrequiredpassport, national_id, driver_license), 1–64 characters.document_refstringoptional{ "kyc_status": "pending" }Errors: KYC_ALREADY_SUBMITTED (409 — already pending/verified), PROFILE_NOT_FOUND (404), EMBED_SCOPE_DENIED (403).
Offers
GET /affiliate/offers
/api/v1/affiliate/offers🔒 Bearer JWTThe 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).
{
"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}
/api/v1/affiliate/offers/{offer_id}🔒 Bearer JWTThe detailed offer card: everything from the list plus terms and multi-level parameters.
termsobjectoptionalattribution_window_hoursintegeroptionalmax_levelsintegeroptionallevel_ratesobjectoptionalIf status = paused, this is reflected in the field, and the UI shows the reason (OFFER_PAUSED).
Tracking links
GET /affiliate/links
/api/v1/affiliate/links🔒 Bearer JWTA list of your tracking links. Query: skip (0+), limit (1–500, default 100).
POST /affiliate/links
/api/v1/affiliate/links🔒 Bearer JWTCreate a link to an offer. Returns a short code for the /r/{code} path.
offer_idintegerrequiredsub_id1stringoptionalsub_id2stringoptionalsub_id3stringoptional{
"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 /affiliate/links/{link_id}
/api/v1/affiliate/links/{link_id}🔒 Bearer JWTA link together with its statistics.
clicksintegeroptionalunique_clicksintegeroptionalconversionsintegeroptionalepcstringoptional{
"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
/api/v1/affiliate/balance🔒 Bearer JWTBalance by currency: payable for withdrawal, in hold, and locked under an active request. Currencies with zero amounts do not appear in the response.
balancesarrayrequiredbalances[].currencystringrequiredbalances[].payablestringrequiredbalances[].holdstringrequiredbalances[].lockedstringrequired{
"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
/api/v1/affiliate/stats🔒 Bearer JWTAggregates of clicks, conversions and commissions: by day, by offer and by network level. Query: date_from, date_to (format YYYY-MM-DD).
period_fromstringoptionalperiod_tostringoptionaltotal_clicksintegerrequiredtotal_conversionsintegerrequiredtotal_commissionstringrequiredby_dayarrayrequiredby_offerarrayrequiredby_levelarrayrequired{
"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
/api/v1/affiliate/payout-methods🔒 Bearer JWTA list of saved payout methods. The details are encrypted and are not exposed — only the masked tail display_last4.
{
"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
/api/v1/affiliate/payout-methods🔒 Bearer JWTAdd a payout method. The details are encrypted with AES-256-GCM before being stored. Requires a full token (embed → 403 EMBED_SCOPE_DENIED).
method_typestringrequiredcard, crypto, etc.), 1–24 characters.detailsobjectrequiredcard_number, wallet_address). Encrypted on the server.is_defaultbooleanoptionalfalse.In the response (201) — the method object with display_last4; the full details are not returned.
DELETE /affiliate/payout-methods/{method_id}
/api/v1/affiliate/payout-methods/{method_id}🔒 Bearer JWTDelete your own payout method. Requires a full token. Success — 204 No Content with no body.