Summary and recommendation
Contrast Security exposes a REST API at `https://app.contrastsecurity.com/Contrast/api/ng/{orgUuid}` for full user lifecycle management.
Authentication requires two headers on every request: `Authorization` (Base64-encoded `username:service-key`) and `API-Key` (your API key from User Settings > Your Account).
Omitting either header returns a 401 with no additional detail.
All org-scoped endpoints require the `orgUuid` path parameter, which is the organization's UUID-not its display name-retrieved from Organization Settings > General.
SuperAdmin endpoints use a separate base path (`/Contrast/api/ng/superadmin/`) and require system-level credentials;
these are only relevant for self-hosted (EOP) deployments.
API quick reference
| Has user API | Yes |
| Auth method | API Key + Service Key (HTTP Basic variant): Authorization header encodes username:service-key in Base64; API key passed as a separate header (API-Key). |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Enterprise |
Authentication
Auth method: API Key + Service Key (HTTP Basic variant): Authorization header encodes username:service-key in Base64; API key passed as a separate header (API-Key).
Setup steps
- Log in to Contrast Security and navigate to User Settings > Your Account.
- Copy your API Key and Service Key from the 'Your Keys' section.
- Obtain your Organization UUID from Organization Settings > General.
- For each request, set the 'Authorization' header to Base64-encoded 'username:service-key'.
- Set the 'API-Key' header to your API Key.
- Set 'Accept: application/json' and 'Content-Type: application/json' headers.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| Organization Admin | Required to create, update, deactivate, or delete users within an organization. | POST/PUT/DELETE user endpoints |
| View (Read) | Minimum role to list or retrieve user details. | GET user endpoints |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| user_id | string | Unique identifier for the user. | system-assigned | immutable | Used in URL path for user-specific operations. |
| username | string | User's login email address. | required | read-only after creation | Must be a valid email. |
| first_name | string | User's first name. | required | optional | |
| last_name | string | User's last name. | required | optional | |
| string | Contact email; typically same as username. | required | optional | ||
| enabled | boolean | Whether the user account is active. | defaults to true | optional | Set to false to deactivate. |
| role | string (enum) | Organization-level role: ROLE_ADMIN, ROLE_EDIT, ROLE_RULES_ADMIN, ROLE_VIEW, ROLE_NO_ACCESS. | required | optional | Controls permissions within the organization. |
| groups | array | Access groups the user belongs to. | optional | optional | Group membership controls application-level access. |
| date_created | timestamp (epoch ms) | When the user was created. | system-assigned | immutable | |
| last_login | timestamp (epoch ms) | Timestamp of most recent login. | system-assigned | system-managed | |
| org_uuid | string | UUID of the organization the user belongs to. | derived from endpoint path | immutable | |
| api_only | boolean | Indicates a service/API-only account with no UI access. | optional | optional | |
| protect_enabled | boolean | Whether Contrast Protect features are enabled for this user. | optional | optional |
Core endpoints
List organization users
- Method: GET
- URL:
https://app.contrastsecurity.com/Contrast/api/ng/{orgUuid}/users - Watch out for: orgUuid is required in the path; using the wrong org UUID returns an empty or unauthorized response.
Request example
GET /Contrast/api/ng/{orgUuid}/users?offset=0&limit=25
Authorization: Basic <base64(user:service-key)>
API-Key: <api-key>
Accept: application/json
Response example
{
"success": true,
"users": [
{"user_id":"abc123","username":"user@example.com","first_name":"Jane","last_name":"Doe","enabled":true,"role":"ROLE_EDIT"}
],
"count": 1
}
Get single user
- Method: GET
- URL:
https://app.contrastsecurity.com/Contrast/api/ng/{orgUuid}/users/{userId} - Watch out for: userId is the internal UUID, not the email address.
Request example
GET /Contrast/api/ng/{orgUuid}/users/{userId}
Authorization: Basic <base64(user:service-key)>
API-Key: <api-key>
Response example
{
"success": true,
"user": {
"user_id":"abc123","username":"user@example.com","first_name":"Jane","last_name":"Doe","enabled":true,"role":"ROLE_EDIT"
}
}
Create (invite) user
- Method: POST
- URL:
https://app.contrastsecurity.com/Contrast/api/ng/{orgUuid}/users - Watch out for: Creating a user sends an invitation email; the account is not active until the user accepts. Requires Organization Admin role.
Request example
POST /Contrast/api/ng/{orgUuid}/users
Content-Type: application/json
{"first_name":"Jane","last_name":"Doe","email":"user@example.com","role":"ROLE_EDIT"}
Response example
{
"success": true,
"user": {
"user_id":"abc123","username":"user@example.com","enabled":true
}
}
Update user
- Method: PUT
- URL:
https://app.contrastsecurity.com/Contrast/api/ng/{orgUuid}/users/{userId} - Watch out for: Username/email cannot be changed after account creation via API.
Request example
PUT /Contrast/api/ng/{orgUuid}/users/{userId}
Content-Type: application/json
{"first_name":"Jane","last_name":"Smith","role":"ROLE_ADMIN"}
Response example
{
"success": true,
"user": {
"user_id":"abc123","role":"ROLE_ADMIN"
}
}
Delete user
- Method: DELETE
- URL:
https://app.contrastsecurity.com/Contrast/api/ng/{orgUuid}/users/{userId} - Watch out for: Deletion is permanent; consider disabling (enabled: false) instead for audit trail preservation.
Request example
DELETE /Contrast/api/ng/{orgUuid}/users/{userId}
Authorization: Basic <base64(user:service-key)>
API-Key: <api-key>
Response example
{
"success": true,
"messages": ["User deleted successfully."]
}
Get current user (self)
- Method: GET
- URL:
https://app.contrastsecurity.com/Contrast/api/ng/profile - Watch out for: This endpoint does not require orgUuid; it returns the authenticated user's profile.
Request example
GET /Contrast/api/ng/profile
Authorization: Basic <base64(user:service-key)>
API-Key: <api-key>
Response example
{
"success": true,
"user": {
"username":"user@example.com","first_name":"Jane","last_name":"Doe"
}
}
List organization roles
- Method: GET
- URL:
https://app.contrastsecurity.com/Contrast/api/ng/{orgUuid}/roles - Watch out for: Role names are case-sensitive strings; use exact values when assigning roles to users.
Request example
GET /Contrast/api/ng/{orgUuid}/roles
Authorization: Basic <base64(user:service-key)>
API-Key: <api-key>
Response example
{
"success": true,
"roles": [
{"name":"ROLE_ADMIN","description":"Organization Administrator"},
{"name":"ROLE_EDIT","description":"Editor"}
]
}
Update user group membership
- Method: PUT
- URL:
https://app.contrastsecurity.com/Contrast/api/ng/{orgUuid}/groups/{groupId}/users - Watch out for: Group membership controls application-level access; org-level role and group membership are separate concerns.
Request example
PUT /Contrast/api/ng/{orgUuid}/groups/{groupId}/users
Content-Type: application/json
{"users":[{"user_id":"abc123"}]}
Response example
{
"success": true,
"messages": ["Group membership updated."]
}
Rate limits, pagination, and events
Rate limits: Official documentation does not publish specific rate limit thresholds or tier-based limits.
Rate-limit headers: No
Retry-After header: No
Rate-limit notes: No rate limit values, headers, or Retry-After behavior documented in official sources as of research date.
Pagination method: offset
Default page size: 25
Max page size: Not documented
Pagination pointer: offset / limit (query parameters)
Webhooks available: No
Webhook notes: Official Contrast Security documentation does not describe a native webhook system for user lifecycle events.
Alternative event strategy: Poll the /users endpoint periodically, or use SAML SSO with JIT provisioning for automated user creation on login.
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Enterprise
- Endpoint: Not documented
Limitations:
- No native SCIM 2.0 endpoint is documented by Contrast Security.
- User provisioning via SSO uses SAML 2.0 with Just-In-Time (JIT) provisioning, not SCIM.
- Okta and OneLogin integrations use SAML-based provisioning; no SCIM connector is published in official docs.
- Automated deprovisioning must be handled via the REST API or manual admin action.
Common scenarios
Three scenarios cover the majority of automation use cases.
First, provisioning: POST to /users with first_name, last_name, email, and a role enum (e.g., ROLE_EDIT);
the API immediately sends an invitation email-there is no silent provisioning option.
Capture the returned user_id, then PUT to /groups/{groupId}/users to assign Access Group membership, which controls application-level access.
Note that group membership can be set before the user accepts the invitation, but access is not effective until acceptance.
Second, deprovisioning: GET /users to resolve the user's internal UUID by email, then PUT to /users/{userId} with {"enabled": false} to deactivate without deleting.
Use DELETE only when permanent removal is explicitly required, as it destroys all associated audit records.
Third, access auditing: paginate GET /users with offset and limit query parameters (default page size 25, no documented maximum), retrieve all pages client-side, then filter on last_login, enabled, and role-the API provides no server-side filtering by these fields.
Building an identity graph from this data means joining user_id, org_uuid, groups, role, enabled, and last_login across all paginated results to produce a complete picture of who has access to what and when they last used it.
Provision a new user and assign to an access group
- POST to /users with first_name, last_name, email, and role to create the user (invitation email sent automatically).
- Capture the returned user_id from the response.
- GET /groups to retrieve the target group's groupId.
- PUT to /groups/{groupId}/users with the user_id to add the user to the appropriate access group.
Watch out for: The user account is not active until the invitation email is accepted; group membership can be set before acceptance but access is not effective until then.
Deactivate a departed employee
- GET /users to find the user by email and retrieve their user_id.
- PUT to /users/{userId} with {"enabled": false} to deactivate the account without deleting it.
- Optionally, remove the user from all groups via PUT /groups/{groupId}/users to revoke application access immediately.
Watch out for: Disabling via 'enabled: false' preserves audit history; DELETE is permanent and removes all associated records.
Bulk-list and audit all organization users
- GET /users?offset=0&limit=25 to retrieve the first page; capture the total 'count' from the response.
- Iterate with incrementing offset values (offset=25, 50, …) until all users are retrieved.
- For each user, inspect 'last_login', 'enabled', and 'role' fields to identify stale or over-privileged accounts.
Watch out for: There is no server-side filtering by role or enabled status; all filtering must be done client-side after retrieval.
Why building this yourself is a trap
The most significant architectural caveat is the absence of native SCIM 2.0: there is no published SCIM endpoint, and IdP integrations (Okta, OneLogin) use SAML with JIT provisioning, which only fires on first login and does not propagate deprovisioning events. Any automated offboarding must be driven by the REST API.
Rate limits are undocumented-no thresholds, no rate-limit headers, and no Retry-After behavior are described in official sources as of the research date; build in conservative retry logic with exponential backoff. Timestamps throughout the API use epoch milliseconds, not ISO 8601, which requires explicit conversion in any downstream system.
Role values are fixed enum strings (ROLE_ADMIN, ROLE_EDIT, ROLE_RULES_ADMIN, ROLE_VIEW, ROLE_NO_ACCESS); the API returns a 400 on invalid values with no suggestion of valid options. Finally, username and email cannot be updated after account creation via the API, so identity corrections require deactivating the old account and creating a new one.
Automate Contrast Security 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.