Users
Manage organization users and team members
Overview
Users represent team members in your organization. Each user has a role that determines their access level and permissions. Users can be assigned to appointments, services, and other organizational tasks. Some fields are auth-gated and only visible to users with the appropriate scopes.
User Roles
| Role | Description |
|---|---|
ADMIN | Full access to all features including organization settings, billing, and user management |
USER | Standard access for technicians and team members |
User Scopes
Scopes provide fine-grained access control for users. Each user can have multiple scopes assigned that determine what actions they can perform within the application.
Appointment Scopes
| Scope | Description |
|---|---|
read:own:appointment | View appointments assigned to the user |
read:all:appointment | View all appointments in the organization |
write:appointment | Create and update appointments |
delete:appointment | Delete appointments |
communication:appointment | Send communications related to appointments |
Customer Scopes
| Scope | Description |
|---|---|
read:customer | View customer information |
read:customer-details | View detailed customer information |
read:customer:telephone-number | View customer phone numbers |
write:customer | Create and update customers |
delete:customer | Delete customers |
Service Scopes
| Scope | Description |
|---|---|
read:service | View service records |
read:pricing:service | View service pricing information |
write:service | Create and update services |
delete:service | Delete services |
communication:service | Send communications related to services |
upload-from-gallery:service | Upload photos from device gallery for services |
Invoice Scopes
| Scope | Description |
|---|---|
read:invoice | View invoices |
read:pricing:invoice | View invoice pricing details |
write:invoice | Create and update invoices |
delete:invoice | Delete invoices |
Inventory Scopes
| Scope | Description |
|---|---|
read:inventory | View inventory items and stock levels |
write:inventory | Create and update inventory items |
delete:inventory | Delete inventory items |
Organization Scopes
| Scope | Description |
|---|---|
update:organization | Update organization settings |
write:reports | Generate and export reports |
List Users (Cursor-based)
Retrieve a cursor-based paginated list of users in the organization using the infiniteUsers query. Accepts a selector with filters and search, along with sorting and pagination parameters.
query InfiniteUsers(
$selector: UsersSelector,
$sort: UserSort,
$first: Int!,
$after: String
) {
infiniteUsers(
selector: $selector,
sort: $sort,
first: $first,
after: $after
) {
edges {
node {
id
firstName
lastName
isActive
isDisplayedOnSchedule
color
createdAt
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
# Variables
{
"first": 20,
"selector": {
"filters": {
"isActive": { "equals": true }
},
"search": "Mike"
}
}Response:
{
"data": {
"infiniteUsers": {
"edges": [
{
"node": {
"id": "clxyz1234567890abcdef",
"firstName": "Mike",
"lastName": "Johnson",
"isActive": true,
"isDisplayedOnSchedule": true,
"color": "#4A90D9",
"createdAt": "2024-01-15T10:00:00Z"
},
"cursor": "eyJpZCI6ImNseHl6MTIzNDU2Nzg5MGFiY2RlZiJ9"
}
],
"pageInfo": {
"hasNextPage": true,
"endCursor": "eyJpZCI6ImNseHl6MTIzNDU2Nzg5MGFiY2RlZiJ9"
}
}
}
}List Users (Offset-based)
Retrieve an offset-based paginated list of users using the paginatedUsers query. Accepts the same selector as infiniteUsers but uses limit and offset for pagination.
query PaginatedUsers(
$selector: UsersSelector,
$sort: UserSort,
$limit: Int!,
$offset: Int!
) {
paginatedUsers(
selector: $selector,
sort: $sort,
limit: $limit,
offset: $offset
) {
nodes {
id
firstName
lastName
isActive
isDisplayedOnSchedule
color
}
totalCount
}
}
# Variables
{
"limit": 20,
"offset": 0,
"selector": {
"filters": {
"isActive": { "equals": true },
"isDisplayedOnSchedule": { "equals": true }
}
}
}Response:
{
"data": {
"paginatedUsers": {
"nodes": [
{
"id": "clxyz1234567890abcdef",
"firstName": "Mike",
"lastName": "Johnson",
"isActive": true,
"isDisplayedOnSchedule": true,
"color": "#4A90D9"
}
],
"totalCount": 12
}
}
}Get User by ID
Retrieve a specific user by their ID. Results are filtered by the authenticated user's organization.
query User($id: ID!) {
user(id: $id) {
id
firstName
lastName
isActive
isDisplayedOnSchedule
color
mobileAppVersion
createdAt
updatedAt
}
}
# Variables
{
"id": "clxyz1234567890abcdef"
}Response:
{
"data": {
"user": {
"id": "clxyz1234567890abcdef",
"firstName": "Mike",
"lastName": "Johnson",
"isActive": true,
"isDisplayedOnSchedule": true,
"color": "#4A90D9",
"mobileAppVersion": "2.5.1",
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-20T08:00:00Z"
}
}
}Get Authenticated User
Retrieve the currently authenticated user. This query returns the full user object for the caller, including auth-gated fields like email, role, and scopes.
query AuthenticatedUser {
authenticatedUser {
id
firstName
lastName
username
email
role
isActive
isDisplayedOnSchedule
color
scopes
hourlyRate
serviceRate
payrollType
createdAt
updatedAt
}
}Response:
{
"data": {
"authenticatedUser": {
"id": "clxyz0000000000admin",
"firstName": "Admin",
"lastName": "User",
"username": "admin@example.com",
"email": "admin@example.com",
"role": "ADMIN",
"isActive": true,
"isDisplayedOnSchedule": true,
"color": "#E74C3C",
"scopes": [
"read:all:appointment",
"write:appointment",
"read:customer",
"write:customer",
"update:organization"
],
"hourlyRate": 2500,
"serviceRate": 5000,
"payrollType": "HOURLY",
"createdAt": "2023-01-15T10:00:00Z",
"updatedAt": "2024-01-20T09:00:00Z"
}
}
}Lookup User
Check if a user exists by their username or email address.
query LookupUser($usernameOrEmail: String!) {
lookupUser(usernameOrEmail: $usernameOrEmail) {
id
firstName
lastName
}
}
# Variables
{
"usernameOrEmail": "mike@example.com"
}Response:
{
"data": {
"lookupUser": {
"id": "clxyz1234567890abcdef",
"firstName": "Mike",
"lastName": "Johnson"
}
}
}Filtering
Both infiniteUsers and paginatedUsers accept a selector parameter with filters and search. The UserFilters type supports the following fields:
| Filter Field | Type | Notes |
|---|---|---|
id | IDFilter | Filter by user ID |
firstName | StringFilter | Filter by first name |
lastName | StringFilter | Filter by last name |
isActive | BooleanFilter | Filter by active status |
isDisplayedOnSchedule | BooleanFilter | Filter by schedule visibility |
username | StringFilter | Auth-gated |
email | StringFilter | Auth-gated |
mobileAppVersion | StringFilter | Filter by mobile app version |
createdAt | DateTimeFilter | Filter by creation date |
updatedAt | DateTimeFilter | Filter by last update date |
role | UserRoleEnumFilter | Auth-gated |
The search field on the selector performs a text search across user fields (max 600 characters).
Field Reference
| Field | Type | Description |
|---|---|---|
id | ID! | Unique identifier (cuid) |
firstName | String! | First name |
lastName | String! | Last name |
username | String(nullable) | Username (auth-gated: requires update:organization scope or self) |
email | String(nullable) | Email address (auth-gated) |
createdAt | Date! | Creation timestamp |
updatedAt | Date! | Last update timestamp |
isActive | Boolean! | Whether the user is active |
isDisplayedOnSchedule | Boolean! | Whether the user is displayed on the schedule |
role | UserRoleEnum(nullable) | User role (auth-gated) |
mobileAppVersion | String(nullable) | Version of the mobile app the user is running |
hourlyRate | Int(nullable) | Hourly pay rate in cents (auth-gated: update:organization) |
serviceRate | Int(nullable) | Service rate in cents (auth-gated: update:organization) |
payrollType | UserPayrollTypeEnum(nullable) | Payroll type (auth-gated) |
color | String(nullable) | Color used for calendar/schedule display |
scopes | [String](nullable) | List of permission scopes (auth-gated) |