# RaidBoss Platform — Full documentation (EN) Docs version: 0.1.1 TypeScript SDK: @raidboss/platform-sdk@0.1.0 Flutter SDK: raidboss_platform_sdk@0.1.3 Default language is Russian. English mirror: /en/llms-full.txt Placeholders: YOUR_APP_CODE, YOUR_PUBLIC_KEY, YOUR_API_URL. --- # Overview Slug: welcome/overview # Welcome to RaidBoss Platform RaidBoss Platform is middleware for product applications. Your app keeps its own UI and business logic. The Platform owns **identity hooks**, **catalog**, **entitlements**, **paywalls**, **payments**, **devices**, **remote config**, and **analytics events**. This documentation is public, versioned, and optimized for humans and AI assistants. **Default language is Russian** at `/…`. English is available via the **EN** language switch or `/en/...` paths. ## Start implementing 1. Create an **application** in the Platform admin console. 2. Create **entitlements** (feature rights), then **plans** with prices, then a **paywall**. 3. Issue a **public SDK key** (`rb_pub_…`). 4. Install an SDK and follow [Quickstart](/welcome/quickstart). | Package | Platform | Current version | | --- | --- | --- | | `@raidboss/platform-sdk` | TypeScript / Web / React Native | see sidebar | | `raidboss_platform_sdk` | Flutter / Dart | see sidebar | ## Core model ```text Application └── Entitlements (feature codes your app checks) └── Plans (what users buy) + Plan prices └── plan → entitlements mapping └── Paywalls (offer UI config for the client) └── Users / devices / payments / subscriptions ``` - Check **entitlement codes**, never plan names. - **Free tier** works offline in the app and does **not** go through payment. - **Paid access** is additive: free local features remain available; paid entitlements unlock extra capabilities. - After purchase, cache the entitlement snapshot locally for offline use. ## Learn more ### Concepts - [Entitlements](/concepts/entitlements) — what to gate in product code - [Plans & prices](/concepts/plans) — catalog and billing types - [Paywalls](/concepts/paywalls) — remote offer configuration - [Free tier & offline](/concepts/free-offline) — offline rules ### SDKs - [TypeScript / Web](/sdk/typescript) - [Flutter](/sdk/flutter) - [API reference](/sdk/api-reference) ### Guides - [Integration checklist](/guides/checklist) - [Payments](/guides/payments) - [License keys](/guides/license-keys) ## For AI assistants Use the machine-readable index: - [`/llms.txt`](/llms.txt) — page index - [`/llms-full.txt`](/llms-full.txt) — full concatenated docs - [`/raw/.md`](/raw/welcome/overview.md) — single page as Markdown See [For AI assistants](/welcome/ai). --- # Quickstart Slug: welcome/quickstart # Quickstart Placeholders used everywhere in these docs: | Placeholder | Meaning | | --- | --- | | `YOUR_API_URL` | Platform API base URL | | `YOUR_APP_CODE` | Application code from admin | | `YOUR_PUBLIC_KEY` | Public SDK key `rb_pub_…` | | `YOUR_ACCESS_TOKEN` | End-user JWT from your auth provider | ## 1. Admin setup (once) 1. Create application → note `YOUR_APP_CODE`. 2. Add entitlements (example codes: `premium_feature`, `cloud_sync`). 3. Create plans (including optional `free`) and attach entitlements to **paid** plans. 4. Create paywall code `main` and attach plan prices. 5. Copy public key `YOUR_PUBLIC_KEY`. ## 2. TypeScript ```ts import { RaidBossPlatformClient } from "@raidboss/platform-sdk"; const platform = new RaidBossPlatformClient({ apiUrl: "YOUR_API_URL", applicationCode: "YOUR_APP_CODE", publicKey: "YOUR_PUBLIC_KEY", getAccessToken: async () => YOUR_ACCESS_TOKEN, }); await platform.registerUser({ platform: "web" }); await platform.registerDevice({ installation_id: "stable-uuid-on-device", platform: "web", app_version: "1.0.0", }); const { data: paywall } = await platform.getPaywall("main"); const canPremium = await platform.hasEntitlement("premium_feature"); ``` ## 3. Flutter ```dart final platform = RaidBossPlatformClient( apiUrl: 'YOUR_API_URL', applicationCode: 'YOUR_APP_CODE', publicKey: 'YOUR_PUBLIC_KEY', getAccessToken: () async => YOUR_ACCESS_TOKEN, ); await platform.registerUser(platform: 'android'); await platform.registerDevice( installationId: installationId, platform: 'android', appVersion: '1.0.0', ); final paywall = await platform.getPaywall(paywallCode: 'main'); final canPremium = await platform.hasEntitlement('premium_feature'); ``` ## 4. Purchase (paid only) ```ts const price = paywall.paywall_items?.[0]?.plan_prices; if (price && !RaidBossPlatformClient.isFreePrice(price)) { const { data } = await platform.createPayment({ plan_price_id: String(price.id), return_url: "https://your.app/pay/return", }); // open data.confirmation_url } ``` Never call `createPayment` for free prices (`is_free` / `works_offline` / `requires_purchase: false`). ## 5. Feature gating rule ```text if feature is free-local → allow (even offline) else if online → sync entitlements, update cache, check code else → check cached entitlements + expires_at ``` Next: [Entitlements](/concepts/entitlements) · [Payments](/guides/payments) · [Offline](/concepts/free-offline) --- # For AI assistants Slug: welcome/ai # For AI assistants Give the model one of these URLs (replace host with your deployed docs origin): | Resource | Purpose | | --- | --- | | `/llms.txt` | Index of all pages + package versions | | `/llms-full.txt` | Full docs concatenated (preferred for one-shot context) | | `/raw/.md` | Single page Markdown | | `/changelog` | What changed in docs / SDKs | ## Prompt template ```text Read https://YOUR_DOCS_HOST/llms-full.txt Integrate RaidBoss Platform into this app. Use placeholders from the docs. Do not invent private admin URLs or secrets. Prefer entitlement codes over plan names. Free tier must work offline without createPayment. Cache entitlements for offline paid access. ``` ## Versioning - Sidebar shows **docs**, **TypeScript**, and **Flutter** versions. - When an SDK ships a breaking or notable change, changelog and `lib/nav.ts` versions are bumped together. - Always prefer `/llms.txt` for the latest published versions before coding. ## Anonymization rules Docs intentionally avoid product-specific branding and private infrastructure details: - Use `YOUR_APP_CODE`, `YOUR_API_URL`, `YOUR_PUBLIC_KEY` - Do not embed service-role keys, payment secrets, or admin credentials in client apps - Example entitlement codes are illustrative (`premium_feature`, `cloud_sync`) --- # Applications Slug: concepts/applications # Applications An **application** is one product that uses the Platform API and SDKs. ## Fields (conceptual) | Field | Role | | --- | --- | | `code` | Stable string id used by SDKs (`YOUR_APP_CODE`) | | `name` | Human label in admin | | `status` | Only `active` apps are served on public endpoints | | Public key | `rb_pub_…` required for paywall and payments | | Payments flag | Enables paid checkout for the app | ## Client identification Every SDK request should identify the app via: - Header `X-Application-Code: YOUR_APP_CODE` (default), or - Header `X-Application-Id: ` when you prefer UUID Plus, when required: - `X-Public-Key: YOUR_PUBLIC_KEY` - `Authorization: Bearer YOUR_ACCESS_TOKEN` for user-scoped routes ## What lives under an application - Entitlements - Plans / prices - Paywalls - Users (memberships) - Devices - Payments / subscriptions - Remote config & feature flags - Analytics events --- # Entitlements Slug: concepts/entitlements # Entitlements An **entitlement** is a feature right. Product code should gate on entitlement **codes**, not on plan titles. ## Admin fields | Field | Required | Notes | | --- | --- | --- | | `code` | yes | Stable, snake/latin, used in `hasEntitlement('code')` | | `name` | yes | Admin label only | | `description` | no | Optional notes | ## Examples (illustrative) ```text premium_feature cloud_sync export_hd ``` ## Runtime ```ts const ok = await platform.hasEntitlement("premium_feature"); ``` ```dart final ok = await platform.hasEntitlement('premium_feature'); ``` `GET .../entitlements/me` returns: - `codes: string[]` — quick membership check - `entitlements[]` — rows with `source`, `starts_at`, `expires_at`, `is_lifetime` ## Mapping Plans grant entitlements via `plan_entitlements`. When a payment succeeds (or a license key activates), the Platform grants those entitlements to the user for the plan period. ## Rules of thumb - Free local features usually have **no** entitlement — they are always allowed in the client. - Paid capabilities **do** have entitlements. - Prefer one entitlement per product capability, not per price point. --- # Plans & prices Slug: concepts/plans # Plans & prices ## Plan | Field | Notes | | --- | --- | | `code` | Stable id (`yearly_premium`, `free`) | | `name` | Shown on paywall | | `billing_type` | `one_time` \| `recurring` \| `lifetime` \| `free` | | `duration_days` | Term length for timed plans | | `is_lifetime` | No expiry when true | | `status` | `active` plans appear on public paywalls | | entitlements | Which rights this plan grants on purchase | ## Price (`plan_prices`) | Field | Notes | | --- | --- | | `amount` | Real charge amount. `0` for free | | `currency` | e.g. `RUB` | | `discount_percent` | UI-only strike-through helper | | `payment_provider` | `yookassa` or `free` | | `is_active` | Must be true to purchase / list | Display helpers returned by API: - `compare_at_amount`, `has_discount`, `display` - `is_free`, `requires_purchase`, `works_offline` ## Free plan semantics - `billing_type: free` and/or `amount: 0` - Exists for catalog / paywall comparison - **Not purchasable** — `createPayment` returns `free_plan_offline` - Client must treat free features as local defaults ## Paid plan semantics - Positive `amount` - On success, Platform creates subscription period and grants plan entitlements - Access is **additive** to free local features --- # Paywalls Slug: concepts/paywalls # Paywalls A **paywall** is a named offer screen configuration for an application. ## Admin fields | Field | Notes | | --- | --- | | `code` | Usually `main` — SDK requests by this code | | `name` | Admin label | | `title` / `subtitle` | Optional copy for the client UI | | `status` | Public endpoint serves `active` only | | items | Ordered list of `plan_price_id`s | ## Item fields | Field | Notes | | --- | --- | | `plan_price_id` | Price to show / purchase | | `position` | Display order | | `is_recommended` | Highlight flag | | `badge` | Optional badge text | | `custom_title` / `custom_subtitle` | Optional overrides | ## Client fetch ```http GET /v1/applications/{app_code}/paywalls/{paywall_code} X-Public-Key: YOUR_PUBLIC_KEY ``` No user JWT required. Nested payload includes plan, price display fields, and plan entitlements for UI bullets. ## Client responsibilities - Render UI from payload (you own the design). - Skip `createPayment` when `is_free` / `works_offline`. - Use `plan_price_id` from the selected item for paid checkout. --- # Free tier & offline Slug: concepts/free-offline # Free tier & offline ## Free tier Free is the **default local mode** of the product app: - Works **without internet** - Does **not** call `createPayment` - Usually has **no server entitlements** - May still appear on the paywall for comparison (`is_free: true`) ## Paid + free together Purchasing a plan **adds** entitlements. It does not remove free local capabilities. ```text effective access = free-local features ∪ active paid entitlements ``` ## Offline with a previous purchase | Actor | Behavior | | --- | --- | | App | On last online sync, persist entitlement snapshot (`codes`, `expires_at`, `is_lifetime`). Offline: allow free-local always; allow paid features from cache until expiry. | | Admin / API | Online only. Revokes and extensions apply on next successful sync. | ## Recommended client algorithm ```text canUse(feature): if feature in FREE_LOCAL_SET: return true if network available: snapshot = getMyEntitlements() saveCache(snapshot) return snapshot.codes.contains(feature) cached = loadCache() if cached missing: return false if entitlement expired (and not lifetime): return false return cached.codes.contains(feature) ``` SDKs currently provide network APIs; **caching is the app’s responsibility** (secure storage / preferences). --- # TypeScript / Web SDK Slug: sdk/typescript # TypeScript / Web — `@raidboss/platform-sdk` ## Install Private monorepo package (path / workspace). Publish target name: ```text @raidboss/platform-sdk ``` ```ts import { RaidBossPlatformClient, createRaidBossClient, RaidBossPlatformError, } from "@raidboss/platform-sdk"; ``` ## Construct ```ts const platform = new RaidBossPlatformClient({ apiUrl: "YOUR_API_URL", applicationCode: "YOUR_APP_CODE", publicKey: "YOUR_PUBLIC_KEY", getAccessToken: async () => tokenOrNull, // applicationId?: string // fetch?: typeof fetch }); ``` ## Methods | Method | Auth | Public key | Notes | | --- | --- | --- | --- | | `getPaywall(code?)` | no | recommended | Default paywall `main` | | `registerUser(...)` | yes | — | Call after login/signup | | `registerDevice(...)` | yes | — | Stable `installation_id` | | `getMyEntitlements()` | yes | — | Snapshot + `codes` | | `hasEntitlement(code)` | yes | — | Convenience | | `hasAnyEntitlement(codes)` | yes | — | Convenience | | `createPayment({ plan_price_id, return_url })` | yes | required | Paid only | | `checkout(planPriceId, returnUrl)` | yes | required | Alias helper | | `activateLicenseKey({ key, installation_id? })` | yes | — | Grants plan rights | | `listDevices()` / `revokeDevice(id)` | yes | — | Device management | | `getConfig(...)` | no | — | Remote config map | | `getFeatures(...)` | yes | — | Feature flags | | `trackEvent` / `trackEvents` | yes | — | Analytics | | `isFreePrice(price)` static | — | — | Detect free offer | ## Free price helper ```ts if (RaidBossPlatformClient.isFreePrice(price)) { // do not createPayment — local free tier } ``` ## Errors Failed HTTP calls throw `RaidBossPlatformError` with `status` and `body`. Free checkout attempt returns API error `free_plan_offline`. ## Example (auth provider) ```ts const platform = createRaidBossClient({ apiUrl: "YOUR_API_URL", applicationCode: "YOUR_APP_CODE", publicKey: "YOUR_PUBLIC_KEY", getAccessToken: async () => session?.access_token ?? null, }); ``` --- # Flutter SDK Slug: sdk/flutter # Flutter — `raidboss_platform_sdk` ## Install Path dependency (monorepo) or your private feed: ```yaml dependencies: raidboss_platform_sdk: path: ../path/to/raidboss_platform_sdk ``` ```dart import 'package:raidboss_platform_sdk/raidboss_platform_sdk.dart'; ``` ## Construct ```dart final platform = RaidBossPlatformClient( apiUrl: 'YOUR_API_URL', applicationCode: 'YOUR_APP_CODE', publicKey: 'YOUR_PUBLIC_KEY', getAccessToken: () async => accessTokenOrNull, ); ``` ## Methods | Method | Auth | Public key | Notes | | --- | --- | --- | --- | | `getPaywall(paywallCode:)` | no | required | Default `main` | | `registerUser(...)` | yes | — | After auth login | | `registerDevice(...)` | yes | — | Stable `installationId` | | `getMyEntitlements()` | yes | — | `EntitlementSnapshot` | | `hasEntitlement(code)` | yes | — | Convenience | | `createPayment(...)` | yes | required | Paid only | | `activateLicenseKey(...)` | yes | — | Key redemption | | `getConfig` / `getFeatures` | config: no / features: yes | — | Remote flags | | `trackEvent(...)` | yes | — | Analytics | | `PaywallData.isFreePrice(i)` | — | — | Free detection | ## Free price helper ```dart if (paywall.isFreePrice(0)) { // local free tier — no createPayment } ``` ## Models - `EntitlementSnapshot` — `codes`, `items`, `has(code)`, `activePlans` - `PaywallData` — raw map + helpers - `PaymentResult` — `paymentId`, `confirmationUrl`, amounts - `RaidBossPlatformException` — `status`, `error`, `message` Call `platform.close()` when disposing a long-lived client if you own the HTTP client lifecycle. --- # API reference Slug: sdk/api-reference # API reference Base URL: `YOUR_API_URL` All JSON. Typical envelope: `{ "data": ... }` or `{ "error": "code", "message": "..." }`. ## Headers | Header | When | | --- | --- | | `Content-Type: application/json` | body requests | | `X-Application-Code: YOUR_APP_CODE` | identify app | | `X-Application-Id: ` | alternative to code | | `X-Public-Key: rb_pub_…` | paywall + payments (required where noted) | | `Authorization: Bearer ` | user-scoped routes | ## Public ### Get paywall ```http GET /v1/applications/{app_code}/paywalls/{paywall_code} X-Public-Key: YOUR_PUBLIC_KEY ``` ### Remote config ```http GET /v1/applications/{app_code}/config?platform=web&app_version=1.0.0 ``` ## Authenticated (end user) ### Register user ```http POST /v1/applications/{app_code}/users/register Authorization: Bearer YOUR_ACCESS_TOKEN ``` ### Entitlements ```http GET /v1/applications/{app_code}/entitlements/me Authorization: Bearer YOUR_ACCESS_TOKEN ``` ### Create payment ```http POST /v1/payments Authorization: Bearer YOUR_ACCESS_TOKEN X-Public-Key: YOUR_PUBLIC_KEY { "application_code": "YOUR_APP_CODE", "plan_price_id": "uuid", "return_url": "https://your.app/return" } ``` Paid only. Free prices → `400 free_plan_offline`. ### License key ```http POST /v1/applications/{app_code}/license-keys/activate Authorization: Bearer YOUR_ACCESS_TOKEN { "key": "RB-....", "installation_id": "optional" } ``` ### Devices ```http POST /v1/devices/register GET /v1/devices?application_code=YOUR_APP_CODE DELETE /v1/devices/{device_id} ``` ### Features ```http GET /v1/applications/{app_code}/features?platform=android&app_version=1.0.0 ``` ### Events ```http POST /v1/events POST /v1/events/batch ``` ## Webhooks (server) Payment provider webhooks hit the Platform API (not the marketing site). Configure the provider URL to the Platform payments webhook path provided in your deployment docs. Never put provider secrets in the client SDK. --- # Integration checklist Slug: guides/checklist # Integration checklist ## Admin - [ ] Application created and `active` - [ ] Entitlement codes defined to match product gates - [ ] Paid plans mapped to entitlements - [ ] Optional free plan for paywall comparison (no entitlements required) - [ ] Paywall `main` active with ordered prices - [ ] Public key issued (`rb_pub_…`) - [ ] Payments enabled + provider credentials for paid apps ## Client - [ ] SDK configured with `YOUR_API_URL`, `YOUR_APP_CODE`, `YOUR_PUBLIC_KEY` - [ ] `getAccessToken` returns end-user JWT after login - [ ] `registerUser` after signup/signin - [ ] `registerDevice` with stable installation id - [ ] Feature gates use entitlement **codes** - [ ] Free-local features never require network - [ ] Paid offers call `createPayment` only when `requires_purchase` - [ ] Entitlement snapshot cached for offline - [ ] Return URL completes payment UX and refreshes entitlements - [ ] No `service_role` / payment secrets in the client ## Verification - [ ] Anonymous device can load paywall with public key - [ ] Logged-in user appears in admin users list - [ ] Device appears in admin devices list - [ ] Test purchase grants expected entitlement codes - [ ] Offline: free works; cached paid works until expiry --- # Payments Slug: guides/payments # Payments ## Flow ```text 1. User selects a paid plan_price on the paywall 2. App calls createPayment(plan_price_id, return_url) 3. API creates provider payment + pending Platform payment row 4. App opens confirmation_url 5. Provider webhook → Platform marks succeeded → grants entitlements / subscription period 6. App refreshes entitlements (and updates local cache) ``` ## Client rules - Require auth JWT + public key - Never send provider secrets from the client - Do not call createPayment for free prices - After return, call `getMyEntitlements()` again ## Response (paid) ```json { "data": { "payment_id": "uuid", "provider_payment_id": "…", "status": "pending", "confirmation_url": "https://…", "amount": 1490, "currency": "RUB", "requires_purchase": true } } ``` ## Free attempt ```json { "error": "free_plan_offline", "is_free": true, "requires_purchase": false, "works_offline": true } ``` ## Display discount `discount_percent` is cosmetic. Charged amount is always `amount`. Strike-through uses `compare_at_amount`. --- # License keys Slug: guides/license-keys # License keys License keys grant a plan’s entitlements to the authenticated user. ```ts await platform.activateLicenseKey({ key: "RB-XXXXXXXX-…", installation_id: "optional-stable-id", }); ``` ```dart await platform.activateLicenseKey( key: 'RB-XXXXXXXX-…', installationId: installationId, ); ``` ## Notes - Requires end-user JWT - On success, refresh entitlements and update offline cache - Key format and redemption limits are enforced server-side - Prefer keys for offline-sold / gift / retail channels; use payments for online checkout --- # Remote config & features Slug: guides/config # Remote config & features ## Config ```ts const { data } = await platform.getConfig({ platform: "web", app_version: "1.0.0", }); // data.config → Record ``` No auth required. Use for non-sensitive remote parameters. ## Features ```ts const { data } = await platform.getFeatures({ platform: "android", app_version: "1.0.0", }); // data.features → Record ``` Auth required. Use for kill-switches and staged rollouts. ## Guidance - Do not put secrets in remote config - Keep defaults in the client for offline - Combine with entitlement checks for monetized capabilities --- # Analytics events Slug: guides/analytics # Analytics events ```ts await platform.trackEvent({ event: "paywall_viewed", platform: "web", app_version: "1.0.0", properties: { paywall_code: "main" }, }); await platform.trackEvents([ { event: "checkout_started", properties: { plan_price_id: "…" } }, { event: "checkout_returned", properties: { status: "pending" } }, ]); ``` ```dart await platform.trackEvent( event: 'paywall_viewed', platform: 'ios', appVersion: '1.0.0', properties: {'paywall_code': 'main'}, ); ``` Requires end-user JWT. Event names are free-form strings; keep a stable taxonomy in your product. --- # Changelog Slug: changelog/index # Changelog Versions in the sidebar: **docs** / **TS** / **Flutter**. **Default language is Russian.** English via the EN switch or `/en/...` paths. ## 2026-07-19 — docs 0.1.1 ### Docs - i18n: Russian by default, English via language switch (`/en/...`) - `/llms.txt` + `/llms-full.txt` = RU; `/en/llms*.txt` = EN ## 2026-07-19 — docs 0.1.0 ### Docs - Initial public documentation site - AI indexes, concepts, guides, SDKs ### `@raidboss/platform-sdk` 0.1.0 - Client: paywall, entitlements, payments, devices, config, features, events - `isFreePrice`; free checkout rejected (`free_plan_offline`) ### `raidboss_platform_sdk` 0.1.3 - Flutter client, `PaywallData.isFreePrice`, entitlement snapshot helpers