Process Queues - Automating Workflows
This guide covers how to manage process queues in Pushpay ChMS using the API. Process queues enable you to create multi-step workflows for background checks, volunteer onboarding, member classes, and other processes that require sequential approvals and actions.
Process Queues - Automating Workflows
This guide covers how to work with process queues in Pushpay ChMS through the API: reading processes and their queues, adding people to queues, tracking them, and updating their status.
Process Queues are being replaced by WorkflowsWorkflows released in September 2026 and are replacing Process Queues in ChMS. Workflow endpoints are not in the public API reference as of October 2026, so this guide covers the Process Queue endpoints that are documented today.
Plan for the switch. A workflow corresponds to a process, a step corresponds to a queue, and a step individual corresponds to a queue individual. Once Process Queues are turned off for a church, the endpoints in this guide stop working for that church.
Overview
With the process queue endpoints you can:
- List a church's processes and the queues (steps) inside them
- Add people to a queue directly, or through a form submission with a queue automation
- See who is in a queue and what their status is
- Update a person's status, assigned manager, due date and completed date
- Remove a person from a queue
Authentication
ChMS API v2 uses OAuth 2.0. See the Getting Started guide to request credentials and the OAuth Authentication page for the token flow.
Every request needs these headers:
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/vnd.ccbchurch.v2+json
Write requests also need Content-Type: application/json. Without it the body is read as empty.
Required Scopes
| Scope | Needed for |
|---|---|
read:process_queues | Listing processes and queues |
read:processes | Getting a single process (GET /processes/{process_id}) |
read:queue_individuals | Reading the people in queues |
write:queue_individuals | Adding people to queues and updating their status |
delete:queue_individuals | Removing people from queues |
read:forms, write:form_responses | Adding people through a form automation (optional) |
Request these when you register your OAuth application.
1. Understanding Process Structures
Process → Queues → Queue Individuals
- Process — a complete workflow, such as "Volunteer Onboarding".
- Queue — one step in that process, such as "Pending Review". Queues have an order.
- Queue individual — one person's entry in one queue, with its own status and dates. A person who moves through four queues has four queue individual records, each with its own
queue_individual_id.
A queue individual's status is one of NOT_STARTED, WAITING, IN_PROCESS or DONE.
Step 1: Get Processes
GET /processes lists the church's processes.
| Query parameter | Description |
|---|---|
name | Search by process name |
campus_ids[] | Filter by campus |
statuses[] | ACTIVE, HIDDEN or ARCHIVED. Must use []; a bare statuses= returns 412. |
managed_by_me | true to return only processes the token's user manages |
sort_by | NAME, STATUS, CAMPUS or MANAGER |
sort_direction | ASC or DESC |
page, per_page | Paging. Read the X-Total-Pages response header. |
Several processes can share the same name, so match on id once you've found the one you want.
curl --request GET \
--url 'https://api.ccbchurch.com/processes?name=Volunteer' \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'[
{
"id": 1154,
"campus_id": 1,
"owner_id": 0,
"name": "Volunteer Onboarding",
"hidden": false,
"archived": false,
"managers": [],
"process_managers": [],
"status": ["ACTIVE"]
}
]Step 2: Get the Queues in a Process
GET /queues?process_ids[]={process_id} lists the queues (steps) in a process.
| Query parameter | Description |
|---|---|
process_ids[] | Filter by process. Repeat it to cover several processes. |
campus_ids[] | Filter by campus |
search_term | Filter by queue name |
managed_by_me | true to return only queues in processes the user manages |
page, per_page | Paging |
Array parameters must use [] and be repeated (process_ids[]=1&process_ids[]=2). A bare process_ids=1 is rejected with a 412.
curl --request GET \
--url 'https://api.ccbchurch.com/queues?process_ids[]=1154' \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'[
{
"id": 3244,
"process_id": 1154,
"process_campus_id": 1,
"name": "Pending Review",
"order_by": 1,
"children": true,
"description": "",
"note": ""
},
{
"id": 3307,
"process_id": 1154,
"process_campus_id": 1,
"name": "Background Check",
"order_by": 2,
"children": false,
"description": "",
"note": ""
}
]- Sort by
order_by, not by the order of the array. Some older processes haveorder_byset to0on every queue, and then the API can't tell you the order. childrenistruewhen completing this queue leads on to another queue.automationsis always empty on this endpoint. To see what a queue does when someone is marked Done (for example, which queueNEXT_QUEUEmoves them into), readqueue.automationson any queue individual row from section 3.
GET /queues/{queue_id} returns one queue's id, name, description, note, managers and process.
2. Adding Individuals to Queues
You can add people to a queue through a form submission (Option A) or directly (Option B).
Option A: Through a Form Automation
A form can be set up in ChMS to add each person who submits it to a queue. This works for people who aren't in ChMS yet: a ChMS admin can later review the form responses and either match the person or create a new profile. If auto-match is turned on for the form, ChMS links the response to an existing profile by name and email.
Setup in ChMS: add an automation to the form that adds respondents to the queue. See How to Add Profiles to a Process Queue.
1. Get the form's required profile fields with GET /forms/{id}:
curl --request GET \
--url https://api.ccbchurch.com/forms/123 \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'{
"id": 123,
"title": "Volunteer Interest",
"profile": {
"id": 131,
"title": "Profile",
"fields": [
{ "id": 552, "name": "FIRST_NAME", "required": true },
{ "id": 553, "name": "LAST_NAME", "required": true },
{ "id": 555, "name": "EMAIL", "required": true },
{ "id": 559, "name": "MOBILE_PHONE", "required": false }
]
}
}2. Get the form's questions with GET /forms/{id}/questions:
curl --request GET \
--url https://api.ccbchurch.com/forms/123/questions \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'[
{
"id": 310,
"type": "checkbox",
"form_id": "123",
"question": "Which ministry are you interested in?",
"required": false,
"single_choice": false,
"choices": [
{ "id": 450, "value": "Kids" },
{ "id": 451, "value": "Worship" }
]
},
{
"id": 849,
"type": "paragraph",
"form_id": "123",
"question": "Tell us about yourself",
"required": true
}
]3. Submit the response with POST /forms/{id}/responses. The queue automation runs when the response is created.
curl --request POST \
--url https://api.ccbchurch.com/forms/123/responses \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"respondent_id": 12345,
"profile": {
"fields": [
{ "field_id": 552, "answer": "Jane" },
{ "field_id": 553, "answer": "Doe" },
{ "field_id": 555, "answer": "[email protected]" }
]
},
"answers": [
{ "question_id": 310, "answer": ["450"] },
{ "question_id": 849, "answer": "I love working with kids." }
]
}'- Use
respondent_id, notindividual_id, to say who the response is for. The request is otherwise treated as a guest submission, and that can fail with a blank403. Leaverespondent_idout for someone who isn't in ChMS yet. - Always send every required profile field, even for someone already in ChMS. Note that the field is keyed
field_idin the request butidinGET /forms/{id}. - Answer formats: checkbox answers are arrays of choice-id strings, and select/radio answers are a single choice-id string. The full table, plus the other traps, is in the form response runbook on the docs MCP server.
- Matching happens only at submission. There's no API call to match an existing unmatched response to a person later.
Option B: Add Directly to a Queue
POST /queues/{queue_id}/individuals adds someone to a queue. Use the queue ID, not the process ID.
| Body field | Required? | Description |
|---|---|---|
individual_id | Yes | The person to add |
status | No | NOT_STARTED (default), WAITING or IN_PROCESS |
assignee_id | No | The queue manager to assign. This must be a manager of the queue (or the token's user, if they can manage the process), and an active profile. |
note | No | A note on the entry |
notification_type | No | Defaults to EMAIL, which emails the queue managers. Send any other value (for example NONE) to add the person without an email. |
curl --request POST \
--url https://api.ccbchurch.com/queues/3244/individuals \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"individual_id": 12345,
"status": "WAITING",
"note": "Submitted volunteer application on the website."
}'A successful add returns 201 Created with the new queue individual record, in the same shape as the rows in section 3. Store its queue_individual_id, because you'll need it to update or remove this entry.
{
"queue_individual_id": 98765,
"queue_id": 3244,
"individual_id": 12345,
"status": "WAITING",
"created": "2026-10-02",
"due": "2026-10-05",
"overdue": false,
"assignee": null
}Validation errors return 400, for example {"errors":["status must be one of WAITING,IN_PROCESS,NOT_STARTED"],"status_type":"NOT_VALID"}. You can't add someone straight into DONE.
3. Retrieving Individuals from Queues
List Queue Individuals
GET /queues_individuals returns one row per person per queue. Note the s in "queues".
| Query parameter | Description |
|---|---|
statuses[] | NOT_STARTED, WAITING, IN_PROCESS, DONE. If you leave this out, people marked DONE are excluded. |
process_ids[] | Filter by process |
queue_ids[] | Filter by queue |
campus_ids[] | Filter by campus |
search_term | Filter by the person's name |
assignment | ASSIGNED_TO_ME or UNASSIGNED. Leave it out to get both. |
sort_by | NAME, PROCESS_NAME, QUEUE_NAME, CAMPUS, STATUS or ASSIGNEE |
sort_direction | ASC or DESC |
page, per_page | Paging. Read the X-Total and X-Total-Pages response headers. |
Sendstatuses[]explicitlyThe default leaves out
DONE, so a query for finished people with nostatuses[]returns an empty list with no error. Always say which statuses you want.
Every row carries its queue_id and process, so one call per process (rather than one per queue) is usually enough.
curl --request GET \
--url 'https://api.ccbchurch.com/queues_individuals?process_ids[]=1154&statuses[]=NOT_STARTED&statuses[]=WAITING&statuses[]=IN_PROCESS&statuses[]=DONE&per_page=100' \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'[
{
"queue_individual_id": 98765,
"queue_id": 3244,
"individual_id": 12345,
"status": "WAITING",
"created": "2026-09-15",
"due": "2026-09-22",
"overdue": false,
"individual": { "id": 12345, "first_name": "Jane", "last_name": "Doe", "name": "Jane Doe", "email": "[email protected]", "campus_id": 1, "family_id": 201 },
"assignee": { "id": 222, "name": "Sam Lee" },
"queue": {
"id": 3244,
"process_id": 1154,
"name": "Pending Review",
"order_by": 1,
"children": true,
"automations": [
{ "id": 3307, "name": "Background Check", "type": "NEXT_QUEUE", "default": "CHECKED" },
{ "id": 1, "name": "Welcome Letter", "type": "MAIL_MERGE", "default": null }
]
},
"process": { "id": 1154, "name": "Volunteer Onboarding" }
}
]created,completedanddueare dates only (YYYY-MM-DD), without a time.completedappears only once the entry isDONE.queue.automationslists the queue's Done actions.NEXT_QUEUEentries give the next queue'sid.
Get One Queue Individual
GET /queues_individuals/{queueIndividualId}— when you have thequeue_individual_id.GET /queues/{queue_id}/individuals/{individual_id}— when you know the queue and the person.
Both return the same shape as a list row, with no extra fields.
curl --request GET \
--url https://api.ccbchurch.com/queues/3244/individuals/12345 \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'4. Updating Status, Manager and Dates
PUT /queues_individuals/{queueIndividualId}/queue updates one entry. Send only the fields you want to change.
| Body field | Description |
|---|---|
status | NOT_STARTED, WAITING, IN_PROCESS or DONE |
manager_id | The queue manager to assign. This must be an existing manager of the queue. Changing it emails the new manager. |
due_date | YYYY-MM-DD |
completed_date | YYYY-MM-DD HH:MM:SS. It must include the time; a date alone returns 400. |
curl --request PUT \
--url https://api.ccbchurch.com/queues_individuals/98765/queue \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"status": "IN_PROCESS",
"manager_id": 222,
"due_date": "2026-10-09"
}'The response is a short summary, not a full queue individual record:
{
"individual": { "id": 12345, "first_name": "Jane", "last_name": "Doe", "name": "Jane Doe", "email_primary": null, "campus_id": 1, "phones": [] },
"queue_id": 3244,
"status": "IN_PROCESS",
"manager": null,
"due_date": "2026-10-09",
"completed_date": "-0001-11-30 00:00:00"
}completed_datereads-0001-11-30 00:00:00when it has never been set. Treat it as empty.- Use the
GETendpoints in section 3 if you need the full record afterwards. - An invalid manager returns
400{"errors":["The manager (ID: 999999) does not exist for the specified queue."],"status_type":"NOT_VALID"}.
There's no notes field on update. You can only add a note when you add someone to a queue (section 2, Option B).
5. Completing a Queue Step
SettingDONEthrough the API does not run the queue's automationsSetting
statustoDONEonly changes the status. ChMS does not run the queue's configured Done actions: it won't move the person to the next queue, add them to a group or event, send a mail merge, or update their profile. Those actions only run when a staff member marks the person Done in ChMS.
To record completion through the API, set the status and completion date together. If you send DONE without completed_date, the entry is marked done but has no completed date.
curl --request PUT \
--url https://api.ccbchurch.com/queues_individuals/98765/queue \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"status": "DONE",
"completed_date": "2026-10-02 14:30:00"
}'To move someone on to the next queue through the API, add them to it yourself with POST /queues/{next_queue_id}/individuals (section 2, Option B). The NEXT_QUEUE entries in queue.automations on the person's current row (section 3) give the next queue's id.
6. Removing Individuals from Queues
DELETE /queue_individuals/{queue_individual_id} removes an entry. Note this path has no s in "queue". It needs the delete:queue_individuals scope and returns 204 No Content.
curl --request DELETE \
--url https://api.ccbchurch.com/queue_individuals/98765 \
--header 'Accept: application/vnd.ccbchurch.v2+json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'Complete Example
A two-step volunteer onboarding process: Pending Review (queue 3244), then Background Check (queue 3307).
# 1. Find the process and its queues
curl 'https://api.ccbchurch.com/processes?name=Volunteer%20Onboarding' \
-H 'Accept: application/vnd.ccbchurch.v2+json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
curl 'https://api.ccbchurch.com/queues?process_ids[]=1154' \
-H 'Accept: application/vnd.ccbchurch.v2+json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
# 2. Add the person to the first queue
curl -X POST https://api.ccbchurch.com/queues/3244/individuals \
-H 'Accept: application/vnd.ccbchurch.v2+json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"individual_id": 12345, "status": "WAITING"}'
# -> queue_individual_id 98765
# 3. Reviewer picks it up
curl -X PUT https://api.ccbchurch.com/queues_individuals/98765/queue \
-H 'Accept: application/vnd.ccbchurch.v2+json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"status": "IN_PROCESS", "manager_id": 222}'
# 4. Review finished: mark done, then add them to the next queue yourself
curl -X PUT https://api.ccbchurch.com/queues_individuals/98765/queue \
-H 'Accept: application/vnd.ccbchurch.v2+json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"status": "DONE", "completed_date": "2026-10-02 14:30:00"}'
curl -X POST https://api.ccbchurch.com/queues/3307/individuals \
-H 'Accept: application/vnd.ccbchurch.v2+json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"individual_id": 12345, "status": "NOT_STARTED"}'
# 5. Check who is still open in the process
curl 'https://api.ccbchurch.com/queues_individuals?process_ids[]=1154&statuses[]=NOT_STARTED&statuses[]=WAITING&statuses[]=IN_PROCESS' \
-H 'Accept: application/vnd.ccbchurch.v2+json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'If you want the queue's own Done actions to run in step 4 (for example, a group join or mail merge set up in ChMS), have a staff member mark the person Done in ChMS instead of making the two step-4 calls.
Troubleshooting
| Symptom | Likely cause |
|---|---|
GET /queues_individuals returns [] but people are in the process | statuses[] was left out, so DONE is excluded. Send all the statuses you want. |
412 on a filter | An array parameter was sent without [], or with an invalid value. The error message lists the valid values. |
405 Method Not Allowed | A path typo, most often /queue_individuals vs /queues_individuals. Only the delete endpoint uses /queue_individuals. |
401 {} on delete | The token is missing delete:queue_individuals. |
401 with an empty body {} | The token is missing the scope for that endpoint. GET /processes/{process_id} needs read:processes, not read:process_queues. |
Blank 403 on a form submission | individual_id was sent instead of respondent_id, or the Content-Type: application/json header is missing. |
400 "assignee_id must be an int and valid" (add) or "The manager … does not exist for the specified queue." (update) | assignee_id / manager_id isn't a manager of that queue. |
Marked DONE but the person didn't move to the next queue | Expected. API status changes don't run queue automations (see section 5). |
| Form submission didn't add the person to the queue | Check the form's automation in ChMS, and that the response was created successfully (201). |
| Endpoints that used to work now fail | Process Queues may have been turned off for the church as part of the move to Workflows. |
API Reference
| Operation | Method | Endpoint |
|---|---|---|
| List processes | GET | /processes |
| Get a process | GET | /processes/{process_id} |
| List queues | GET | /queues |
| Get a queue | GET | /queues/{queue_id} |
| Add via form | POST | /forms/{id}/responses |
| Add to a queue | POST | /queues/{queue_id}/individuals |
| List queue individuals | GET | /queues_individuals |
| Get a queue individual | GET | /queues_individuals/{queueIndividualId} |
| Get by queue and individual | GET | /queues/{queue_id}/individuals/{individual_id} |
| Update status, manager, dates | PUT | /queues_individuals/{queueIndividualId}/queue |
| Remove from a queue | DELETE | /queue_individuals/{queue_individual_id} |
Related Documentation
- Learning about Workflows
- How to Add Profiles to a Process Queue
- How to Use Auto-match for Logged Out Form Responses
- Background Checks guide
- Docs MCP server — lets your AI coding tools answer questions against these docs
Updated 22 days ago
