Summary and recommendation
The AWS IAM Identity Center developer surface splits across two distinct API namespaces: the IdentityStore API (`identitystore`) for user and group CRUD, and the SSO API (`sso`) for permission sets and account assignments. IAM policies must explicitly cover both namespaces - gaps between `identitystore:*` and `sso-directory:*` are a common source of authorization failures.
All IdentityStore API calls are authenticated via AWS Signature Version 4; the SCIM endpoint uses a Bearer token with a hard 1-year expiry that must be rotated manually.
For teams managing identity at scale across many SaaS and cloud targets, Stitchflow's MCP server with ~100 deep IT/identity integrations provides a unified alternative to maintaining per-service API clients for each provider.
API quick reference
| Has user API | Yes |
| Auth method | AWS Signature Version 4 (SigV4) for the IdentityStore API; Bearer token (access token) for the SCIM endpoint |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Free (included with AWS account; no additional cost) |
Authentication
Auth method: AWS Signature Version 4 (SigV4) for the IdentityStore API; Bearer token (access token) for the SCIM endpoint
Setup steps
- Create an IAM principal (user or role) with an attached policy granting identitystore:* and/or sso-directory:* actions.
- Obtain AWS credentials (access key + secret, or assume an IAM role) for the calling principal.
- Sign all IdentityStore API requests using AWS Signature Version 4 with service name 'identitystore'.
- Retrieve your IdentityStoreId from the IAM Identity Center console (Settings > Identity source tab); format is d-xxxxxxxxxx.
- For SCIM: In the IAM Identity Center console go to Settings > Automatic provisioning > Enable, then copy the SCIM endpoint URL and the generated access token (shown only once).
- Pass the SCIM access token as 'Authorization: Bearer
' on every SCIM HTTP request. - Rotate the SCIM access token before its 1-year expiry; AWS sends reminders starting at 90 days before expiry.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| identitystore:CreateUser | IAM action to create a user in the identity store. | CreateUser API call |
| identitystore:DescribeUser | IAM action to retrieve a user by UserId. | DescribeUser API call |
| identitystore:ListUsers | IAM action to list/search users in the identity store. | ListUsers API call |
| identitystore:UpdateUser | IAM action to update user attributes via Operations array. | UpdateUser API call |
| identitystore:DeleteUser | IAM action to delete a user from the identity store. | DeleteUser API call |
| identitystore:GetUserId | IAM action to resolve a UserId from an alternate identifier (e.g., UserName or externalId). | GetUserId API call |
| identitystore:CreateGroupMembership | IAM action to add a user to a group. | CreateGroupMembership API call |
| identitystore:ListGroupMembershipsForMember | IAM action to list all groups a user belongs to. | ListGroupMembershipsForMember API call |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| UserId | string (UUID) | System-generated unique identifier for the user in the identity store. | returned | required (path param) | Format: UUID or IdentityStoreId-UUID depending on migration history. |
| IdentityStoreId | string | Globally unique identifier for the identity store (e.g., d-1234567890). | required | required | Pattern: d-[0-9a-f]{10} or UUID format for migrated stores. |
| UserName | string | Unique login name for the user. Max 128 chars. Characters '<>;:%' are excluded. | required | via Operations | Reserved names 'Administrator' and 'AWSAdministrators' cannot be used. |
| DisplayName | string | Human-readable display name (e.g., 'John Doe'). Required when used in IAM Identity Center. Max 1024 chars. | required (for IAM Identity Center) | via Operations (AttributePath: 'displayName') | Marked sensitive; redacted in SDK string representations. |
| Name | object (Name) | Structured name object with sub-fields: GivenName, FamilyName, MiddleName, HonorificPrefix, HonorificSuffix, Formatted. | optional | via Operations (AttributePath: 'name.givenName', 'name.familyName', etc.) | Must use dot-notation sub-paths (e.g., name.givenName) in UpdateUser Operations, not the top-level 'name' key. |
| Emails | array of Email objects | Email addresses. Each object has Value, Type, Primary fields. Fixed array size of 1 item. | optional | via Operations | Only a single email value is supported; multivalue attributes will fail SCIM sync. |
| PhoneNumbers | array of PhoneNumber objects | Phone numbers. Each object has Value, Type, Primary. Fixed array size of 1 item. | optional | via Operations (AttributePath: 'phoneNumbers') | Only a single phone number value is supported. |
| Addresses | array of Address objects | Physical addresses. Each object has StreetAddress, Locality, Region, PostalCode, Country, Formatted, Type, Primary. | optional | via Operations | Single-value only; multivalue will fail. |
| NickName | string | Alternate/informal name for the user. Max 1024 chars. | optional | via Operations | Marked sensitive in SDK. |
| ProfileUrl | string | URL of the user's personal or professional website/blog. Max 1024 chars. | optional | via Operations | |
| Title | string | User's job title (e.g., 'Contractor'). Max 1024 chars. | optional | via Operations | |
| UserType | string | Indicates the type of user (e.g., 'temp'). Values are unspecified/free-form. Max 1024 chars. | optional | via Operations | |
| PreferredLanguage | string | User's preferred language (e.g., 'en-us'). Max 1024 chars. | optional | via Operations | |
| Locale | string | Geographical region or location of the user (e.g., 'NA'). Max 1024 chars. | optional | via Operations | Marked sensitive in SDK. |
| Timezone | string | User's timezone (e.g., 'pdt'). Max 1024 chars. | optional | via Operations | |
| Birthdate | string (YYYY-MM-DD) | User's birthdate in ISO date format. | optional | via Operations | Available in SDK v2 (aws-sdk-go-v2); may not be present in older SDK versions. |
| ExternalIds | array of ExternalId objects | External identifiers from an external IdP. Each object has Issuer and Id. | optional | via Operations | Used to correlate the user with an external identity provider record. |
| Extensions (aws:identitystore:enterprise) | map (Document type) | Enterprise extension attributes: employeeNumber, costCenter, organization, division, department, manager. | optional | via Operations (AttributePath: 'aws:identitystore:enterprise' or 'aws:identitystore:enterprise.department') | Only 'aws:identitystore:enterprise' is a supported extension name. Max 10 map entries. Not supported by Java V1, Go V1, or older AWS CLI versions. |
Core endpoints
CreateUser
- Method: POST
- URL:
https://identitystore.{region}.amazonaws.com/identitystores/{IdentityStoreId}/users - Watch out for: DisplayName is required when used in IAM Identity Center (not just the generic Identity Store). Reserved names 'Administrator' and 'AWSAdministrators' are rejected.
Request example
{
"IdentityStoreId": "d-1234567890",
"UserName": "johndoe",
"DisplayName": "John Doe",
"Name": {"GivenName": "John", "FamilyName": "Doe"},
"Emails": [{"Value": "johndoe@example.com", "Type": "work", "Primary": true}]
}
Response example
{
"UserId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111",
"IdentityStoreId": "d-1234567890"
}
DescribeUser
- Method: GET
- URL:
https://identitystore.{region}.amazonaws.com/identitystores/{IdentityStoreId}/users/{UserId} - Watch out for: Requires the internal UserId (UUID), not UserName. Use GetUserId first to resolve a UserName or externalId to a UserId.
Request example
GET /identitystores/d-1234567890/users/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111
Response example
{
"UserId": "a1b2c3d4-...",
"UserName": "johndoe",
"DisplayName": "John Doe",
"Emails": [{"Value": "johndoe@example.com", "Primary": true}],
"IdentityStoreId": "d-1234567890"
}
GetUserId
- Method: POST
- URL:
https://identitystore.{region}.amazonaws.com/identitystores/{IdentityStoreId}/users/$resolve - Watch out for: Replaces the deprecated Filters parameter on ListUsers. Use this to look up a user by UserName or ExternalId without paginating the full user list.
Request example
{
"IdentityStoreId": "d-1234567890",
"AlternateIdentifier": {
"UniqueAttribute": {"AttributePath": "userName", "AttributeValue": "johndoe"}
}
}
Response example
{
"UserId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111",
"IdentityStoreId": "d-1234567890"
}
ListUsers
- Method: GET
- URL:
https://identitystore.{region}.amazonaws.com/identitystores/{IdentityStoreId}/users - Watch out for: Filtering by UserName via the Filters parameter is deprecated; use GetUserId instead. MaxResults max is 100.
Request example
GET /identitystores/d-1234567890/users?MaxResults=50&NextToken=<token>
Response example
{
"Users": [{"UserId": "...", "UserName": "johndoe", "DisplayName": "John Doe"}],
"NextToken": "<next-page-token>"
}
UpdateUser
- Method: PUT
- URL:
https://identitystore.{region}.amazonaws.com/identitystores/{IdentityStoreId}/users/{UserId} - Watch out for: Uses a patch-style Operations array (not a full-replace PUT). Nested name fields require dot-notation paths (e.g., 'name.givenName', not 'name' with an object value). Passing 'name' as a list causes a ValidationException.
Request example
{
"IdentityStoreId": "d-1234567890",
"UserId": "a1b2c3d4-...",
"Operations": [
{"AttributePath": "name.givenName", "AttributeValue": "Jonathan"},
{"AttributePath": "displayName", "AttributeValue": "Jonathan Doe"}
]
}
Response example
HTTP 200 (empty body on success)
DeleteUser
- Method: DELETE
- URL:
https://identitystore.{region}.amazonaws.com/identitystores/{IdentityStoreId}/users/{UserId} - Watch out for: Deleting a user does not automatically remove their permission set assignments or account assignments in IAM Identity Center. Those must be cleaned up separately via the SSO API.
Request example
DELETE /identitystores/d-1234567890/users/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111
Response example
HTTP 200 (empty body on success)
CreateGroupMembership
- Method: POST
- URL:
https://identitystore.{region}.amazonaws.com/identitystores/{IdentityStoreId}/memberships - Watch out for: GroupId must be resolved first (use GetGroupId). MemberId is a union type; only UserId is currently supported.
Request example
{
"IdentityStoreId": "d-1234567890",
"GroupId": "g1b2c3d4-...",
"MemberId": {"UserId": "a1b2c3d4-..."}
}
Response example
{
"MembershipId": "m1b2c3d4-5678-90ab-cdef-EXAMPLE33333",
"IdentityStoreId": "d-1234567890"
}
ListGroupMembershipsForMember
- Method: GET
- URL:
https://identitystore.{region}.amazonaws.com/identitystores/{IdentityStoreId}/memberships-for-member - Watch out for: Returns MembershipId and GroupId only; call DescribeGroup separately to get group display names.
Request example
{
"IdentityStoreId": "d-1234567890",
"MemberId": {"UserId": "a1b2c3d4-..."},
"MaxResults": 50
}
Response example
{
"GroupMemberships": [{"MembershipId": "...", "GroupId": "..."}],
"NextToken": "<token>"
}
Rate limits, pagination, and events
- Rate limits: IAM Identity Center Identity Store APIs are throttled at 20 transactions per second (TPS) per AWS Region. The limit applies independently to each Region where IAM Identity Center is enabled. Throttling returns a ThrottlingException. No standard HTTP rate-limit headers are documented for this service.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: The ThrottlingException response includes a 'RetryAfterSeconds' field indicating how long to wait before retrying. AWS SDKs implement automatic exponential backoff. Quota increase requests must originate from a management or delegated administrator account.
- Pagination method: token
- Default page size: 0
- Max page size: 100
- Pagination pointer: NextToken (request) / NextToken (response); page size controlled by MaxResults parameter
| Plan | Limit | Concurrent |
|---|---|---|
| All AWS accounts (default) | 20 TPS per Region for Identity Store APIs | 0 |
- Webhooks available: No
- Webhook notes: IAM Identity Center does not offer outbound webhooks for identity events. There is no push-based notification mechanism for user/group changes.
- Alternative event strategy: Use Amazon EventBridge with AWS CloudTrail as the event source. CloudTrail logs all IAM Identity Center API calls (CreateUser, UpdateUser, DeleteUser, etc.) as management events, which can trigger EventBridge rules to invoke Lambda, SNS, SQS, or other targets for near-real-time automation.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Free (included with AWS account; no additional cost)
Endpoint: https://scim.{region}.amazonaws.com/{tenantId}/scim/v2 (e.g., https://scim.us-east-2.amazonaws.com/11111111111-2222-3333-4444-555555555555/scim/v2)
Supported operations: GET /Users (list/filter users), POST /Users (create user), GET /Users/{id} (get user by SCIM id), PUT /Users/{id} (replace user), PATCH /Users/{id} (update user attributes), DELETE /Users/{id} (deprovision user), GET /Groups (list groups), POST /Groups (create group), GET /Groups/{id} (get group), PUT /Groups/{id} (replace group), PATCH /Groups/{id} (update group / manage members), DELETE /Groups/{id} (delete group)
Limitations:
- Only Bearer token authentication is supported; other SCIM auth schemes are not.
- SCIM access tokens have a 1-year validity and must be manually rotated before expiry.
- Multivalue attributes (multiple emails, phone numbers per user) are not supported; attempts will fail.
- After enabling automatic SCIM provisioning, users can no longer be added or edited directly in the IAM Identity Center console.
- IAM Identity Center does not support outbound synchronization; changes made via the IdentityStore API are not pushed back to the external IdP.
- Users must be assigned to the IdP application before they can be provisioned into IAM Identity Center.
- The externalId SCIM mapping should point to a stable, unique, immutable IdP identifier to prevent loss of AWS entitlements on attribute changes.
- SCIM endpoint and access token are only displayed once at time of enablement; must be copied immediately.
Common scenarios
Three workflows cover the majority of lifecycle operations.
Provisioning a new employee requires: resolving existence via GetUserId, calling CreateUser (DisplayName is required - not optional - for Identity Center users), then CreateGroupMembership using resolved GroupId, and finally CreateAccountAssignment in the sso namespace for actual AWS access.
Attribute updates use UpdateUser with a patch-style Operations array; nested name fields must use dot-notation paths (name. givenName, not a top-level name object) or the request returns a ValidationException.
Deprovisioning must sequence group membership removal, then account assignment deletion via the sso namespace, and only then DeleteUser - skipping the SSO cleanup steps leaves active IAM role sessions alive for up to 12 hours.
The default rate limit is 20 TPS per Region; use exponential backoff on ThrottlingException, and note that quota increase requests must originate from the management or delegated administrator account.
Provision a new employee and assign to a group
- Call GetUserId with UserName to check if user already exists (handle ResourceNotFoundException as 'not found').
- Call CreateUser with UserName, DisplayName, Name (GivenName/FamilyName), and Emails.
- Capture the returned UserId.
- Call GetGroupId with the target group's DisplayName to resolve its GroupId.
- Call CreateGroupMembership with the GroupId and MemberId (UserId) to add the user to the group.
- Optionally call CreateAccountAssignment (sso namespace) to grant the user/group access to specific AWS accounts and permission sets.
Watch out for: DisplayName is required for IAM Identity Center users. If the group does not exist yet, call CreateGroup first. Account assignments are managed in the 'sso' namespace, not 'identitystore'.
Update user attributes (e.g., department change)
- Call GetUserId with the user's UserName to resolve their UserId.
- Call UpdateUser with an Operations array targeting specific AttributePaths.
- For name sub-fields use dot-notation: 'name.givenName', 'name.familyName'.
- For enterprise extension fields use: 'aws:identitystore:enterprise.department'.
- Verify the update by calling DescribeUser and inspecting the returned attributes.
Watch out for: Do not pass the top-level 'name' key with an object value - use sub-field dot-notation paths. The Operations array supports up to 100 items per request. If using an external IdP with SCIM, changes made via the IdentityStore API will not sync back to the IdP.
Deprovision a departing employee
- Call GetUserId with the user's UserName to resolve their UserId.
- Call ListGroupMembershipsForMember to enumerate all group memberships.
- Call DeleteGroupMembership for each MembershipId to remove the user from all groups.
- Call ListAccountAssignments (sso namespace) to find all account/permission-set assignments.
- Call DeleteAccountAssignment (sso namespace) for each assignment to revoke AWS access.
- Call DeleteUser to remove the user from the identity store.
Watch out for: DeleteUser alone does not revoke AWS account access. Steps 4–5 (SSO namespace) must be completed first, or the user's existing sessions may persist until they expire. If SCIM is enabled, deprovisioning should be triggered from the IdP side, not directly via the API.
Why building this yourself is a trap
The most common integration traps are: (1) DescribeUser and DeleteUser require the internal UUID (UserId), not UserName - always call GetUserId first; (2) the Filters parameter on ListUsers for UserName lookup is deprecated - use GetUserId instead; (3) Emails, PhoneNumbers, and Addresses arrays are capped at exactly 1 item - passing multiple values fails silently or with a validation error; (4) the aws:identitystore:enterprise extension (department, costCenter, manager, etc.) is not supported by Java SDK v1, Go SDK v1, or older AWS CLI versions; (5) SCIM access token expiry is silent - if the token lapses, automatic provisioning stops with no in-band error, and the only signal is an AWS Health Dashboard notification starting 90 days before expiry; (6) once SCIM automatic provisioning is enabled, the console blocks all manual user creation and editing, so any emergency fix must go through the IdP or the IdentityStore API directly.
Automate AWS IAM Identity Center 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.