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 Workflows

Workflows 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

ScopeNeeded for
read:process_queuesListing processes and queues
read:processesGetting a single process (GET /processes/{process_id})
read:queue_individualsReading the people in queues
write:queue_individualsAdding people to queues and updating their status
delete:queue_individualsRemoving people from queues
read:forms, write:form_responsesAdding 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 parameterDescription
nameSearch by process name
campus_ids[]Filter by campus
statuses[]ACTIVE, HIDDEN or ARCHIVED. Must use []; a bare statuses= returns 412.
managed_by_metrue to return only processes the token's user manages
sort_byNAME, STATUS, CAMPUS or MANAGER
sort_directionASC or DESC
page, per_pagePaging. 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 parameterDescription
process_ids[]Filter by process. Repeat it to cover several processes.
campus_ids[]Filter by campus
search_termFilter by queue name
managed_by_metrue to return only queues in processes the user manages
page, per_pagePaging

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 have order_by set to 0 on every queue, and then the API can't tell you the order.
  • children is true when completing this queue leads on to another queue.
  • automations is always empty on this endpoint. To see what a queue does when someone is marked Done (for example, which queue NEXT_QUEUE moves them into), read queue.automations on 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, not individual_id, to say who the response is for. The request is otherwise treated as a guest submission, and that can fail with a blank 403. Leave respondent_id out 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_id in the request but id in GET /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 fieldRequired?Description
individual_idYesThe person to add
statusNoNOT_STARTED (default), WAITING or IN_PROCESS
assignee_idNoThe 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.
noteNoA note on the entry
notification_typeNoDefaults 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 parameterDescription
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_termFilter by the person's name
assignmentASSIGNED_TO_ME or UNASSIGNED. Leave it out to get both.
sort_byNAME, PROCESS_NAME, QUEUE_NAME, CAMPUS, STATUS or ASSIGNEE
sort_directionASC or DESC
page, per_pagePaging. Read the X-Total and X-Total-Pages response headers.
⚠️

Send statuses[] explicitly

The default leaves out DONE, so a query for finished people with no statuses[] 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, completed and due are dates only (YYYY-MM-DD), without a time.
  • completed appears only once the entry is DONE.
  • queue.automations lists the queue's Done actions. NEXT_QUEUE entries give the next queue's id.

Get One Queue Individual

  • GET /queues_individuals/{queueIndividualId} — when you have the queue_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 fieldDescription
statusNOT_STARTED, WAITING, IN_PROCESS or DONE
manager_idThe queue manager to assign. This must be an existing manager of the queue. Changing it emails the new manager.
due_dateYYYY-MM-DD
completed_dateYYYY-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_date reads -0001-11-30 00:00:00 when it has never been set. Treat it as empty.
  • Use the GET endpoints 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

⚠️

Setting DONE through the API does not run the queue's automations

Setting status to DONE only 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

SymptomLikely cause
GET /queues_individuals returns [] but people are in the processstatuses[] was left out, so DONE is excluded. Send all the statuses you want.
412 on a filterAn array parameter was sent without [], or with an invalid value. The error message lists the valid values.
405 Method Not AllowedA path typo, most often /queue_individuals vs /queues_individuals. Only the delete endpoint uses /queue_individuals.
401 {} on deleteThe 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 submissionindividual_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 queueExpected. API status changes don't run queue automations (see section 5).
Form submission didn't add the person to the queueCheck the form's automation in ChMS, and that the response was created successfully (201).
Endpoints that used to work now failProcess Queues may have been turned off for the church as part of the move to Workflows.

API Reference

OperationMethodEndpoint
List processesGET/processes
Get a processGET/processes/{process_id}
List queuesGET/queues
Get a queueGET/queues/{queue_id}
Add via formPOST/forms/{id}/responses
Add to a queuePOST/queues/{queue_id}/individuals
List queue individualsGET/queues_individuals
Get a queue individualGET/queues_individuals/{queueIndividualId}
Get by queue and individualGET/queues/{queue_id}/individuals/{individual_id}
Update status, manager, datesPUT/queues_individuals/{queueIndividualId}/queue
Remove from a queueDELETE/queue_individuals/{queue_individual_id}

Related Documentation


Did this page help you?