Documentation
Integrate Serial Subscriptions
Start by adding the official Serial Subscriptions skills to your repository or editor environment. That pulls in guides, snippets, and checklists for integrating the npm package—without replacing the install step below.
npx skills add serialsubscriptions/skills
Then install the library itself:
npm install @serialsubscriptions/platform-integration
Peer dependency: next@^15.0.0. Runtime: Node.js 18+.
Optional storage backends—install only what you use:
- Redis (recommended for production sessions, cache, and storage):
npm install ioredis - Postgres (storage only):
npm install pg - Memory backend works with no extra packages (development and simple setups)
What this library does
@serialsubscriptions/platform-integration helps your Next.js or Node.js backend talk to your Serial Subscriptions site. In one package you get:
- Session management — cookies, session data, and role-aware access on the server
- OAuth2 / OIDC authentication — login, callback, logout wired to your issuer
- Subscription plans — load plans with features and limits for the logged-in customer
- Usage metering — report and query usage against subscription limits
- Caching — namespaced cache (Redis or memory) for JWKS and other data
Everything is created from a single factory, createSSI(), so you configure integration once and reuse it across routes and components.
Configure once: createSSI()
Create a small shared module (for example src/lib/ssi.ts) and export a singleton:
import { createSSI } from '@serialsubscriptions/platform-integration';
export const ssi = createSSI({
issuerBaseUrl: process.env.SSI_ISSUER_BASE_URL!,
clientId: process.env.SSI_CLIENT_ID!,
clientSecret: process.env.SSI_CLIENT_SECRET!,
redirectUri: process.env.SSI_REDIRECT_URI!,
storage: { backend: 'redis', container: 'ssi_storage', url: process.env.REDIS_URL! },
cache: { backend: 'redis', container: 'ssi', url: process.env.REDIS_URL! },
});
The ssi object exposes:
| Method | Purpose |
|---|---|
ssi.session(req) | Build a SessionManager from an incoming Request (API routes, Route Handlers). |
ssi.sessionAsync() | SessionManager in contexts without a Request (e.g. Next.js server components); cookies are detected automatically. |
ssi.auth() | AuthServer — login redirect, callback, logout. |
ssi.plans(domain) | SubscribedPlanManager — fetch plans, features, and limits for a Serial Subscriptions site URL. |
ssi.usage(domain, token) | UsageApi — report and query usage events. |
ssi.cache | SSICache — namespaced Redis or memory cache. |
Environment variables (required)
Set these in your deployment (and local .env):
SSI_ISSUER_BASE_URL=https://auth.example.com
SSI_CLIENT_ID=your-client-id
SSI_CLIENT_SECRET=your-client-secret
SSI_REDIRECT_URI=https://yourapp.com/api/v1/auth/callback
REDIS_URL=redis://localhost:6379
NEXT_PUBLIC_BASE_URL=https://yourapp.com
NEXT_PUBLIC_BASE_URL is used by the SessionClient on the frontend. Storage and cache URLs can follow the pattern shown in createSSI(); the skills bundle’s setup reference lists alternate backends and advanced options.
Values such as issuer URL, client ID, and secrets are tied to your Serial Subscriptions project—use the same sources as in Deploy the demo app or your project dashboard Integrate flow.
Auth routes (Next.js App Router)
Add a catch-all route, for example app/api/v1/auth/[...slug]/route.ts, that delegates to ssi.auth() so these endpoints work:
GET /api/v1/auth/login— redirect to the identity providerGET /api/v1/auth/callback— exchange the code for tokens and set the session cookieGET /api/v1/auth/logout— clear the session and redirect to provider logoutGET /api/v1/auth/session— return session claims and TTL as JSON
The skills package includes a complete route template in its setup reference—copy it rather than inventing handlers from scratch.
Session management
Server (SessionManager)
Use ssi.session(req) wherever you have the incoming request:
import { ssi } from '@/src/lib/ssi';
import { sessionRoles } from '@serialsubscriptions/platform-integration';
export async function GET(req: Request) {
const session = ssi.session(req);
const auth = await session.requireAuth({ roles: sessionRoles.userRoles });
if (!auth.authorized) {
return Response.json({ ok: false, error: auth.reason }, { status: auth.status });
}
const { sessionId, claims } = auth;
// ...
}
In server components without a Request, use await ssi.sessionAsync().
Prefer requireAuth() for protecting routes; it returns a clear authorized / not-authorized result with HTTP-friendly status codes. Common helpers include getSessionData(), getClaim(), hasRole(), setSession(), and clearSession().
Role constants
Do not hardcode role name strings. Import:
import { sessionRoles } from '@serialsubscriptions/platform-integration';
sessionRoles.adminRoles // platform_admin, owner, admin
sessionRoles.userRoles // member, owner, admin, billing, readonly
sessionRoles.allRoles
Client (SessionClient)
For browser code and client-side navigation guards:
import { SessionClient } from '@serialsubscriptions/platform-integration';
const client = SessionClient.getSessionClient();
if (await client.isLoggedIn()) {
const orgName = await client.get('organization_name');
}
requireAuth() on the client is for UX only—always enforce roles and permissions again on the server.
Subscription plans (SubscribedPlanManager)
Resolve plans and limits after you have an access token from the session:
const session = ssi.session(req);
const accessToken = await session.getSessionData(session.sessionId!, 'access_token');
const plans = ssi.plans('https://account.example.com');
plans.setBearerToken(accessToken as string);
const allPlans = await plans.getAllPlans();
const plan = await plans.getPlanById(1);
Use access_token, not id_token, for setBearerToken() and platform APIs.
Helpers include getLimitMax(), isLimitEnabled(), and limitAllowsOverage() for named limits (for example api_calls).
Usage API (UsageApi)
Report consumption against subscription limits and read usage back:
const usage = ssi.usage('https://account.example.com', bearerToken);
await usage.reportEvent({ limit_id: 123, amount: 1 });
await usage.reportEvents([
{ limit_id: 123, amount: 1, project_id: 42, aggregate_id: '…', aggregate_name: '…' },
{ limit_id: 456, amount: 5, project_id: 42 },
]);
const allUsage = await usage.getUsageAll();
const limitUsage = await usage.getUsage(123);
Prefer reportEvents() with multiple rows in one call when possible—one request and a single atomic transaction on the platform.
Cache (SSICache)
Configured through createSSI(). Typical use:
await ssi.cache.set('my:key', { data: 'value' }, 60);
const val = await ssi.cache.get<MyType>('my:key');
const jwksCache = ssi.cache.withPrefix('jwks');
const data = await ssi.cache.remember('expensive:op', 300, async () => {
return await fetchExpensiveData();
});
Integration checklist
- Run
npx skills add serialsubscriptions/skillsandnpm install @serialsubscriptions/platform-integration(plusioredisorpgif needed). - Set required environment variables.
- Add
src/lib/ssi.ts(or equivalent) with a single exportedcreateSSI()instance. - Add
app/api/v1/auth/[...slug]/route.tsusing the template from the skills setup reference. - Use
ssi.session(req)in API routes that must be authenticated. - Use
SessionClient.getSessionClient()in the frontend for session state. - Use
ssi.plans(domain)withsetBearerToken(accessToken)for plan and limit data. - Use
ssi.usage(domain, token)for metering. - Use
sessionRolesconstants everywhere you check roles. - Create one
SessionManagerper request—never share across requests.
Rules of thumb
- One session per request — bind
ssi.session(req)to the current request; do not reuse across concurrent requests. - access_token for APIs — use the OAuth access_token for
SubscribedPlanManager,UsageApi, and other Serial Subscriptions HTTP APIs. - Prefer
requireAuth()on the server for guards instead of ad hochasRole()chains. - Batch usage — use
reportEvents([...])instead of many singlereportEvent()calls when you can. - Token refresh —
getSessionData()/getClaim()handle refresh when the access token is near expiry; avoid duplicating refresh logic.
Related documentation
- Create a project — provision a Serial Subscriptions instance and project.
- Deploy the demo app — environment variables and deployment pattern that match this library.
For API-level detail (every method and option), rely on the references shipped with the serialsubscriptions/skills bundle (SETUP, SESSION-MANAGER, SESSION-CLIENT, SUBSCRIBED-PLAN-MANAGER, USAGE-API, CACHE) after you run npx skills add serialsubscriptions/skills.