Summary and recommendation
The SalesLoft REST API v2 (base: https://api.salesloft.com/v2) supports OAuth 2.0 authorization code flow and personal API key (Bearer token) authentication.
Read access to user resources requires the `read` scope;
write operations on user-adjacent resources require `write`.
A critical caveat: user creation (inviting new users) is not supported via the REST API - provisioning must go through SCIM or the UI.
The API is team-scoped;
cross-team operations are not possible from a single token.
For teams building against an identity graph, the user object exposes `id`, `guid`, `email`, `active`, `role`, `team_id`, and `created_at` as the primary fields for identity resolution and lifecycle tracking.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (authorization code flow) or API Key (personal access token passed as Bearer token) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: OAuth 2.0 (authorization code flow) or API Key (personal access token passed as Bearer token)
Setup steps
- Register an OAuth application in the SalesLoft Developer Portal to obtain a client_id and client_secret.
- Redirect the user to https://accounts.salesloft.com/oauth/authorize with response_type=code, client_id, redirect_uri, and scope parameters.
- Exchange the returned authorization code for an access_token and refresh_token via POST to https://accounts.salesloft.com/oauth/token.
- Include the access_token as a Bearer token in the Authorization header on all API requests.
- Alternatively, generate a personal API key from SalesLoft Settings > API Keys and use it directly as a Bearer token (no OAuth flow required for server-side integrations).
Required scopes
| Scope | Description | Required for |
|---|---|---|
| read | Read access to all resources the authenticated user can access. | GET /users, GET /users/{id} |
| write | Write access to create and update resources. | POST and PATCH operations on user-adjacent resources |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique SalesLoft user ID. | system-assigned | immutable | Used as path parameter in /users/{id}. |
| guid | string | Global unique identifier (UUID) for the user. | system-assigned | immutable | Stable cross-environment identifier. |
| name | string | Full display name of the user. | required | optional | Derived from first_name + last_name. |
| first_name | string | User's first name. | required | optional | |
| last_name | string | User's last name. | required | optional | |
| string | Primary email address and login identifier. | required | optional | Must be unique within the team. | |
| title | string | Job title of the user. | optional | optional | |
| active | boolean | Whether the user account is active. | defaults to true | optional | Setting to false deactivates the user. |
| role | object | Role assigned to the user (e.g., Admin, User). | optional | optional | Contains id and name sub-fields. |
| team_id | integer | ID of the team the user belongs to. | system-assigned | immutable | |
| time_zone | string | User's configured time zone (IANA format). | optional | optional | |
| phone | string | User's phone number. | optional | optional | |
| phone_extension | string | Phone extension. | optional | optional | |
| linkedin_url | string | LinkedIn profile URL. | optional | optional | |
| created_at | datetime (ISO 8601) | Timestamp when the user was created. | system-assigned | immutable | |
| updated_at | datetime (ISO 8601) | Timestamp of last update. | system-assigned | system-assigned | |
| slug | string | URL-safe identifier for the user. | system-assigned | immutable | |
| email_client_configured | boolean | Whether the user has configured an email client integration. | system-assigned | read-only | |
| crm_id | string | Linked CRM (e.g., Salesforce) user ID. | optional | optional | |
| group | object | Group membership object. | optional | optional | Contains id and name. |
Core endpoints
List Users
- Method: GET
- URL:
https://api.salesloft.com/v2/users - Watch out for: Inactive users are excluded by default. Pass include_deactivated=true to include them.
Request example
GET /v2/users?page=1&per_page=25&include_paging_counts=true
Authorization: Bearer {token}
Response example
{
"data": [{"id":1,"name":"Jane Doe","email":"jane@example.com","active":true}],
"metadata": {"paging":{"per_page":25,"current_page":1,"next_page":2,"prev_page":null}}
}
Get User
- Method: GET
- URL:
https://api.salesloft.com/v2/users/{id} - Watch out for: Returns 404 if the user ID does not belong to the authenticated user's team.
Request example
GET /v2/users/12345
Authorization: Bearer {token}
Response example
{
"data": {"id":12345,"name":"Jane Doe","email":"jane@example.com","active":true,"role":{"id":2,"name":"Admin"}}
}
Get Current User (Me)
- Method: GET
- URL:
https://api.salesloft.com/v2/me - Watch out for: Useful for resolving the identity of the OAuth token owner without knowing the user ID in advance.
Request example
GET /v2/me
Authorization: Bearer {token}
Response example
{
"data": {"id":12345,"name":"Jane Doe","email":"jane@example.com","team_id":99}
}
List Team Members
- Method: GET
- URL:
https://api.salesloft.com/v2/team_members - Watch out for: team_members is a separate resource from users; it reflects team-level membership and may include additional hierarchy fields.
Request example
GET /v2/team_members?page=1&per_page=25
Authorization: Bearer {token}
Response example
{
"data": [{"id":1,"user":{"id":12345,"name":"Jane Doe"}}],
"metadata": {"paging":{"per_page":25,"current_page":1}}
}
List Groups
- Method: GET
- URL:
https://api.salesloft.com/v2/groups - Watch out for: Groups are hierarchical; parent_id references a parent group. Used for organizing users into reporting structures.
Request example
GET /v2/groups
Authorization: Bearer {token}
Response example
{
"data": [{"id":10,"name":"West Coast Team","parent_id":null}]
}
List Roles
- Method: GET
- URL:
https://api.salesloft.com/v2/roles - Watch out for: Role IDs are needed when assigning roles via SCIM or when filtering users by role.
Request example
GET /v2/roles
Authorization: Bearer {token}
Response example
{
"data": [{"id":1,"name":"User"},{"id":2,"name":"Admin"}]
}
List Pending Invitations
- Method: GET
- URL:
https://api.salesloft.com/v2/pending_email_accounts - Watch out for: User provisioning (inviting new users) is not directly supported via the REST API; use SCIM or the SalesLoft UI for user creation.
Request example
GET /v2/pending_email_accounts
Authorization: Bearer {token}
Response example
{
"data": [{"id":5,"email":"newuser@example.com","created_at":"2024-01-10T00:00:00Z"}]
}
SCIM – List Users
- Method: GET
- URL:
https://api.salesloft.com/scim/v2/Users - Watch out for: SCIM token is generated separately from OAuth tokens; it is a long-lived token configured in SalesLoft Settings > SCIM.
Request example
GET /scim/v2/Users?startIndex=1&count=25
Authorization: Bearer {scim_token}
Response example
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults":50,
"Resources":[{"id":"12345","userName":"jane@example.com","active":true}]
}
Rate limits, pagination, and events
- Rate limits: SalesLoft enforces per-minute and per-day rate limits on API requests. The official docs note a default limit of 600 requests per minute per OAuth application. Daily limits are not publicly documented with specific numbers.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: When the rate limit is exceeded, the API returns HTTP 429. The response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers per official docs. Retry-After header behavior is not explicitly documented.
- Pagination method: offset
- Default page size: 25
- Max page size: 100
- Pagination pointer: page and per_page
| Plan | Limit | Concurrent |
|---|---|---|
| Default (all plans) | 600 requests per minute | 0 |
- Webhooks available: No
- Webhook notes: SalesLoft does not offer native outbound webhooks for user lifecycle events (create, update, deactivate) via its standard REST API. Webhooks are available for cadence and activity events but not user management events.
- Alternative event strategy: Poll GET /v2/users with updated_at filtering to detect user changes, or use SCIM provisioning with an IdP (Okta, Entra ID) to push user lifecycle events into SalesLoft.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://api.salesloft.com/scim/v2
Supported operations: GET /scim/v2/Users – list users, GET /scim/v2/Users/{id} – get user, POST /scim/v2/Users – create/provision user, PUT /scim/v2/Users/{id} – replace user, PATCH /scim/v2/Users/{id} – update user attributes or deactivate, GET /scim/v2/Groups – list groups, GET /scim/v2/Groups/{id} – get group, POST /scim/v2/Groups – create group, PATCH /scim/v2/Groups/{id} – update group membership, DELETE /scim/v2/Groups/{id} – delete group
Limitations:
- Requires SSO (SAML) to be configured before SCIM can be enabled.
- Enterprise plan required; not available on lower tiers.
- SCIM token is a single long-lived token generated in SalesLoft Settings; rotation requires manual regeneration.
- User deletion via SCIM deactivates the user rather than permanently deleting the account.
- Officially documented IdP integrations are Okta and Microsoft Entra ID (Azure AD); other IdPs may work but are not officially supported.
- Google Workspace and OneLogin are not listed as officially supported IdPs.
Common scenarios
Three integration patterns cover the majority of user lifecycle use cases.
For auditing, paginate GET /v2/users with per_page=100 and include_paging_counts=true;
omit include_deactivated=true only if you intentionally want to exclude inactive accounts - leaving it out will cause headcount discrepancies against your IdP.
For provisioning, SCIM 2.0 (https://api.salesloft.com/scim/v2) is the only supported path for user creation;
it requires Enterprise plan, active SAML SSO, and a separately generated SCIM bearer token distinct from OAuth tokens.
For deprovisioning, the IdP sends PATCH /scim/v2/Users/{id} with active=false;
this deactivates the account but does not delete it or reassign owned cadences - that cleanup step remains manual.
Rate limits default to 600 requests per minute;
monitor X-RateLimit-Remaining on every response and implement exponential backoff on HTTP 429.
OAuth access tokens expire and require refresh_token rotation for long-lived integrations;
personal API keys do not expire but carry the full permissions of the generating user with no scope restrictions.
Audit all active users in a SalesLoft team
- Authenticate using a personal API key or OAuth 2.0 access token with read scope.
- GET https://api.salesloft.com/v2/users?page=1&per_page=100&include_paging_counts=true
- Read metadata.paging.next_page; if non-null, increment page and repeat until next_page is null.
- Collect all user objects; filter by active=true for active-only audit.
Watch out for: Omitting include_deactivated=true means deactivated users will not appear in results, which may cause discrepancies vs. IdP user counts.
Provision a new user via SCIM from Okta
- Confirm Enterprise plan and SSO (SAML) are active in SalesLoft.
- In SalesLoft Settings > SCIM, generate a SCIM bearer token.
- In Okta, add the SalesLoft SCIM application and configure the SCIM base URL as https://api.salesloft.com/scim/v2 with the bearer token.
- Assign the Okta user to the SalesLoft application; Okta will POST /scim/v2/Users with userName (email), name, and active=true.
- SalesLoft creates the user and sends an invitation email to complete account setup.
Watch out for: The user receives an invitation email even when provisioned via SCIM; they must accept it to activate their account. SCIM does not bypass the email verification step.
Deactivate a departed user via SCIM
- In the IdP (Okta or Entra ID), unassign or deprovision the user from the SalesLoft application.
- The IdP sends PATCH /scim/v2/Users/{id} with {"Operations":[{"op":"replace","path":"active","value":false}]}.
- SalesLoft sets the user's active flag to false, revoking login access.
- Verify deactivation by calling GET https://api.salesloft.com/v2/users/{id} and confirming active=false.
Watch out for: SCIM deactivation does not delete the user or reassign their cadences/tasks. Reassignment of owned records must be done manually in the SalesLoft UI or via separate API calls.
Why building this yourself is a trap
The most common integration trap is assuming the REST API and SCIM API share authentication context - they do not. The SCIM bearer token is a separate long-lived credential configured in SalesLoft Settings > SCIM; it is not an OAuth token and does not rotate automatically.
A second trap: GET /v2/users silently excludes deactivated users by default, which means any identity graph sync that omits include_deactivated=true will produce an incomplete user roster and miss offboarded accounts.
A third caveat specific to SCIM: even users provisioned programmatically via SCIM receive an invitation email and must accept it before their account is fully active - SCIM does not bypass email verification. Finally, pagination requires explicitly passing include_paging_counts=true to receive total count metadata;
without it, you cannot determine whether you have retrieved all records without walking every page to exhaustion.
Automate SalesLoft 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.