Stitchflow
Terraform Cloud logo

Terraform Cloud 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

The Terraform Cloud REST API follows JSON:API spec throughout - all requests and responses use `Content-Type: application/vnd.api+json`, and every payload wraps fields inside `data.attributes` and `data.relationships`.

Authentication is via Bearer token in the `Authorization` header;

three token types exist (User, Organization, Team), each with different scope ceilings.

Organization tokens cannot call `/account/details`;

that endpoint requires a user token.

Pagination is offset-based using `page[number]` and `page[size]` parameters, with a default page size of 20 and a maximum of 100.

The API enforces a rate limit of 30 requests per second per IP across all plans, returning HTTP 429 on breach.

`X-RateLimit-Limit` and `X-RateLimit-Remaining` headers are present;

`Retry-After` behavior is not explicitly documented.

For identity graph construction, note that user identity is keyed on the username string (e.g., `jsmith`), not a UUID.

There is no lookup-by-email endpoint;

to resolve an email to a membership ID, you must call `GET /api/v2/organizations/{org}/organization-memberships` and filter client-side.

Membership IDs use the `ou-*` prefix and are required for deletion operations.

API quick reference

Has user APIYes
Auth methodBearer token (API token in Authorization header)
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredEnterprise

Authentication

Auth method: Bearer token (API token in Authorization header)

Setup steps

  1. Log in to Terraform Cloud.
  2. Navigate to User Settings > Tokens.
  3. Click 'Create an API token', provide a description, and copy the generated token.
  4. Include the token in all API requests as: Authorization: Bearer
  5. Alternatively, use an Organization token (Settings > API Tokens) or Team token for scoped access.

Required scopes

Scope Description Required for
User API Token Full access to resources the user can access; acts as the authenticated user. Account details, organization membership, team membership operations on behalf of the user.
Organization API Token Scoped to a single organization; cannot perform user-specific account operations. Managing organization memberships and invitations at the org level.
Team API Token Scoped to a single team; limited to team-level operations. Managing team membership for the specific team.

User object / data model

Field Type Description On create On update Notes
id string Unique identifier for the user (username). auto-assigned immutable Used as the resource ID in JSON:API relationships.
username string The user's Terraform Cloud username. required not updatable via API Unique across Terraform Cloud.
email string The user's email address. required updatable via account endpoint Used for invitations; returned in account details.
avatar-url string (URL) URL to the user's avatar image. auto-assigned not directly updatable via API Gravatar-based.
is-service-account boolean Indicates if the user is a service account. auto-assigned immutable
two-factor object Two-factor authentication status: {enabled, verified, delivery}. auto-assigned managed via UI/account settings Read-only via API.
permissions object Computed permissions object for the authenticated user. auto-assigned derived from team/org membership Read-only.
status string Membership status in organization context (e.g., 'invited', 'active'). set to 'invited' on org invite transitions to 'active' on acceptance Appears on organization-membership objects, not the user object directly.

Core endpoints

Get current user account details

  • Method: GET
  • URL: https://app.terraform.io/api/v2/account/details
  • Watch out for: Returns details for the token owner only; cannot retrieve arbitrary users by ID via this endpoint.

Request example

GET /api/v2/account/details
Authorization: Bearer <token>
Content-Type: application/vnd.api+json

Response example

{
  "data": {
    "id": "user-abc123",
    "type": "users",
    "attributes": {
      "username": "jsmith",
      "email": "jsmith@example.com",
      "avatar-url": "https://..."
    }
  }
}

List organization memberships

  • Method: GET
  • URL: https://app.terraform.io/api/v2/organizations/{organization_name}/organization-memberships
  • Watch out for: Requires an Organization token or user token with owner permissions. Paginated with page[number]/page[size].

Request example

GET /api/v2/organizations/my-org/organization-memberships
Authorization: Bearer <token>

Response example

{
  "data": [
    {
      "id": "ou-abc123",
      "type": "organization-memberships",
      "attributes": {
        "status": "active",
        "email": "jsmith@example.com"
      }
    }
  ]
}

Invite user to organization

  • Method: POST
  • URL: https://app.terraform.io/api/v2/organizations/{organization_name}/organization-memberships
  • Watch out for: User receives an email invitation; membership status is 'invited' until accepted. Requires at least one team relationship in the payload.

Request example

POST /api/v2/organizations/my-org/organization-memberships
Content-Type: application/vnd.api+json

{"data":{"type":"organization-memberships","attributes":{"email":"newuser@example.com"},"relationships":{"teams":{"data":[{"type":"teams","id":"team-xyz"}]}}}}

Response example

{
  "data": {
    "id": "ou-newid",
    "type": "organization-memberships",
    "attributes": {
      "status": "invited",
      "email": "newuser@example.com"
    }
  }
}

Remove user from organization

  • Method: DELETE
  • URL: https://app.terraform.io/api/v2/organization-memberships/{organization_membership_id}
  • Watch out for: Use the organization-membership ID (ou-*), not the username. This removes the user from the org and all its teams.

Request example

DELETE /api/v2/organization-memberships/ou-abc123
Authorization: Bearer <token>

Response example

HTTP 204 No Content

List team members

  • Method: GET
  • URL: https://app.terraform.io/api/v2/teams/{team_id}/users
  • Watch out for: Returns user objects, not team-membership objects. Requires team token, org token, or owner user token.

Request example

GET /api/v2/teams/team-xyz/users
Authorization: Bearer <token>

Response example

{
  "data": [
    {
      "id": "user-abc123",
      "type": "users",
      "attributes": {"username": "jsmith"}
    }
  ]
}

Add users to team

  • Method: POST
  • URL: https://app.terraform.io/api/v2/teams/{team_id}/relationships/users
  • Watch out for: The user must already be an organization member. The 'id' field is the username string, not a numeric ID.

Request example

POST /api/v2/teams/team-xyz/relationships/users
Content-Type: application/vnd.api+json

{"data":[{"type":"users","id":"jsmith"}]}

Response example

HTTP 204 No Content

Remove users from team

  • Method: DELETE
  • URL: https://app.terraform.io/api/v2/teams/{team_id}/relationships/users
  • Watch out for: Does not remove the user from the organization, only from the specified team.

Request example

DELETE /api/v2/teams/team-xyz/relationships/users
Content-Type: application/vnd.api+json

{"data":[{"type":"users","id":"jsmith"}]}

Response example

HTTP 204 No Content

Show organization membership

  • Method: GET
  • URL: https://app.terraform.io/api/v2/organization-memberships/{organization_membership_id}
  • Watch out for: Membership ID must be known in advance; there is no lookup-by-email endpoint. Retrieve it from the list endpoint first.

Request example

GET /api/v2/organization-memberships/ou-abc123
Authorization: Bearer <token>

Response example

{
  "data": {
    "id": "ou-abc123",
    "type": "organization-memberships",
    "attributes": {"status": "active", "email": "jsmith@example.com"}
  }
}

Rate limits, pagination, and events

  • Rate limits: Terraform Cloud enforces rate limiting on API requests. The documented limit is 30 requests per second per IP address for most endpoints. Responses include rate-limit headers.
  • Rate-limit headers: Yes
  • Retry-After header: No
  • Rate-limit notes: When the rate limit is exceeded, the API returns HTTP 429. The X-RateLimit-Limit and X-RateLimit-Remaining headers are documented. Retry-After header behavior is not explicitly documented in official sources.
  • Pagination method: offset
  • Default page size: 20
  • Max page size: 100
  • Pagination pointer: page[number] and page[size]
Plan Limit Concurrent
All plans 30 requests/second per IP 0
  • Webhooks available: No
  • Webhook notes: Terraform Cloud does not offer webhooks for user-management events (user invited, user removed, etc.). Notifications are available for run events only, not user lifecycle events.
  • Alternative event strategy: Poll the organization-memberships list endpoint to detect changes, or use audit log streaming (available on Plus/Enterprise) to capture user-related audit events.

SCIM API status

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

Limitations:

  • SCIM provisioning is not natively available in Terraform Cloud as of the current documentation.
  • User provisioning via SSO (SAML) is supported on Plus and Enterprise plans, but this is JIT (just-in-time) provisioning, not SCIM.
  • The context reference indicates no native SCIM; Enterprise plan is required for SSO/SAML-based JIT provisioning.
  • Automated deprovisioning requires manual API calls or IdP-side session termination; there is no SCIM DELETE support.

Common scenarios

Invite and assign: Retrieve the target team ID via GET /api/v2/organizations/{org}/teams, then POST /api/v2/organizations/{org}/organization-memberships with the user's email and team ID in the relationships payload.

The team relationship is mandatory - the API will reject an invite without it.

Poll GET /api/v2/organization-memberships/{id} to detect the invitedactive status transition;

the user must accept the email invitation before the membership activates.

Remove from org: List memberships via GET /api/v2/organizations/{org}/organization-memberships, filter by email client-side to obtain the ou-* membership ID, then call DELETE /api/v2/organization-memberships/{id}.

This removes the user from all teams in the org simultaneously.

Verify by confirming the record no longer appears in the list response.

If the user holds an owner role, ensure at least one other owner exists before deletion or the call will be blocked.

Directory sync without SCIM: Retrieve current team members via GET /api/v2/teams/{team_id}/users, diff against your directory source, then POST additions and DELETE removals against /api/v2/teams/{team_id}/relationships/users.

Users being added must already be org members;

run the invite flow first if not.

Schedule this job on a polling interval - no webhooks exist for user-management events.

On Plus/Enterprise with SAML configured, be aware that team assignments set via API may be overridden by SAML attribute mappings on the user's next login.

Invite a new user to an organization and assign to a team

  1. Obtain the target team ID by calling GET /api/v2/organizations/{org}/teams and locating the team by name.
  2. POST to /api/v2/organizations/{org}/organization-memberships with the user's email and the team ID in the relationships payload.
  3. The user receives an invitation email; poll GET /api/v2/organization-memberships/{id} to check when status transitions from 'invited' to 'active'.

Watch out for: The team relationship is mandatory in the invite payload. If the user already has a Terraform Cloud account, they must accept the invitation before the membership becomes active.

Remove a user from the organization

  1. Call GET /api/v2/organizations/{org}/organization-memberships and filter by email to find the membership ID (ou-*).
  2. Call DELETE /api/v2/organization-memberships/{organization_membership_id}.
  3. Verify removal by confirming the membership no longer appears in the list response.

Watch out for: This removes the user from all teams in the org simultaneously. If the user is an owner, ensure another owner exists before removal to avoid losing org access.

Sync team membership from an external directory (no SCIM)

  1. Retrieve current team members: GET /api/v2/teams/{team_id}/users.
  2. Retrieve desired members from your directory source.
  3. For users to add: POST /api/v2/teams/{team_id}/relationships/users (users must already be org members; invite first if not).
  4. For users to remove: DELETE /api/v2/teams/{team_id}/relationships/users with the list of usernames to remove.
  5. Schedule this sync job periodically since no webhooks exist for user-management events.

Watch out for: SCIM is not available; all sync logic must be implemented in custom tooling. SSO/SAML team sync (if configured) may override API-set memberships on user login for Plus/Enterprise plans.

Why building this yourself is a trap

SCIM is not natively available at any plan tier in Terraform Cloud. What exists on Plus and Enterprise is SAML-based just-in-time (JIT) provisioning, which creates accounts on first login but does not support automated deprovisioning. Removing access requires explicit API calls or manual admin action;

there is no SCIM DELETE path.

For teams building an identity graph across their SaaS stack, Terraform Cloud's user model introduces two resolution challenges: identity is keyed on username strings rather than stable UUIDs, and there is no direct email-to-user lookup endpoint.

Any pipeline that needs to correlate Terraform Cloud membership with an IdP directory must maintain its own email-to-username mapping, refreshed by polling the memberships list.

Webhooks are absent for all user lifecycle events; run-event notifications are the only webhook surface. This means any automated access review or joiner-mover-leaver workflow must be built on scheduled polling, with the 30 req/s rate limit as the binding constraint for large organizations.

An MCP server with 60+ deep IT/identity integrations can abstract this polling complexity, but the underlying API limitations remain regardless of the integration layer.

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

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