Stitchflow
SaltStack logo

SaltStack User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 16, 2026

Summary and recommendation

SaltStack exposes user-adjacent operations through its Salt NetAPI (rest_cherrypy or rest_tornado), reachable at the customer's self-hosted salt-master address on port 8000 by default.

The API is not active by default;

it must be explicitly enabled in /etc/salt/master and the salt-master service restarted before any requests are possible.

Authentication is token-based via an eauth backend (PAM, LDAP, MySQL, file, or others);

POST credentials to /login to receive a session token, then pass it as X-Auth-Token on all subsequent requests.

Critically, Salt does not manage human user accounts natively.

Any identity graph built on top of SaltStack must treat the eauth backend not the Salt API

as the authoritative user store, since the API cannot create or delete OS or LDAP users.

SCIM 2.0 is not available at any tier;

Okta and OneLogin are documented as supported SSO IdPs but without SCIM provisioning connectors.

API quick reference

Has user APIYes
Auth methodToken-based (eauth). POST credentials to /login to receive a session token; pass token in X-Auth-Token header for subsequent requests. Supports multiple eauth backends: pam, ldap, file, mysql, and others configured in master config.
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredEnterprise

Authentication

Auth method: Token-based (eauth). POST credentials to /login to receive a session token; pass token in X-Auth-Token header for subsequent requests. Supports multiple eauth backends: pam, ldap, file, mysql, and others configured in master config.

Setup steps

  1. Enable a NetAPI module (rest_cherrypy or rest_tornado) in /etc/salt/master under 'rest_cherrypy:' or 'rest_tornado:' stanza.
  2. Configure 'external_auth' in /etc/salt/master, specifying eauth backend (e.g., pam, ldap) and per-user or per-group permissions.
  3. Restart the salt-master service to apply configuration changes.
  4. POST to /login with username, password, and eauth backend name to receive a token.
  5. Include the returned token in the X-Auth-Token header for all subsequent API requests.

Required scopes

Scope Description Required for
eauth permission entry (e.g., '.*' or specific module.function) Salt uses eauth permission lists in master config rather than OAuth scopes. Each user or group is granted access to specific Salt modules, functions, or minion targets. All API operations; granularity is per-function (e.g., 'test.ping', 'state.*', '@wheel', '@runner')
@wheel Grants access to Salt Wheel modules, which include user/key management operations. User and key management via /wheel endpoint
@runner Grants access to Salt Runner modules for orchestration and reporting. Runner-based operations via /run or /hook endpoints

User object / data model

Field Type Description On create On update Notes
username string The user's login name as defined in the eauth backend (e.g., PAM, LDAP). required immutable Managed in the underlying eauth backend, not directly via Salt API.
password string User password, validated against the configured eauth backend. required backend-managed Never stored by Salt itself; delegated to eauth backend.
eauth string The eauth backend to authenticate against (e.g., 'pam', 'ldap', 'file'). required n/a Must match a configured backend in master's external_auth config.
token string Session token returned after successful /login. Used as X-Auth-Token. server-generated n/a Token TTL is controlled by 'token_expire' in master config (default 43200 seconds / 12 hours).
expire integer (unix timestamp) Token expiration time returned in /login response. server-generated n/a Configurable via 'token_expire' in /etc/salt/master.
perms array List of Salt functions/modules the user is permitted to call, as resolved from eauth config. server-resolved n/a Defined in master config external_auth block, not editable via API.
start integer (unix timestamp) Token creation time returned in /login response. server-generated n/a Informational only.
user string Username echoed back in /login response and token introspection. server-generated n/a Read-only in API responses.

Core endpoints

Authenticate / Create Session Token

  • Method: POST
  • URL: https://<salt-master>:8000/login
  • Watch out for: Token TTL defaults to 12 hours. Tokens are not revocable via API; logout via /logout invalidates the server-side session.

Request example

POST /login
Content-Type: application/json

{"username": "saltadmin", "password": "secret", "eauth": "pam"}

Response example

{
  "return": [{
    "token": "d4e5f6...",
    "expire": 1700000000,
    "start": 1699956800,
    "user": "saltadmin",
    "eauth": "pam",
    "perms": [".*"]
  }]
}

Logout / Invalidate Session Token

  • Method: POST
  • URL: https://<salt-master>:8000/logout
  • Watch out for: Only invalidates the token used in the request; no bulk token revocation endpoint exists.

Request example

POST /logout
X-Auth-Token: d4e5f6...

Response example

{
  "return": "Your token has been cleared"
}

List Minion Keys (user-adjacent: key management)

  • Method: GET
  • URL: https://<salt-master>:8000/keys
  • Watch out for: Requires '@wheel' permission in eauth config. This manages minion keys, not human user accounts.

Request example

GET /keys
X-Auth-Token: d4e5f6...

Response example

{
  "return": {
    "minions": ["web01", "db01"],
    "minions_pre": [],
    "minions_rejected": []
  }
}

Run Wheel Function (e.g., user management via wheel modules)

  • Method: POST
  • URL: https://<salt-master>:8000/wheel
  • Watch out for: Wheel functions run synchronously on the master. Requires '@wheel' eauth permission.

Request example

POST /wheel
X-Auth-Token: d4e5f6...
Content-Type: application/json

{"fun": "key.list_all"}

Response example

{
  "return": [{
    "tag": "salt/wheel/...",
    "data": {
      "return": {"minions": ["web01"]}
    }
  }]
}

Execute Salt Function on Minions

  • Method: POST
  • URL: https://<salt-master>:8000/
  • Watch out for: Default client is 'local' (async). Use 'local_async' for fire-and-forget or 'local_batch' for batched execution.

Request example

POST /
X-Auth-Token: d4e5f6...
Content-Type: application/json

{"client": "local", "tgt": "*", "fun": "test.ping"}

Response example

{
  "return": [{"web01": true, "db01": true}]
}

List Jobs

  • Method: GET
  • URL: https://<salt-master>:8000/jobs
  • Watch out for: Job history retention is controlled by master's 'keep_jobs' setting (default 24 hours).

Request example

GET /jobs
X-Auth-Token: d4e5f6...

Response example

{
  "return": [{
    "20231115120000000001": {
      "Function": "test.ping",
      "Target": "*"
    }
  }]
}

Get Job Result

  • Method: GET
  • URL: https://<salt-master>:8000/jobs/<jid>
  • Watch out for: Returns empty if job has not yet completed or results have expired.

Request example

GET /jobs/20231115120000000001
X-Auth-Token: d4e5f6...

Response example

{
  "return": [{
    "web01": {"retcode": 0, "return": true}
  }]
}

Run Runner Function

  • Method: POST
  • URL: https://<salt-master>:8000/run
  • Watch out for: Requires '@runner' eauth permission. Runner functions execute on the master and may be long-running.

Request example

POST /run
X-Auth-Token: d4e5f6...
Content-Type: application/json

{"client": "runner", "fun": "manage.status"}

Response example

{
  "return": [{
    "up": ["web01"],
    "down": []
  }]
}

Rate limits, pagination, and events

  • Rate limits: No official rate-limit documentation found for Salt NetAPI. Rate limiting is not a built-in feature of rest_cherrypy or rest_tornado as documented; operators may implement it at the reverse-proxy layer.

  • Rate-limit headers: No

  • Retry-After header: No

  • Rate-limit notes: No rate-limit headers or Retry-After behavior documented in official Salt NetAPI sources.

  • Pagination method: none

  • Default page size: 0

  • Max page size: 0

  • Pagination pointer: Not documented

  • Webhooks available: Yes

  • Webhook notes: Salt supports an event-driven webhook mechanism via the /hook endpoint in rest_cherrypy. External systems can POST to /hook/ to inject events into the Salt event bus. Salt can also fire events outbound via Reactor system or salt-api event stream (GET /events as SSE stream).

  • Alternative event strategy: Use GET /events (Server-Sent Events stream) to consume the Salt event bus in real time from external systems.

  • Webhook events: salt/auth (minion authentication events), salt/job//ret (job return events), salt/key (key management events), salt/minion//start (minion start events), Custom tags via /hook/

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: Enterprise
  • Endpoint: Not documented

Limitations:

  • Salt (VMware Tanzu Salt / Broadcom) does not expose a native SCIM 2.0 endpoint.
  • User provisioning is delegated to the configured eauth backend (LDAP, PAM, etc.).
  • SSO integration (SAML/LDAP) is available in enterprise configurations but does not constitute SCIM provisioning.
  • Okta and OneLogin are documented as supported IdPs for SSO, but SCIM provisioning connectors are not officially documented.

Common scenarios

Three integration patterns are well-supported by the NetAPI.

For remote command execution, POST to /login for a token, then POST to / with client, tgt, fun, and arg fields;

use local_async and poll /jobs/ for commands that may exceed the synchronous timeout.

For LDAP-based access control, configure external_auth in /etc/salt/master with group/user permission mappings, restart the master, and let users authenticate with eauth: 'ldap'

permission changes in master config require a master restart or SIGHUP, but LDAP group membership changes take effect on next login without restart.

For event-driven automation, open a persistent GET /events request with Accept: text/event-stream to consume the Salt event bus as a Server-Sent Events stream.

Implement reconnect logic with exponential backoff;

the stream closes on master restart or token expiry.

The /hook/ endpoint allows external systems to inject events into the Salt event bus for bidirectional integration.

Authenticate and execute a remote command via REST API

  1. POST /login with {username, password, eauth} to obtain a session token.
  2. Store the returned 'token' value.
  3. POST / with X-Auth-Token header, body: {client: 'local', tgt: '', fun: '<module.function>', arg: [...]}.
  4. Parse the 'return' array in the response for per-minion results.

Watch out for: If using 'local' client, the response is synchronous only if the minion responds within the timeout. Use 'local_async' and poll /jobs/ for long-running commands.

Integrate LDAP-based user access control

  1. Configure 'external_auth' in /etc/salt/master with 'ldap:' backend and group/user permission mappings.
  2. Restart salt-master to apply.
  3. Users POST to /login with eauth: 'ldap' and their LDAP credentials.
  4. Salt validates credentials against LDAP and returns a token with permissions resolved from LDAP group membership.
  5. No API call is needed to add/remove users; manage access by modifying LDAP group membership and master config.

Watch out for: Permission changes in master config require a master restart or SIGHUP; LDAP group membership changes take effect on next login without restart.

Monitor Salt events via SSE stream for automation triggers

  1. POST /login to obtain a session token.
  2. Open a long-lived GET /events request with X-Auth-Token header and Accept: text/event-stream.
  3. Parse incoming SSE events for relevant tags (e.g., 'salt/job/*/ret', 'salt/auth').
  4. Trigger downstream automation based on event tag and data payload.

Watch out for: The /events stream is a persistent HTTP connection. Implement reconnect logic with exponential backoff; the stream will close if the salt-master restarts or the token expires.

Why building this yourself is a trap

Several non-obvious constraints will affect integration reliability. Token TTL is global (token_expire in master config); per-user token lifetimes are not supported, and tokens cannot be bulk-revoked - only the specific token passed to /logout is invalidated.

eauth permissions are defined in static master config files, not via API, meaning permission changes always require a master restart or SIGHUP and cannot be applied programmatically at runtime.

The /events SSE endpoint holds a persistent HTTP connection and is not suitable for short-lived HTTP clients without explicit timeout and reconnect handling. No rate limiting is built into rest_cherrypy or rest_tornado; operators must implement it at the reverse-proxy layer.

The API has no pagination support, and job history retention defaults to 24 hours (keep_jobs in master config), so polling /jobs/ for results must happen within that window. There is no official public SaaS base URL; every deployment uses a customer-specific salt-master host and port, which complicates any standardized identity graph connector targeting SaltStack environments.

Automate SaltStack 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.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 16, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste