Easy-to-use Planning Center API wrapper with built-in rate limiting and flexible authentication.
npm install planning-center-api- 🔐 Flexible Authentication: Basic auth or Bearer token with auto-refresh
- ⚡ Rate Limiting: Automatic rate limit handling with retries
- 🔄 Auto Token Refresh: Automatically refreshes access tokens when needed
- 📝 TypeScript: Full TypeScript support with types
- 🎯 Fluent API: Intuitive, chainable API design
Create a .env or .env.local file:
PCO_API_CLIENT_ID=your_client_id
PCO_API_SECRET=your_client_secretThen just initialize without config:
import { PlanningCenter } from "planning-center-api";
const pc = new PlanningCenter();
// Automatically uses PCO_API_CLIENT_ID and PCO_API_SECRET from ENVimport { PlanningCenter } from "planning-center-api";
const pc = new PlanningCenter({
auth: {
type: "basic",
clientId: "your_client_id",
clientSecret: "your_client_secret",
},
});const pc = new PlanningCenter({
auth: {
type: "bearer",
bearerToken: "your_access_token",
refreshToken: "your_refresh_token", // required for autoRefresh
autoRefresh: true, // enables automatic token refresh
clientId: "your_oauth_client_id", // required for token refresh
clientSecret: "your_oauth_client_secret", // required for token refresh
lastRefreshedAt: new Date(), // optional, for proactive refresh
tokenExpiryMs: 7200000, // optional, defaults to 2 hours
onTokenRefresh: async (tokens) => {
// This callback is called whenever tokens are refreshed
console.log("New access token:", tokens.accessToken);
console.log("New refresh token:", tokens.refreshToken);
// Save these tokens to your database or storage
await saveTokens(tokens);
},
},
});Note: If you don't provide clientId and clientSecret, the client will look for these environment variables:
PCO_CLIENT_IDorNEXT_PUBLIC_PCO_CLIENT_IDPCO_SECRETorPCO_CLIENT_SECRET
const { data } = await pc.people.person("123").get();
console.log(data); // Person objectconst { data } = await pc.people.person().create({
first_name: "Jane",
last_name: "Doe",
birthdate: "1990-01-01",
});await pc.people.person("123").update({
first_name: "John",
last_name: "Smith",
});await pc.people.person("123").delete();const pc = new PlanningCenter({
auth: {
/* ... */
},
rateLimitDelay: 100, // ms between requests (default: 100)
maxRetries: 3, // max retries for rate limits (default: 3)
autoPaginate: true, // automatically fetch all pages (default: true)
});When using bearer token auth with autoRefresh: true, the wrapper handles token refresh in two ways:
Automatically refreshes when a request fails with 401 Unauthorized.
If you provide lastRefreshedAt, it will proactively refresh tokens 5 minutes before they expire (default 2 hour expiry).
The recommended way to handle token refresh is to provide an onTokenRefresh callback. This callback is called automatically whenever tokens are refreshed (either reactively or proactively), allowing you to save the new tokens to your database or storage:
// Example: Create a singleton client in your app
import { PlanningCenter } from "planning-center-api";
let accessToken = "initial_access_token";
let refreshToken = "initial_refresh_token";
const pc = new PlanningCenter({
auth: {
type: "bearer",
bearerToken: accessToken,
refreshToken: refreshToken,
autoRefresh: true,
clientId: process.env.PCO_CLIENT_ID!, // OAuth client ID
clientSecret: process.env.PCO_SECRET!, // OAuth client secret
lastRefreshedAt: new Date("2024-01-01T10:00:00Z"), // When token was issued
tokenExpiryMs: 7200000, // 2 hours (default)
onTokenRefresh: async (tokens) => {
// This is called automatically when tokens are refreshed
accessToken = tokens.accessToken;
refreshToken = tokens.refreshToken;
// Save to your database
await db.updateTokens({
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken,
lastRefreshedAt: new Date(),
});
},
},
});
// Now you can use the client throughout your app
// and it will automatically handle token refresh
const { data } = await pc.people.person("123").get();When fetching data, you can include related resources using the include option. The included resources are returned in the included array of the response:
// Fetch workflow cards with related resources
const response = await pc.people.workflow('123').listCards({
include: 'assignee,current_step,person,workflow'
});
// Access the main data
console.log(response.data); // Array of workflow cards
// Access included resources
console.log(response.included); // Array of related Person, WorkflowStep, Workflow objects
// Find a specific included resource
const assignee = response.included?.find(
item => item.type === 'Person' && item.id === '456'
);The included array contains all related resources requested via the include parameter. Each resource has type, id, attributes, and optionally relationships and links.
By default, the client automatically fetches all pages for list requests (autoPaginate: true). You can control this behavior globally or per-request:
const pc = new PlanningCenter({
auth: { /* ... */ },
autoPaginate: false, // Disable auto-pagination globally
});// Get only the first 25 people (no auto-pagination)
const { data } = await pc.people.list({
per_page: 25,
autoPaginate: false,
});
// Get exactly 50 people starting at offset 100
const { data } = await pc.people.list({
per_page: 50,
offset: 100,
autoPaginate: false,
});
// Auto-paginate with custom page size (fetches all pages, 100 at a time)
const { data } = await pc.people.list({
per_page: 100,
autoPaginate: true,
});You can also manually paginate through results:
let offset = 0;
const perPage = 25;
let hasMore = true;
while (hasMore) {
const response = await pc.people.list({
per_page: perPage,
offset: offset,
autoPaginate: false,
});
console.log(`Fetched ${response.data.length} people`);
// Check if there's a next page
hasMore = !!response.links?.next;
offset += perPage;
}Main client class.
auth: Authentication configuration (required)type:'basic'or'bearer'- For basic:
clientIdandclientSecret - For bearer:
bearerToken, optionalrefreshToken,autoRefresh,lastRefreshedAt,tokenExpiryMs, andonTokenRefresh
rateLimitDelay: Milliseconds between requests (default: 100)maxRetries: Maximum retries for rate limiting (default: 3)autoPaginate: Automatically fetch all pages for GET requests (default: true)
Access the People app via pc.people.
Returns a PersonResource for the given ID.
Methods available on a person resource:
get(): Get person by IDcreate(attributes): Create a new personupdate(attributes): Update a persondelete(): Delete a person
MIT