Summary and recommendation
Monday.com's API is a single GraphQL endpoint (`POST https://api.monday.com/v2`) - there are no REST-style resource URLs. Authentication uses either a personal API token (no `Bearer` prefix in the Authorization header) or an OAuth 2.0 access token (`Bearer` prefix required). Personal tokens do not expire; OAuth tokens do and require refresh handling.
User operations require `users:read` and `users:write` scopes at minimum. Team membership mutations require `teams:read` and `teams:write`. Rate limits are complexity-based (10,000,000 units/minute across all plans), with per-query complexity returned in every response body under the `complexity` object.
HTTP 429 signals limit exhaustion; retry after 60 seconds.
For teams building an identity graph across SaaS applications, Monday.com's user object exposes `id`, `email`, `is_admin`, `is_guest`, `is_view_only`, `enabled`, and a nested `teams` array - sufficient to reconstruct account-level role state and team membership in a single paginated query.
API quick reference
| Has user API | Yes |
| Auth method | API Token (personal or app token) or OAuth 2.0 (for apps/integrations) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: API Token (personal or app token) or OAuth 2.0 (for apps/integrations)
Setup steps
- For personal API token: navigate to monday.com profile avatar → Admin → API → copy the personal API token.
- Pass the token in the HTTP Authorization header:
Authorization: <token>(no 'Bearer' prefix for personal tokens). - For OAuth 2.0 app tokens: register an app at https://monday.com/developers/apps, configure redirect URIs and scopes, implement the authorization code flow to obtain an access token.
- Pass OAuth access token in the Authorization header:
Authorization: Bearer <access_token>. - All requests must be POST to https://api.monday.com/v2 with Content-Type: application/json and a GraphQL body.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| me:read | Read the authenticated user's own profile | Fetching current user info |
| users:read | Read all users in the account | Listing users, fetching user details |
| users:write | Modify user attributes (name, title, etc.) | Updating user fields via API |
| account:read | Read account-level information including plan and seat counts | Account and team membership queries |
| teams:read | Read team membership and team details | Listing teams and their members |
| teams:write | Add or remove users from teams | Team membership management |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | ID | Unique numeric user identifier | system-assigned | immutable | Used as primary key in all user operations |
| name | String | Full display name of the user | required | updatable | |
| String | Primary email address | required | updatable via SCIM; limited via GraphQL | Email changes may require admin privileges | |
| title | String | Job title | optional | updatable | |
| phone | String | Phone number | optional | updatable | |
| location | String | User's location/timezone label | optional | updatable | |
| time_zone_identifier | String | IANA timezone string (e.g., America/New_York) | optional | updatable | |
| photo_thumb | String | URL to thumbnail profile photo | system-assigned | read-only via API | |
| photo_original | String | URL to full-size profile photo | system-assigned | read-only via API | |
| is_admin | Boolean | Whether the user has account admin privileges | optional | updatable by admin | |
| is_guest | Boolean | Whether the user is a guest (limited access) | set at invite time | read-only via GraphQL | Guests have restricted board access |
| is_view_only | Boolean | Whether the user is a viewer (no edit rights) | set at invite time | read-only via GraphQL | |
| enabled | Boolean | Whether the user account is active | true by default | deactivate via SCIM or admin UI | Deactivation via GraphQL not directly supported; use SCIM |
| teams | [Team] | List of teams the user belongs to | optional | via add/remove team mutations | Nested object; query separately |
| account | Account | The account this user belongs to | system-assigned | immutable | |
| created_at | String (ISO 8601) | Timestamp when the user was created | system-assigned | immutable | |
| url | String | Profile URL within monday.com | system-assigned | immutable | |
| birthday | String | User's birthday (YYYY-MM-DD) | optional | updatable | |
| skills | [String] | List of skill tags on the user profile | optional | updatable |
Core endpoints
List Users
- Method: POST
- URL:
https://api.monday.com/v2 - Watch out for: Uses page-based pagination (page + limit), not cursor. Max limit is 500 per page.
Request example
{
"query": "{ users(limit: 50, page: 1) { id name email is_admin is_guest enabled } }"
}
Response example
{
"data": {
"users": [
{ "id": "12345", "name": "Jane Doe", "email": "jane@example.com", "is_admin": false, "is_guest": false, "enabled": true }
]
}
}
Get User by ID
- Method: POST
- URL:
https://api.monday.com/v2 - Watch out for: ids parameter accepts an array; returns empty array if user not found rather than an error.
Request example
{
"query": "{ users(ids: [12345]) { id name email title phone time_zone_identifier teams { id name } } }"
}
Response example
{
"data": {
"users": [
{ "id": "12345", "name": "Jane Doe", "email": "jane@example.com", "title": "Engineer", "teams": [] }
]
}
}
Get Current (Me) User
- Method: POST
- URL:
https://api.monday.com/v2 - Watch out for: Requires only me:read scope; useful for token validation.
Request example
{
"query": "{ me { id name email is_admin account { id name } } }"
}
Response example
{
"data": {
"me": { "id": "12345", "name": "Jane Doe", "email": "jane@example.com", "is_admin": true }
}
}
Invite User
- Method: POST
- URL:
https://api.monday.com/v2 - Watch out for: user_role accepts: member, guest, viewer. Name is null until the user completes signup. Requires admin token.
Request example
{
"query": "mutation { invite_user_to_account(email: \"new@example.com\", user_role: member) { id name email } }"
}
Response example
{
"data": {
"invite_user_to_account": { "id": "67890", "name": null, "email": "new@example.com" }
}
}
Update User
- Method: POST
- URL:
https://api.monday.com/v2 - Watch out for: Cannot update email or role type via this mutation; use SCIM for email changes.
Request example
{
"query": "mutation { update_user(id: 12345, user_details: { title: \"Senior Engineer\", phone: \"+1-555-0100\" }) { id title phone } }"
}
Response example
{
"data": {
"update_user": { "id": "12345", "title": "Senior Engineer", "phone": "+1-555-0100" }
}
}
Deactivate User
- Method: POST
- URL:
https://api.monday.com/v2 - Watch out for: Deactivated users retain their data but cannot log in. Reactivation requires admin action. This mutation may require Enterprise plan.
Request example
{
"query": "mutation { deactivate_user(user_id: 12345) { id enabled } }"
}
Response example
{
"data": {
"deactivate_user": { "id": "12345", "enabled": false }
}
}
Add User to Team
- Method: POST
- URL:
https://api.monday.com/v2 - Watch out for: Returns partial success; always check failed_users array. Requires teams:write scope.
Request example
{
"query": "mutation { add_users_to_team(team_id: 111, user_ids: [12345, 67890]) { successful_users { id name } failed_users { id } } }"
}
Response example
{
"data": {
"add_users_to_team": { "successful_users": [{ "id": "12345", "name": "Jane Doe" }], "failed_users": [] }
}
}
Remove User from Team
- Method: POST
- URL:
https://api.monday.com/v2 - Watch out for: Removing a user from all teams does not deactivate them. Requires teams:write scope.
Request example
{
"query": "mutation { remove_users_from_team(team_id: 111, user_ids: [12345]) { successful_users { id } failed_users { id } } }"
}
Response example
{
"data": {
"remove_users_from_team": { "successful_users": [{ "id": "12345" }], "failed_users": [] }
}
}
Rate limits, pagination, and events
- Rate limits: monday.com enforces complexity-based rate limits per minute. Each query has a complexity cost; the default limit is 10,000,000 complexity units per minute. Additionally, there is a per-minute request cap. Complexity cost is returned in every response.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: Response body includes
complexityobject withbefore,query, andafterfields. HTTP 429 is returned when limits are exceeded. Retry after 60 seconds. Pagination reduces per-query complexity. - Pagination method: cursor
- Default page size: 50
- Max page size: 500
- Pagination pointer: limit / cursor (via
users(limit: N, page: N)- monday.com uses page-based offset for users query, not cursor)
| Plan | Limit | Concurrent |
|---|---|---|
| Free / Basic / Standard | 10,000,000 complexity units/minute; ~60 requests/minute | 0 |
| Pro | 10,000,000 complexity units/minute; higher request throughput | 0 |
| Enterprise | 10,000,000 complexity units/minute; highest throughput; dedicated support for limit increases | 0 |
- Webhooks available: Yes
- Webhook notes: monday.com supports webhooks for board and item events, but user lifecycle events (user created, deactivated, role changed) are not exposed as webhook triggers. Webhooks are configured per-board via the API or UI.
- Alternative event strategy: Poll the
usersGraphQL query on a schedule to detect user changes. For provisioning events, use SCIM 2.0 push from your IdP (Okta, Entra ID, OneLogin). - Webhook events: create_item, change_column_value, create_update, delete_item, item_moved_to_any_group, create_subitem, change_subitem_column_value
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://auth.monday.com/scim/v2
Supported operations: GET /Users – list all users, GET /Users/{id} – get user by SCIM ID, POST /Users – provision new user, PUT /Users/{id} – replace user attributes, PATCH /Users/{id} – partial update (activate/deactivate), GET /Groups – list teams as SCIM groups, POST /Groups – create team, PATCH /Groups/{id} – add/remove group members, DELETE /Groups/{id} – delete team
Limitations:
- Requires Enterprise plan
- SSO (SAML) must be configured before enabling SCIM
- SCIM token is separate from GraphQL API token; generated in Admin → Security → SCIM
- User deprovisioning sets enabled=false; does not permanently delete the user
- Email address is the unique identifier for matching; duplicates cause provisioning errors
- Supported IdPs with native connectors: Okta, Microsoft Entra ID (Azure AD), OneLogin
Common scenarios
Provisioning a new employee requires two sequential mutations: invite_user_to_account (returns a user id) followed by add_users_to_team using that id and the target team_id. Always check the failed_users array in the team assignment response - the mutation returns partial success without raising an error.
Note that the invited user's name and profile fields are null until they accept the email invitation; team assignment succeeds immediately regardless.
For offboarding at scale, SCIM 2.0 is the recommended path over the GraphQL deactivate_user mutation, which may be restricted to Enterprise plans. Configure SCIM in Admin → Security → SCIM, set the base URL to https://auth.monday.com/scim/v2 in your IdP (Okta, Entra ID, or OneLogin), and deactivation in the IdP will push PATCH /Users/{id} with active: false. Critical caveat: SSO must be fully enforced before SCIM deprovisioning is reliable - users with password login can bypass SCIM-driven deactivation if SSO is not enforced.
For access auditing, query users(limit: 500, page: 1, kind: all) and filter on is_admin: true. Pagination is page-integer-based, not cursor-based; increment page until the returned array length is less than the limit value. Maximum page size is 500.
Provision a new employee and assign to a team
- POST to https://api.monday.com/v2 with
invite_user_to_accountmutation using the employee's email and role (member). - Capture the returned user
idfrom the mutation response. - POST to https://api.monday.com/v2 with
add_users_to_teammutation using the captured user id and the target team_id. - Verify
successful_usersin the response; log anyfailed_usersfor retry.
Watch out for: The invited user must accept the email invitation before their profile is fully populated. Team assignment succeeds immediately even before acceptance.
Sync user deprovisioning via SCIM on employee offboarding
- Configure SCIM in monday.com Admin → Security → SCIM; copy the SCIM bearer token.
- In your IdP (Okta/Entra/OneLogin), set the SCIM base URL to https://auth.monday.com/scim/v2 and paste the token.
- When the employee is deactivated in the IdP, the IdP sends PATCH /Users/{id} with
active: falseto monday.com SCIM. - monday.com sets the user's enabled status to false, preventing login while retaining data.
Watch out for: SSO must be fully configured and enforced before SCIM deprovisioning works. If SSO is not enforced, users can still log in with password even after SCIM deactivation.
Audit all admin users in the account
- POST to https://api.monday.com/v2 with query
{ users(limit: 500, page: 1, kind: all) { id name email is_admin created_at } }. - Filter the response for records where
is_admin: true. - Increment
pageand repeat until the returned array has fewer items than the limit (indicating last page). - Export the collected admin records for compliance review.
Watch out for: Max page size is 500. For accounts with >500 users, multiple paginated requests are required. There is no cursor; use page integer increments.
Why building this yourself is a trap
The most consequential API caveat is the SCIM/GraphQL ID namespace mismatch: SCIM externalId maps to the GraphQL user id, but the two systems use separate identifiers internally. Any identity graph that joins Monday.com user records with IdP records must explicitly map across these namespaces or risk provisioning duplicates or failed lookups.
A second structural trap is the Update User mutation's scope: update_user cannot modify email address or role type. Email changes require SCIM PATCH; role changes require manual admin action or SCIM. Assuming the GraphQL API provides full attribute writeback will produce silent gaps in automated workflows.
Webhooks do not emit user lifecycle events - there are no triggers for user creation, deactivation, or role changes. Any pipeline that needs to react to user state changes must poll the users query on a schedule or rely entirely on IdP-pushed SCIM events.
This makes event-driven identity graph updates impossible without an external polling layer.
Automate Monday.com 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.