Summary and recommendation
Domo's REST Users API (base URL: https://api.domo.com/v1) uses OAuth 2.0 client_credentials grant for authentication. Obtain a Bearer token by POSTing to https://api.domo.com/oauth/token?grant_type=client_credentials&scope=user with Basic auth (client_id:client_secret); tokens expire in 3600 seconds and must be refreshed proactively.
The API supports list, get, create, update (PUT only - full replace, not PATCH), and delete operations on user objects. Core user fields include id (numeric integer), email, role (case-sensitive string), name, title, department, employeeNumber, and active (boolean). Pagination is offset-based with a default page size of 50 and a maximum of 500 per request.
The instance-wide rate limit is 3,000 requests/minute across all endpoints; HTTP 429 is returned on breach, and X-RateLimit-Limit / X-RateLimit-Remaining headers are present in responses.
For identity graph construction, the user object exposes employeeNumber, email, alternateEmail, department, title, and location - sufficient to join Domo user records against HR systems or a broader identity graph without requiring a separate directory lookup.
There is no server-side filter by email on GET /v1/users; callers must paginate the full user list and filter client-side, or use the SCIM endpoint (https://<instance>.domo.com/api/scim/v2/Users?filter=userName eq "email") for server-side filtering.
SCIM 2.0 is available on Enterprise plans and requires SSO to be fully configured first; its Bearer token is generated separately in the Domo Admin UI and is distinct from the OAuth client_credentials token.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (client_credentials grant) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: OAuth 2.0 (client_credentials grant)
Setup steps
- Log in to Domo and navigate to developer.domo.com.
- Create a new client application under 'My Client IDs' to obtain a client_id and client_secret.
- Assign the required scopes (e.g., 'user') to the client application.
- POST to https://api.domo.com/oauth/token?grant_type=client_credentials&scope=user with Basic auth (client_id:client_secret) to receive a Bearer access_token.
- Include the access_token as 'Authorization: Bearer
' in all subsequent API requests. - Tokens expire; re-request as needed (expiry returned in token response as expires_in seconds).
Required scopes
| Scope | Description | Required for |
|---|---|---|
| user | Read and write access to user objects (list, create, update, delete users). | All Users API operations |
| audit | Access to audit log data. | Audit/activity log endpoints |
| data | Access to DataSet and DataFlow resources. | DataSet API operations (not user management) |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique numeric identifier for the user. | system-assigned | read-only | Used as path parameter in all user-specific endpoints. |
| name | string | Full display name of the user. | required | optional | |
| string | Primary email address; used as login identifier. | required | optional | Must be unique within the Domo instance. | |
| role | string | Domo role name (e.g., 'Admin', 'Privileged', 'Editor', 'Participant', 'Social'). | required | optional | Role names are case-sensitive. Custom roles are not supported via this field in the v1 API. |
| title | string | Job title of the user. | optional | optional | |
| department | string | Department the user belongs to. | optional | optional | |
| phone | string | Phone number of the user. | optional | optional | |
| location | string | Physical location or office of the user. | optional | optional | |
| timezone | string | IANA timezone string for the user (e.g., 'America/Chicago'). | optional | optional | |
| locale | string | Locale setting for the user (e.g., 'en-US'). | optional | optional | |
| employeeNumber | string | Employee ID or number from HR systems. | optional | optional | |
| alternateEmail | string | Secondary email address for the user. | optional | optional | |
| createdAt | string (ISO 8601) | Timestamp when the user was created. | system-assigned | read-only | |
| updatedAt | string (ISO 8601) | Timestamp of the last update to the user record. | system-assigned | read-only | |
| active | boolean | Whether the user account is active. | optional (defaults to true) | optional | Setting to false deactivates the user without deleting them. |
Core endpoints
List Users
- Method: GET
- URL:
https://api.domo.com/v1/users - Watch out for: Returns an array (not a wrapped object). Use offset+limit for pagination; max limit is 500 per request.
Request example
GET /v1/users?limit=50&offset=0
Authorization: Bearer <token>
Response example
[
{
"id": 123456789,
"name": "Jane Doe",
"email": "jane@example.com",
"role": "Editor"
}
]
Get User
- Method: GET
- URL:
https://api.domo.com/v1/users/{userId} - Watch out for: userId is a numeric integer, not a UUID or email string.
Request example
GET /v1/users/123456789
Authorization: Bearer <token>
Response example
{
"id": 123456789,
"name": "Jane Doe",
"email": "jane@example.com",
"role": "Editor",
"title": "Analyst",
"department": "Finance"
}
Create User
- Method: POST
- URL:
https://api.domo.com/v1/users - Watch out for: The sendInvite query parameter (true/false) controls whether an invitation email is sent. Defaults to true if omitted.
Request example
POST /v1/users?sendInvite=false
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "John Smith",
"email": "john@example.com",
"role": "Participant"
}
Response example
{
"id": 987654321,
"name": "John Smith",
"email": "john@example.com",
"role": "Participant"
}
Update User
- Method: PUT
- URL:
https://api.domo.com/v1/users/{userId} - Watch out for: This is a full PUT (replace), not a partial PATCH. Omitting optional fields may clear existing values. Always include all desired fields.
Request example
PUT /v1/users/987654321
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "John Smith",
"role": "Editor",
"title": "Senior Analyst"
}
Response example
{
"id": 987654321,
"name": "John Smith",
"email": "john@example.com",
"role": "Editor",
"title": "Senior Analyst"
}
Delete User
- Method: DELETE
- URL:
https://api.domo.com/v1/users/{userId} - Watch out for: Deletion is permanent and cannot be undone via API. Content owned by the deleted user may be reassigned or lost.
Request example
DELETE /v1/users/987654321
Authorization: Bearer <token>
Response example
HTTP 204 No Content
Get User by Email (search)
- Method: GET
- URL:
https://api.domo.com/v1/users - Watch out for: There is no native filter-by-email query parameter in v1. Callers must paginate through all users and filter client-side, or use SCIM /Users?filter=userName eq "email" for server-side filtering.
Request example
GET /v1/users?limit=500&offset=0
Authorization: Bearer <token>
Response example
[
{"id": 123, "email": "target@example.com", ...}
]
Get OAuth Token
- Method: POST
- URL:
https://api.domo.com/oauth/token - Watch out for: Tokens expire in 3600 seconds (1 hour). Applications must handle token refresh proactively to avoid 401 errors mid-operation.
Request example
POST /oauth/token?grant_type=client_credentials&scope=user
Authorization: Basic base64(client_id:client_secret)
Response example
{
"access_token": "eyJ...",
"token_type": "bearer",
"expires_in": 3600,
"scope": "user"
}
List Groups (for group membership management)
- Method: GET
- URL:
https://api.domo.com/v1/groups - Watch out for: Group membership management (add/remove users from groups) requires separate endpoints under /v1/groups/{groupId}/users. The 'user' scope alone may not be sufficient; verify the client app has appropriate permissions.
Request example
GET /v1/groups?limit=50&offset=0
Authorization: Bearer <token>
Response example
[
{"id": 111, "name": "Finance Team", "active": true}
]
Rate limits, pagination, and events
- Rate limits: Domo enforces per-customer rate limits on API calls. The Users API is subject to a limit of 3,000 requests per minute across all API calls for a given customer instance.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: HTTP 429 is returned when the rate limit is exceeded. Domo recommends exponential backoff. The X-RateLimit-Limit and X-RateLimit-Remaining headers are present in responses. No official per-endpoint sub-limits are documented.
- Pagination method: offset
- Default page size: 50
- Max page size: 500
- Pagination pointer: offset and limit
| Plan | Limit | Concurrent |
|---|---|---|
| All plans | 3,000 requests/minute (instance-wide across all API endpoints) | 0 |
- Webhooks available: No
- Webhook notes: Domo does not offer native outbound webhooks for user lifecycle events (create, update, delete) via the standard API. Event-driven automation for user changes is not natively supported through a webhook subscription model.
- Alternative event strategy: Use Domo's Workflows (automation builder) or poll the Users API on a schedule to detect changes. For provisioning events, SCIM with an IdP (Okta, Entra ID) provides near-real-time push-based provisioning.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://
.domo.com/api/scim/v2 Supported operations: GET /Users (list users), GET /Users/{id} (get user), POST /Users (create user), PUT /Users/{id} (replace user), PATCH /Users/{id} (update user attributes), DELETE /Users/{id} (delete/deprovision user), GET /Groups (list groups), POST /Groups (create group), PATCH /Groups/{id} (update group membership), DELETE /Groups/{id} (delete group), GET /ServiceProviderConfig, GET /Schemas
Limitations:
- Requires Enterprise plan.
- SSO must be configured and enabled as a prerequisite before SCIM can be activated.
- SCIM provisioning is configured through the IdP (Okta, Entra ID); Domo acts as the SCIM service provider.
- Custom Domo roles may not map cleanly via SCIM group-to-role provisioning without additional configuration.
- SCIM filter support (e.g., filter=userName eq) is available for user lookups but complex filters may not be fully supported.
- Bearer token for SCIM is generated separately within Domo's SSO/SCIM configuration UI, not via the OAuth client_credentials flow.
Common scenarios
Three primary automation scenarios are well-supported by the API:
Bulk onboarding from an HR system: POST /v1/users per employee with sendInvite=false to suppress premature invitation emails during batch creation. Store the returned numeric id for downstream group membership calls (PATCH /v1/groups/{groupId}/users). Trigger invitations in a controlled second pass once the full batch is validated.
Deprovisioning a departed employee: There is no server-side email filter, so locate the user's numeric id by paginating GET /v1/users and matching email client-side. Prefer PUT /v1/users/{userId} with active=false over DELETE to preserve content ownership; DELETE /v1/users/{userId} is permanent and irreversible via API, and owned content (cards, DataSets) may be orphaned. Enterprise customers using Okta or Entra ID should route deprovisioning through SCIM, which triggers deactivation or deletion in Domo automatically.
Audit and reconciliation against an external directory: Paginate GET /v1/users at limit=500 until a page returns fewer results than the limit, then diff the full list against the external directory by email. Flag users present in Domo but absent from the directory as deprovisioning candidates. At scale, full pagination can approach rate limits; schedule reconciliation jobs during off-peak hours.
Bulk onboard new employees from HR system
- Obtain OAuth token via POST /oauth/token?grant_type=client_credentials&scope=user.
- For each new employee record from HR, POST /v1/users with name, email, role, title, department, and sendInvite=false.
- Store the returned numeric user id for future updates.
- After all users are created, trigger invitation emails in a second pass or rely on SSO for first login.
- Optionally, PATCH /v1/groups/{groupId}/users to add users to relevant Domo groups.
Watch out for: sendInvite defaults to true; always explicitly set sendInvite=false during bulk imports to prevent premature invitation emails before onboarding is complete.
Deprovision a departed employee
- Obtain OAuth token with 'user' scope.
- GET /v1/users with pagination to find the user's numeric id by matching email client-side (no server-side email filter in v1).
- Optionally, PUT /v1/users/{userId} with active=false to deactivate without deleting, preserving content ownership.
- If full removal is required, DELETE /v1/users/{userId}.
- If using SCIM via Okta/Entra, deprovisioning the user in the IdP will automatically trigger SCIM DELETE or deactivation in Domo.
Watch out for: DELETE is permanent. Prefer deactivation (active=false via PUT) if content preservation is important. SCIM-based deprovisioning via IdP is the recommended approach for Enterprise customers.
Sync Domo users to an external directory (audit/reconciliation)
- Obtain OAuth token with 'user' scope.
- GET /v1/users?limit=500&offset=0 and collect all pages by incrementing offset until fewer than limit results are returned.
- Compare the full user list against the external directory (e.g., Active Directory, HR system) by email.
- Identify users present in Domo but not in the directory (candidates for deprovisioning).
- Identify users in the directory but not in Domo (candidates for provisioning).
- Execute create/delete/update calls as needed based on reconciliation results.
Watch out for: With no server-side email filter, full pagination is required for reconciliation. At 500 users/page, large instances may require many requests and approach rate limits. Schedule reconciliation jobs during off-peak hours.
Why building this yourself is a trap
The most consequential API caveat is the PUT-only update model: GET /v1/users/{userId} first, merge your changes into the full object, then PUT the complete payload. Omitting any field on a PUT will silently clear its existing value. There is no PATCH on the v1 REST API (PATCH is available only via SCIM).
A second structural trap is the absence of a server-side email filter on the v1 Users endpoint - any lookup by email requires full pagination, which is expensive at scale and can exhaust rate limits in large instances.
Custom roles defined in the Domo UI are not assignable via the v1 role field; only the five default role name strings (Admin, Privileged, Editor, Participant, Social) are valid, and they are case-sensitive.
Finally, the SCIM Bearer token and the OAuth client_credentials token are entirely separate credentials with separate generation flows - conflating them is a common integration failure point.
Teams building on top of Stitchflow's MCP server with 60+ deep IT/identity integrations can surface Domo user data into a unified identity graph without re-implementing pagination, token refresh, or the PUT-merge pattern from scratch.
Automate Domo workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.