@playaos/api-client
Official TypeScript SDK for the PlayaOS REST API.
Installation
npm install @playaos/api-client
# or
pnpm add @playaos/api-clientRequirements: Node.js 18+. Zero runtime dependencies — uses the global fetch API.
Quick Start
import { createClient } from "@playaos/api-client";
const client = createClient({
baseUrl: "https://api.playaos.app",
apiKey: process.env.PLAYAOS_API_KEY!,
});
// List active members
const members = await client.members.list({ status: "active" });
// Get org config
const org = await client.org.get();Configuration
const client = createClient({
/** Base URL for authenticated /api/v1 routes */
baseUrl: string;
/** Optional override for /api/embed/v1 routes when they live on a different host */
embedBaseUrl?: string;
/** API key in the format pk_live_* */
apiKey: string;
/** Optional: the acting member's PlayaOS session JWT, sent as X-PlayaOS-Member-Token */
memberToken?: string;
});Generate the apiKey in the PlayaOS platform console under your org's Developer → API Keys page. embedBaseUrl is optional. If you omit it, embed requests reuse baseUrl.
Use it when your deployment splits route families across hosts, for example:
baseUrl = "https://api.playaos.app"for/api/v1/*embedBaseUrl = "https://embed.example.com"for/api/embed/v1/*
Member-scoped calls
An API key identifies the camp, not a person. Endpoints that return one member's own data (dues, payments, application, shelter assignment, tickets, vehicle passes, scholarships, onboarding progress, check-ins, referrals, notifications, shift signup, the full members.get profile) additionally require the acting member's session JWT and return 401 without it. See Authentication → Member tokens.
Keep one org key on your server and derive a per-member client per request:
const shared = createClient({ baseUrl: "https://api.playaos.app", apiKey: process.env.PLAYAOS_API_KEY! });
const me = shared.withMember(session.access_token);
await me.members.get("me"); // who am I in this camp — profile id, role, contact
await me.dues.list(); // only the signed-in member's rows
await me.payments.page(); // memberId defaults to the signed-in memberA non-admin member is pinned to their own records; an admin / super_admin member may pass any member id or list the whole camp.
Members
// List all members, optionally filtered
const members = await client.members.list();
const admins = await client.members.list({ role: "admin" });
const active = await client.members.list({ status: "active" });
// Get a specific member by profile ID
const member = await client.members.get("uuid-here");Applications
// List applications
const applications = await client.applications.list();
// Filter by status and/or year
const pending = await client.applications.list({ status: "pending" });
const this_year = await client.applications.list({ year: 2025 });Dues
// List dues for all members
const dues = await client.dues.list();
// Filter by user or year
const userDues = await client.dues.list({ userId: "uuid-here" });
const yearDues = await client.dues.list({ year: 2025 });Shifts
// List all shifts
const shifts = await client.shifts.list();
// Filter options
const published = await client.shifts.list({ publishedOnly: true });
const range = await client.shifts.list({ fromDate: "2025-08-01", toDate: "2025-09-05" });
const byYear = await client.shifts.list({ year: 2025 });Org
// Get your camp's org config (name, slug, settings, features, plan)
const org = await client.org.get();Error Handling
The SDK throws ApiClientError for non-2xx responses:
import { ApiClientError } from "@playaos/api-client";
try {
const member = await client.members.get("not-a-real-id");
} catch (err) {
if (err instanceof ApiClientError) {
console.error(err.status); // 404
console.error(err.code); // "NOT_FOUND"
console.error(err.message); // "Member not found"
}
}TypeScript Types
All types are exported from the package:
import type {
Member,
MemberRole,
Application,
ApplicationStatus,
DuesStatus,
Shift,
ShiftSignup,
ShiftType,
OrgConfig,
} from "@playaos/api-client";