Overview

OAuth Authentication

We use OAuth 2.0 to secure our endpoints, so you'll need to submit a request to get an application created.

Request OAuth API credentials

To request access, please fill out the API access request form.


API Base URL

All API calls must be made against:

https://api.ccbchurch.com

Do not call https://[subdomain].ccbchurch.com directly.

Authentication Overview

Vendors and churches authorize access to a church's data with the three-legged OAuth 2.0 authorization code flow.

EnvironmentAuthorization URLToken URL
Productionhttps://oauth.ccbchurch.com/oauth/authorizehttps://api.ccbchurch.com/oauth/token
Sandboxhttps://beta-oauth.ccbchurch.com/oauth/authorizehttps://api-beta.ccbchurch.com/oauth/token

Sandbox credentials required

Only sandbox-specific API credentials work with the sandbox servers, and production credentials only work in production.


System Auth

This is the required first step to gain access to a church's data. A Master Administrator, or someone with permission to edit system-wide settings, must authorize your application before you can access that church's data. This is a one-time step per church: after it, you keep access by refreshing tokens, not by asking the admin again.

📘

ChMS admin with "edit system-wide settings" is required

You can learn about how to add the "edit system-wide settings" permission to your ChMS user account in this article https://support.pushpay.com/s/article/Getting-Started-Admin-Privileges#Who-Assign

Contact your ChMS master administrator. You can find the list of contacts from within ChMS in the Help & Support menu at the top-right of the screen. It will be listed as "Support: contact your church's support team"

Step 1 — Send the admin to the Authorization URL

Redirect the admin's browser (a GET) to the Authorization URL with these query string parameters.

ParameterRequired?Description
client_idRequiredThe OAuth client ID Pushpay issued for your application. Use your real client ID, not a placeholder from sample code.
response_typeRequiredMust always be code.
redirect_uriRequiredWhere to send the admin afterwards. Must match the redirect URI registered for your application character for character, including the scheme (https://, or http:// for localhost), port, path and any trailing slash. URL-encode it.
stateRecommendedAn unguessable value you generate per request. It comes back unchanged on the redirect so you can confirm the response belongs to a request you started (CSRF protection).
subdomainOptionalThe church's ChMS subdomain (for https://mychurch.ccbchurch.com, use mychurch). Skips the step where the admin types it in.
scopeOptionalSpace-separated list of scopes. Must be a subset of the scopes granted to your application. If omitted, all granted scopes are requested.
resource_owner_authOptionalSet to true to run Identity Auth instead of System Auth. Omit it for System Auth.

This step isn't an API call from your server. Your app sends the admin's browser to a URL like this one, for example with a link, a button, or an HTTP 302 redirect:

GET https://oauth.ccbchurch.com/oauth/authorize
 ?client_id=YOUR_CLIENT_ID
 &response_type=code
 &redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback
 &state=8f2c1e7a9b
 &subdomain=mychurch

The admin then:

  • enters the church subdomain, if you didn't send subdomain
  • signs in to ChMS, if not already signed in
  • reviews the requested scopes and approves your application
⚠️

"Error accessing page — If you came here from a value added partner's site…"

This page means the authorize request was rejected before sign-in, almost always because client_id or redirect_uri doesn't match what is registered. No redirect back to your app happens in this case. Check for a placeholder client_id first, then compare redirect_uri against the registered value exactly.

Step 2 — Receive the authorization code

You don't make a call in this step. After the admin approves, Pushpay redirects their browser to your redirect_uri, so your callback endpoint receives an incoming GET request. There's no JSON body: the values arrive as query string parameters on the URL.

https://app.example.com/oauth/callback
 ?code=AUTHORIZATION_CODE
 &state=8f2c1e7a9b
Query parameterDescription
codeThe authorization code. You exchange it for tokens in Step 3.
stateThe same state value you sent in Step 1, returned unchanged.

Your callback handler should read these two parameters, check that state matches the value you sent, and then make the Step 3 call from your server. The code is short-lived and single-use, so exchange it right away.

Step 3 — Exchange the code for tokens

POST to the Token URL from your server. Never do this from a browser or mobile app, because the request contains your client secret.

Headers

HeaderValue
Acceptapplication/vnd.ccbchurch.v2+json
Content-Typeapplication/x-www-form-urlencoded
AuthorizationOptional. Basic base64(client_id:client_secret), as an alternative to sending them in the body.

Body parameters

ParameterRequired?Description
grant_typeRequiredMust be authorization_code.
codeRequiredThe code value from Step 2.
redirect_uriRequiredMust be exactly the same redirect_uri string you sent in Step 1. If it differs, the request fails and the code is used up, so you'll need to restart from Step 1.
client_idRequiredYour OAuth client ID. Not needed in the body if you send a Basic Authorization header.
client_secretRequiredYour OAuth client secret. Not needed in the body if you send a Basic Authorization header.

You don't send subdomain here, because the code already identifies the church.

curl -X POST https://api.ccbchurch.com/oauth/token \
  -H "Accept: application/vnd.ccbchurch.v2+json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=AUTHORIZATION_CODE" \
  --data-urlencode "redirect_uri=https://app.example.com/oauth/callback" \
  --data-urlencode "client_id=YOUR_CLIENT_ID" \
  --data-urlencode "client_secret=YOUR_CLIENT_SECRET"

Response — 200 OK

{
  "access_token": "{access token}",
  "token_type": "bearer",
  "expires_in": 7200,
  "scope": "read:individuals read:groups",
  "refresh_token": "{refresh token}"
}
FieldDescription
access_tokenSend as the Bearer token on every API call.
token_typeAlways bearer.
expires_inLifetime of the access token in seconds (7200 = 2 hours).
scopeSpace-separated scopes the token actually holds. This can be fewer than you asked for.
refresh_tokenUse it to get a new access token without involving the admin again. Store it securely.

Step 4 — Call the API

Send the access token as a Bearer token on every request, together with the v2 Accept header:

curl https://api.ccbchurch.com/individuals \
  -H "Authorization: Bearer {access token}" \
  -H "Accept: application/vnd.ccbchurch.v2+json"

Refreshing Tokens

Access tokens expire after two hours (expires_in: 7200). To get a new one, POST the refresh token to the same Token URL. Refresh tokens have no fixed expiry, but each one works only once.

Headers: same as the code exchange (Accept: application/vnd.ccbchurch.v2+json, Content-Type: application/x-www-form-urlencoded, optional Basic Authorization).

Body parameters

ParameterRequired?Description
grant_typeRequiredMust be refresh_token.
refresh_tokenRequiredThe most recent refresh_token you received.
client_idRequiredYour OAuth client ID. Not needed in the body if you send a Basic Authorization header.
client_secretRequiredYour OAuth client secret. Not needed in the body if you send a Basic Authorization header.

scope is not accepted here. The new token keeps the scopes of the one it replaces.

curl -X POST https://api.ccbchurch.com/oauth/token \
  -H "Accept: application/vnd.ccbchurch.v2+json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "refresh_token={refresh token}" \
  --data-urlencode "client_id=YOUR_CLIENT_ID" \
  --data-urlencode "client_secret=YOUR_CLIENT_SECRET"

Response — 200 OK

{
  "access_token": "{new access token}",
  "token_type": "bearer",
  "expires_in": 7200,
  "scope": "read:individuals read:groups",
  "refresh_token": "{new refresh token}"
}
⚠️

Refresh tokens rotate — always save the new one

Every successful refresh returns a new refresh_token and immediately revokes both the previous access token and the previous refresh token. If you keep using the old refresh token, you'll get invalid_grant and need the admin to authorize again. If several workers share one token, make sure only one of them refreshes it, and save the new pair before anything else uses it.

A refresh also fails with invalid_grant if the church has removed your application's approval.


Token Errors

Token endpoint errors use the standard OAuth 2.0 error body:

{
  "error": "invalid_client",
  "error_description": "Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method."
}
HTTPerrorCommon causes
401invalid_requestMissing grant_type, code or refresh_token; the code is expired, already used, or unknown; redirect_uri doesn't match Step 1; the church's approval of your app is missing.
401invalid_clientMissing client_id/client_secret, unknown client, or wrong secret (including mixing sandbox and production credentials).
401invalid_grantThe refresh token is unknown, already used (rotated), or revoked; the church removed its approval; or the individual is no longer valid (see above).
401unsupported_grant_typegrant_type isn't authorization_code or refresh_token.

Additional Resources


Did this page help you?