Summary and recommendation
Apache Superset exposes a FAB-based REST API at /api/v1 authenticated via JWT Bearer tokens obtained from POST /api/v1/security/login (provider='db' only; OAuth/LDAP users cannot use this endpoint without a separate local admin account).
The user CRUD Security API (/api/v1/security/users/*) is disabled by default and requires FAB_ADD_SECURITY_API=True in superset_config.py - without this flag, all security CRUD endpoints return 404 and are absent from Swagger. The API is explicitly marked beta in FAB documentation, meaning breaking changes are expected across minor Superset versions.
There is no native SCIM 2.0 endpoint and no webhook system for user lifecycle events. For teams needing automated provisioning across a broad identity surface, Stitchflow's MCP server with ~100 deep IT/identity integrations provides a more stable abstraction layer than building directly against Superset's beta Security API.
API quick reference
| Has user API | Yes |
| Auth method | JWT Bearer token (FAB-issued). Obtain via POST /api/v1/security/login with username, password, and provider. Pass as Authorization: Bearer <token> header on all subsequent requests. CSRF token required for state-mutating calls when using session cookies. |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Not available in open-source Apache Superset. Preset (managed commercial offering) may provide easier IdP integration but does not advertise a native SCIM 2.0 endpoint. |
Authentication
Auth method: JWT Bearer token (FAB-issued). Obtain via POST /api/v1/security/login with username, password, and provider. Pass as Authorization: Bearer
Setup steps
- Ensure Superset is running and an admin account exists (created via
superset fab create-adminor docker-init). - POST to /api/v1/security/login with JSON body {"username":"admin","password":"
","provider":"db","refresh":true} to receive access_token and refresh_token. - Include Authorization: Bearer
header on all API requests. - For state-mutating endpoints (POST/PUT/DELETE) when using session-cookie auth, also fetch a CSRF token via GET /api/v1/security/csrf_token/ and include it as X-CSRFToken header.
- To enable the user CRUD Security API (beta), add FAB_ADD_SECURITY_API = True to superset_config.py and restart Superset. Security endpoints then appear in Swagger at /swagger/v1.
- If using OAuth2/LDAP for UI login, a separate local admin account with AUTH_TYPE=AUTH_DB can still authenticate against /api/v1/security/login using provider='db' for API access.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| Admin role (FAB) | Superset uses role-based permissions managed by Flask-AppBuilder, not OAuth scopes. The calling user must hold the Admin role to perform user CRUD operations via the Security API. | All /api/v1/security/users/* operations (create, read, update, delete) |
| can_list on UserDBModelView | FAB permission auto-generated for listing users. Assigned to Admin role by default. | GET /api/v1/security/users/ |
| can_add on UserDBModelView | FAB permission auto-generated for creating users. Assigned to Admin role by default. | POST /api/v1/security/users/ |
| can_edit on UserDBModelView | FAB permission auto-generated for editing users. Assigned to Admin role by default. | PUT /api/v1/security/users/{pk} |
| can_delete on UserDBModelView | FAB permission auto-generated for deleting users. Assigned to Admin role by default. | DELETE /api/v1/security/users/{pk} |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Auto-incremented primary key for the user record. | auto-assigned | immutable | Used as {pk} in single-user endpoints. |
| username | string | Unique login identifier for the user. | required | optional | Must be unique across the instance. |
| first_name | string | User's first name. | required | optional | Mapped from given_name in OAuth flows. |
| last_name | string | User's last name. | required | optional | Mapped from family_name in OAuth flows. |
| string | User's email address. Must be unique. | required | optional | Used as identity in LDAP/OAuth auto-registration. | |
| password | string (write-only) | Plaintext password on create/update; stored as hashed value (scrypt by default). | required for AUTH_DB users | optional | Not returned in GET responses. Not applicable for LDAP/OAuth users. |
| active | boolean | Whether the user account is active and can log in. | optional (defaults to true) | optional | Set to false to disable login without deleting the account. |
| roles | array of role objects [{id, name}] | List of FAB roles assigned to the user (e.g., Admin, Alpha, Gamma, Public, sql_lab). | optional (defaults to AUTH_USER_REGISTRATION_ROLE if set) | optional | Do not alter built-in roles directly; create custom roles instead. Role IDs can be fetched from GET /api/v1/security/roles/. |
| login_count | integer | Number of successful logins. | auto (0) | system-managed | Read-only; updated by FAB on each successful authentication. |
| last_login | datetime (ISO 8601) | Timestamp of the user's most recent login. | null | system-managed | Read-only. |
| created_on | datetime (ISO 8601) | Timestamp when the user record was created. | auto | immutable | Read-only. |
| changed_on | datetime (ISO 8601) | Timestamp of the last modification to the user record. | auto | auto | Read-only; updated on any PUT. |
Core endpoints
Obtain JWT access token
- Method: POST
- URL:
/api/v1/security/login - Watch out for: provider must be 'db' for local accounts. OAuth-authenticated users cannot obtain a JWT via this endpoint without a local fallback admin account.
Request example
POST /api/v1/security/login
Content-Type: application/json
{"username":"admin","password":"admin",
"provider":"db","refresh":true}
Response example
{
"access_token": "eyJ...",
"refresh_token": "eyJ..."
}
Get CSRF token (required for mutating calls with session auth)
- Method: GET
- URL:
/api/v1/security/csrf_token/ - Watch out for: Required as X-CSRFToken header on POST/PUT/DELETE when WTF_CSRF_ENABLED=True (default). Not needed when using pure Bearer token auth with WTF_CSRF_ENABLED=False.
Request example
GET /api/v1/security/csrf_token/
Authorization: Bearer <access_token>
Response example
{
"result": "<csrf_token_string>"
}
List users (requires FAB_ADD_SECURITY_API=True)
- Method: GET
- URL:
/api/v1/security/users/ - Watch out for: Returns 403 if FAB_ADD_SECURITY_API is not set to True. Pagination params must be inside the Rison q parameter, not bare query params.
Request example
GET /api/v1/security/users/?q=(page:0,page_size:25)
Authorization: Bearer <access_token>
Response example
{
"count": 42,
"result": [
{"id":1,"username":"admin",
"first_name":"Admin","last_name":"User",
"email":"admin@example.com","active":true,
"roles":[{"id":1,"name":"Admin"}]}
]
}
Get single user by ID (requires FAB_ADD_SECURITY_API=True)
- Method: GET
- URL:
/api/v1/security/users/{pk} - Watch out for: pk is the integer user ID, not username. Requires Admin role on the calling user.
Request example
GET /api/v1/security/users/5
Authorization: Bearer <access_token>
Response example
{
"id": 5,
"result": {
"username": "jdoe",
"first_name": "Jane",
"last_name": "Doe",
"email": "jdoe@example.com",
"active": true,
"roles": [{"id":2,"name":"Alpha"}]
}
}
Create user (requires FAB_ADD_SECURITY_API=True)
- Method: POST
- URL:
/api/v1/security/users/ - Watch out for: Beta endpoint - breaking changes possible across Superset versions. Role IDs must be fetched first from GET /api/v1/security/roles/. Password field only applies to AUTH_DB users.
Request example
POST /api/v1/security/users/
Authorization: Bearer <access_token>
Content-Type: application/json
{"username":"jdoe","first_name":"Jane",
"last_name":"Doe","email":"jdoe@example.com",
"password":"SecurePass1!","active":true,
"roles":[{"id":2}]}
Response example
{
"id": 12,
"result": {
"username": "jdoe",
"email": "jdoe@example.com",
"active": true
}
}
Update user (requires FAB_ADD_SECURITY_API=True)
- Method: PUT
- URL:
/api/v1/security/users/{pk} - Watch out for: Full PUT replaces the resource; omitting roles will clear them. Use only fields you intend to change and include all required fields.
Request example
PUT /api/v1/security/users/12
Authorization: Bearer <access_token>
Content-Type: application/json
{"active":false,"roles":[{"id":3}]}
Response example
{
"id": 12,
"result": {
"active": false,
"roles": [{"id":3,"name":"Gamma"}]
}
}
Delete user (requires FAB_ADD_SECURITY_API=True)
- Method: DELETE
- URL:
/api/v1/security/users/{pk} - Watch out for: Permanently deletes the user record. No soft-delete. Consider setting active=false instead for reversible deactivation.
Request example
DELETE /api/v1/security/users/12
Authorization: Bearer <access_token>
Response example
{
"message": "OK"
}
Get current authenticated user info (always available, no flag needed)
- Method: GET
- URL:
/api/v1/me/ - Watch out for: This endpoint is part of the public API (no FAB_ADD_SECURITY_API flag required). Returns info for the token owner only.
Request example
GET /api/v1/me/
Authorization: Bearer <access_token>
Response example
{
"result": {
"username": "admin",
"first_name": "Admin",
"last_name": "User",
"email": "admin@example.com",
"roles": [{"name":"Admin"}]
}
}
Rate limits, pagination, and events
- Rate limits: Apache Superset (self-hosted, open source) does not enforce built-in API rate limits by default. Rate limiting is the operator's responsibility and must be implemented at the infrastructure layer (e.g., nginx, API gateway). Flask-Limiter is a dependency of FAB but is not pre-configured with limits in Superset's default config.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: No official rate limit documentation exists for the open-source distribution. Preset (managed Superset) may impose its own limits but is a separate commercial product.
- Pagination method: offset
- Default page size: 20
- Max page size: 100
- Pagination pointer: Pagination params (page, page_size) must be embedded inside the Rison-encoded
qquery parameter, e.g. GET /api/v1/security/users/?q=(page:0,page_size:25). Passing page/page_size as bare query params does not work reliably. FAB_API_MAX_PAGE_SIZE config key controls the server-side ceiling.
| Plan | Limit | Concurrent |
|---|---|---|
| Self-hosted (open source) | No default limit; operator-configured | 0 |
- Webhooks available: No
- Webhook notes: Apache Superset does not provide a native webhook system. There is no event-subscription mechanism for user lifecycle events (create, update, delete) in the open-source distribution.
- Alternative event strategy: Use the LogRestApi endpoints (GET /api/v1/log/) to poll audit logs for user activity. For real-time event streaming, Superset offers AsyncEventsRestApi (GET /api/v1/async_event/) via Server-Sent Events (SSE), but this covers query/chart events, not user management events.
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Not available in open-source Apache Superset. Preset (managed commercial offering) may provide easier IdP integration but does not advertise a native SCIM 2.0 endpoint.
- Endpoint: Not documented
Limitations:
- No native SCIM 2.0 support in Apache Superset.
- SSO/IdP provisioning requires a custom SecurityManager implementation.
- User auto-registration on first SSO login is supported via AUTH_USER_REGISTRATION=True, but this is JIT provisioning, not SCIM push.
- Role mapping from IdP groups to Superset roles is possible via AUTH_ROLES_MAPPING config but requires manual configuration.
Common scenarios
Three automation scenarios are supported by the API, each with meaningful caveats. First, programmatic user provisioning: POST /api/v1/security/users/ with role IDs fetched dynamically from GET /api/v1/security/roles/ - role integer IDs are not stable across reinstalls and must be resolved at runtime.
Second, soft deactivation: PUT /api/v1/security/users/{pk} with active:false - but PUT is a full replacement, so omitting any field (especially roles) will clear it; always send the complete desired user object.
Third, JIT SSO provisioning via AUTH_USER_REGISTRATION=True: users are created on first IdP login, but there is no SCIM push, no pre-provisioning, and no automatic deactivation when a user is removed from the IdP - Superset's database must be reconciled manually or via the Security API.
Pagination on list endpoints must be encoded inside the Rison q parameter (e. g.
, ? q=(page:0,page_size:25)); bare query params are silently ignored and always return the first 20 results.
Provision a new user and assign a role programmatically
- Ensure FAB_ADD_SECURITY_API=True is set in superset_config.py and Superset has been restarted.
- POST /api/v1/security/login with admin credentials (provider='db') to obtain access_token.
- GET /api/v1/security/roles/?q=(page:0,page_size:100) to retrieve available roles and their integer IDs.
- POST /api/v1/security/users/ with body {username, first_name, last_name, email, password, active:true, roles:[{id:
}]}. - Capture the returned user id for future reference.
- Optionally verify with GET /api/v1/security/users/{pk}.
Watch out for: Role IDs are integers and must be fetched dynamically - they are not stable across Superset reinstalls. The password field is only meaningful for AUTH_DB users; LDAP/OAuth users should be created without a password and will authenticate via their IdP.
Deactivate (soft-disable) a user without deleting them
- Obtain JWT access_token via POST /api/v1/security/login.
- Find the user's integer ID via GET /api/v1/security/users/?q=(filters:!((col:username,opr:eq,value:'jdoe'))).
- PUT /api/v1/security/users/{pk} with body {active:false} plus all other required fields (username, first_name, last_name, email, roles) to avoid clearing them.
- Confirm the user can no longer log in.
Watch out for: PUT is a full replacement in FAB's REST API. Omitting roles in the PUT body will clear all role assignments. Always include the full desired state of the user object, not just the changed fields.
Integrate IdP SSO with auto-provisioning (JIT, no SCIM)
- Install Authlib: pip install Authlib.
- In superset_config.py set AUTH_TYPE=AUTH_OAUTH, configure OAUTH_PROVIDERS with client_id, client_secret, api_base_url, access_token_url, authorize_url for your IdP (Okta, Entra, etc.).
- Set AUTH_USER_REGISTRATION=True and AUTH_USER_REGISTRATION_ROLE='Gamma' to auto-create users on first SSO login.
- Optionally configure AUTH_ROLES_MAPPING to map IdP group claims to Superset roles.
- Optionally set AUTH_ROLES_SYNC_AT_LOGIN=True to re-sync roles on every login.
- Run superset init to synchronize permissions.
- For API access by service accounts, maintain a separate local admin account (AUTH_DB) and use provider='db' on /api/v1/security/login.
Watch out for: This is JIT provisioning only - users are created on first login, not pushed from the IdP. There is no SCIM endpoint to pre-provision users or sync deletions. Deprovisioned IdP users will still exist in Superset's database and must be deactivated manually via the Security API or CLI.
Why building this yourself is a trap
The Security API's beta status is the primary risk for any production automation: FAB breaking changes across minor Superset versions can silently break provisioning pipelines with no deprecation notice. CSRF protection (WTF_CSRF_ENABLED=True by default) requires an additional token-fetch step on every mutating call when using session-cookie auth.
Superset does not natively validate externally-issued OIDC/JWT tokens (e.g., from Okta or Entra ID) for API calls - a custom SecurityManager subclass is required to accept external Bearer tokens, adding implementation surface. Running superset init at any point re-synchronizes all built-in role permissions to defaults, which can silently overwrite custom permission assignments.
There are no built-in rate limits (operator must configure at the infrastructure layer), no rate-limit response headers, and no retry-after signaling. Teams building lifecycle automation against this API should treat every endpoint under /api/v1/security/users/* as potentially unstable and version-pin their Superset deployments accordingly.
Automate Apache Superset 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.