Stitchflow
ManageEngine logo

ManageEngine 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 ServiceDesk Plus Cloud API (v3) exposes separate resource trees for requesters (`/api/v3/requesters`) and technicians (`/api/v3/technicians`), each with distinct endpoints, scopes, and licensing consequences.

Authentication on Cloud is OAuth 2.0 issued by Zoho Accounts (`accounts.zoho.com/oauth/v2/token`) - not ManageEngine's own domain - requiring an app registration in the Zoho API Console and the `SDPOnDemand.technicians.ALL` or `SDPOnDemand.users.ALL` scope depending on the resource.

On-Premise uses a static API key passed as `TECHNICIAN_KEY`; the base URL structure and endpoint paths differ entirely from Cloud v3. When building an identity graph across your stack, map both `requester.id` and `technician.id` per user, since a single employee can exist in both resource trees simultaneously and each must be deprovisioned independently.

API quick reference

Has user APIYes
Auth methodOAuth 2.0 (Cloud); API Key via request header (On-Premise)
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredEnterprise

Authentication

Auth method: OAuth 2.0 (Cloud); API Key via request header (On-Premise)

Setup steps

  1. Cloud (OAuth 2.0): Register an application in the Zoho API Console at api-console.zoho.com.
  2. Cloud: Select 'Server-based Application' or 'Self Client', choose the SDPOnDemand scope (SDPOnDemand.users.ALL or granular scopes).
  3. Cloud: Generate authorization code, exchange for access_token and refresh_token via POST to https://accounts.zoho.com/oauth/v2/token.
  4. Cloud: Pass access token in Authorization header: 'Authorization: Zoho-oauthtoken {access_token}'.
  5. On-Premise: Navigate to Admin > API > Generate API Key in the ServiceDesk Plus web console.
  6. On-Premise: Pass the key as query parameter 'TECHNICIAN_KEY={api_key}' or in the request header.

Required scopes

Scope Description Required for
SDPOnDemand.users.ALL Full read/write access to user (requester) records Create, read, update, delete requesters/users
SDPOnDemand.users.READ Read-only access to user records List and get requester/user details
SDPOnDemand.users.WRITE Write access to create and update user records Create and update requesters/users
SDPOnDemand.technicians.ALL Full access to technician (agent) records Manage technician accounts

User object / data model

Field Type Description On create On update Notes
id long Unique system-generated identifier for the user auto-generated read-only Required in URL path for update/delete operations
name string Full display name of the requester required optional
email_id string Primary email address of the user required optional Used as login identifier for cloud; must be unique
phone string Primary phone number optional optional
mobile string Mobile phone number optional optional
department object Department the user belongs to; contains id and name optional optional Pass as {"id": ""}
job_title string User's job title optional optional
employee_id string Employee ID from HR system optional optional
location object Site/location object; contains id and name optional optional
is_vip_user boolean Marks user as VIP for priority handling optional optional
is_technician boolean Indicates if the user is a technician/agent read-only read-only Technicians are managed via separate /technicians endpoint
user_scope string Access scope: GLOBAL_SCOPE or SITE_SCOPE optional optional
time_zone object User's time zone preference optional optional
language object Preferred language for UI optional optional
created_time datetime Timestamp when the user record was created auto-generated read-only

Core endpoints

List Users (Requesters)

  • Method: GET
  • URL: https://{domain}.sdpondemand.manageengine.com/api/v3/requesters
  • Watch out for: All query parameters must be JSON-encoded and passed as the 'input_data' query parameter, not as standard query params.

Request example

GET /api/v3/requesters?input_data={"list_info":{"row_count":10,"start_index":0}}
Authorization: Zoho-oauthtoken {access_token}

Response example

{
  "requesters": [{"id":"123","name":"Jane Doe","email_id":"jane@example.com"}],
  "list_info": {"total_count":50,"row_count":10,"start_index":0}
}

Get User (Requester) by ID

  • Method: GET
  • URL: https://{domain}.sdpondemand.manageengine.com/api/v3/requesters/{id}
  • Watch out for: Returns a single 'requester' object (not array) wrapped in the key 'requester'.

Request example

GET /api/v3/requesters/123
Authorization: Zoho-oauthtoken {access_token}

Response example

{
  "requester": {
    "id": "123",
    "name": "Jane Doe",
    "email_id": "jane@example.com",
    "department": {"id":"10","name":"IT"}
  }
}

Create User (Requester)

  • Method: POST
  • URL: https://{domain}.sdpondemand.manageengine.com/api/v3/requesters
  • Watch out for: Body must be form-encoded with 'input_data' key containing JSON string, not raw JSON body with Content-Type: application/json.

Request example

POST /api/v3/requesters
Content-Type: application/x-www-form-urlencoded

input_data={"requester":{"name":"John Smith","email_id":"john@example.com","job_title":"Engineer"}}

Response example

{
  "requester": {
    "id": "456",
    "name": "John Smith",
    "email_id": "john@example.com"
  },
  "response_status": {"status_code":2000,"status":"success"}
}

Update User (Requester)

  • Method: PUT
  • URL: https://{domain}.sdpondemand.manageengine.com/api/v3/requesters/{id}
  • Watch out for: Uses PUT (full-style), but only fields included in input_data are updated; omitted fields are not cleared.

Request example

PUT /api/v3/requesters/456
Content-Type: application/x-www-form-urlencoded

input_data={"requester":{"job_title":"Senior Engineer","phone":"555-1234"}}

Response example

{
  "requester": {"id":"456","name":"John Smith","job_title":"Senior Engineer"},
  "response_status": {"status_code":2000,"status":"success"}
}

Delete User (Requester)

  • Method: DELETE
  • URL: https://{domain}.sdpondemand.manageengine.com/api/v3/requesters/{id}
  • Watch out for: Deleting a requester who has open tickets may fail or require ticket reassignment first.

Request example

DELETE /api/v3/requesters/456
Authorization: Zoho-oauthtoken {access_token}

Response example

{
  "response_status": {
    "status_code": 2000,
    "status": "success"
  }
}

List Technicians

  • Method: GET
  • URL: https://{domain}.sdpondemand.manageengine.com/api/v3/technicians
  • Watch out for: Technicians (agents) are a separate resource from requesters (end users); they consume licensed seats.

Request example

GET /api/v3/technicians?input_data={"list_info":{"row_count":10,"start_index":0}}
Authorization: Zoho-oauthtoken {access_token}

Response example

{
  "technicians": [{"id":"789","name":"Alice Tech","email_id":"alice@example.com"}],
  "list_info": {"total_count":5,"row_count":5}
}

Create Technician

  • Method: POST
  • URL: https://{domain}.sdpondemand.manageengine.com/api/v3/technicians
  • Watch out for: Creating a technician consumes a licensed technician seat. Exceeding seat count returns an error.

Request example

POST /api/v3/technicians
Content-Type: application/x-www-form-urlencoded

input_data={"technician":{"name":"Bob Agent","email_id":"bob@example.com"}}

Response example

{
  "technician": {"id":"999","name":"Bob Agent","email_id":"bob@example.com"},
  "response_status": {"status_code":2000,"status":"success"}
}

Search Users by Email

  • Method: GET
  • URL: https://{domain}.sdpondemand.manageengine.com/api/v3/requesters
  • Watch out for: search_fields must be nested inside list_info in the input_data JSON; field names are case-sensitive.

Request example

GET /api/v3/requesters?input_data={"list_info":{"search_fields":{"email_id":"jane@example.com"}}}
Authorization: Zoho-oauthtoken {access_token}

Response example

{
  "requesters": [{"id":"123","name":"Jane Doe","email_id":"jane@example.com"}],
  "list_info": {"total_count":1}
}

Rate limits, pagination, and events

  • Rate limits: ManageEngine does not publicly document specific rate limit thresholds for ServiceDesk Plus Cloud API. Limits are enforced per account/plan but exact numbers are not published in official docs.
  • Rate-limit headers: No
  • Retry-After header: No
  • Rate-limit notes: No official rate limit documentation found. Contact ManageEngine support for account-specific limits. On-premise limits depend on server resources.
  • Pagination method: offset
  • Default page size: 100
  • Max page size: 100
  • Pagination pointer: list_info.row_count and list_info.start_index
Plan Limit Concurrent
Standard Not publicly documented 0
Professional Not publicly documented 0
Enterprise Not publicly documented 0
  • Webhooks available: Yes
  • Webhook notes: ServiceDesk Plus Cloud supports webhooks (called 'Notification Rules' or 'Custom Triggers') that can POST to external URLs when ticket or user events occur. Configuration is done via Admin > Automation > Custom Triggers.
  • Alternative event strategy: For polling-based sync, use the List Requesters endpoint with date-based filtering on created_time or last_updated_time fields.
  • Webhook events: Request created, Request updated, Request closed, User (requester) added, Technician added

SCIM API status

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

Limitations:

  • ServiceDesk Plus (Cloud and On-Premise) does NOT natively support SCIM 2.0 provisioning.
  • Other ManageEngine products such as PAM360 and Identity360 do support SCIM.
  • Okta and Entra ID integrations with ServiceDesk Plus use SSO (SAML) only, not SCIM provisioning.
  • User provisioning must be done via the REST API or manual import (CSV).
  • The context scim_reference notes Enterprise plan requirement but this refers to other ME products, not ServiceDesk Plus.

Common scenarios

Three lifecycle operations require explicit sequencing. For provisioning, POST to /api/v3/requesters with a form-encoded body (application/x-www-form-urlencoded) where all fields are nested inside a single input_data key as a JSON string - raw application/json bodies return a 400.

For department or attribute sync, there is no bulk update endpoint; each user requires a separate PUT call, so implement retry logic and back-off for large orgs. For offboarding, the DELETE on /api/v3/requesters/{id} will be blocked if the user has open tickets; query /api/v3/requests filtered by `requester.

idfirst, reassign or close tickets, then delete. If the user also holds a technician account, a separate DELETE to/api/v3/technicians/{technician_id}` is required to free the licensed seat - omitting this step leaves a billable ghost record.

Provision a new employee as a requester on hire

  1. POST /api/v3/requesters with input_data containing name, email_id, department.id, job_title, employee_id.
  2. Capture the returned requester.id for future reference in your HR system.
  3. Optionally assign the user to a site/location via the location field in the same create call.
  4. If the employee is also an IT agent, separately POST /api/v3/technicians with their details to create a technician account (consumes a license seat).

Watch out for: Body must be form-encoded (application/x-www-form-urlencoded) with input_data as a JSON string. Raw JSON bodies will return a 400 error.

Sync department changes from HR system

  1. Query your HR system for users with department changes since last sync.
  2. For each changed user, GET /api/v3/requesters?input_data={"list_info":{"search_fields":{"email_id":"{email}"}}} to find their SDP requester ID.
  3. PUT /api/v3/requesters/{id} with input_data containing the updated department object {"id": ""}.
  4. Log the response_status.status_code (2000 = success) for audit trail.

Watch out for: There is no bulk update endpoint; each user update requires a separate API call. For large orgs, implement rate limiting and retry logic in your sync script.

Deprovision a user on offboarding

  1. GET /api/v3/requesters with search_fields on email_id to retrieve the requester ID.
  2. Check if the user has open tickets by querying /api/v3/requests with requester.id filter.
  3. Reassign or close any open tickets before proceeding.
  4. DELETE /api/v3/requesters/{id} to remove the requester record.
  5. If the user was also a technician, separately DELETE /api/v3/technicians/{technician_id} to free the license seat.

Watch out for: DELETE will fail if the user has open/pending tickets. There is no 'deactivate' or 'suspend' state for requesters in ServiceDesk Plus - deletion is the only deprovisioning option via API.

Why building this yourself is a trap

The most common integration failure point is the non-standard request encoding: every write operation and every filtered list query must encode parameters as a JSON string inside the input_data form field, not as a JSON body or standard query string.

Pagination uses list_info.start_index (0-based) and list_info.row_count inside the same input_data object, with a hard maximum of 100 records per page. Rate limits are not publicly documented for any plan tier and no Retry-After header is returned on throttle responses - callers must implement conservative back-off without guidance from the API itself.

Finally, ServiceDesk Plus has no SCIM 2.0 endpoint; any IdP integration (Okta, Entra ID) covers SAML SSO only, meaning the REST API is the sole path for programmatic lifecycle management in this product.

Automate ManageEngine 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