Embed Data API browser-side, origin-gated
Read-only JSON. Без bearer-токена. Гейт по
tenant.allowed_origins на стороне Recca: если
Origin запроса не в whitelist — 403 с пустым ACAO.
GET /embed/v1/data/cabinet/:user_id
GET /embed/v1/data/cabinet/ub_GB_xyz?tenant=agp
Origin: https://partner-site.example.com
→ 200 OK
Access-Control-Allow-Origin: https://partner-site.example.com
{
"user": { "id_surrogate": "ub_GB_xyz", "display_name": "Иван Иванов",
"badge": { "role_label": "Участник программы" },
"claim_required": false },
"tenant": { "slug": "agp", "display_name": "AGP Promo" },
"kpi": { "leads_count": 12,
"cleared_kopecks": 1280000, "pending_kopecks": 280000,
"cleared_formatted": "12 800 ₽", "pending_formatted": "2 800 ₽" },
"recent_leads": [...], "transactions": [...]
} GET /embed/v1/data/bonuses/:user_id
GET /embed/v1/data/bonuses/ub_GB_xyz?tenant=agp&page=1&page_size=10
Origin: https://partner-site.example.com
→ 200 OK
{ "user": {...}, "tenant": {...},
"totals": { "cleared_kopecks", "pending_kopecks", "cleared_formatted", "pending_formatted", "currency" },
"accounts": [...], "transactions": [...],
"pagination": { "page", "page_size", "total", "total_pages" } } GET /embed/v1/data/leads/:user_id
GET /embed/v1/data/leads/ub_GB_xyz?tenant=agp&status=all|new|processed|converted|rejected&page=1&page_size=10
→ 200 OK
{ "user", "tenant", "leads": [{id, status, status_human, contact_name, relative_time}],
"filters": {status}, "pagination": {...} } GET /embed/v1/data/referral-tree/:user_id
GET /embed/v1/data/referral-tree/ub_GB_xyz?tenant=agp&max_depth=3
→ 200 OK
{ "user", "tenant",
"root": {user_id, display_name, depth: 0, parent_user_id: null},
"levels": [ [root], [...children], [...grand-children] ],
"total_downline": 42, "hit_depth_cap": false, "max_depth": 3 } GET /embed/v1/data/partners/:user_id
GET /embed/v1/data/partners/ub_GB_xyz?tenant=agp&page=1&page_size=20
→ 200 OK
{ "user", "tenant",
"partners": [{user_id, display_name, partnership_role, joined_at, is_active}],
"pagination": {...} } Embed Action API browser-side, bearer JWT
POST /embed/v1/data/referral-link
POST /embed/v1/data/referral-link?tenant=agp
Origin: https://partner-site.example.com
Authorization: Bearer <jwt-from-oauth-popup>
Content-Type: application/json
{ "target_type": "website" | "offer" | "shop" | "partner" | "storefront",
"target_id": "https://partner-site.example.com/offer/sochi-2026",
"recommendation_text": "...", // optional
"promo_code": "..." // optional
}
→ 200 OK
{ "short_url": "https://app.recca.ru/d/abc",
"signed_ref": "sig.123...",
"deeplink_id": "dl_01HXVB...",
"code": "abc",
"reused": false } Bearer JWT — из OAuth popup (/oauth/authorize). Cross-tenant токен отклоняется 403. Идемпотентно — повторный запрос с теми же параметрами возвращает существующий deeplink_id.
Federation API server-to-server, X-Recca-Tenant-Key
POST /federation/v1/users/upsert
POST /federation/v1/users/upsert
X-Recca-Tenant-Key: rtk_live_8f2c4e9b...
{ "external_id": "mysite:vk:12345",
"vk_id": "12345", "email": "user@example.ru",
"full_name": "Иван Иванов", "email_verified": false }
→ 200 OK
{ "tenant_recca_id": "ub_GB_xyz", // surrogate to pass as user-id to widgets
"recca_user_id": "usr_01HXVB...",
"action": "created" } POST /federation/v1/leads
POST /federation/v1/leads
X-Recca-Tenant-Key: rtk_live_...
{ "referrer_user_id": "usr_01HXVB...",
"contact": { "name": "Анна", "email": "a@example.ru", "phone": "+7..." },
"offer_id": "ofr_01HXVB...",
"metadata": { "source": "form-1" } }
→ 200 OK
{ "lead_id": "lead_01HXVB...", "status": "new" } POST /federation/v1/conversions/register
POST /federation/v1/conversions/register
X-Recca-Tenant-Key: rtk_live_...
{ "lead_id": "lead_01HXVB...",
"amount_kopecks": 1280000, "currency": "RUB",
"external_order_id": "order-42" }
→ 200 OK
{ "conversion_id": "cnv_01HXVB...",
"cascaded_bonuses": [{ "user_id", "amount_kopecks", "tier" }] } OAuth (browser popup)
GET /oauth/authorize?client_id=<slug>&response_type=code&scope=profile:read&state=<csrf>&popup=1
User approves → popup posts message back to opener:
{ type: "recca:auth-success",
token: "<jwt>", // bearer for /embed/v1/data/referral-link
user_id: "ub_GB_xyz",
org_id: "org_01HXVB...",
expires_at: 1234567890123,
state: "<csrf>" } Troubleshooting
- 403 на JSON fetch — Origin не в allowed_origins. Добавьте в /org/<slug>/integration.
- 401 на POST referral-link — JWT отсутствует или истёк. Триггер
<recca-login-button>. - Webhook signatures не совпадают — body парсится JSON до verify; используйте
express.raw(). - users.upsert returns 401 — tenant API key revoked или не отправлен в X-Recca-Tenant-Key.