User Management Action API

Use an HTTPS POST request to the action API for your organization to create, update, provision, and remove users or user groups, and add or remove administrative privileges for users.

https://usermanagement.adobe.io/v2/usermanagement/action/{orgId}

The body of this request contains a JSON commands structure that you use to specify which actions to perform for which user or user group. This page describes the general usage of the action API. The payload command structure and syntax, and details of all action steps that can be performed are discussed separately.

When a request has been understood and at least partially completed, it returns with HTTP status 200.

Throttle Limits: Maximum 10 requests per minute per a client. See Throttling Limits for full details.

Parameters

Name Type Req? Description
orgId path true The unique identifier for an organization. This is a string of the form A495E53@AdobeOrg where the prefix before the @ is a hexadecimal number. You can find this value as part of the URL path for the organization in the Admin Console or in the adobe.io console for your User Management integration.
testOnly query false A boolean value indicating whether to run the commands in test mode. If true, parameter syntactic and (limited) semantic checking is done, but the specified operations are not performed, so no user accounts or group memberships are created, changed, or deleted.
X-Api-Key header true The API key specified in the Adobe IO Console for the UMAPI integration used to authorize this session. See the Getting Started documentation for details. The header name is X-Api-Key and this parameter is its value.
Authorization header true The OAuth token generated by the authorization server from the JWT exchange which starts every UMAPI session. (This token usually begins with the letters ‘ey’.) The header name is Authorization, and this parameter, preceded by the word ‘Bearer’ and a space, is its value, as in Authorization: Bearer ey....
Content-type header false Used to specify the content type of the request data. Must be application/json
X-Request-Id header false An arbitrary string used to identify the corresponding response to a request. If a header with this name is provided in the request, the exact same header will be included in the response to that request.
request body true JSON payload containing a series of commands. See Request Body.

Using Test Mode

Supply the testOnly parameter in the POST request to the Action API in order to test the behavior of the command set.


POST https://usermanagement.adobe.io/v2/usermanagement/action/{myOrgID}?testOnly=true
-------------------------- body ----------------------------
JSON commands
------------------------- headers --------------------------
Accept: application/json
Content-Type: application/json
x-api-key: {myApiKey}
Authorization: Bearer {myAccessToken}

When you set the testOnly flag in an action call, the user-management service checks all commands for validity but does not make any actual changes in your user data. Use the flag to make sure that you are passing proper values without risk to the integrity of your user data. The response to a test-mode request reports that 0 operations were “completed”. The “completedInTestMode” field reports the number of commands expected to complete without error, subject to limitations.

{ "result": "partial",
  "completed" : 0,
  "completedInTestMode" : 3,
  "notCompleted" : 1,
  "errors" : [...]

Limitations of Test Mode

In normal operation, a command would create a user and then take actions on that user. When you create a user in test mode, the creation operation is not executed. All subsequent actions on that user should then fail. In order to make the test mode useful, it marks as successful any operation that refers to a non-existent user but is otherwise valid.

The test behavior generally reflects the normal behavior of a command set, but the need to ignore the non-existent user leads to limitations of the test that you should be aware of. Some sequences of commands that succeed in test mode do not succeed in normal operation. For example, two attempts to create the same users passes test mode, but is otherwise an error.

Test Behavior Cases

The following table shows the behavior of test mode for particular test cases.

Command Types Test Behavior
Create user Reports errors in command syntax, organization or user type mismatches, conflicting users. Does not create user.
Update user info Reports syntax or field errors. Checks user validity, except non-existent user. Does not make any changes to an existing user or report an error for non-existent user.
Add or remove product access Checks validity of product profile names. Reports error in syntax and limits. Does not make any membership changes for an existing user or report an error for non-existent user.
Remove user Reports errors in command syntax, organization mismatches. Does not remove an existing user or report an error for non-existent user.

Request Body

The JSON commands structure contained in your POST request specifies a sequence of commands. Each command entry specifies a user or user-group, and a sequence of steps to be performed for that user or user-group.

The JSON commands structure allows a maximum of 10 users or user groups to be operated on per request.


Responses

Content-Type: application/json

:warning: Use only those properties that are documented in the Response Properties section. Additional fields can appear in the response, but should not be relied upon.

200 OK

The request was understood and at least partially completed. The response body returns a more complete description of the result in JSON format. If the result status is:

  • success: All the actions were completed. completed field will equal the total of commands processed.
  • partial: Some of the actions failed. completed and notCompleted fields with identify the number of commands that succeeded and failed.
  • error: All the requested actions failed. completed will be 0 and notCompleted will show the number of requests that failed.

When the result is partial or error, the errors field lists will include the specific actions that failed with corresponding error information. Warnings can also be returned with details of deprecated commands. Warnings will not cause the command to fail.

When using the testOnly parameter, the field completedInTestMode will be populated with the number of successful commands processed. In this scenario the completed field will be 0 as no commands will have been fully processed.

Examples

Error status:

{
  "completed": 0,
  "notCompleted": 1,
  "completedInTestMode": 0,
  "errors": [
    {
      "index": 0,
      "step": 0,
      "message": "String too long in command for field: country, max length 2",
      "errorCode": "error.command.string.too_long"
    }
  ],
  "result": "error"
}

Partial status:

{
  "completed": 5,
  "notCompleted": 5,
  "completedInTestMode": 0,
  "errors": [
    {
      "index": 1,
      "step": 0,
      "requestID": "Two2_123456",
      "message": "User Id does not exist: test@test_fake.us",
      "user": "test@test_fake.us",
      "errorCode": "error.user.nonexistent"
    },
    {
      "index": 3,
      "step": 0,
      "requestID": "Four4_123456",
      "message": "Group NON_EXISTING_GROUP was not found",
      "user": "user4@example.com",
      "errorCode": "error.group.not_found"
    },
    {
      "index": 5,
      "step": 0,
      "requestID": "Six6_123456",
      "message": "User Id does not exist: test@test_fake.fake",
      "user": "test6@test_fake.fake",
      "errorCode": "error.user.nonexistent"
    },
    {
      "index": 7,
      "step": 0,
      "requestID": "Eight8_123456",
      "message": "Changes to users are only allowed in claimed domains.",
      "user": "fake8@faketest.com",
      "errorCode": "error.domain.trust.nonexistent"
    },
    {
      "index": 9,
      "step": 0,
      "requestID": "Ten10_123456",
      "message": "Group NON_EXISTING_GROUP was not found",
      "user": "user10@example.com",
      "errorCode": "error.group.not_found"
    }
  ],
  "result": "partial",
  "warnings": [
    {
      "warningCode": "warning.command.deprecated",
      "requestID": "Four4_123456",
      "index": 3,
      "step": 0,
      "message": "'product' command is deprecated. Please use productConfiguration.",
      "user": "user4@example.com"
    },
    {
      "warningCode": "warning.command.deprecated",
      "requestID": "Ten10_123456",
      "index": 9,
      "step": 0,
      "message": "'product' command is deprecated. Please use productConfiguration.",
      "user": "user10@example.com"
    }
  ]
}

Success status:

{
  "completed": 1,
  "notCompleted": 0,
  "completedInTestMode": 0,
  "result": "success"
}

Response Properties

message: string
Only returned if initial validation of the request fails. It is not populated when a 200 status is returned.

{
  "result": "error.organization.invalid_id",
  "message": "Bad organization Id"
}

result: string, possible values: { "success", "error", "partial", "error.apikey.invalid", "error.command.malformed", "error.organization.invalid", "error.organization.migrating" }
The status of the request. This property can be used to manage error handling as the value will either be success or a corresponding error. If the result status is:

  • success: All the actions were completed. completed field will equal the total of commands processed.
  • partial: Some of the actions failed. completed and notCompleted fields with identify the number of commands that succeeded and failed.
  • error: All the requested actions failed. completed will be 0 and notCompleted will show the number of requests that failed.

completed: integer
The number of user commands that were successful.

notCompleted: integer
The number of user commands that were unsuccessful. When non-zero the errors field lists the specific actions that failed, with error information.

completedInTestMode: integer
The number of users that were completed in testOnly mode.

errors:
An array of errors. Each error entry is an object with the attributes below. This section is ommitted if no errors were generated.

  • index: integer; The 0-based index of the command entry in the commands structure.
  • step: string; The 0-based index of the action step within that command entry.
  • message: string; A description of the error.
  • errorCode: string; The error type. See Errors for a full list.
  • requestID: string; A developer-defined ID passed into the request which you can use to match this response to a specific request.
  • user: string; The user defined in the root of the command entry.

warnings:
An array of warnings. Each warning entry is an object with the attributes below. This section is ommitted if no warnings were generated.

  • index: integer; The 0-based index of the command entry in the commands structure.
  • step: string; The 0-based index of the action step within that command entry.
  • message: string; A description of the warning.
  • warningCode: string; The warning type. See Errors for a full list.
  • requestID: string; A developer-defined ID passed into the request which you can use to match this response to a specific request.
  • user: string; The user defined in the root of the command entry.

Schema Model

{
  "completed": 0,
  "completedInTestMode": 0,
  "errors": [
    {
      "errorCode": "string",
      "index": 0,
      "message": "string",
      "requestID": "string",
      "step": 0,
      "user": "string"
    }
  ],
  "message": "string",
  "notCompleted": 0,
  "result": "string",
  "warnings": [
    {
      "index": 0,
      "message": "string",
      "requestID": "string",
      "step": 0,
      "user": "string",
      "warningCode": "string"
    }
  ]
}

Responses with Error Status

If the response has a status other than 200, the request was not processed. The status code indicates the reason type of error; this section provides some common causes for these errors.

400 Bad Request

Some parameters of the request were not understood by the server or the Service Account Integration certificate has expired.

401 Unauthorized

Possible causes are:

  • Invalid or expired token.
  • Invalid Organization.
< HTTP/1.1 401 Unauthorized
< Content-Type: */*
< Date: Thu, 22 Jun 2017 09:47:04 GMT
< WWW-Authenticate: Bearer realm="JIL", error="invalid_token", error_description="The access token is invalid"
< X-Request-Id: user-assigned-request-id
< Content-Length: 0
< Connection: keep-alive

403 Forbidden

Possible causes are:

  • Missing API key.
  • The organization is currently migrating. Either from DMA or to One Console.
  • API key is not permitted access.
< HTTP/1.1 403 Forbidden
< Date: Thu, 22 Jun 2017 09:41:22 GMT
< X-Request-Id: user-assigned-request-id
< Content-Length: 0
< Connection: keep-alive

Throttling Limits

To protect the availability of the Adobe back-end user identity systems, the User Management API imposes limits on client access to the data. Limits apply to the number of calls that an individual client can make within a time interval, and global limits apply to access by all clients within the time period. For this API the throttling limits are as follows:

  • Maximum calls per client: 10 requests per a minute
  • Maximum calls for the application: 100 requests per a minute

When the client or global access limit is reached, further calls fail with HTTP error status 429 Too Many Requests. The Retry-After header is included in the 429 response, and provides the minimum amount of time that the client should wait until retrying. See RFC 7231 for full information.

The User Management API recommends limiting your syncs to two hourly intervals and consider scheduling your sync for a time that works best for you, taking into account other timezones and clients. This will help to prevent how often your client is throttled.

The following sample shows a 429 response with the Retry-After header detailing the number of seconds to wait before retry:

========================= RESPONSE =========================
Status code: 429
-------------------------- header --------------------------
Content-Type: application/json
Date: Fri, 19 Jan 2018 10:31:43 GMT
Retry-After: 38
Server: APIP
X-Request-Id: iEUtsLiFgj3R4xsbirAyZlMyaxRTo8Xo
Content-Length: 54
Connection: keep-alive
--------------------------- body ---------------------------
{
  "error_code" : "429050",
  "message" : "Too many requests"
}
============================================================

Because of the global limits, and because the specific limits may change, you cannot simply limit the rate at which you make your own calls. You must handle rate-limit errors by retrying the failed calls. We recommend an exponential backoff retry technique for handling such errors.

Handling error responses

When you retry a failed request, the retry can also fail, and can fall back into the retry loop, adding to the system overload. The exponential backoff retry method increases the period between retries, so that the client makes fewer calls while the system is overloaded.

To implement an exponential backoff method, you increase the retry interval with each failed request. You should retry sending the request after a certain number of seconds, and increase that interval by a random amount with each attempt. For example, you can double the retry period each time, or escalate it by a power of 2, and then add a small random delay between failures.

A small random delay, known as “jitter,” prevents the “herd effect” that can occur if many clients attempt to reconnect to a recovering system at the same time. Without jitter, all of the retries could occur after 20 seconds, then 40 seconds, and so on. With the jitter, different retries occur at slightly different intervals. This allows the system to recover without further overloading it.

For an example of how to implement this error-handling method, see “Retrying Requests” in the User Management Walkthrough.