Webhooks
Webhooks allow a third-party application to receive real-time data payloads when something changes in Planning Center. In most cases, webhooks are a faster, more efficient alternative to polling the API for data changes.
Permissions
All users who can log in can establish and manage webhook subscriptions at https://api.planningcenteronline.com/webhooks.
Webhooks are created under your specific user account and they only return data you have permission to see within Planning Center.
Request & Responses
You can inspect the request and your server response of every webhook at https://api.planningcenteronline.com/webhooks. Requests are POST only. Responding with a 410 will deactivate the webhook subscription.
Payloads
Other than the payload data, each webhook delivery includes a unique ID, a type, and an attempt number. Use this data for logging and surfacing helpful messaging in your application.
Rate-limits
At present, there are no rate-limits for webhooks.
Failures & Retries
If the delivery of webhook event fails, you'll receive an email after it's failed for an hour. If the webook continues to fail, a second email will be sent on the final try before it's deactivated (after 5.4 days since the first failure). If a large number of deliveries to the same endpoint are failing, some of these emails will be suppressed.
If we experience a very high number of delivery failures for a single endpoint within in a 24 hour window the subscription will be deactivated and an email will be sent.
Note: We will only send failure notification emails for webhook subscriptions that the user themself created. We will not send a notification if the webhook subscription was created on behalf of the user by an OAuth Application.
Retry Schedule
If a webhook is met with anything other than a 200 status, it will be retried up to 16 times along the following schedule after the first failure: 30 seconds, 1 minute, 2 minutes, 4 minutes, 8 minutes, 16 minutes, 32 minutes, 64 minutes, 2.1 hours, 4.2 hours, 8.5 hours, 17 hours, and finally once per day for up to 4 days.
You can manually redeliver a webhook at https://api.planningcenteronline.com/webhooks.
Authenticity
If you wish to verify that a webhook was sent by Planning Center you can use the value of the X-PCO-Webhooks-Authenticity header as a signature.
This value is an HMAC-SHA256 digest that uses the authenticity_secret (from your subscription) as the key, and the webhook body as the message. Here is some sample Ruby code to verify the signature:
require 'openssl'
# get this secret string of characters from the Webhooks UI https://api.planningcenteronline.com/webhooks
secret = '2608ab74db02d670279278a90585fca300953f0a19fd96529cb61bb92ed39d86'
# raw unformatted payload
data = '{"data":[{"id":"cac783dc-75e7-4c5b-88e8-502d9d8682ae","type":"EventDelivery","attributes":{"name":"people.v2.events.person.updated","attempt":1,"payload":"{\"data\":{\"type\":\"Person\",\"id\":\"125587227\",\"attributes\":{\"accounting_administrator\":false,\"anniversary\":null,\"avatar\":\"https://avatars.planningcenteronline.com/uploads/initials/K.png\",\"birthdate\":null,\"can_create_forms\":false,\"can_email_lists\":false,\"child\":false,\"created_at\":\"2023-04-28T19:05:52Z\",\"demographic_avatar_url\":\"https://avatars.planningcenteronline.com/uploads/initials/K.png\",\"directory_status\":\"no_access\",\"first_name\":\"Kid\",\"gender\":null,\"given_name\":null,\"grade\":null,\"graduation_year\":null,\"inactivated_at\":null,\"last_name\":\"222\",\"medical_notes\":null,\"membership\":null,\"middle_name\":null,\"name\":\"Kid 222\",\"nickname\":null,\"passed_background_check\":false,\"people_permissions\":null,\"remote_id\":null,\"school_type\":null,\"site_administrator\":false,\"status\":\"active\",\"updated_at\":\"2023-06-19T14:50:30Z\"},\"relationships\":{\"primary_campus\":{\"data\":null},\"gender\":{\"data\":null}},\"links\":{\"\":\"https://api.planningcenteronline.com/people/v2/people/125587227/\",\"addresses\":\"https://api.planningcenteronline.com/people/v2/people/125587227/addresses\",\"apps\":\"https://api.planningcenteronline.com/people/v2/people/125587227/apps\",\"connected_people\":\"https://api.planningcenteronline.com/people/v2/people/125587227/connected_people\",\"emails\":\"https://api.planningcenteronline.com/people/v2/people/125587227/emails\",\"field_data\":\"https://api.planningcenteronline.com/people/v2/people/125587227/field_data\",\"household_memberships\":\"https://api.planningcenteronline.com/people/v2/people/125587227/household_memberships\",\"households\":\"https://api.planningcenteronline.com/people/v2/people/125587227/households\",\"inactive_reason\":null,\"marital_status\":null,\"message_groups\":\"https://api.planningcenteronline.com/people/v2/people/125587227/message_groups\",\"messages\":\"https://api.planningcenteronline.com/people/v2/people/125587227/messages\",\"name_prefix\":null,\"name_suffix\":null,\"notes\":\"https://api.planningcenteronline.com/people/v2/people/125587227/notes\",\"organization\":\"https://api.planningcenteronline.com/people/v2/people/125587227/organization\",\"person_apps\":\"https://api.planningcenteronline.com/people/v2/people/125587227/person_apps\",\"phone_numbers\":\"https://api.planningcenteronline.com/people/v2/people/125587227/phone_numbers\",\"platform_notifications\":\"https://api.planningcenteronline.com/people/v2/people/125587227/platform_notifications\",\"primary_campus\":null,\"school\":null,\"social_profiles\":\"https://api.planningcenteronline.com/people/v2/people/125587227/social_profiles\",\"workflow_cards\":\"https://api.planningcenteronline.com/people/v2/people/125587227/workflow_cards\",\"workflow_shares\":\"https://api.planningcenteronline.com/people/v2/people/125587227/workflow_shares\",\"self\":\"https://api.planningcenteronline.com/people/v2/people/125587227\",\"html\":\"https://people.planningcenteronline.com/people/AC125587227\"}},\"included\":[],\"meta\":{\"can_include\":[\"addresses\",\"emails\",\"field_data\",\"households\",\"inactive_reason\",\"marital_status\",\"name_prefix\",\"name_suffix\",\"organization\",\"person_apps\",\"phone_numbers\",\"platform_notifications\",\"primary_campus\",\"school\",\"social_profiles\"],\"parent\":{\"id\":\"127\",\"type\":\"Organization\"},\"public\":{\"attributes\":{\"merged\":null}}}}"},"relationships":{"organization":{"data":{"type":"Organization","id":"127"}}}}]}'
digest = OpenSSL::Digest.new('SHA256')
mac = OpenSSL::HMAC.hexdigest(digest, secret, data)
# validate the mac matches that in the webhook X-PCO-Webhooks-Authenticity header
p mac # => "a0782fde663e7ee25a99b9d44f45434120ff027ba98ef333a83a4590fcc97828"