Stitchflow
Heroku logo

Heroku User Management API Guide

API workflow

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

UpdatedMar 11, 2026

Summary and recommendation

The Heroku Platform API (v3) exposes full CRUD for Team and Enterprise Account membership via REST endpoints authenticated with OAuth 2.0 Bearer tokens or direct API keys. All requests require the `Accept: application/vnd.heroku+json; version=3` header - omitting it returns a 406.

Rate limits are enforced at 4500 requests/hour per token, with remaining quota surfaced in the `RateLimit-Remaining` response header.

Pagination uses HTTP Range headers rather than query parameters. The next page cursor is returned in the `Next-Range` response header; callers must re-issue the request with that value as the `Range` header to advance. Default page size is 200; callers can request up to 1000 per page using `max=1000` in the Range header.

Heroku has no native SCIM 2.0 endpoint. Automated provisioning and deprovisioning must be implemented directly against the Platform API team and enterprise member endpoints.

Okta, OneLogin, and Microsoft Entra ID integrations rely on SAML SSO with JIT provisioning - not SCIM - meaning deprovisioning is not handled by the IdP and must be driven by the Platform API or manual action.

API quick reference

Has user APIYes
Auth methodOAuth 2.0 Bearer Token (also supports API Key via HTTP Basic Auth with token as password)
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredEnterprise

Authentication

Auth method: OAuth 2.0 Bearer Token (also supports API Key via HTTP Basic Auth with token as password)

Setup steps

  1. Log in to Heroku and navigate to Account Settings > Applications.
  2. Create an OAuth Authorization or generate a direct API token via heroku authorizations:create CLI command.
  3. For OAuth 2.0 flow: register a client at https://api.heroku.com/oauth/clients, redirect user to https://id.heroku.com/oauth/authorize with desired scopes.
  4. Exchange authorization code for access token via POST https://id.heroku.com/oauth/token.
  5. Include token in requests as Authorization: Bearer <token> header.
  6. Set Accept: application/vnd.heroku+json; version=3 header on all requests.

Required scopes

Scope Description Required for
global Full access to account, apps, and all resources. All user management operations when using OAuth tokens.
read Read-only access to account and app data. Listing team members, reading account info.
write Write access to account and app data. Creating/updating team members and invitations.
read-protected Read access to protected account fields (e.g., email). Reading sensitive account fields.
write-protected Write access to protected account fields. Updating sensitive account fields.
identity Read access to account identity information. Fetching current user identity.

User object / data model

Field Type Description On create On update Notes
id string (UUID) Unique identifier for the account. system-generated immutable Used as stable identifier across API calls.
email string User's email address; used as login identifier. required updatable Must be unique across Heroku.
name string Full name of the account holder. optional updatable
verified boolean Whether the account has a verified payment method. system-set read-only Required for certain resource types.
created_at datetime (ISO 8601) Timestamp when the account was created. system-generated immutable
updated_at datetime (ISO 8601) Timestamp of last account update. system-generated system-updated
allow_tracking boolean Whether the user allows usage tracking. optional updatable
beta boolean Whether the user is enrolled in beta features. optional updatable
two_factor_authentication boolean Whether MFA/2FA is enabled on the account. system-set read-only via API Managed via account settings UI.
delinquent_at datetime|null Timestamp when account became delinquent due to billing. system-set read-only
role string Role of the member within a team (admin, member, viewer, collaborator). required for team membership updatable Field present on team-member objects, not base account object.
federated boolean Whether the account is managed via SSO/federated identity. system-set read-only Relevant for Enterprise accounts with SSO enabled.

Core endpoints

Get current account info

  • Method: GET
  • URL: https://api.heroku.com/account
  • Watch out for: Returns info for the token owner only. Cannot fetch arbitrary user accounts by ID without team/enterprise context.

Request example

GET /account HTTP/1.1
Host: api.heroku.com
Authorization: Bearer <token>
Accept: application/vnd.heroku+json; version=3

Response example

{
  "id": "01234567-89ab-cdef-0123-456789abcdef",
  "email": "user@example.com",
  "name": "Jane Smith",
  "verified": true,
  "two_factor_authentication": true,
  "created_at": "2024-01-15T10:00:00Z"
}

Update account

  • Method: PATCH
  • URL: https://api.heroku.com/account
  • Watch out for: Only updates the authenticated user's own account. No admin endpoint to update arbitrary user accounts.

Request example

PATCH /account HTTP/1.1
Host: api.heroku.com
Authorization: Bearer <token>
Accept: application/vnd.heroku+json; version=3
Content-Type: application/json

{"name": "Jane Doe", "allow_tracking": false}

Response example

{
  "id": "01234567-89ab-cdef-0123-456789abcdef",
  "email": "user@example.com",
  "name": "Jane Doe",
  "allow_tracking": false
}

List team members

  • Method: GET
  • URL: https://api.heroku.com/teams/{team_name_or_id}/members
  • Watch out for: Requires team admin role. Uses Range header for pagination, not query params.

Request example

GET /teams/my-team/members HTTP/1.1
Host: api.heroku.com
Authorization: Bearer <token>
Accept: application/vnd.heroku+json; version=3

Response example

[
  {
    "id": "01234567-89ab-cdef-0123-456789abcdef",
    "email": "member@example.com",
    "role": "member",
    "federated": false,
    "created_at": "2024-03-01T00:00:00Z"
  }
]

Create/invite team member

  • Method: PUT
  • URL: https://api.heroku.com/teams/{team_name_or_id}/members
  • Watch out for: Uses PUT (idempotent). If user does not have a Heroku account, an invitation email is sent. Role must be one of: admin, member, viewer, collaborator.

Request example

PUT /teams/my-team/members HTTP/1.1
Host: api.heroku.com
Authorization: Bearer <token>
Accept: application/vnd.heroku+json; version=3
Content-Type: application/json

{"email": "newuser@example.com", "role": "member"}

Response example

{
  "id": "01234567-89ab-cdef-0123-456789abcdef",
  "email": "newuser@example.com",
  "role": "member",
  "created_at": "2025-01-01T00:00:00Z"
}

Update team member role

  • Method: PATCH
  • URL: https://api.heroku.com/teams/{team_name_or_id}/members
  • Watch out for: Email is used as the identifier in the body, not a path parameter.

Request example

PATCH /teams/my-team/members HTTP/1.1
Host: api.heroku.com
Authorization: Bearer <token>
Accept: application/vnd.heroku+json; version=3
Content-Type: application/json

{"email": "member@example.com", "role": "admin"}

Response example

{
  "email": "member@example.com",
  "role": "admin",
  "updated_at": "2025-06-01T00:00:00Z"
}

Remove team member

  • Method: DELETE
  • URL: https://api.heroku.com/teams/{team_name_or_id}/members/{member_email_or_id}
  • Watch out for: Removes user from team but does not delete their Heroku account. Does not revoke app collaborator access granted outside the team.

Request example

DELETE /teams/my-team/members/member@example.com HTTP/1.1
Host: api.heroku.com
Authorization: Bearer <token>
Accept: application/vnd.heroku+json; version=3

Response example

{
  "email": "member@example.com",
  "role": "member"
}

List enterprise account members

  • Method: GET
  • URL: https://api.heroku.com/enterprise-accounts/{enterprise_account_name_or_id}/members
  • Watch out for: Requires Enterprise account. Endpoint path uses enterprise account name or UUID, not team name.

Request example

GET /enterprise-accounts/my-enterprise/members HTTP/1.1
Host: api.heroku.com
Authorization: Bearer <token>
Accept: application/vnd.heroku+json; version=3

Response example

[
  {
    "user": {"id": "abc123", "email": "admin@corp.com"},
    "permissions": ["view", "manage"],
    "federated": true
  }
]

Create enterprise account member

  • Method: POST
  • URL: https://api.heroku.com/enterprise-accounts/{enterprise_account_name_or_id}/members
  • Watch out for: Permissions array values differ from team roles. Valid values include: view, manage, billing. Enterprise plan required.

Request example

POST /enterprise-accounts/my-enterprise/members HTTP/1.1
Host: api.heroku.com
Authorization: Bearer <token>
Accept: application/vnd.heroku+json; version=3
Content-Type: application/json

{"email": "newadmin@corp.com", "permissions": ["view"]}

Response example

{
  "user": {"id": "def456", "email": "newadmin@corp.com"},
  "permissions": ["view"],
  "created_at": "2025-06-01T00:00:00Z"
}

Rate limits, pagination, and events

  • Rate limits: Heroku Platform API enforces rate limits per token. Standard limit is 4500 requests per hour per token.
  • Rate-limit headers: Yes
  • Retry-After header: No
  • Rate-limit notes: Rate limit status returned in RateLimit-Remaining header. When limit is exceeded, API returns HTTP 429. Limits apply per authorization token, not per account.
  • Pagination method: cursor
  • Default page size: 200
  • Max page size: 1000
  • Pagination pointer: Range header (e.g., Range: id ]<last_id>; max=200)
Plan Limit Concurrent
All plans (per OAuth token) 4500 requests/hour 0
  • Webhooks available: Yes
  • Webhook notes: Heroku supports app-level webhooks via the Platform API. Webhooks can be configured to fire on specific events including API entity changes.
  • Alternative event strategy: No dedicated user/member lifecycle webhook events (e.g., team member added/removed). Polling the team members endpoint is required for member change detection.
  • Webhook events: api:addon-attachment, api:app, api:build, api:collaborator, api:domain, api:dyno, api:formation, api:release, api:sni-endpoint, api:ssl-endpoint

SCIM API status

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

Limitations:

  • Heroku does not offer a native SCIM 2.0 API endpoint.
  • SSO is supported via SAML with JIT (Just-In-Time) provisioning for Enterprise accounts.
  • Automated provisioning/deprovisioning requires use of the Platform API team/enterprise member endpoints directly or via an IdP integration that calls the Platform API.
  • Okta, OneLogin, and Microsoft Entra ID integrations use SAML SSO with JIT, not SCIM.

Common scenarios

Provisioning a new team member uses a PUT to /teams/{team}/members with email and role in the request body. The PUT is idempotent, so re-sending is safe.

If the target user has no existing Heroku account, Heroku sends an invitation email automatically - the member object is not fully active until the invitation is accepted, so callers should poll GET /teams/{team}/members to confirm membership state before treating provisioning as complete.

Deprovisioning requires a DELETE to /teams/{team}/members/{email_or_id}. This removes the user from the Team but does not delete their Heroku account and does not remove any direct app collaborator access granted outside the Team context. If the user was added as a collaborator on individual apps, each app must be cleaned up separately via DELETE /apps/{app}/collaborators/{email}. Personal API tokens and OAuth authorizations created by the removed user are also not invalidated automatically and must be revoked independently.

For identity graph construction across an Enterprise Account, call GET /enterprise-accounts/{enterprise}/members with enterprise admin credentials and paginate via Next-Range headers until exhausted. Each member object includes a federated boolean field that distinguishes SSO-managed accounts from direct accounts - useful for segmenting IdP-governed identities from manually provisioned ones. Cross-referencing this endpoint against per-Team member lists (which use a separate path prefix and separate pagination) is required to build a complete picture of user access across all Teams under an Enterprise Account.

Provision a new team member

  1. Authenticate and obtain a Bearer token with global or write scope.
  2. PUT https://api.heroku.com/teams/{team}/members with body {"email": "user@example.com", "role": "member"}.
  3. If user has no Heroku account, Heroku sends an invitation email automatically.
  4. Poll GET /teams/{team}/members to confirm membership status once invitation is accepted.

Watch out for: Member does not appear as fully active until they accept the invitation. The PUT is idempotent so re-sending is safe.

Deprovision / remove a team member

  1. Authenticate with a team admin token.
  2. DELETE https://api.heroku.com/teams/{team}/members/{email_or_id}.
  3. Verify removal by calling GET /teams/{team}/members and confirming the user is absent.
  4. If the user was also a direct app collaborator, separately remove them from each app via DELETE /apps/{app}/collaborators/{email}.

Watch out for: Removing a team member does not remove them as a direct app collaborator if they were added outside the team context. Both must be cleaned up separately.

Audit all members across an Enterprise account

  1. Authenticate with an enterprise account admin token.
  2. GET https://api.heroku.com/enterprise-accounts/{enterprise}/members with Accept: application/vnd.heroku+json; version=3.
  3. Check Next-Range response header; if present, repeat request with Range: <Next-Range value> header to fetch next page.
  4. Continue until no Next-Range header is returned.
  5. For each member, cross-reference federated field to identify SSO-managed vs. direct accounts.

Watch out for: Default page size is 200; use max=1000 in Range header to reduce round trips. Enterprise endpoint is separate from team-level member endpoints.

Why building this yourself is a trap

The most significant API-layer trap is the identifier inconsistency across endpoints: PATCH operations on team members use email address as the identifier in the request body, while DELETE operations accept either email or UUID in the path. Callers building generic provisioning logic must handle this per-endpoint rather than assuming a uniform identifier strategy.

A second trap is the invitation-pending state. A PUT to create a team member returns a 200 and a member object, but the user is not active until they accept the invitation email.

Downstream logic that assumes immediate activation - such as granting app-level access immediately after provisioning - will operate on a member who cannot yet authenticate. There is no webhook event for invitation acceptance; polling is the only detection mechanism.

Enterprise Account endpoints use a separate path prefix (/enterprise-accounts/) and a different permissions model from Team endpoints. Valid permission values for Enterprise members (view, manage, billing) do not map to Team role strings (admin, member, viewer). Conflating the two models in shared provisioning code is a common source of 422 errors.

Additionally, there are no dedicated member lifecycle webhook events - no signal fires when a team member is added or removed - so any event-driven identity graph that depends on Heroku membership changes must rely entirely on scheduled polling of the member endpoints.

Automate Heroku 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 11, 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