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 API | Yes |
| 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. |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Enterprise |
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
- Enable a NetAPI module (rest_cherrypy or rest_tornado) in /etc/salt/master under 'rest_cherrypy:' or 'rest_tornado:' stanza.
- Configure 'external_auth' in /etc/salt/master, specifying eauth backend (e.g., pam, ldap) and per-user or per-group permissions.
- Restart the salt-master service to apply configuration changes.
- POST to /login with username, password, and eauth backend name to receive a token.
- 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 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/
Authenticate and execute a remote command via REST API
- POST /login with {username, password, eauth} to obtain a session token.
- Store the returned 'token' value.
- POST / with X-Auth-Token header, body: {client: 'local', tgt: '
', fun: '<module.function>', arg: [...]}. - 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/
Integrate LDAP-based user access control
- Configure 'external_auth' in /etc/salt/master with 'ldap:' backend and group/user permission mappings.
- Restart salt-master to apply.
- Users POST to /login with eauth: 'ldap' and their LDAP credentials.
- Salt validates credentials against LDAP and returns a token with permissions resolved from LDAP group membership.
- 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
- POST /login to obtain a session token.
- Open a long-lived GET /events request with X-Auth-Token header and Accept: text/event-stream.
- Parse incoming SSE events for relevant tags (e.g., 'salt/job/*/ret', 'salt/auth').
- 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/
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.