Skip to main content
Every TalkValue CLI command supports --json (or auto-emits JSON when piped). jq is the standard tool for slicing that JSON into the exact shape you need: a single field, a count, a CSV, a chart-ready projection. The snippets below cover the patterns that keep showing up in real scripts.
Prerequisites: authenticated CLI and jq installed. On macOS, brew install jq. On Debian/Ubuntu, sudo apt install jq. On Windows, winget install jqlang.jq.

The envelope

Every list command returns this shape:
{
  "data": [ /* items */ ],
  "pagination": { "page": 0, "pageSize": 20, "totalElements": 4321, "totalPages": 217 }
}
Every single-resource command returns:
{ "data": { /* item */ } }
Errors land on stderr as:
{ "error": { "message": "..." } }
Almost every snippet below opens with .data or .data[].

Project: pick specific fields

talkvalue path person list --json | jq '.data[] | {id, name, primaryEmail}'
Returns one object per person with only the three fields. The same pattern works on any list:
talkvalue path event list --json   | jq '.data[] | {id, name, startAt}'
talkvalue path channel list --json | jq '.data[] | {id, name, personCount}'
talkvalue path company list --json | jq '.data[] | {id, displayName, domain}'

Extract a single field as a flat list

talkvalue path person list --json | jq -r '.data[].primaryEmail'
-r (raw output) strips JSON quotes so you get one email per line. Ready to pipe into mail, wc -l, or a follow-up CLI call.

Filter: keep rows that match

# People at Acme Inc.
talkvalue path person list --json \
  | jq '[.data[] | select(.companyName == "Acme Inc.")]'

# People who registered for an event this month
SINCE=$(date -u +"%Y-%m-01T00:00:00Z")
talkvalue path event person list 18 --json \
  | jq --arg since "$SINCE" '[.data[] | select(.joinedAt >= $since)]'

# Companies with a domain set
talkvalue path company list --json \
  | jq '[.data[] | select(.domain != null and .domain != "")]'
Wrap the pipeline in [ ... ] to collect the matches into an array. Drop the brackets for a stream of unwrapped objects.

Count

# Total people
talkvalue path person list --page-size 1 --json | jq '.pagination.totalElements'

# Registrants this month
talkvalue path event person list 18 --json \
  | jq --arg since "$SINCE" '[.data[] | select(.joinedAt >= $since)] | length'

# Job titles ranked for one company
talkvalue path company person list 88 --json \
  | jq '[.data[] | .jobTitle] | group_by(.) | map({title: .[0], count: length}) | sort_by(-.count) | .[0:10]'
length on an array returns its size. pagination.totalElements returns the server-side total without pulling every page.

Group and count by company

talkvalue path event person list 18 --page-size 500 --json \
  | jq '[.data[] | .companyName // "Unknown"]
        | group_by(.)
        | map({company: .[0], count: length})
        | sort_by(-.count)'
The // "Unknown" fallback keeps people without a company in the group instead of producing a null bucket.

Project to CSV

talkvalue path person list --json \
  | jq -r '["id","name","primaryEmail","companyName"],
           (.data[] | [.id, .name, .primaryEmail, .companyName])
           | @csv' \
  > people.csv
@csv quotes fields, escapes commas, and produces a valid CSV file. The first line is the header, the rest are rows.

Walk every page

page=0
while :; do
  resp=$(talkvalue path person list --page "$page" --page-size 100 --json)
  echo "$resp" | jq -e '.data | length > 0' >/dev/null || break
  echo "$resp" | jq '.data[]'
  page=$((page + 1))
done
jq -e exits non-zero when the test is false, so the loop stops the first time a page returns no rows. Streaming .data[] keeps every record on stdout for further piping.

Combine: count by company across all pages

page=0
EVENT_ID=18
all=$(
  while :; do
    resp=$(talkvalue path event person list "$EVENT_ID" --page "$page" --page-size 100 --json)
    echo "$resp" | jq -e '.data | length > 0' >/dev/null || break
    echo "$resp" | jq '.data[]'
    page=$((page + 1))
  done
)
echo "$all" \
  | jq -s 'group_by(.companyName // "Unknown")
           | map({company: .[0].companyName // "Unknown", count: length})
           | sort_by(-.count)'
The page-walk streams every record. jq -s (slurp) collects them back into a single array for the group-and-count step.

Read the error envelope

err=$(talkvalue path person get 999999 --json 2>&1 1>/dev/null)
code=$?
if [ "$code" -ne 0 ]; then
  echo "exit=$code message=$(echo "$err" | jq -r '.error.message')" >&2
fi
Captures stderr separately from stdout so you get both the exit code (see Exit codes) and the structured error message in one place.

Tips

  • Quote your jq filter in single quotes so the shell doesn’t expand $ characters before jq sees them.
  • Pass dynamic values with --arg name value (string) or --argjson name value (parsed JSON). Inside the filter, reference them as $name.
  • Pretty-printed JSON is the default. Pass -c for one-record-per-line output that streams cleanly into other tools.
  • jq has a built-in now function. now | strftime("%Y-%m-%d") makes timestamped filenames trivial.

See also