Skip to main content

Users and roles

Everyone who acts in the warrant workflow is a member of an agency with a role. The role decides what they may do, and it is what X-On-Behalf-Of resolves against on every write.

The built-in roles

RoleTypicallyCan
PHYSICIANClinical staffCreate and submit applications, comment
OFFICERLaw enforcement, clerksCreate and submit applications, comment
JUDGEThe benchReview, sign, reject, and be routed applications
ADMINAgency administratorsEverything above, plus users, roles, templates, settings, and deletion

These four exist in every agency and cannot be edited or removed. An agency that needs a different split of duties makes a custom role.

Adding a member

curl -s -X POST "$BASE/users" \
-H "x-api-key: $API_KEY" \
-H "X-On-Behalf-Of: admin.reyes@example.gov" \
-H "Content-Type: application/json" \
-d '{
"email": "clerk.hayes@example.gov",
"full_name": "Dana Hayes",
"title": "Records Clerk",
"role": "OFFICER"
}'

The record exists immediately, so work can be routed and attributed to them. They sign in once they have completed identity setup, and is_verified reflects that.

Listing members

curl -s "$BASE/users?role=JUDGE&active_only=true" -H "x-api-key: $API_KEY"

For a compact directory, suitable for filling a picker:

curl -s "$BASE/users/members" -H "x-api-key: $API_KEY"
[{"id": "a83f5c07-…", "full_name": "Hon. Maria Alvarez", "role": "JUDGE"}]

To find who can take a submitted application, use assignable judges, which also reports current workload.

Changing a role

curl -s -X PUT "$BASE/users/$USER_ID/role?role=ADMIN" \
-H "x-api-key: $API_KEY" \
-H "X-On-Behalf-Of: admin.reyes@example.gov"

The change applies immediately. It does not rewrite anything that person has already done: a draft they created stays theirs to submit, and every past action keeps the role it was taken under in the audit trail.

Deactivating

curl -s -X DELETE "$BASE/users/$USER_ID" \
-H "x-api-key: $API_KEY" \
-H "X-On-Behalf-Of: admin.reyes@example.gov"

Deactivation ends someone's ability to act. Their record and their history stay exactly where they are, because a warrant signed by a judge who has since retired is still a warrant signed by that judge. There is no way to delete a member out from under the audit trail.

Custom roles

A custom role is a named set of permissions, scoped to your agency.

curl -s "$BASE/permissions" -H "x-api-key: $API_KEY"
{
"categories": [
{
"category": "Warrants",
"permissions": [
{"key": "warrant:create", "label": "Create warrant applications", "description": "…"},
{"key": "warrant:sign", "label": "Sign warrants", "description": "…"}
]
}
]
}

Build the role from those keys:

curl -s -X POST "$BASE/roles" \
-H "x-api-key: $API_KEY" \
-H "X-On-Behalf-Of: admin.reyes@example.gov" \
-H "Content-Type: application/json" \
-d '{
"name": "Records Clerk",
"description": "Reads applications and the audit trail, and files attachments.",
"permissions": ["warrant:view_all", "file:upload", "audit:view"],
"color": "#3979AC"
}'

PUT /v1/roles/{roleId} changes a role, and the change applies at once to everyone holding it. DELETE /v1/roles/{roleId} removes it, but only once nobody holds it: move those members to another role first, or the call returns 409.

Built-in roles are returned by GET /v1/roles with is_system: true and cannot be modified. Pass include_system=false to list only your own.

Permissions and scopes are different things

They combine, and both have to allow an action:

  • A scope is on the API key. It limits what the integration can attempt.
  • A permission is on the person in X-On-Behalf-Of. It limits what that person may do.

A key with warrants:sign acting for a records clerk cannot sign. A judge cannot sign through a key that lacks the scope. This is deliberate: the key bounds the integration, the role bounds the person, and a legal signature requires both.