4.2.9.1 Configurable workqueue API
Workqueue endpoint – configuring queues with slugs & Elasticsearch queries
Path (countryconfig repo)
src/api/workqueue/
The workqueue endpoint in the countryconfig service is responsible for returning the list of workqueues that the client should show, including:
a slug (identifier),
label/description (for the UI),
and the Elasticsearch query that defines which records appear in that queue.
Everything is configured in:
src/api/workqueue/workqueueConfig.tsand wired into user permissions via:
src/data-seeding/roles/roles.tsAt runtime, OpenCRVS Core uses these configs to:
Decide which queues a user can see (based on their role→queue slugs), and
Run the Elasticsearch query for each queue to fetch the records that should be listed there.
The queries themselves are built using the QueryExpressions DSL from EventIndex in opencrvs-core, which compiles down to Elasticsearch queries.
1. What lives in workqueueConfig.ts
workqueueConfig.tsworkqueueConfig.ts defines all available workqueues for your country. Conceptually it’s a list (or map) of objects, one per queue, along the lines of:
export const workqueueConfig = [
{
slug: 'draft',
label: { ... },
description: { ... },
// optional: icon, sort, etc.
query: <QueryExpression>
},
{
slug: 'ready-for-review',
label: { ... },
query: <QueryExpression>
},
...
]Each entry represents one queue in the UI, such as:
draftin-progressready-for-reviewrequires-updatessent-for-approvalready-to-printready-to-issueetc.
The slug is the key thing:
It’s how the client and roles refer to the queue.
It’s what you reference in
roles.tswhen granting access.
The query is a typed QueryExpression that describes which events should appear in that queue, using the same status semantics as the product docs (In progress, Ready for review, Requires updates, Ready to print, Ready to issue, etc.). documentation.opencrvs.org+1
2. How roles use workqueue slugs
The seeded roles in:
src/data-seeding/roles/roles.tsinclude a mapping from role → allowed workqueue slugs.
Roughly:
{
id: 'REGISTRATION_AGENT',
// ...
scopes: [
// ...
'workqueue[id=assigned-to-you|recent|requires-updates-self|sent-for-review]',
// ...
]
}This means:
When a user logs in, their roles determine which queue slugs they can see.
The client then asks the workqueue endpoint for definitions and filters them down to the ones the user is allowed to access.
That’s why a Field Agent, Registration Agent and Registrar see different queues (even if some share the same underlying statuses).
So the flow is:
roles.tssays: “This role can see queuesA,B,C…”workqueueConfig.tssays: “QueueAmeans this label + this ES query,Bmeans …”The workqueue endpoint exposes that whole config to the UI & core.
3. The query: using QueryExpressions → Elasticsearch
QueryExpressions → ElasticsearchThe query field in each workqueue config is where the real power lives.
Instead of hand-writing Elasticsearch JSON, you use the QueryExpressions DSL from opencrvs-core (the same layer used for search & dedup queries).
Patterns you’ll typically see:
import { and, or, not, field, user } from '@opencrvs/events/EventIndex' // conceptually
export const draftQueue = {
slug: 'draft',
label: { ... },
query: and(
field('status').isEqualTo('IN_PROGRESS'),
field('isDraft').isEqualTo(true),
user('scope').has('record.declare.birth') // user-specific filtering
)
}The key ideas:
field('...')refers to an indexed property of the event (e.g. status, event type, office, assigned user, timestamps).Logical combinators like
and,or,notbuild complex boolean queries.User-aware helpers like
user('officeId')oruser('primaryOfficeId')can scope records to the current user’s office or jurisdiction.At runtime, these QueryExpressions are compiled into Elasticsearch queries against the event index (see
EventIndex.tsin opencrvs-core).
This is how you implement things like:
“Draft” → events with status
IN_PROGRESScreated by the current user.“Ready for review” → events with status
IN_REVIEWorVALIDATEDassigned to the user’s office.“Requires updates” → events with status
REQUIRES_UPDATES.“Ready to print” → events with status
REGISTEREDwith no certificates yet.
So each queue is literally “just” an ES query defined via this DSL.
4. Mental model
You can think of the workqueue endpoint as:
“A country-specific list of named Elasticsearch queries (
slug+label+QueryExpression) that define which records appear in each queue, and which queues each role can access.”
workqueueConfig.ts→ defines the queues and their queries.roles.ts→ decides who can see which queues (by slug).EventIndex/QueryExpressions→ turns those definitions into real ES queries over the event index.
Last updated