Session Auth & Managed Subscriptions
Let users browse KinesteX views and chat with the AI trainer for free, while workout generation requires an active subscription that your backend controls. KinesteX stores each user's subscription status and enforces it server-side — the client app can't bypass it.
Fully optional. If you never set a subscription status, nothing changes for your users.
All management calls are server-to-server with your API key (x-api-key header). Never ship the API key in your app.
The flow:
1. Mint a session for the user, declaring their subscription status:
1curl -X POST https://data.kinestex.com/api/sessions \
2 -H "x-api-key: YOUR_API_KEY" -H "content-type: application/json" \
3 -d '{"user_id": "user-123", "is_subscribed": false}'
4# → { "session_id": "ksx_sess_…", "user_id": "user-123", "is_subscribed": false, "expires_at": "…" }2. Launch the KinesteX view with that session_id as usual — the SDK authenticates server-side and the raw API key never reaches the client. The user can browse, complete onboarding, and chat freely.
3. At the generation step, KinesteX posts { "type": "open_subscription_flow", "source": "generate_workout" } to your app instead of generating. Present your paywall on top of the KinesteX view.
4. After the purchase, grant the subscription from your backend, then tell the view the flow finished by posting { "subscription_result": "purchased" } (or "dismissed") into the webview. On "purchased" the parked generation resumes automatically — no extra tap.
Managing subscriptions:
| Action | Call |
| Grant | POST /api/user-subscriptions {"user_id": "user-123"} — idempotent; creates the user if they've never launched |
| Revoke | DELETE /api/user-subscriptions/user-123 — blocks generation from their next attempt |
| List active | GET /api/user-subscriptions?limit=100&offset=0 |
| Set at session mint | POST /api/sessions with "is_subscribed": true/false — omit to leave the stored status unchanged |
Status changes take effect on the user's next request — no relaunch needed.
Rules & enforcement:
- Status is tri-state: never set → nothing is gated (default);
true→ generation allowed;false→ generation blocked. - Only generation is gated (creating, modifying, or getting a recommended workout). Q&A, results discussion, and starting an already-generated workout stay available.
- Enforcement is server-side: an unsubscribed generation attempt returns
403 {"code": "not_subscribed"}and the view re-opens your subscription flow. Reporting"purchased"without actually granting on the backend will not unlock generation. - For users with a backend-managed status, that status overrides the
isSubscribedlaunch flag. Users without one keep the launch-flag behavior unchanged.
Web/redirect integrations (no host app): pass subscriptionReturnUrl in the launch config instead. KinesteX redirects there at the generation step; after purchase, send the user back with a fresh session — generation unlocks automatically.
For the client-side handshake (events, edge cases, per-platform code), see the subscription gating guide.
Blocking views for a user (`excluded_options`). Add excluded_options when you mint the session to stop that user from opening specific KinesteX views:
1curl -X POST https://data.kinestex.com/api/sessions \
2 -H "x-api-key: YOUR_API_KEY" -H "content-type: application/json" \
3 -d '{"user_id": "user-123", "excluded_options": ["trainer", "challenge"]}'
4# → { "session_id": "ksx_sess_…", "user_id": "user-123", "excluded_options": ["trainer", "challenge"], "expires_at": "…" }| Value | Blocks |
trainer | AI Trainer chat |
workout | Workout view |
custom-workout | Custom workout view |
plan | Plan view, including plan onboarding |
personalized-plan | Personalized plan flow |
challenge | Challenge view |
experience | Games and assessments |
camera | Camera component |
home | The full KinesteX home experience |
- The list is saved on the user and applies to every later session until you send a new one. A new list replaces the old one,
[]removes all exclusions, and leaving the field out keeps the saved list. - Values are case-insensitive. An unknown value fails the whole call with
400and nothing is saved. - The check runs once, at launch, for the view you launch (the SDK sends its launch URL with the verify call). Screens the user reaches by navigating inside KinesteX are not checked, so excluding
trainerdoes not hide the trainer inside the home experience; excludehomefor that. - A blocked view doesn't load. It shows an error screen with "Cannot verify: " followed by the server's message (currently "You don't have access to this. Please contact app support.") and a back button. Your app receives
error_occurredwithseverity: "critical"and the same text indata. The session stays valid for every other view. - If you call
session-verifyyourself for a blocked view, it returns403 {"error": "…", "code": "access_excluded"}. Checkcode, not the message, and don't treat it as a sign-in failure.