mcpField
T
tiun-sdk

tiun-sdk

github.com/tiun-appDevelopment035 installsUpdated 6d ago
Repository

About tiun-sdk

tiun-sdk is a guidance skill for agents integrating or debugging tiun’s JavaScript and React Native SDKs. It covers subscriptions, one-time purchases, and time-based access, with separate platform guidance and rules for secure authorization.

Key features

  1. 01Guide subscription and one-time purchase setup
  2. 02Explain time-based access flows
  3. 03Distinguish web and React Native SDKs
  4. 04Cover MCP discovery and installation
  5. 05Guide backend token verification

Use cases

  1. 01Integrate subscription checkout in a web app
  2. 02Gate access after a one-time purchase
  3. 03Add metered access to paid content
  4. 04Debug SDK setup or environment mismatches

MCP support

The skill directs agents to detect and offer installation of tiun’s MCP, or proceed manually if unavailable or declined. When connected and authenticated, the MCP can enumerate providers and products to ground integration choices.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/tiun-app/skills --skill tiun-sdk

After installing, type/tiun-sdkin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
api-reference.md
discovery.md
events.md
frameworks.md
installation.md
mcp.md
one-time.md
overview.md
react-native.md
server-verification.md
subscriptions.md
time-based.md
troubleshooting.md

Details

From the project README

tiun SDK skill

Use this skill whenever the user is integrating, extending, or debugging code that uses @tiun/sdk or @tiun/react-native-sdk. Authoritative upstream docs: https://docs.tiun.io/llms-full.txt.

What tiun is

A commercial backend platform providing authentication, payments, and entitlements through a single JS SDK. The tiun singleton supports three flows; you pick based on the integrator's intent (see "Step 0 — Discovery before code"):

  • Subscriptions — persistent user accounts (OTP login), recurring billing, per-product entitlements in user.productAccess[]. Entry point: tiun.checkout({ productId }). See references/subscriptions.md.
  • One-time purchases — same accounts and same entry point as subscriptions, but a single fixed fee and permanent entitlement: no interval, no trial, no renewal, no expiry. Entry point: tiun.checkout({ productId }). See references/one-time.md.
  • Time-based — per-session anonymous access (no account required), metered by time spent on paid content. Entry point: tiun.start() + paywallShow / paywallHide events. See references/time-based.md.

Install: npm install @tiun/sdk. Init: tiun.init({ snippetId, language: 'en' }) on app start (idempotent).

tiun runs live and sandbox as two independent parallel environments. The SDK's sandbox flag selects which one — see references/installation.md.

React Native is a separate SDK. @tiun/react-native-sdk uses <TiunProvider> + useTiun() / useTiunEvent(), selects the environment with host (no sandbox flag), needs a returnUrl deep link, and covers subscriptions and one-time purchases only. Everything about it is in references/react-native.md. The tiun.* code, Minimal working example, and per-framework wiring below are web-only — never generate them in a React Native app.

Step 0 — Discovery before code

Platform first. Check the project before asking anything: a react-native dependency in package.json (or ios/ + android/ folders) means React Native → follow references/react-native.md alongside this discovery; otherwise it's the web SDK. In React Native, time-based is not available — if the user asks for it, say so in 0b.

Before writing any integration code, establish four things. Ask only for items the user has not already stated. If their prompt is fully specified ("wire up subscription with product p-test-pro in sandbox to gate /watch/*"), skip discovery.

Use a structured question primitive (e.g. AskQuestion) if your client supports one; otherwise ask inline in chat.

0a. Detect the MCP and offer to install if missing. See references/mcp.md for per-client install instructions and detection. Three states:

  • MCP present and authed → use it to enumerate providers/products in 0c.
  • MCP present, not authed → prompt auth once; if declined, proceed in manual mode.
  • MCP absent → offer to install (single-sentence value proposition: "fetch your snippetId and productIds directly so we avoid copy-paste errors"). If declined, proceed in manual mode.

0b. Establish integration mode. Subscription, one-time purchase, time-based, or a combination? See references/discovery.md for cues that map user language to each mode. Do not infer mode from the account's inventory. A provider with only one product type today may be planning another tomorrow.

0c. Gather identifiers. Questions depend on mode and MCP availability:

  • Subscription, MCP present → list providers, ask user to pick; list products, ask user to pick one or multiple tiers; confirm environment from the provider's sandbox/live tag.
  • Subscription, no MCP → ask for snippetId; ask one product or multiple tiers; for each, ask productId and label; confirm sandbox or live (the p-test-... / p-live-... prefix is a strong hint).
  • One-time, MCP present → list providers, ask user to pick; list products, ask user to pick the product(s) to unlock; confirm environment from the provider's sandbox/live tag. Do not ask about tiers — one-time products are not tiered.
  • One-time, no MCP → ask for snippetId; for each product, ask productId and label; confirm sandbox or live (prefix is a strong hint).
  • Time-based, MCP present → list providers, ask user to pick; confirm environment from provider tag.
  • Time-based, no MCP → ask for snippetId; confirm sandbox or live.

When the MCP is present, ground questions in inventory ("you have a sandbox time-based product p-test-xxx — wire that up, or set up subscription products first?") rather than asking blind.

0d. Identify what to gate. Without this step the agent is generating boilerplate with no target. Ask:

  • Which routes/components/features require access?
  • What should signed-out users see, and how does the app's journey start? (offering or pricing page / account-first app where users sign in before meeting an upgrade / full-screen paywall / teaser + subscribe). Keep the journey the app is built for.
  • What should authenticated-but-no-access users see? (typically the same UX with "upgrade" copy)
  • Is there a free preview? (If yes, this is often a cue the user actually wants time-based, not subscription — feed back into 0b.)
  • For multi-tier: which routes/features map to which tier?

When to do what

TaskRoute to
Discovery, scoping, MCP detection, mode selectionreferences/discovery.md
MCP installation per client, detection, authreferences/mcp.md
What tiun is, supported platforms, product model, hosted-UI boundariesreferences/overview.md
Install / init / config / per-host env injectionreferences/installation.md
Method or property lookupreferences/api-reference.md
Event names and payloadsreferences/events.md
Subscription gating (accounts + recurring billing), choosing checkout vs loginreferences/subscriptions.md
One-time purchase gating (fixed fee, permanent access)references/one-time.md
Time-based paywall (per-session, anonymous, metered)references/time-based.md
Trusted backend authorizationreferences/server-verification.md
Framework wiring (any stack) + lifecycle matrixreferences/frameworks.md
"Overlay not appearing", SSR errors, sandbox mismatch, broken overlay injectionsreferences/troubleshooting.md
React Native: install, return deep link, TiunProvider, hooks, host, device testingreferences/react-native.md

Load-bearing rules

  1. tiun.init({ snippetId, language }) runs on the client, early in startup. In Next.js/Nuxt, wrap in a client-only component/plugin. Never call it on the server. init is idempotent — calling it again merges config rather than re-initializing, so no isInitialized guard is needed.
  2. snippetId is not a secret. It identifies the environment. Use NEXT_PUBLIC_* / public runtime config.
  3. userChange is the source of truth for checkout-based gating (subscriptions and one-time purchases), not a one-shot tiun.user read. Entitlements change mid-session. In the web SDK the payload includes event: 'init' | 'login' | 'checkout' | 'logout' | 'update' for cases where you need to know what triggered the change. React Native's payload has no event field — use the separate login / logout events there.
  4. Never trust the client for authorization. For protected server resources: tiun.getUserVerificationToken() returns a JWT (5-minute lifetime), send it to your backend, the backend validates it via POST /live_api/s2s/v1/users/verification with X-TIUN-API-KEY. For time-based, use the sessionId from paywallHide against PATCH /live_api/s2s/v1/sessions/{sessionId}/status. See references/server-verification.md.
  5. The hosted UI is a black box — do not reimplement it and do not modify it. tiun renders checkout, login, and the connect overlay in shadow DOM. Open them with tiun.checkout({ productId }), tiun.login(), or tiun.start(), and stop there. Never pierce shadowRoot to query, append, or observe nodes; never inject text, tooltips, badges, or banners into it; never target it with CSS (host selectors, ::part, ::slotted, !important overrides); never reposition or wrap it; never read values out of its inputs. Its internal structure is unversioned and changes without notice, and it sits inside a payment flow — an injection breaks silently and takes checkout down with it. The only supported surfaces are init() config (language, tone) and dashboard settings. If the customer needs extra copy or explanation, put it on their own page next to the trigger, before the overlay opens — not inside it. Anything the config and dashboard do not expose is a request to [email protected], not a client-side patch. See references/overview.md.
  6. Methods before ready may queue or no-op. Methods like checkout/login/start/setContent/logout already call ensureInitialized() and await this.waitForReady() internally — no need to wrap them.
  7. Unsubscribe handlers returned from tiun.on(...) on component unmount to avoid stale listeners. Or call tiun.destroy() if the whole subtree is going away.
  8. Live and sandbox are independent parallel environments. Each has its own snippet ID, products, product-ID prefix (p-live-... vs p-test-...), API keys, and API base URL (https://api.tiun.live vs https://api-sandbox.tiun.live). The environment switch (sandbox flag on web, host in React Native) must match the dashboard view and the product-ID prefix, or checkout silently fails. localhost is blocked in live and enabled by default in sandbox. Each environment has its own Settings → Environment setup (live domain for web, app scheme for apps).
  9. No runtime product-list API. Products are configured in the tiun dashboard (my.tiun.business) and referenced by hardcoded productId strings. Do not invent methods like tiun.getProducts() or REST endpoints; direct users to the dashboard.
  10. No webhooks. tiun does not emit webhooks. For backend integration, drive state from the JWT returned by getUserVerificationToken() or the sessionId from paywallHide. Do not invent webhook endpoints or event payloads.
  11. Stay upstream-faithful. All code examples must match https://docs.tiun.io. If a pattern isn't documented upstream (custom SSR wiring, multi-env setups, backend SDKs in other languages, etc.), point the user at the upstream docs rather than inventing a snippet.
  12. Do not infer integration mode from the account's product inventory. Always confirm with the user (see "Step 0"). What the MCP reports is what exists; it does not report what the integrator wants to build.
  13. A one-time product ID in productAccess is permanent. It enters at checkout and never leaves — event: 'update' never removes it. Do not generate expiry, renewal, cancellation, trial, or revocation handling for a one-time product; that is dead code for state that cannot occur. Renewal and cancellation belong to subscriptions only. See references/one-time.md.
  14. Do not wrap SDK methods to add isInitialized / waitForReady guards. tiun.checkout, tiun.login, tiun.start, tiun.setContent, and tiun.logout already do both internally. Wrapper helpers around these methods are noise.
  15. Do not pass baseUrl to init(). It is an internal-only field reserved for tiun's own infrastructure. In the web SDK, use sandbox: true for non-production environments; that is the only public environment switch, and the SDK routes to the matching API host automatically. (React Native is the exception: it has no sandbox flag and selects the environment with host — see references/react-native.md.)
  16. language is a closed enum: 'en' | 'de' | 'fr', case-insensitive. Unsupported values trigger a one-time console warning and fall back to 'en'. Do not generate other values.
  17. Do not branch app logic on error.code. It's a string but there's no published enum; codes can change between SDK versions. Display err.message to users and log err.code for support.
  18. Write runtime config to files the bundler actually loads. .env.example is documentation and is never evaluated. Vite loads .env / .env.local; Next.js loads .env.local and requires the NEXT_PUBLIC_* prefix for client-exposed values; Nuxt loads via runtimeConfig.public in nuxt.config.ts. See references/installation.md for the per-host table.
  19. Use authentication for account entry and checkout for purchases. Signing up or signing in → tiun.login(); buying a product or plan → tiun.checkout({ productId }); signing out → tiun.logout(). Choose by the action's purpose, not its label. Checkout handles the authentication and returning-customer cases that come with purchasing, so a purchase action opens it directly rather than routing through a separate login. This is about what a purchase action opens, not about app structure — purchase-first and account-first journeys are both valid. See references/subscriptions.md → "Choosing checkout or login".
  20. React Native checkout needs one scheme in three places. The dashboard's Settings → Environment setup → App scheme (e.g. myapp://), the native registration (iOS Info.plist + RCTLinkingManager forwarding in AppDelegate; Android intent filter + android:launchMode="singleTask"), and the returnUrl in TiunConfig (e.g. myapp://tiun/return). Reuse the app's existing scheme rather than inventing one, and tell the user to register it in the dashboard for the environment host points at. Any mismatch means payment succeeds but checkout never returns. See references/react-native.md.

Minimal working example

Three primitives — works in every supported environment. Adapt the reactive primitive (state hook, ref, signal, store, DOM update) to your stack.

import { tiun } from '@tiun/sdk';

tiun.init({ snippetId: 'YOUR_SNIPPET_ID', language: 'en' });

const off = tiun.on('userChange', ({ event, isAuthenticated, user }) => {
  // push into your framework's reactive primitive
});

document.querySelector('#buy').onclick =
  () => tiun.checkout({ productId: 'p-live-pro' });

// off(); tiun.destroy();

Full per-mode walkthroughs in references/subscriptions.md, references/one-time.md, and references/time-based.md. Per-framework adaptations in references/frameworks.md.

Decision cues

  • User says "subscription", "recurring", "members", "paid account", "products and tiers" → subscription flow. Route to subscriptions.md.
  • User says "buy once", "pay once", "one-time", "lifetime", "lifetime deal", "single payment", "unlock forever", "fixed fee", "perpetual license", "no subscription" → one-time flow. Route to one-time.md.
  • User says "article paywall", "watch a video then pay", "session", "no account needed", "donation prompt", "first N seconds free" → time-based flow. Route to time-based.md.
  • User says "videos behind a paywall", "premium content" (ambiguous) → ask which mode. Don't guess.
  • User says "how do I get my agent to integrate tiun?" / "set this up with an AI coding agent" → point at the upstream Agent integration guide on docs.tiun.io and the npx skills add tiun-app/skills quickstart in references/mcp.md.
  • User asks "how do I list products?" / "get all products?" → products are configured in the dashboard at my.tiun.business; there is no runtime product-list API. (The MCP exposes inventory to the agent for setup; this is not a runtime SDK feature.)
  • User mentions "verify on the backend", "protect API", "trust the client" → server verification (X-TIUN-API-KEY header; per-environment base URLs).
  • User mentions sandbox / localhost / "why won't it work locally" → confirm sandbox: true + sandbox snippet ID + p-test-... product IDs (live is hard-blocked on localhost).
  • User asks to restyle the overlay, relabel its fields, or add help text / a note / a tooltip inside checkout or login → not supported. The overlay's internals are off limits (Rule 5); put the copy on their own page next to the trigger, and route branding requests to [email protected].
  • User is wiring purchase, sign-in, or sign-out actions → match each to its purpose: purchases open tiun.checkout({ productId }), account entry opens tiun.login(), sign-out calls tiun.logout(). Keep the app's existing journey (Rule 19).
  • Errors like "overlay doesn't appear", "methods called before ready" → troubleshooting.
  • Project is React Native, or user mentions @tiun/react-native-sdk, TiunProvider, useTiun, "iOS/Android app", "deep link", "returnUrl", "checkout doesn't come back to the app" → references/react-native.md. Not the WebView section of frameworks.md, and not @tiun/sdk.
  • User wants time-based billing in a React Native app → not supported by the React Native SDK; say so and point at [email protected].

Comments

Markdown supported