@playaos/api-client
v0.15.0
Published
Typed API client for PlayaOS — manage camp members, dues, shifts, applications, waivers, and annotations
Maintainers
Readme
@playaos/api-client
Typed JavaScript/TypeScript client for the PlayaOS REST API.
Installation
npm install @playaos/api-clientQuick start
import { createClient } from "@playaos/api-client";
const client = createClient({
baseUrl: "https://api.playaos.app",
apiKey: "pk_live_...",
});
// List members
const members = await client.members.list();
// Filter by role
const admins = await client.members.list({ role: "admin" });
// Get a single member
const member = await client.members.get("uuid-here");
// List applications
const pending = await client.applications.list({ status: "pending", year: 2025 });
// List dues status
const dues = await client.dues.list({ year: 2025 });
// List shifts
const shifts = await client.shifts.list({ publishedOnly: true, year: 2025 });
// Get org config
const org = await client.org.get();
// Submit an application through the embed host
const created = await client.applications.create({
acknowledged_expectations: true,
first_name: "Alice",
last_name: "Smith",
email: "[email protected]",
phone: "5551234567",
birthday: "1990-01-01",
hometown: "Reno",
how_heard: "friend",
burning_man_before: "no",
shelter_type: "shiftpod_tent",
ticket_status: "have_ticket",
build_available: false,
strike_available: false,
about_yourself: "I am writing more than fifty characters about myself for this example.",
agrees_to_principles: true,
});If your deployment serves both route families from the same host, omit embedBaseUrl and the client will reuse baseUrl.
Route hosts
baseUrlis used for authenticated/api/v1/*routes.embedBaseUrloptionally overrides the host for/api/embed/v1/*routes.- When
embedBaseUrlis omitted, embed requests default tobaseUrl.
This is useful when a deployment splits API-key-authenticated routes and embed routes onto different hosts. PlayaOS production serves both route families from https://api.playaos.app, so most integrations can omit embedBaseUrl.
Authentication
Generate an API key in the PlayaOS platform console under your org's Developer → API Keys page. Keys use the format pk_live_* and are scoped to specific resources.
For the full /api/v1 auth model, member-scoping rules, route conventions, and examples for packing and bike rentals, see docs/guides/public-api.md. The live OpenAPI document remains the complete endpoint inventory.
Common required scopes:
| Endpoint | Scope |
|----------|-------|
| members.list / members.get | members:read |
| applications.list | applications:read |
| dues.list | dues:read |
| shifts.list | shifts:read |
| org.get | org:read |
Member-scoped calls
An API key acts as the camp, not as a person. Endpoints that return one member's own data — their dues, payments, application, shelter, tickets, onboarding progress, notifications, shift signup — also require the acting member's PlayaOS session JWT, sent as X-PlayaOS-Member-Token. Without it those endpoints return 401; with it, a non-admin member is pinned to their own records (asking for someone else's is 403, or 404 for a row id), while an admin/super_admin member may name any member or list the whole camp.
Hold one org key server-side and derive a per-member client from it:
const shared = createClient({ baseUrl: "https://api.playaos.app", apiKey: process.env.PLAYAOS_API_KEY! });
// `session.access_token` is the signed-in user's Supabase JWT from auth.playaos.app
const me = shared.withMember(session.access_token);
const profile = await me.members.get("me"); // who am I in this camp (profile id, role, contact)
const myDues = await me.dues.list({ year: 2026 }); // pinned to the signed-in member
const page = await me.payments.page(); // memberId defaults to the signed-in memberOrg-level endpoints (org.get, shifts.list, documents.list, contacts.list, …) do not require the member token, so the derived client works for both — but a token that is sent must still verify: an expired or invalid one is a 401 on any /api/v1 route that installs the member middleware, org-level or not. Refresh the session before deriving the client.
Error handling
import { ApiClientError } from "@playaos/api-client";
try {
const member = await client.members.get("nonexistent-id");
} catch (err) {
if (err instanceof ApiClientError) {
console.error(err.status, err.code, err.message);
// 404, "NOT_FOUND", "Member not found"
// err.retryAfter is the Retry-After header in seconds when the server sends a
// numeric one (commonly on 429/503); undefined otherwise.
}
}MCP server
For AI agent access, use the companion @playaos/mcp package.
