SDKsPreview · 3 min read

Next.js SDK


Use @perkamo/nextjs when a Next.js app needs trusted Perkamo server calls plus browser-safe Client Component helpers.

The package supports the stable Next.js LTS lines: Next.js 16 Active LTS and Next.js 15 Maintenance LTS. It requires Node.js 20.9 or newer.

Install

bash

npm install @perkamo/nextjs @perkamo/browser @perkamo/sdk

App Router token routes

Create client tokens from trusted route handlers. The route verifies your app session, self-signs with a Perkamo signing key and returns only a short-lived client token to Client Components.

Create a signing key in the Perkamo console under Settings → Security → Signing keys. It gives you a KID (shown in the creation dialog and then listed in the table) and a one-time secret (shown once, backend-only). Read both from environment variables — the names are your own choice:

ts

// app/api/perkamo/token/route.ts
import { createPerkamoClientTokenRoute } from "@perkamo/nextjs/server";
import { auth } from "@/auth";

export const POST = createPerkamoClientTokenRoute({
kid: process.env.PERKAMO_SIGNING_KID, // KID
signingSecret: process.env.PERKAMO_SIGNING_SECRET, // backend-only
getUserId: async () => (await auth())?.user?.id ?? null,
scope: ["customer:read", "events:write"],
});
OptionWhat it is
kidKID of your signing key; goes in the token header. Safe to expose.
signingSecretThe signing key's one-time secret that signs the token. Backend-only.
getUserIdResolves the authenticated user id; return null to refuse with 401.
scopeClient permissions: customer:read, events:write, stream:read.
eventsOptional allow-list of event names the token may emit.
ttlSecondsToken lifetime in seconds. Default 600; clamped to the key's maximum.

Use a separate stream-token route when customer streams are enabled:

ts

// app/api/perkamo/stream-token/route.ts
import { createPerkamoClientTokenRoute } from "@perkamo/nextjs/server";
import { auth } from "@/auth";

export const POST = createPerkamoClientTokenRoute({
kid: process.env.PERKAMO_SIGNING_KID,
signingSecret: process.env.PERKAMO_SIGNING_SECRET,
getUserId: async () => (await auth())?.user?.id ?? null,
scope: ["stream:read"],
});

kid and signingSecret also accept an async function returning the value, so you can load them from a secret manager instead of environment variables.

Server Components and actions

Use @perkamo/nextjs/server only from trusted server code.

ts

import { createPerkamoNextServerClient } from "@perkamo/nextjs/server";

const perkamo = createPerkamoNextServerClient({
apiKey: () => process.env.PERKAMO_SECRET_KEY,
});

export async function trackPurchase(userId: string, orderId: string) {
"use server";

await perkamo.emit(
userId,
"purchase.completed",
{ order_id: orderId },
{ txId: orderId },
);
}

The server client exposes the same trusted helpers as @perkamo/sdk, including program() and eventCatalog() for backend admin tooling:

ts

const events = await perkamo.eventCatalog();

Client Components

Use @perkamo/nextjs/client only from Client Components.

tsx

"use client";

import { PerkamoProvider, usePerkamoCustomerJson } from "@perkamo/nextjs/client";

export function LoyaltyPanel() {
return (
<PerkamoProvider>
<Points />
</PerkamoProvider>
);
}

function Points() {
const { customer, loading, error, refresh } = usePerkamoCustomerJson();

if (loading) return <p>Loading...</p>;
if (error) return <button onClick={refresh}>Retry</button>;

return <p>{customer?.wallets.points ?? 0} points</p>;
}

PerkamoProvider defaults to:

  • POST /api/perkamo/token
  • POST /api/perkamo/stream-token

Override tokenEndpoint and streamTokenEndpoint when your app uses different route names.

Security boundary

  • Server keys stay in route handlers, Server Components or server actions.
  • Client Components receive only short-lived client tokens from your own Next.js

routes.

  • getUserId() should return your immutable application user id after verifying

the current session.

  • Token helpers return 401 when no user id is available.
  • Helpers default to the hosted Perkamo API. Set baseUrl only when Perkamo

support supplies a different API URL.

  • Perkamo API failures use PerkamoApiError from @perkamo/sdk, including

request id, retry-after and rate-limit metadata when available.