Summary and recommendation
The JFrog Access API (base: https://<server>/access/api/v1) is the current recommended surface for programmatic user management.
API Keys are deprecated in Artifactory 7.x - all integrations should use Bearer tokens scoped to applied-permissions/admin for user and group operations.
A parallel legacy surface exists under /artifactory/api;
JFrog recommends migrating away from it.
For identity graph use cases mapping users to groups, groups to Permission Targets, and Permission Targets to repositories
the Access API is the authoritative source, but assembling a complete access graph requires multiple sequential calls: list users, GET per-user for full detail, GET per-group for membership, and separate permission target queries.
API quick reference
| Has user API | Yes |
| Auth method | Bearer token (JFrog Access Token) or HTTP Basic Auth. API Keys are deprecated as of Artifactory 7.x. Access Tokens are the recommended method. |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise X |
Authentication
Auth method: Bearer token (JFrog Access Token) or HTTP Basic Auth. API Keys are deprecated as of Artifactory 7.x. Access Tokens are the recommended method.
Setup steps
- Log in to JFrog Platform as an admin.
- Navigate to Administration > User Management > Access Tokens.
- Click 'Generate Token', set scope (e.g., 'applied-permissions/admin' for full user management), expiry, and description.
- Copy the generated token; it is shown only once.
- Use the token as a Bearer token in the Authorization header: 'Authorization: Bearer
'. - Alternatively, use HTTP Basic Auth with username:password encoded in Base64 for legacy compatibility.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| applied-permissions/admin | Full administrative access including user and group management. | Create, update, delete users and groups; manage permissions. |
| applied-permissions/user | Standard user-level access; cannot manage other users. | Read own profile; generate own tokens. |
| member-of-groups: |
Scopes the token to permissions of a specific group. | Delegated access scoped to a group's permissions. |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| username | string | Unique login name for the user. | required | immutable (used as path param) | Case-sensitive in some contexts. |
| string | User's email address. | required | optional | Used for notifications and identity. | |
| password | string | User's password (hashed at rest). | required (unless SSO-only) | optional | Not returned in GET responses. |
| admin | boolean | Whether the user has platform admin privileges. | optional (default: false) | optional | Grants full administrative access. |
| profile_updatable | boolean | Whether the user can update their own profile. | optional (default: true) | optional | |
| internal_password_disabled | boolean | Disables internal password login; forces SSO. | optional (default: false) | optional | Relevant for SSO-enforced environments. |
| groups | array[string] | List of group names the user belongs to. | optional | optional | Replaces existing group membership on update. |
| custom_data | object | Key-value map of custom attributes. | optional | optional | Available in Access API v1. |
| status | string | Account status (e.g., 'enabled', 'disabled'). | optional | optional | Used to disable accounts without deletion. |
| realm | string | Authentication realm (e.g., 'internal', 'ldap', 'saml'). | optional | read-only after creation | Determines auth provider. |
| last_logged_in | string (ISO 8601) | Timestamp of last login. | n/a | read-only | Returned in GET responses. |
| realm_attributes | string | Additional realm-specific attributes. | optional | optional | Used for LDAP/SAML attribute mapping. |
Core endpoints
List all users
- Method: GET
- URL:
https://<server>/access/api/v1/users - Watch out for: Returns a summary list; individual GET is needed for full user details including custom_data.
Request example
GET /access/api/v1/users
Authorization: Bearer <token>
Response example
[
{"username":"jsmith","email":"jsmith@example.com","admin":false,"groups":["developers"]},
{"username":"admin","email":"admin@example.com","admin":true,"groups":[]}
]
Get a specific user
- Method: GET
- URL:
https://<server>/access/api/v1/users/{username} - Watch out for: Password field is never returned in GET responses.
Request example
GET /access/api/v1/users/jsmith
Authorization: Bearer <token>
Response example
{
"username": "jsmith",
"email": "jsmith@example.com",
"admin": false,
"profile_updatable": true,
"groups": ["developers"],
"realm": "internal"
}
Create a user
- Method: PUT
- URL:
https://<server>/access/api/v1/users/{username} - Watch out for: Uses PUT (idempotent); sending PUT to an existing username updates the user rather than erroring.
Request example
PUT /access/api/v1/users/jsmith
Content-Type: application/json
{
"email": "jsmith@example.com",
"password": "S3cur3P@ss",
"admin": false,
"groups": ["developers"]
}
Response example
{
"username": "jsmith",
"email": "jsmith@example.com",
"admin": false,
"groups": ["developers"]
}
Update a user
- Method: PATCH
- URL:
https://<server>/access/api/v1/users/{username} - Watch out for: PATCH replaces the groups array entirely; partial group updates require fetching current groups first.
Request example
PATCH /access/api/v1/users/jsmith
Content-Type: application/json
{
"email": "jsmith-new@example.com",
"groups": ["developers","qa"]
}
Response example
{
"username": "jsmith",
"email": "jsmith-new@example.com",
"groups": ["developers","qa"]
}
Delete a user
- Method: DELETE
- URL:
https://<server>/access/api/v1/users/{username} - Watch out for: Deletion is immediate and irreversible; associated tokens are also revoked.
Request example
DELETE /access/api/v1/users/jsmith
Authorization: Bearer <token>
Response example
HTTP 204 No Content
List all groups
- Method: GET
- URL:
https://<server>/access/api/v1/groups - Watch out for: Group membership details require a separate GET per group.
Request example
GET /access/api/v1/groups
Authorization: Bearer <token>
Response example
[
{"group_name": "developers", "description": "Dev team"},
{"group_name": "qa", "description": "QA team"}
]
Create or update a group
- Method: PUT
- URL:
https://<server>/access/api/v1/groups/{groupName} - Watch out for: The members array is replaced wholesale on PUT; omitting it removes all members.
Request example
PUT /access/api/v1/groups/developers
Content-Type: application/json
{
"description": "Development team",
"members": ["jsmith","adoe"]
}
Response example
{
"group_name": "developers",
"description": "Development team",
"members": ["jsmith","adoe"]
}
Create Access Token
- Method: POST
- URL:
https://<server>/access/api/v1/tokens - Watch out for: The access_token value is returned only once at creation; store it securely immediately.
Request example
POST /access/api/v1/tokens
Content-Type: application/json
{
"username": "jsmith",
"scope": "applied-permissions/user",
"expires_in": 3600
}
Response example
{
"token_id": "abc123",
"access_token": "eyJ...",
"expires_in": 3600,
"scope": "applied-permissions/user",
"token_type": "Bearer"
}
Rate limits, pagination, and events
Rate limits: JFrog does not publicly document specific numeric rate limits for the REST API in official docs. Rate limiting behavior may vary by deployment type (SaaS vs. self-managed) and plan.
Rate-limit headers: No
Retry-After header: No
Rate-limit notes: No explicit rate limit values or headers are documented in official JFrog REST API documentation as of the policy date. Self-managed instances are limited by server resources.
Pagination method: offset
Default page size: 0
Max page size: 0
Pagination pointer: offset / limit (used in some list endpoints; not uniformly documented across all user endpoints)
Webhooks available: Yes
Webhook notes: JFrog Artifactory supports webhooks for repository and artifact events. User management events (user created/deleted) are not documented as webhook triggers in official docs.
Alternative event strategy: For user lifecycle events, poll the Access API or use SCIM provisioning callbacks via your IdP.
Webhook events: artifact.deployed, artifact.deleted, artifact.moved, artifact.copied, docker.push, docker.delete, release_bundle.created, release_bundle.signed, release_bundle.deleted
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise X
Endpoint: https://
/access/api/v1/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, DELETE /Users/{id} – delete user, GET /Groups – list groups, GET /Groups/{id} – get group, POST /Groups – create group, PUT /Groups/{id} – replace group, PATCH /Groups/{id} – update group, DELETE /Groups/{id} – delete group, GET /ServiceProviderConfig, GET /Schemas
Limitations:
- Requires Enterprise X plan (SaaS or self-managed).
- SCIM token must be generated from Administration > User Management > SCIM in the JFrog Platform UI.
- SCIM provisioning manages platform-level users; repository-level permissions still require separate configuration.
- Not all SCIM attributes map 1:1 to JFrog user fields; custom attributes may require custom_data mapping.
- SCIM and direct REST API user management should not be used simultaneously for the same users to avoid conflicts.
Common scenarios
Three primary automation scenarios emerge from the available endpoints.
Provisioning a new developer requires a PUT to /access/api/v1/users/{username} with admin: false and a groups array
but the target group must exist first or the assignment may be silently ignored depending on platform version.
Offboarding without deletion uses PATCH with {"status": "disabled"} plus a token revocation call;
note that status field behavior should be validated against your specific platform version, as some versions use internal_password_disabled for SSO enforcement instead.
Bulk provisioning at Enterprise X tier uses the SCIM 2.0 endpoint at /access/api/v1/scim/v2
do not mix direct REST API writes with SCIM-managed accounts for the same users, as SCIM will overwrite manually set attributes on the next sync cycle.
Provision a new developer with group membership
- POST /access/api/v1/tokens to obtain an admin Bearer token (or use a pre-existing admin token).
- PUT /access/api/v1/users/{username} with email, password, admin:false, and groups:["developers"] to create the user.
- Verify creation with GET /access/api/v1/users/{username}.
- Optionally PUT /access/api/v1/groups/developers with updated members array to confirm group membership is reflected.
Watch out for: If the developers group does not yet exist, create it first with PUT /access/api/v1/groups/developers, otherwise the user creation will fail or the group assignment will be silently ignored depending on platform version.
Disable a user account (offboarding without deletion)
- PATCH /access/api/v1/users/{username} with body {"status": "disabled"} to disable login.
- POST /access/api/v1/tokens/revoke to revoke all active tokens for the user (use token_id or username filter).
- Optionally PATCH to remove the user from all groups by setting groups:[].
Watch out for: Status field behavior (enabled/disabled) should be verified against your specific JFrog Platform version; some versions use internal_password_disabled for SSO-only enforcement rather than a status field.
Bulk user provisioning via SCIM from an IdP (Enterprise X)
- Confirm Enterprise X plan is active on the JFrog Platform instance.
- Navigate to Administration > User Management > SCIM in the JFrog UI and generate a SCIM token.
- Configure your IdP (e.g., Okta, Azure AD) with SCIM base URL https://
/access/api/v1/scim/v2 and the generated SCIM Bearer token. - Map IdP user attributes to SCIM schema fields (userName → username, emails[0].value → email, groups → groups).
- Trigger a provisioning sync from the IdP; verify users appear in JFrog via GET /access/api/v1/scim/v2/Users.
- Test deprovisioning by removing a user in the IdP and confirming DELETE is propagated to JFrog.
Watch out for: Do not mix direct REST API user management with SCIM-provisioned users for the same accounts; SCIM will overwrite or conflict with manually set attributes on the next sync cycle.
Why building this yourself is a trap
Several non-obvious behaviors create integration risk. PUT on /users/{username} is upsert - it creates or updates silently, so create-only semantics require an existence check first. PATCH on a user replaces the groups array entirely;
partial group updates require fetching current membership before writing. Similarly, PUT on a group replaces the members array wholesale - omitting it removes all members.
Rate limits are not publicly documented for either SaaS or self-managed deployments, and no rate-limit headers are returned, so retry logic must be implemented defensively without guidance from the API itself. In HA self-managed deployments, the Access service runs on its own port (default 8082) and must be independently reachable.
Automate JFrog 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.