Skip to main content
Get a list of donors from your church organization in a few steps.

Prerequisites

  • API access approved for your organization (request access)
  • An OAuth client registered with Grain (client ID and redirect URI)
  • Your church organization ID (see Organizations)

1. Authorize the user

Redirect the user to the authorization endpoint with PKCE parameters:
https://app.grainledger.com/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=YOUR_REDIRECT_URI
  &scope=openid%20email%20profile
  &code_challenge=YOUR_CODE_CHALLENGE
  &code_challenge_method=S256
  &state=RANDOM_STATE
The user signs in and approves access at https://app.grainledger.com/oauth/consent.

2. Exchange the authorization code

curl -X POST "https://api.grainledger.com/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=AUTHORIZATION_CODE" \
  -d "redirect_uri=YOUR_REDIRECT_URI" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "code_verifier=YOUR_CODE_VERIFIER"
const response = await fetch("https://api.grainledger.com/oauth/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: new URLSearchParams({
    grant_type: "authorization_code",
    code: authorizationCode,
    redirect_uri: redirectUri,
    client_id: clientId,
    code_verifier: codeVerifier,
  }),
});

const { access_token, refresh_token } = await response.json();
import requests

response = requests.post(
    "https://api.grainledger.com/oauth/token",
    data={
        "grant_type": "authorization_code",
        "code": authorization_code,
        "redirect_uri": redirect_uri,
        "client_id": client_id,
        "code_verifier": code_verifier,
    },
)
tokens = response.json()
access_token = tokens["access_token"]
Save the access_token and refresh_token from the response.

3. List donors

Replace the placeholders with your access token and organization ID:
curl -X GET "https://api.grainledger.com/api/v1/donors?page=1&limit=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "X-Organization-Id: YOUR_ORGANIZATION_ID" \
  -H "Content-Type: application/json"
const response = await fetch(
  "https://api.grainledger.com/api/v1/donors?page=1&limit=20",
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "X-Organization-Id": organizationId,
      "Content-Type": "application/json",
    },
  }
);

const data = await response.json();
console.log(data.donors);
response = requests.get(
    "https://api.grainledger.com/api/v1/donors",
    params={"page": 1, "limit": 20},
    headers={
        "Authorization": f"Bearer {access_token}",
        "X-Organization-Id": organization_id,
    },
)
data = response.json()
print(data["donors"])
A 200 response with a donors array and pagination object means your integration is working.

Example response

{
  "donors": [
    {
      "id": "cm123abc456def",
      "firstName": "Sarah",
      "lastName": "Johnson",
      "email": "sarah@gracechurch.org"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 142,
    "totalPages": 8
  }
}

Try it in the browser

Open List donors in the API reference and use the Try it playground after authorizing with your OAuth token.

Next steps

Last modified on July 8, 2026