Summary and recommendation
HubSpot exposes user management through two distinct API surfaces that use separate authentication tokens and serve different use cases. The Settings Users API (`/settings/v3/users`) accepts either a Private App Bearer token or an OAuth 2.0 access token and supports full CRUD on user records, role assignments, and team membership.
The SCIM 2.0 API (`/scim/v2/Users`) uses a separate portal-generated Bearer token - not interchangeable with OAuth or Private App tokens - and requires Enterprise tier with SSO fully active before it can be enabled.
For identity graph construction, the canonical user identifier is HubSpot's internal numeric `id` field. Email address is not accepted as a path parameter in the Settings API; callers must resolve email to `id` via `GET /settings/v3/users` with client-side filtering before any update or delete operation.
Store this `id` at provisioning time to avoid repeated lookup calls against rate limits.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (recommended for public/multi-portal integrations) or Private App Bearer token (recommended for single-portal internal use). SCIM uses a separate portal-generated Bearer token. |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise (SSO is a prerequisite for SCIM; SSO is only available on Enterprise tier across all Hubs) |
Authentication
Auth method: OAuth 2.0 (recommended for public/multi-portal integrations) or Private App Bearer token (recommended for single-portal internal use). SCIM uses a separate portal-generated Bearer token.
Setup steps
- Private App: In HubSpot portal go to Settings → Integrations → Private Apps → Create a private app, select required scopes, copy the generated access token.
- OAuth 2.0: Register an app at developers.hubspot.com, configure redirect URI, request authorization via https://app.hubspot.com/oauth/authorize with required scopes, exchange code for access_token and refresh_token at https://api.hubapi.com/oauth/v1/token.
- Pass the token in every request as HTTP header: Authorization: Bearer {token}.
- SCIM: Enable SSO (Enterprise required), navigate to Settings → Security → User Provisioning, enable SCIM, and copy the generated SCIM bearer token. This token is separate from OAuth/private app tokens.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| crm.objects.users.read | Read user records via the User Provisioning API | GET /settings/v3/users, GET /settings/v3/users/{userId} |
| crm.objects.users.write | Create, update, and delete user records | POST /settings/v3/users, PUT /settings/v3/users/{userId}, DELETE /settings/v3/users/{userId} |
| settings.users.read | Read user settings and role assignments | GET /settings/v3/users, GET /settings/v3/users/{userId} |
| settings.users.write | Create and update user settings and role assignments | POST /settings/v3/users, PUT /settings/v3/users/{userId} |
| settings.users.teams.read | Read team membership | GET /settings/v3/users/teams |
| settings.users.teams.write | Create and update team membership | POST /settings/v3/users/teams |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string | HubSpot-assigned unique numeric user ID | system-generated | read-only | Used as path parameter for single-user operations; not the email address |
| string | User's email address; serves as login identifier | required | optional | Must be unique within the portal; changing email triggers re-verification | |
| firstName | string | User's first name | optional | optional | |
| lastName | string | User's last name | optional | optional | |
| roleId | string | ID of the HubSpot role assigned to the user | optional | optional | Roles are portal-specific; retrieve valid IDs via GET /settings/v3/users/roles |
| primaryTeamId | string | ID of the user's primary team | optional | optional | Teams must exist before assignment; only available on Professional/Enterprise |
| secondaryTeamIds | array |
IDs of additional teams the user belongs to | optional | optional | |
| superAdmin | boolean | Whether the user has super-admin privileges | optional | optional | Only an existing super-admin can grant this flag via API |
| sendWelcomeEmail | boolean | Whether to send a HubSpot invite email on user creation | optional (default: true) | not applicable | Set to false for silent provisioning (e.g., SCIM or automated flows) |
Core endpoints
List all users
- Method: GET
- URL:
https://api.hubapi.com/settings/v3/users - Watch out for: Returns only active portal users; pending-invitation users are not included.
Request example
GET /settings/v3/users?limit=100 HTTP/1.1
Host: api.hubapi.com
Authorization: Bearer {token}
Response example
{
"results": [
{"id":"12345678","email":"jane@example.com",
"firstName":"Jane","lastName":"Doe",
"roleId":"987","superAdmin":false}
],
"paging":{"next":{"after":"NTI1Cg%3D%3D"}}
}
Get single user
- Method: GET
- URL:
https://api.hubapi.com/settings/v3/users/{userId} - Watch out for: userId must be the HubSpot numeric ID, not the user's email address.
Request example
GET /settings/v3/users/12345678 HTTP/1.1
Host: api.hubapi.com
Authorization: Bearer {token}
Response example
{
"id":"12345678",
"email":"jane@example.com",
"firstName":"Jane",
"lastName":"Doe",
"roleId":"987",
"superAdmin":false
}
Create user
- Method: POST
- URL:
https://api.hubapi.com/settings/v3/users - Watch out for: Creating a user does not assign a paid product seat; seat assignment must be done in the portal UI or via SCIM. sendWelcomeEmail defaults to true.
Request example
POST /settings/v3/users HTTP/1.1
Host: api.hubapi.com
Authorization: Bearer {token}
Content-Type: application/json
{"email":"new@example.com","firstName":"New",
"lastName":"User","roleId":"987","sendWelcomeEmail":false}
Response example
{
"id":"99887766",
"email":"new@example.com",
"firstName":"New",
"lastName":"User",
"roleId":"987",
"superAdmin":false
}
Update user
- Method: PUT
- URL:
https://api.hubapi.com/settings/v3/users/{userId} - Watch out for: This is a full PUT replacement for mutable fields; omitting optional fields may clear them. Always include all desired values.
Request example
PUT /settings/v3/users/99887766 HTTP/1.1
Host: api.hubapi.com
Authorization: Bearer {token}
Content-Type: application/json
{"firstName":"Updated","roleId":"1001",
"primaryTeamId":"456"}
Response example
{
"id":"99887766",
"email":"new@example.com",
"firstName":"Updated",
"roleId":"1001",
"superAdmin":false
}
Delete user
- Method: DELETE
- URL:
https://api.hubapi.com/settings/v3/users/{userId} - Watch out for: Deletion is irreversible via API. CRM data owned by the deleted user is not auto-reassigned; orphaned records must be handled separately.
Request example
DELETE /settings/v3/users/99887766 HTTP/1.1
Host: api.hubapi.com
Authorization: Bearer {token}
Response example
HTTP/1.1 204 No Content
List roles
- Method: GET
- URL:
https://api.hubapi.com/settings/v3/users/roles - Watch out for: Roles are portal-specific and depend on which Hubs are active. Always fetch before assigning roleId.
Request example
GET /settings/v3/users/roles HTTP/1.1
Host: api.hubapi.com
Authorization: Bearer {token}
Response example
{
"results": [
{"id":"987","name":"Sales Rep","requiresBilledSeat":true},
{"id":"988","name":"Viewer","requiresBilledSeat":false}
]
}
List teams
- Method: GET
- URL:
https://api.hubapi.com/settings/v3/users/teams - Watch out for: Teams are only available on Professional and Enterprise plans. Calls on Starter return HTTP 403.
Request example
GET /settings/v3/users/teams HTTP/1.1
Host: api.hubapi.com
Authorization: Bearer {token}
Response example
{
"results": [
{"id":"456","name":"EMEA Sales",
"userIds":["12345678"]}
]
}
SCIM list users
- Method: GET
- URL:
https://api.hubapi.com/scim/v2/Users - Watch out for: Uses a separate SCIM bearer token generated in portal settings, not the OAuth/private app token. Requires Enterprise tier with SSO active.
Request example
GET /scim/v2/Users?startIndex=1&count=100 HTTP/1.1
Host: api.hubapi.com
Authorization: Bearer {scim_token}
Response example
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults":2,
"Resources":[{"id":"12345678",
"userName":"jane@example.com","active":true}]
}
Rate limits, pagination, and events
- Rate limits: Per-portal limits enforced by HubSpot. Free/Starter: 100 requests/10 seconds, 250,000 requests/day. Professional/Enterprise: 150 requests/10 seconds, 500,000 requests/day. HTTP 429 returned on breach.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: Headers returned: X-HubSpot-RateLimit-Daily, X-HubSpot-RateLimit-Daily-Remaining, X-HubSpot-RateLimit-Interval-Milliseconds, X-HubSpot-RateLimit-Max, X-HubSpot-RateLimit-Remaining. On 429, Retry-After header indicates seconds to wait. Batch/search endpoints count as multiple calls.
- Pagination method: cursor
- Default page size: 100
- Max page size: 100
- Pagination pointer: after
| Plan | Limit | Concurrent |
|---|---|---|
| Free / Starter | 100 requests / 10 seconds; 250,000 requests / day | 0 |
| Professional | 150 requests / 10 seconds; 500,000 requests / day | 0 |
| Enterprise | 150 requests / 10 seconds; 500,000 requests / day | 0 |
- Webhooks available: No
- Webhook notes: HubSpot's Webhooks API covers CRM object events (contacts, deals, companies, etc.) but does not emit user-management events such as user created, updated, or deleted within a portal.
- Alternative event strategy: Poll GET /settings/v3/users on a schedule to detect changes. For IdP-driven provisioning, use SCIM which provides synchronous provisioning and deprovisioning. HubSpot native workflow automation does not cover internal user lifecycle events.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise (SSO is a prerequisite for SCIM; SSO is only available on Enterprise tier across all Hubs)
Endpoint: https://api.hubapi.com/scim/v2
Supported operations: GET /Users – list users, GET /Users/{id} – get single user, POST /Users – create user, PUT /Users/{id} – replace user, PATCH /Users/{id} – update user attributes, DELETE /Users/{id} – deactivate/remove user, GET /Groups – list groups/teams, POST /Groups – create group, PATCH /Groups/{id} – update group membership, DELETE /Groups/{id} – delete group
Limitations:
- SSO must be fully configured and active before SCIM can be enabled; SSO requires Enterprise tier.
- Only one SCIM integration (one IdP app) is permitted per HubSpot portal at a time.
- SCIM bearer token is generated in portal settings and does not expire automatically; must be rotated manually.
- SCIM-editable attributes are limited to email, firstName, lastName, and active status. Role and team assignments must be managed via the Settings API or portal UI.
- Microsoft Entra ID group sync should be disabled in the Entra SCIM app to avoid users entering a quarantine state; manage team membership in HubSpot directly.
- Deactivating a user via SCIM (active: false) removes portal access but does not delete CRM data.
- Although pricing documentation references SCIM from Professional tier, the SSO prerequisite means effective SCIM access requires Enterprise.
Common scenarios
Provisioning a new user requires four sequential calls: fetch roles (GET /settings/v3/users/roles), fetch teams (GET /settings/v3/users/teams), create the user (POST /settings/v3/users with roleId, primaryTeamId, and sendWelcomeEmail), then store the returned id.
Creating a user via API does not assign a paid product seat; seat assignment must be completed in the portal UI or handled through SCIM's active flag toggling. Omitting this step leaves the user provisioned but without access to Sales Hub or Service Hub features.
Deprovisioning requires resolving the user's id first (paginate GET /settings/v3/users, filter by email), then issuing DELETE /settings/v3/users/{userId}. The API returns HTTP 204 on success. Deletion via API is irreversible and does not auto-reassign CRM records - contacts, deals, and tickets owned by the deleted user become orphaned and must be explicitly reassigned via the CRM Owners API or portal UI to avoid pipeline reporting gaps.
For IdP-driven provisioning at scale, the SCIM 2.0 endpoint (https://api.hubapi.com/scim/v2) supports create, replace, patch, and deactivate operations. Only one SCIM app is permitted per portal; adding a second IdP requires removing the existing integration, which interrupts active provisioning. SCIM-editable attributes are limited to email, firstName, lastName, and active status - role and team assignments fall back to the Settings API or portal UI.
Provision a new employee with role and team assignment
- Generate a Private App token with scopes settings.users.read and settings.users.write.
- Fetch available roles: GET https://api.hubapi.com/settings/v3/users/roles to find the correct roleId.
- Fetch available teams: GET https://api.hubapi.com/settings/v3/users/teams to find the correct primaryTeamId.
- Create the user: POST https://api.hubapi.com/settings/v3/users with body {email, firstName, lastName, roleId, primaryTeamId, sendWelcomeEmail: true}.
- Store the returned id for future updates or deprovisioning.
Watch out for: Creating the user does not assign a paid product seat. Seat assignment must be completed in the HubSpot portal UI. If sendWelcomeEmail is true, the invite is sent immediately on creation.
Deprovision a departing employee
- Look up the user's HubSpot ID: GET https://api.hubapi.com/settings/v3/users, paginate through results and filter by email to find the id.
- Delete the user: DELETE https://api.hubapi.com/settings/v3/users/{userId}.
- Verify HTTP 204 response confirming removal.
- Reassign the user's CRM records (contacts, deals) to another owner via the CRM Owners API or portal UI, as deletion does not auto-reassign.
Watch out for: Deletion is irreversible via API. Orphaned CRM records may affect pipeline reporting if not reassigned promptly.
Set up SCIM auto-provisioning with Okta
- Confirm the HubSpot portal is on Enterprise tier and SSO is fully configured (Settings → Security → SSO).
- In HubSpot: navigate to Settings → Security → User Provisioning, enable SCIM, and copy the generated SCIM bearer token.
- In Okta: add the HubSpot application from the Okta Integration Network, go to the Provisioning tab, set SCIM connector base URL to https://api.hubapi.com/scim/v2, set authentication to Bearer Token, paste the HubSpot SCIM token.
- Enable Provisioning features in Okta: Push New Users, Push Profile Updates, Deactivate Users.
- Assign users or groups in Okta to the HubSpot app; Okta will POST /scim/v2/Users for each assigned user.
- Test by assigning a pilot user in Okta and verifying the user appears in HubSpot Settings → Users.
Watch out for: Only one SCIM app is allowed per HubSpot portal. SCIM-provisioned user attributes (name, email) are owned by Okta; edits made directly in HubSpot will be overwritten on the next Okta sync.
Why building this yourself is a trap
The most common integration failure is token confusion: the SCIM Bearer token and the OAuth/Private App token are generated through entirely different flows and are not interchangeable. Passing an OAuth token to the SCIM endpoint returns an auth error that can be misread as a permissions or scope problem.
The PUT /settings/v3/users/{userId} endpoint is a full replacement for all mutable fields. Omitting any optional field in the request body clears it server-side - a silent data loss that affects role assignments and team membership without returning an error. Always include all desired field values on every PUT call.
Rate limits differ by plan tier: Free/Starter portals are capped at 100 requests per 10 seconds and 250,000 per day; Professional and Enterprise portals receive 150 requests per 10 seconds and 500,000 per day. Batch and search endpoints each count as multiple calls against these limits.
On 429, respect the Retry-After header and implement exponential backoff - the X-HubSpot-RateLimit-Remaining and X-HubSpot-RateLimit-Daily-Remaining headers provide real-time budget visibility.
HubSpot does not emit webhook events for user lifecycle changes, so any identity graph that needs to stay current must poll GET /settings/v3/users on a schedule or rely on SCIM for synchronous provisioning and deprovisioning signals.
Automate HubSpot 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.