Summary and recommendation
dbt Cloud exposes a REST API at `https://cloud.getdbt.com/api/v2` and a SCIM 2.0 endpoint at `https://cloud.getdbt.com/api/scim/v2/` (Enterprise only). Authentication uses `Authorization: Token <value>` - not `Bearer`; using `Bearer` returns 401. Service Account Tokens with Account Admin permissions are required for any user or group write operation.
A v3 API exists for some newer endpoints but carries partial beta status; verify stability in the official docs before building against it. Stitchflow connects to dbt Cloud via these APIs as part of an identity graph spanning 60+ deep IT and identity integrations, enabling cross-system access visibility without manual reconciliation.
API quick reference
| Has user API | Yes |
| Auth method | Bearer token (personal user token or service account token) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: Bearer token (personal user token or service account token)
Setup steps
- Log in to dbt Cloud and navigate to Account Settings > API Tokens.
- For personal access: generate a User API Token under your profile.
- For service accounts: create a Service Account Token under Account Settings > Service Tokens, assign required permissions (e.g., Account Admin or Member).
- Pass the token in the Authorization header: 'Authorization: Token
'. - For SCIM provisioning, generate a dedicated Service Account Token with Account Admin permissions and supply it to your IdP.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| Account Admin | Full administrative access to the account including user, group, and permission management. | Creating/updating/deleting users, managing groups, SCIM provisioning |
| Member | Read access to account resources; can list users and projects. | Listing users and account metadata (read-only) |
| Job Admin | Manage jobs and runs; no user management access. | Job and run operations only |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique user identifier. | system-assigned | immutable | Used in all user-specific endpoint paths. |
| string | User's email address. | required | supported | Must be unique within the account. | |
| first_name | string | User's first name. | optional | supported | |
| last_name | string | User's last name. | optional | supported | |
| username | string | dbt Cloud username (often matches email). | optional | supported | |
| is_active | boolean | Whether the user account is active. | defaults to true | supported | Set to false to deactivate without deleting. |
| account_id | integer | The dbt Cloud account the user belongs to. | required (path param) | immutable | |
| created_at | datetime | Timestamp of user creation. | system-assigned | immutable | ISO 8601 format. |
| updated_at | datetime | Timestamp of last update. | system-assigned | system-assigned | |
| groups | array | List of group IDs the user belongs to. | optional | supported via group endpoints | Group membership controls project permissions. |
| permissions | array | Permission sets assigned to the user. | optional | supported | Includes account-level and project-level permission sets. |
| license_type | string | License type: 'developer', 'read_only', or 'it'. | optional | supported | Affects seat billing. 'it' = IT/admin license. |
| sso_id | string | External SSO identifier for the user. | set by IdP via SCIM | managed by IdP | Populated when SCIM/SSO is enabled. |
| auth_provider | string | Authentication provider (e.g., 'saml', 'password'). | system-assigned | immutable |
Core endpoints
List users in account
- Method: GET
- URL:
https://cloud.getdbt.com/api/v2/accounts/{account_id}/users/ - Watch out for: Pagination uses offset+limit; max 100 per page. Iterate using total_count from extra.pagination.
Request example
GET /api/v2/accounts/12345/users/?limit=100&offset=0
Authorization: Token <service_token>
Response example
{
"status": {"code": 200},
"data": [
{"id": 1, "email": "user@example.com",
"first_name": "Jane", "is_active": true,
"license_type": "developer"}
],
"extra": {"pagination": {"count": 1, "total_count": 1}}
}
Get single user
- Method: GET
- URL:
https://cloud.getdbt.com/api/v2/accounts/{account_id}/users/{user_id}/ - Watch out for: User ID must be the dbt Cloud internal integer ID, not email.
Request example
GET /api/v2/accounts/12345/users/67/
Authorization: Token <service_token>
Response example
{
"status": {"code": 200},
"data": {
"id": 67, "email": "user@example.com",
"first_name": "Jane", "last_name": "Doe",
"is_active": true, "license_type": "developer"
}
}
Invite/create user
- Method: POST
- URL:
https://cloud.getdbt.com/api/v2/accounts/{account_id}/invitations/ - Watch out for: Users are invited via email; they must accept before becoming active. SCIM bypasses this flow for SSO-enabled accounts.
Request example
POST /api/v2/accounts/12345/invitations/
Authorization: Token <service_token>
{"email": "newuser@example.com",
"license_type": "developer",
"groups": [101, 102]}
Response example
{
"status": {"code": 201},
"data": {
"id": 89, "email": "newuser@example.com",
"state": "pending"
}
}
Update user (license/permissions)
- Method: POST
- URL:
https://cloud.getdbt.com/api/v2/accounts/{account_id}/users/{user_id}/ - Watch out for: dbt Cloud v2 uses POST (not PATCH) for updates on user objects. Omitting a field may reset it; send all desired values.
Request example
POST /api/v2/accounts/12345/users/67/
Authorization: Token <service_token>
{"license_type": "read_only",
"groups": [101]}
Response example
{
"status": {"code": 200},
"data": {"id": 67, "license_type": "read_only",
"groups": [101]}
}
Deactivate/delete user
- Method: DELETE
- URL:
https://cloud.getdbt.com/api/v2/accounts/{account_id}/users/{user_id}/ - Watch out for: DELETE deactivates the user rather than permanently removing them from the system. The user record is retained.
Request example
DELETE /api/v2/accounts/12345/users/67/
Authorization: Token <service_token>
Response example
{
"status": {"code": 200},
"data": {"id": 67, "is_active": false}
}
List groups
- Method: GET
- URL:
https://cloud.getdbt.com/api/v2/accounts/{account_id}/groups/ - Watch out for: Groups control project-level permissions. Users must be assigned to groups to inherit project access.
Request example
GET /api/v2/accounts/12345/groups/
Authorization: Token <service_token>
Response example
{
"status": {"code": 200},
"data": [
{"id": 101, "name": "Analysts",
"account_id": 12345,
"assign_by_default": false}
]
}
Assign user to group
- Method: POST
- URL:
https://cloud.getdbt.com/api/v2/accounts/{account_id}/groups/{group_id}/ - Watch out for: Sending the users array replaces existing membership; include all desired user IDs to avoid unintended removals.
Request example
POST /api/v2/accounts/12345/groups/101/
Authorization: Token <service_token>
{"users": [67, 68]}
Response example
{
"status": {"code": 200},
"data": {"id": 101, "name": "Analysts",
"users": [67, 68]}
}
List permission sets
- Method: GET
- URL:
https://cloud.getdbt.com/api/v2/accounts/{account_id}/permissions/ - Watch out for: Permission sets are account-scoped or project-scoped. Project-scoped permissions require specifying project_id in the assignment.
Request example
GET /api/v2/accounts/12345/permissions/
Authorization: Token <service_token>
Response example
{
"status": {"code": 200},
"data": [
{"id": 5, "name": "Account Admin",
"state": 1, "account_id": 12345}
]
}
Rate limits, pagination, and events
- Rate limits: dbt Cloud enforces rate limits on API requests. Official documentation states limits vary by plan but does not publish exact per-minute numbers publicly. Requests exceeding limits receive HTTP 429 responses.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: HTTP 429 is returned when rate limited. Retry-After header is included. Exact limits are not published in official docs; contact dbt Labs support for Enterprise-specific limits.
- Pagination method: offset
- Default page size: 100
- Max page size: 100
- Pagination pointer: offset and limit
| Plan | Limit | Concurrent |
|---|---|---|
| Developer (Free) | Not publicly specified | 0 |
| Team | Not publicly specified | 0 |
| Enterprise | Not publicly specified; higher limits negotiable | 0 |
- Webhooks available: No
- Webhook notes: dbt Cloud supports outbound webhooks for job run events (run started, completed, errored) but does not offer webhooks for user management events such as user creation, deactivation, or group changes.
- Alternative event strategy: Poll the /users/ endpoint periodically to detect user changes, or rely on SCIM provisioning events from your IdP for user lifecycle management.
- Webhook events: job.run.started, job.run.completed, job.run.errored
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://cloud.getdbt.com/api/scim/v2/
Supported operations: GET /Users – list users, GET /Users/{id} – get user, POST /Users – provision user, PATCH /Users/{id} – update user attributes or deactivate, DELETE /Users/{id} – deprovision user, GET /Groups – list groups, POST /Groups – create group, PATCH /Groups/{id} – update group membership, DELETE /Groups/{id} – delete group
Limitations:
- Requires Enterprise plan and SSO (SAML 2.0) to be configured before SCIM can be enabled.
- Supported IdPs: Okta and Microsoft Entra ID (Azure AD). Google Workspace and OneLogin are not officially supported.
- SCIM token must be a Service Account Token with Account Admin permissions.
- Group push from IdP maps to dbt Cloud permission groups; group names must match exactly.
- SCIM does not support provisioning project-level permission assignments directly; those must be configured in dbt Cloud after group creation.
- User deprovisioning via SCIM deactivates the user (sets active=false) but does not permanently delete the record.
Common scenarios
Three automation patterns cover the majority of dbt Cloud identity lifecycle work via API. For provisioning individual developers, POST to /api/v2/accounts/{account_id}/invitations/ with license_type, email, and target group IDs - but note the user must accept an email invitation before their account activates.
fully silent provisioning requires SCIM on an SSO-enabled Enterprise account. For SCIM-based lifecycle automation via Okta or Microsoft Entra ID, point the IdP SCIM connector at https://cloud.getdbt.com/api/scim/v2/ using a Service Account Token.
group names must match dbt Cloud permission group names exactly (case-sensitive) or the IdP will create duplicate groups instead of mapping to existing ones.
For bulk deactivation via REST, paginate GET /users/ using offset and limit (max 100 per page), then issue DELETE /users/{user_id}/ per target - this sets is_active=false but retains the user record. for SCIM-managed accounts, always deprovision through the IdP to keep SCIM state consistent.
Provision a new developer user via REST API
- Generate a Service Account Token with Account Admin permissions in dbt Cloud Account Settings.
- POST to /api/v2/accounts/{account_id}/invitations/ with email, license_type='developer', and desired group IDs.
- User receives an email invitation and must accept to activate their account.
- After acceptance, GET /api/v2/accounts/{account_id}/users/ filtered by email to retrieve the internal user ID.
- Optionally POST to /api/v2/accounts/{account_id}/groups/{group_id}/ to confirm group membership with the full user list.
Watch out for: The invitation flow requires user action; for fully automated provisioning without email confirmation, use SCIM with an SSO-enabled Enterprise account.
Automate user lifecycle with SCIM via Okta
- Confirm Enterprise plan is active and SAML 2.0 SSO is configured and tested in dbt Cloud.
- In dbt Cloud Account Settings > SCIM, generate a SCIM Service Account Token.
- In Okta, add the dbt Cloud application from the Okta Integration Network.
- Configure SCIM provisioning in Okta: set SCIM connector base URL to https://cloud.getdbt.com/api/scim/v2/, set auth to Bearer token using the SCIM token.
- Enable provisioning features: Push New Users, Push Profile Updates, Push Groups, Deactivate Users.
- Assign Okta users/groups to the dbt Cloud app; Okta will POST /Users to provision them.
- Create matching groups in Okta and push them; group names must match dbt Cloud permission group names exactly.
- Test by assigning a user in Okta and verifying they appear as active in dbt Cloud.
Watch out for: Group names are case-sensitive and must match exactly between Okta and dbt Cloud. Mismatches result in new groups being created rather than mapped to existing ones.
Bulk deactivate users via REST API
- GET /api/v2/accounts/{account_id}/users/?limit=100&offset=0 and paginate through all pages to build a full user list.
- Filter the list for users to deactivate (e.g., by email domain or last login date – note: last_login is not exposed in v2; cross-reference with IdP data).
- For each target user, send DELETE /api/v2/accounts/{account_id}/users/{user_id}/.
- Verify deactivation by checking is_active=false in a subsequent GET for each user.
- Implement retry logic with exponential backoff for any HTTP 429 responses.
Watch out for: DELETE sets is_active=false but retains the user record. If the user is re-invited later, they may reactivate the same record. For SCIM-managed accounts, perform deactivation through the IdP to keep SCIM state consistent.
Why building this yourself is a trap
Several non-obvious behaviors create integration risk. The v2 API uses POST for both create and update on user objects - there is no PATCH on the v2 user endpoint, and omitting a field in an update payload may reset it to a default value; always send the full desired state.
Sending a partial users array to a group endpoint replaces the entire membership list, making partial group updates destructive by default. The last_login field is not exposed in the v2 user object, so identifying inactive users via API alone is not possible - cross-referencing IdP data or Enterprise audit logs is required.
Rate limits are not publicly documented; implement exponential backoff on HTTP 429 responses and use the Retry-After header.
Finally, dbt Cloud webhooks cover job run events only - there are no webhook events for user creation, deactivation, or group changes, so any identity graph sync must rely on polling the /users/ endpoint or on SCIM push events from the IdP.
Automate dbt Cloud 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.