A read-only as-built reviewer for a Genesys Cloud CX organisation. One click traverses nine
REST endpoint families — users, stations, phones, sites, phone base settings, queues and groups with full
member lists — joins everything into one row per user, and streams the run live with a terminal-style API log. The finished report is a 13-column table you can search, filter eight ways, sort and
export to CSV, Excel or PDF, entirely in the browser.
Continuously integrated and updated. This is a published sample of the user guide. The App Suite changes frequently, so this page may not reflect the latest features, screens and behaviour. The current guide is available in the application through the QVCCS apps portal, included with every Managed Professional Services tier.
At a glance
as-built configuration report
one row per user
multi-user
adaptive rate pacing
live API log
csv · xlsx · pdf
strictly read-only
Security at a glance
Config Validator
Read-only
Sign-in
OAuth client credentials you supply; held on the server for your session only and never sent to the browser
Stores
Nothing at rest; report data lives only in your browser tab and is gone on refresh
AI
No AI
Exports
CSV, Excel and PDF of the filtered rows
Genesys Cloud permissions
View on Users, Authorization, Telephony, Routing and Groups; optional organization:readonly
The As-Built Configuration Validator answers a question the standard Genesys Cloud admin UI
makes difficult: “For every user in my org, what phone do they have, is it allocated and set as their
default station, what queues are they in, which groups do they belong to, and what roles have they been
assigned?”
One click runs a full traversal of the org’s configuration. The app pages through users, stations,
phones, sites, phone base settings, queues (with member lists) and groups (with member lists), joins
everything into one row per user, and streams progress live to the browser. Once the report lands,
everything else — search, eight filters, sorting, three export formats — happens instantly in the browser
with no further API traffic.
Typical use cases
Go-live / as-built validation — verify every agent has a phone that is both allocated
and configured as their default station (the “✓ Allocated + Default” provisioning filter is the
health check this app was built around).
Audit & handover documentation — export the full user/telephony matrix to XLSX or a
landscape PDF.
Role / queue / group reviews — filter by a specific role, queue or group and see exactly who
holds it, including on deactivated accounts.
Contact-data hygiene — the Profile Contacts column shows every address on the user record
(login email, work/home/mobile phones, extra emails, SMS) with the primary entries starred.
Read-only by design. The application only ever issues GET requests
against the Genesys API, plus the one POST /oauth/token needed to sign in. Nothing in your
organisation is modified, and no report data is stored by the app — it lives only in your browser tab
and disappears on refresh.
2Quick start
Create an OAuth client in Genesys Cloud (Menu > IT and Integrations > OAuth), grant type
Client Credentials, with read-only permissions for Users, Authorization, Telephony, Routing and
Groups (see §6). Note the Client ID and Secret.
Sign in — open the app, pick your region (14 supported), paste Client ID and Client Secret, press Sign in. Credentials are validated with one real OAuth call and held on the server; they are never sent to the browser.
Run the report — press ▶ Run Report (or just Enter). Watch the progress bar
and the API Communications Log. Small orgs take seconds; large orgs several minutes, because calls are
deliberately paced under Genesys rate limits.
Filter and export — combine the free-text search with the State / Phone / Role / Queue /
Group / Site / Phone Status / Provisioning filters, then export the filtered rows to CSV, XLSX
or PDF.
Sessions: several users can be signed in at once, each with their own credentials and their own adaptive rate pacer. Sessions end after a period of inactivity; signing in again with the same Client ID + region reclaims your earlier session.
3UI walkthrough
Sign-in page
A split-screen page: a royal-blue brand rail on the left (hidden below 1024 px) and the form on
the right. The form fields, in order:
Region — a dropdown of the 14 Genesys regions (
Mumbai, Seoul, Sydney, Tokyo, Canada Central, Frankfurt, Ireland, London, Zurich, UAE, São Paulo,
US East Virginia, US East 2 FedRAMP, US West Oregon).
Client ID and Client Secret of the OAuth Client Credentials grant.
On failure you return to the sign-in page and the exact Genesys error message renders in a red box. As a convenience the page remembers your Client ID and region in your browser and pre-fills them next time; the secret is never stored.
Dashboard & report run
Header — the “GC” mark and app title on the left; a centred organisation banner
(resolved via /api/v2/organizations/me at sign-in — it stays an italic “—” if the OAuth client lacks the
permission); region label and “Generated” timestamp on the right after a run.
Run Report card — a session indicator (“Signed in”), the ▶ Run Report button (disabled and showing “⏳ Running…” while a run is live),
📖 Guide (opens this manual in a new tab) and Sign out. Pressing Enter
anywhere also starts a run.
Progress panel — spinner, stage message and a 0–100% bar driven by live progress (users → stations → phones → sites → base settings → queues → groups →
mapping).
API Communications Log — a terminal-style panel that opens (and auto-clears) at the start of
every run. Each log entry renders as a timestamped line with a colour-coded tag:
INFO, SUCCESS (green), FETCH (violet), API (blue,
one line per page fetched with running totals), WARN (amber — member-fetch failures,
pacing changes) and ERROR (red). Clear and Hide/Show buttons sit in its
header; the panel persists after completion so the full API trace can be reviewed next to the data.
Error banner — fatal errors render here. If the live connection is lost (typically an ended session or a service restart) the banner shows “Connection to server lost — returning to sign-in…” and the page returns to the sign-in page.
Stats bar — six chips with the raw totals loaded: Users, Stations, Phones, Sites, Queues,
Groups (unaffected by filters). A result counter on the toolbar’s right shows
filtered / total users.
The result table (13 columns)
Sticky header, zebra striping, and a 1500 px minimum width with horizontal scrolling on narrow
viewports. Nine columns sort on click (↑/↓ indicator); the default sort is Name A→Z.
Column
Sortable
Content
Name
yes (last name)
Full name with department/title beneath in small type.
Email
yes
Login email, mono, ellipsised — hover for the full value.
Status
yes
User state badge: active (green) / inactive (amber) / deleted (red).
Profile Contacts
—
One row per contact entry: a ★ that lights green on primary entries, the value (emails violet, SMS amber, phones grey), an extension tag (x1234) and a type tag (Work, Work 2–4, Home, Mobile, Main, Other, SMS, Work Email…).
Default Phone
yes
The contact entry flagged primary for voice, in green.
Roles / Queues / Groups
—
Colour-coded pills (roles blue, queues blue, groups violet); first 3 shown, then a “+N” chip whose hover tooltip lists the rest.
Assigned Phone
yes
Resolved phone name; WebRTC softphones get a “WebRTC” badge.
Phone Type
yes
Phone base settings name (e.g. WebRTC Phone, AudioCodes …).
Site
yes
Telephony site of the phone (station-site fallback).
Free-text search — matches name, email, department, title, every contact string (including
its [mediaType]/[subType]/[primary] tags, so “mobile” or
“primary” work as searches), role names, phone name/type/site and station name.
Queue and group names are not in the search index — use their dropdowns.
State — Active / Inactive / Deleted.
Phone presence — Has Phone / No Phone / Phone Allocated / Phone Unallocated.
Role, Queue, Group, Site — dropdowns populated from the loaded data itself.
Phone Status — logical buckets over the raw status: Operational/Active, Degraded/Inactive,
Failed/Error, Deleted, Unknown, and “— No Phone”.
Provisioning — the go-live health check: ✓ Allocated + Default (good),
⚠ Allocated, not Default, ✗ Phone, not Allocated, — No Phone at all (§4).
Exports (respect current filters)
All three exports contain exactly the filtered rows at the moment you click, are generated
entirely in the browser, and silently do nothing when the filtered set is empty.
Internet dependency: the export components and webfonts load from public providers when the dashboard page loads. If the browser had no internet access at that moment, the export buttons cannot work until the page is reloaded with connectivity. Report data itself is never sent to them.
4Domain concepts
The user → station → phone chain
Connecting a Genesys user to a phone requires traversing three object types. The validator resolves
the chain through three paths, in order:
WebRTC path — station.webRtcUserId links station → user, and
phone.lines[0].id === station.id links phone → station. The most reliable path for
WebRTC softphones.
User-station path — user.station.associatedStation.id (the currently logged-in
station) or user.station.defaultStation.id (the permanently configured one), resolved
through a phone-by-station index (physical phones link via phone.station.id).
Fallback — any station whose associatedUser / defaultUser matches
the user.
Allocated vs. default station
Allocated (“✓ allocated”) — a Genesys phone record was successfully resolved for the user’s
station. Unallocated means a station exists but no phone record maps to it.
Default station (“★ default stn”) — the resolved station’s id equals
user.station.defaultStation.id, i.e. the assignment is permanent configuration, not just
a transient login association. An agent who is allocated but not default will lose their phone
at next logout — which is exactly what the “⚠ Allocated, not Default” provisioning filter surfaces.
WebRTC badge — shown when the station’s type contains webrtc
(e.g. inin_webrtc_softphone).
Profile contacts and the primary star
The Profile Contacts column merges three sources from the user record, in order: the login
email (user.email / username — what Genesys shows as “Main Email”), every entry
in addresses[] (work/home/mobile/main/other phones, extra emails, SMS numbers, extensions),
and any primaryContactInfo[] entries not already captured. Primary detection compares
normalised values — phone formatting is stripped to digits, emails lowercased — so
“(+1 202-555-0143)” and “+12025550143” match. Deduplication mirrors the Genesys UI: phones dedupe on
the normalised number, emails dedupe case-sensitively (the same email in different case shows
twice, as Genesys does).
Phone status semantics
The status badge prefers the edge-reported operational status
(phone.status.edges[0].status: Operational, Degraded, Failed…). When the edge reports
“Unknown” or nothing, the phone’s administrative state is used instead (active → Active,
inactive → Inactive, deleted → Deleted), and only then does it fall back to “Unknown”. The Phone Status
filter groups these into buckets: Operational+Active, Degraded+Inactive, Failed+Error, Deleted,
Unknown, plus a synthetic “— No Phone” bucket.
Provisioning states
Filter value
Meaning
Action usually needed
✓ Allocated + Default
Phone resolved, allocated, and the station is the user’s configured default
None — correctly provisioned
⚠ Allocated, not Default
Phone works now but is only a transient association
Set the station as the user’s default station
✗ Phone, not Allocated
A phone record exists but is not marked allocated to the user
Fix the phone/station assignment
— No Phone at all
No phone record resolved by any path
Provision a phone/station
Why deleted and inactive users appear: users are fetched with
state=any on purpose, so as-built reviews catch lingering queue/group/role assignments on
deactivated accounts. Use the State filter to hide them.
5Report pipeline & resilience
The eight stages
A report run is one long-lived live connection. The app works through the stages below, reporting progress and log lines throughout, then sends the entire joined result in one go when it completes:
#
Stage
Notes
1
Users
expand=authorization,station&state=any — roles and station links ride along; all user states included
2
Stations
Full station inventory
3
Phones
expand=status — edge-reported operational status included
4
Sites
Site id → name map
5
Phone base settings
One GET per unique id, fired in batches of 10 — resolves the “Phone Type” column
6
Queues + members
Queue list, then per-queue member lists 8-wide with a 90 s per-queue cap
7
Groups + members
Group list, then per-group member lists only 2-wide (tight Genesys rate bucket), 90 s cap, per-page log lines
8
Join
Lookup maps built; one row assembled per user; totals reported
All list endpoints request pageSize=200 (the Genesys maximum) to halve round-trips.
Adaptive per-session pacer
Every Genesys call in a session flows through a pacer that enforces a minimum spacing between calls —
even parallel fetchers serialise through it, so the on-wire rate stays steady regardless of concurrency:
Baseline 250 ms spacing (≈4 req/s, under Genesys’ standard 5 req/s); floor 200 ms.
On each HTTP 429 spacing multiplies ×1.5, capped at 2000 ms (0.5 req/s emergency floor).
The log shows “⚠ Rate limit hit — slowing to N req/s”.
Relaxation — after 30 consecutive non-429 responses, spacing eases ×0.85 back toward the
baseline (“✓ Sustained success — easing pace”), so one brief throttle doesn’t slow the whole run.
Retry & token policy
429 / 503 — up to 3 attempts, honouring Retry-After (default 5 s) plus up
to 40% random jitter, capped at 60 s per wait, and each 429 also adapts the pacer so subsequent
calls don’t burst straight back into the limit.
401 mid-run — exactly one token refresh + one retry. Refreshes are concurrency-guarded:
parallel fetchers that hit expiry simultaneously all await the same OAuth call.
Proactive refresh — tokens are treated as expired 60 s early and refreshed before a
call fires. Each request times out after 30 s.
Membership timeouts — each queue/group member fetch is capped at 90 s so one hung
(typically dynamic-membership) group can’t stall the stream; failures are logged as warnings and the
report still completes.
Why groups run only 2-wide: Genesys applies independent rate-limit buckets per
endpoint family, and /api/v2/groups/{id}/members has a much tighter bucket than queue
members — 9 parallel calls reliably triggered 429s with a 60-second Retry-After. Two
concurrent fetches stay under the threshold while still overlapping slow groups.
6Genesys endpoints & permissions
Endpoint
Options
Used for
POST
login.<region>/oauth/token
client_credentials
Sign-in and mid-run token refresh
GET
/api/v2/organizations/me
best-effort at sign-in
Org display name for the header banner
GET
/api/v2/users
expand=authorization,station&state=any
Users incl. roles + station links, all states
GET
/api/v2/stations
—
Station inventory
GET
/api/v2/telephony/providers/edges/phones
expand=status
Phones incl. edge status
GET
/api/v2/telephony/providers/edges/sites
—
Site id → name map
GET
…/edges/phonebasesettings/{id}
batches of 10
Phone-type names
GET
/api/v2/routing/queues · …/queues/{id}/members
members 8-wide, 90 s cap
Queue membership per user
GET
/api/v2/groups · …/groups/{id}/members
members 2-wide, 90 s cap
Group membership per user
OAuth client permissions
Assign a role to the Client Credentials grant covering these read-only permission areas:
Permission area
Level
Used for
Users (directory)
View
User profiles, names, emails, states, contact addresses
Authorization
View
Role assignments via expand=authorization
Telephony
View
Phones, stations, sites, phone base settings
Routing
View
Queues and queue member lists
Groups
View
Groups and group member lists
Organization (organization:readonly)
View — optional
Org name banner only; sign-in and reports work without it
Read-only posture: apart from the OAuth token request, every Genesys call the app makes is a GET. Missing a permission never breaks the whole run — the affected
fetches fail, are logged as warnings, and the corresponding columns simply come back empty.
7Storage, privacy & security
Nothing persists. No database and nothing written to storage. Report data exists only in the browser tab; sessions exist only while you are signed in and end if the service is restarted.
Credentials stay on the server. The Client Secret is held on the server for the session’s lifetime only and is never sent to the browser, which holds only a secure session cookie. Credentials never appear in web addresses, browser history or referrer headers.
Login hint only. The login page remembers Client ID + region in your browser as a convenience; the secret is never stored in the browser.
Session lifecycle. Sessions end after a period of inactivity, discarding their credentials; a repeat sign-in with the same Client ID + region reclaims your earlier session. Sign out ends the session immediately.
Gated pages. The dashboard and report require a signed-in session; only the sign-in page and this guide are public.
Outbound traffic. The app talks only to your org’s Genesys region. Your browser loads export components and webfonts from public providers at page load; report data is never sent to any third party.
8Technology
Web application — the adaptive per-session pacer, a resilient read-only Genesys client (30 s per-request timeout, retry and pacing) and the live report engine. No database.
Browser interface — a sign-in page and a dashboard that receives the live report and does all table rendering, searching, filtering, sorting and exporting in your browser.
Streaming — progress, the API log and the finished report all arrive over one live connection.
Persistence — none: sessions exist only while you are signed in, report data only in the browser tab.
9Troubleshooting
“The service is busy – try again shortly” on sign-in.
The service is handling as many sign-ins as it can. If you already hold a session (an orphaned tab), sign in again with the same Client ID and region — your earlier session is reused instead of rejecting you. Otherwise try again shortly or ask another user to sign out.
Login fails with an OAuth error.
The message on the login page comes straight from Genesys. Typical causes: wrong region selected
for the org, a regenerated Client Secret, or the OAuth client is not a Client Credentials grant.
Check Menu > IT and Integrations > OAuth.
The run slows mid-way (“⚠ Rate limit hit — slowing to N req/s”).
Genesys returned HTTP 429. Expected behaviour, not a fault: the adaptive pacer multiplies the gap
between calls ×1.5 per throttle (floor 0.5 req/s) and eases back after 30 consecutive successes. The
run continues; it just takes longer. Note that concurrent runs by several signed-in users share the
same org-side rate limits.
“Connection to server lost — returning to sign-in…”
The live connection ended — usually a session that ended after a period of inactivity, or a service restart. The page returns to the sign-in page; sign in again.
Phone Type shows “—” for some phones.
Those phones’ base-settings lookups failed — the log line “⚠ Phone base settings: X/Y loaded ·
N failed” counts them. Usually transient throttling: re-run the report. It can also mean the OAuth
client lacks telephony read permission on those objects.
“⚠ Queue/Group … member fetch failed” warnings in the log.
Individual membership lookups can time out (90 s cap on a huge dynamic group) or exhaust their
3 retries under throttling. The report still completes; affected users simply won’t show that
queue/group. Re-running usually fills the gaps — the group lane is intentionally only 2-wide because
Genesys’ /groups/{id}/members bucket answers bursts with 60-second
Retry-After penalties.
The organisation banner stays “—”.
The OAuth client lacks permission for GET /api/v2/organizations/me
(organization:readonly). Sign-in and reports work regardless; only the banner is blank.
Users show no queues at all, but roles look right.
Roles arrive on the user objects (expand=authorization), but queue membership needs
/api/v2/routing/queues/{id}/members. If the client lacks routing read permission every
queue’s member fetch fails (see the log warnings). Grant routing View and re-run.
Export buttons do nothing.
Two possibilities: the filtered row set is empty (exports are a deliberate no-op), or the export
libraries never loaded because the browser had no internet access when the page loaded — reload with
connectivity and re-run the report.
A search for a queue or group name returns nothing.
By design queue/group names are not in the free-text index — use the Queues / Groups dropdowns
(they are populated from your org’s actual data after the run) and combine with search as needed.
The progress bar briefly steps backwards near the end.
Cosmetic: the “Mapping data…” stage reports a lower percentage than the membership stages that
precede it. The run is fine — the finished report follows moments later.
Why do deleted/inactive users appear?
Users are fetched with state=any on purpose so reviews catch lingering assignments on
deactivated accounts. Use the State filter to hide them.
Is anything written to my org or stored on the server?
No writes ever occur (GET-only traversal, plus the OAuth token POST). Credentials are held only on the server for your session’s lifetime; report data exists only in your browser tab and disappears
on refresh.
QVCCS Config Validator — as-built configuration validation for Genesys Cloud CX. Strictly read-only against the Genesys APIs; nothing stored — report data lives only in your browser tab.
Continuously integrated and updated. This is a published sample of the user guide. The App Suite changes frequently, so this page may not reflect the latest features, screens and behaviour. The current guide is available in the application through the QVCCS apps portal, included with every Managed Professional Services tier.
Included at every tier
Use Config Validator with QVCCS Managed Professional Services.
The App Suite comes with every Bronze, Silver, Gold and Diamond subscription, used by our engineers and your administrators alike – backed by the certified QVCCS bench.