Integration Summary
Church Management Platform provides a fully integrated, two-way API connection with Planning Center Online (PCO), a widely-used church management platform. This integration synchronizes people, families, and groups between both systems, ensuring data stays consistent regardless of where changes originate.
What Gets Synced
| Data | Inbound (PCO → Local) | Outbound (Local → PCO) |
|---|---|---|
| People (demographics, contacts) | Pull | Push |
| Families / Households | Pull | Push |
| Groups | Pull | Read-Only in PCO |
| Group Memberships | Pull | Push |
| Check-In Records | Read | Not Writable |
| Calendar Events | Read | Not Writable |
PCO API Overview
Planning Center Online exposes a REST API conforming to the JSON:API 1.0
specification. Each PCO product module has its own versioned API endpoint. The API supports
sideloading related resources (?include=), field-level filtering
(?where[field]=value), ordering, and cursor-based pagination (max 100 per page).
Key Query Features
- Sideloading:
?include=emails,phone_numbers— fetch related resources in a single request. - Filtering:
?where[first_name]=Johnor?where[updated_at][gte]=...for incremental sync. - Search:
?where[search_name_or_email_or_phone_number]=...— combined search. - Ordering:
?order=last_nameor?order=-created_at(descending). - Pagination:
?per_page=100(max 100). Followlinks.nextURL for next page.
Base URLs
| Module | Base URL | API Version | Used By Integration |
|---|---|---|---|
| People | https://api.planningcenteronline.com/people/v2/ | 2022-07-14 | Read/Write |
| Check-Ins | https://api.planningcenteronline.com/check-ins/v2/ | 2019-07-17 | Read |
| Groups | https://api.planningcenteronline.com/groups/v2/ | 2018-08-01 | Read/Write |
| Calendar | https://api.planningcenteronline.com/calendar/v2/ | 2021-07-20 | Read |
| Services | https://api.planningcenteronline.com/services/v2/ | — | Not Used |
| Giving | https://api.planningcenteronline.com/giving/v2/ | — | Not Used |
Authentication
The integration uses Personal Access Tokens (PAT) for server-to-server
authentication via HTTP Basic Auth. Credentials are configured per-church in
appsettings.json under the PlanningCenter section.
- Auth method: HTTP Basic Auth with
AppId:Secret - Token type: Personal Access Token (never expires until revoked)
- Scope: Full read/write access to the organization's PCO data
- Configuration:
PlanningCenter:AppIdandPlanningCenter:Secretin appsettings
OAuth 2.0 is also supported by PCO for multi-tenant SaaS deployments (access tokens expire after 2 hours with long-lived refresh tokens). The current single-church integration uses PAT for simplicity.
Write Capabilities Matrix
PCO's API is heavily read-biased. The People module is fully writable, Groups support membership writes only, and Check-Ins/Calendar are entirely read-only.
| Resource | Module | Create | Update | Delete | Notes |
|---|---|---|---|---|---|
| Person | People | Yes | Yes | Yes | remote_id field stores our people.id |
| People | Yes | Yes | Yes | Scoped to person: /people/{id}/emails |
|
| Phone Number | People | Yes | Yes | Yes | PCO auto-normalizes to E.164 |
| Address | People | Yes | Yes | Yes | On Person (not Household) |
| Household | People | Yes | Yes | Yes | Maps to our families table |
| Household Membership | People | Yes | Yes | Yes | No relationship role — just member |
| Group Membership | Groups | Yes | Yes | No | Role: "leader" or "member" only |
| Group | Groups | Read-Only | Read-Only | No | Groups created in PCO admin only |
| Check-In Record | Check-Ins | Read-Only | Read-Only | No | Check-ins performed locally stay local |
| Calendar Event | Calendar | Read-Only | Read-Only | No | Events managed in PCO Calendar admin |
Read Capabilities Matrix
All PCO modules provide comprehensive read access. The integration uses read endpoints for inbound sync, data comparison, and admin-facing read-only views.
| Resource | Module | List | Get | Sideload | Filter |
|---|---|---|---|---|---|
| People | People | Yes | Yes | emails, phone_numbers, addresses | name, email, phone, updated_at |
| Households | People | Yes | Yes | people (members) | name, updated_at |
| Groups | Groups | Yes | Yes | memberships | name, enrollment_status |
| Group Types | Groups | Yes | Yes | — | — |
| Group Memberships | Groups | Yes | Yes | person | role |
| Check-Ins | Check-Ins | Yes | Yes | event, person, locations | date, event |
| Check-In Locations | Check-Ins | Yes | Yes | — | kind, age/grade range |
| Calendar Events | Calendar | Yes | Yes | — | future, starts_at |
| Event Instances | Calendar | Yes | Yes | — | future, starts_at, recurrence |
All Endpoints Used
Complete list of PCO API endpoints consumed by the Church Management Platform integration.
People Module (Read/Write)
| Method | Endpoint | Purpose |
|---|---|---|
GET | /people/v2/people | List all people (paginated, with sideloaded contacts) |
GET | /people/v2/people/{id} | Get single person by PCO ID |
POST | /people/v2/people | Create person (outbound push) |
PATCH | /people/v2/people/{id} | Update person demographics |
GET | /people/v2/people/{id}/emails | List person's emails |
POST | /people/v2/people/{id}/emails | Add email to person |
GET | /people/v2/people/{id}/phone_numbers | List person's phone numbers |
POST | /people/v2/people/{id}/phone_numbers | Add phone to person |
GET | /people/v2/people/{id}/addresses | List person's addresses |
GET | /people/v2/households | List all households (with sideloaded members) |
POST | /people/v2/households | Create household (outbound push) |
POST | /people/v2/households/{id}/household_memberships | Add person to household |
Groups Module (Read + Membership Write)
| Method | Endpoint | Purpose |
|---|---|---|
GET | /groups/v2/groups | List all groups |
GET | /groups/v2/groups/{id} | Get single group |
GET | /groups/v2/group_types | List group types |
GET | /groups/v2/groups/{id}/memberships | List group members |
POST | /groups/v2/groups/{id}/memberships | Add member to group (outbound push) |
PATCH | /groups/v2/groups/{id}/memberships/{mid} | Update membership role |
Check-Ins Module (Read-Only)
| Method | Endpoint | Purpose |
|---|---|---|
GET | /check-ins/v2/check_ins | List check-in records |
GET | /check-ins/v2/events | List check-in events |
GET | /check-ins/v2/stations | List check-in stations |
GET | /check-ins/v2/labels | List label templates |
GET | /check-ins/v2/headcounts | Aggregate attendance counts |
Calendar Module (Read-Only)
| Method | Endpoint | Purpose |
|---|---|---|
GET | /calendar/v2/events | List calendar events |
GET | /calendar/v2/event_instances | List event occurrences (supports future filter) |
Inbound Sync (Pull from PCO)
Inbound sync pulls data from Planning Center into the local database. The sync service
(PcoSyncService) supports four sync types that can be triggered individually or as
a full sync.
Sync Types
| Sync Type | What It Does |
|---|---|
people | Streams all PCO people (with emails + phones), matches by pco_id → email → name+dob, upserts demographics and contacts |
families | Streams PCO households (with members), creates/updates families and family_members, syncs addresses |
groups | Streams PCO groups, creates under auto-created "PCO Groups" department in church_group hierarchy |
full | Runs people → families → groups in sequence |
Matching Logic
When pulling people from PCO, the sync service uses a three-tier matching strategy:
- pco_id match — if a local person already has a
pco_idmatching the PCO record, update in place. - Email match — if no pco_id match, search local
person_emailsfor a matching email address. - Name + DOB match — if no email match, compare first_name + last_name + date of birth as a fallback.
- Create new — if no match found, create a new local person record with the PCO data.
Data Ownership
- PCO authoritative for: demographics (first_name, last_name, gender, dob), contact info (emails, phones, addresses)
- Local authoritative for: operational data (allergies, photo_url, is_verified, group memberships, check-in records)
via PCO Sync page
PCO records (paginated)
pco_id → email → name
Track progress in pco_sync_log
Outbound Push (Local → PCO)
Outbound push sends locally-created or modified records to Planning Center. The push service supports four push types.
Push Types
| Push Type | What It Does |
|---|---|
people | Pushes local people (without pco_id) to PCO, creates Person + Email + Phone records, stores PCO ID back on local record |
families | Pushes local families to PCO as Households, creates HouseholdMembership records for members |
group-memberships | Pushes local group memberships to PCO Groups (creates membership, maps leader status to PCO role) |
full | Runs people → families → group-memberships in sequence |
via PCO Sync page
without pco_id
(rate-limited)
on local record
Real-Time Webhooks
PCO pushes real-time change notifications via webhooks, reducing the need for polling.
Church Management Platform receives and processes these events at POST /api/pco/webhook.
Supported Webhook Events
| Event | Action |
|---|---|
person.created | Creates a new local person record with PCO demographics and pco_id |
person.updated | Updates local person demographics from PCO data |
person.destroyed | Marks local person as inactive (is_active=false) |
Security
- Webhook payloads are verified via
X-PCO-Webhooks-Authenticityheader using HMAC-SHA256 - Webhook secret is configured in
PlanningCenter:WebhookSecret - PCO retries up to 16 times with exponential backoff over 5+ days on delivery failure
Read-Only Data Endpoints
The admin UI exposes several read-only views of PCO data for comparison and verification purposes.
| Endpoint | Returns |
|---|---|
GET /api/pco/checkins | Recent PCO check-in records (name, security code, kind, timestamps) |
GET /api/pco/calendar-events | PCO calendar event instances (name, description, dates, recurrence, location) |
GET /api/pco/group-types | PCO group types (name, description, church center visibility) |
GET /api/pco/checkin-locations | PCO check-in locations (name, kind, age/grade range, capacity) |
Entity Mapping
How Church Management Platform entities map to Planning Center equivalents. The pco_id column
on local tables stores the corresponding PCO record ID for bidirectional lookup.
| Church Management Platform | PCO Equivalent | Sync Direction | Mapping Key |
|---|---|---|---|
people | Person (People API) | Push & Pull | people.pco_id + PCO remote_id |
person_emails | Email (People API) | Push & Pull | Matched by email address |
person_phones | PhoneNumber (People API) | Push & Pull | Both E.164 normalized |
families | Household (People API) | Push & Pull | families.pco_id |
family_members | HouseholdMembership | Push & Pull | Via person + household pco_ids |
church_group | Group (Groups API) | Pull Only | church_group.pco_id |
group_membership | Membership (Groups API) | Push & Pull | Via person + group pco_ids |
events | EventInstance (Calendar) | Pull Only | events.pco_id |
locations | Location (Check-Ins) | Pull Only | locations.pco_id |
checkins | CheckIn (Check-Ins) | Read Only | Check-ins stay local |
Model Differences
Key structural differences between Church Management Platform and Planning Center data models.
Families / Households
- PCO HouseholdMembership has no relationship role (no Parent/Child/Spouse). Child status comes from Person's
childboolean. - PCO addresses are on Person (not Household). Our system stores addresses on
families. - Our
authorized_pickup_idsandrelationshipstable have no PCO equivalent.
Groups
- Our 3-level hierarchy (Department → Area → Group) is richer than PCO's flat GroupType → Group structure.
- PCO membership role is only "leader" or "member" — no Coach/Other from our
small_group_leader_type. - Our small group metadata (meeting day, frequency, area, childcare) has no PCO equivalent. PCO stores
scheduleas freetext.
Check-Ins
- PCO uses a Station model (physical iPads) vs. our software-based kiosk.
- PCO resolves children to rooms via Location age/grade filters. Our system uses hierarchical group-based event resolution.
- Check-ins performed in Church Management Platform cannot be pushed to PCO (API limitation).
Rate Limits & Performance
PCO enforces 100 requests per minute per organization. The integration uses several strategies to work within this constraint.
Rate Limit Mitigation
- SemaphoreSlim rate limiter in
PcoApiClient— throttles at 90 req/min to stay safely under the 100/min limit - Sideloading —
?include=emails,phone_numbersfetches contacts in one request instead of per-person calls - IAsyncEnumerable pagination — streams results page-by-page without loading all data into memory
- Incremental sync —
?where[updated_at][gte]=...pulls only records changed since last sync - Webhooks — real-time person change notifications eliminate polling for People module
Bulk Operation Timing
| Operation | API Calls | Estimated Time |
|---|---|---|
| List 5,000 people (with contacts) | ~50 GET requests | ~30 seconds |
| Push 5,000 people + contacts | ~15,000 requests | ~2.5 hours |
| Full initial sync | ~25,000+ requests | ~4+ hours |
Admin UI
The integration is managed through the PCO Sync page at /admin/pco-sync,
accessible to ADMIN users via the sidebar and dashboard.
Features
- Connection status — shows whether PCO credentials are configured, with "Test Connection" button
- Sync actions — buttons for People, Families, Groups, and Full Sync (inbound pull)
- Push actions — buttons for People, Families, Group Memberships, and Full Push (outbound)
- Live progress — running sync/push shows real-time created/updated/skipped counters (polled every 3 seconds)
- Sync history — table of last 20 runs with type, status badge, timing, and record counts
- Webhook config — shows webhook URL, secret status, and subscription count
Configuration
All PCO integration settings are in appsettings.json under the PlanningCenter section.
Each church deployment configures its own PCO credentials — no code changes required.
// appsettings.json
{
"PlanningCenter": {
"AppId": "", // PCO Personal Access Token App ID
"Secret": "", // PCO Personal Access Token Secret
"WebhookSecret": "" // HMAC-SHA256 secret for webhook verification
}
}
Database Schema Support
pco_id TEXTcolumn onpeople,families,locations,events,church_group(with partial unique indexes)pco_sync_logtable tracks all sync/push runs (type, status, timing, record counts, error messages)- PCO webhook controller at
POST /api/pco/webhook(HMAC-SHA256 verified, no authentication required)
Backend Components
| Component | Location | Purpose |
|---|---|---|
IPcoApiClient | API/Services/ | HTTP client with rate limiting, pagination, JSON:API parsing |
IPcoSyncService | API/Services/ | Sync orchestrator (people, families, groups, push) |
PcoController | API/Controllers/ | Admin endpoints for config, sync, push, status, history |
PcoWebhookController | API/Controllers/ | Webhook receiver with HMAC verification |
PcoSyncLog | Core/Entities/ | Entity for sync run tracking |
PcoModels/ | Shared/ | JSON:API response models for PCO data |