SDKsStable · 4 min read

JavaScript SDK

Backend JavaScript and TypeScript SDK setup for trusted Perkamo API calls.


Use @perkamo/sdk from trusted server-side JavaScript or TypeScript. Do not bundle this package with a server API key into browser, mobile or embedded widget code.

Install

bash

npm install @perkamo/sdk

ts

import { createPerkamoClient } from "@perkamo/sdk";

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

Only apiKey is required for a normal hosted API integration.

Advanced client options:

OptionRequiredDescription
apiKeyyesServer API key string or async supplier.
baseUrlnoCustom, staging or private endpoint override.
fetchnoCustom fetch-compatible adapter.
timeoutMsnoOptional request timeout override. Defaults to none.

The SDK defaults to the hosted Perkamo API. You normally do not need to pass an API URL.

Identify a customer

Use your application's stable user id as the Perkamo customer id. Send e-mail, name and internal customer metadata as trusted customer traits from backend code:

ts

import type { PerkamoCustomerTraits } from "@perkamo/sdk";

const optionalLocale: string | undefined = undefined;
const traits: PerkamoCustomerTraits = {
email: "customer@example.test",
name: "Customer Test",
crm_id: "crm_123",
locale: optionalLocale,
};

await perkamo.identify("customer_123", traits);

You can also pass traits inline:

ts

await perkamo.identify("customer_123", {
email: "customer@example.test",
name: "Customer Test",
crm_id: "crm_123",
});

Only send non-secret facts your application is allowed to share with Perkamo. Calling identify() again updates traits for the same customer. A common pattern is to call it from trusted backend code after registration, login, email verification, consent changes or customer updates. Keep emit() for behavioral events such as purchases, lessons, referrals and app actions.

Undefined object properties in traits are omitted by JSON serialization. Remove undefined values from arrays before sending customer traits. Event context remains stricter: strip undefined values before calling emit() or batch(). Perkamo Console uses common identity traits such as e-mail, name, phone, external id, customer id and CRM id for customer display and operator search.

Emit one event

ts

await perkamo.emit(
"customer_123",
"purchase.completed",
{ order_id: "order_1092", amount: 12900, currency: "CZK" },
{ txId: "order_1092", occurredAt: new Date("2026-06-01T10:00:00.000Z") },
);

For business-critical events, always pass a stable txId. The SDK can generate one when omitted, but natural ids make retries and support investigations clearer.

Use Events to confirm the emitted event and Customers to inspect the resulting customer state.

Send a batch

ts

await perkamo.batch([
{
user_id: "customer_123",
event: "lesson.finished",
transaction_id: "lesson:intro:user:customer_123",
context: { lesson_id: "intro" },
occurred_at: new Date().toISOString(),
},
]);

Batch accepts up to 100 events. Each event requires an explicit transaction_id.

Read a customer

ts

const customer = await perkamo.customer("customer_123");

if (customer.flags.perks.welcome_member) {
showWelcomeBadge();
}

Read the event catalog

Use program() or eventCatalog() from trusted backend code when an internal admin tool needs the configured event keys and labels for the active Space.

ts

const events = await perkamo.eventCatalog();

for (const item of events) {
console.log(item.event, item.title);
}

Do not use this as a wallet editing API. Send operators to the Perkamo Console for audited manual wallet or perk changes.

Handle API errors

ts

import { PerkamoApiError } from "@perkamo/sdk";

try {
await perkamo.emit("customer_123", "purchase.completed", {}, { txId: "order_1092" });
} catch (error) {
if (error instanceof PerkamoApiError) {
console.error(error.status, error.requestId, error.retryAfter);
}
}

requestId, retryAfter and rateLimit are populated when the API or edge gateway returns the corresponding headers.

Customer streams

The SDK exposes subscribeCustomer(userId, streamToken, onCustomer, onError?) as a preview helper for the planned customer stream flow. Do not depend on it as stable v1 API behavior yet: the public stable v1 HTTP controller currently documents server-key REST endpoints, not browser-token /v1/client/* routes.

When customer streams are enabled for an integration, they must use short-lived tokens returned by the customer backend after it calls Perkamo. Never pass a server API key into browser code.

Configure browser key metadata and allowed origins in Browser SDK.

ts

const subscription = perkamo.subscribeCustomer(
"customer_123",
streamToken,
(customer) => renderProgress(customer.wallets.points ?? 0),
);

subscription.close();

Core helpers

@perkamo/sdk-core is runtime-neutral and contains event validation, canonical JSON and transaction id helpers.

ts

import { assertSafeEventInput, createTransactionId } from "@perkamo/sdk-core";

const input = {
user_id: "customer_123",
event: "purchase.completed",
transaction_id: createTransactionId(),
context: { order_id: "order_1092" },
};

assertSafeEventInput(input);