Refresh Token: Used to generate short-lived access tokens without user interaction.
2. Fetch Existing Incidents from IBM SOAR
- Purpose: Retrieve current incidents to determine the latest synced incident ID.
INCIDENT_URL = "https://9.30.57.77/rest/orgs/201/incidents"
headers = {'Authorization': 'Basic NGVkMTBiZjAtZjU1MS00MTE5LWI0YzQtN2ViZDBjNThiOTBi...'}
incident_list = requests.request("GET", url=INCIDENT_URL, headers=headers, verify=False)
3. Query ManageEngine for New Requests
Purpose: Fetch unresolved incidents from ManageEngine filtered by the latest IBM SOAR incident ID.
list_info = {
"list_info": {
"row_count": 10,
"filter_by": {"id": "17028000000026038"},
"search_criteria": {
"field": "display_id",
"condition": "greater than",
"value": json.loads(incident_list.text)[0]['external_id']
}
}
}
params = {"input_data": json.dumps(list_info)}
request_list = requests.get(ME_REQUEST_URL, headers=ME_headers, params=params, verify=False)
4. Create Incidents in IBM SOAR
Purpose: Loop through ManageEngine requests and create corresponding incidents in SOAR.
for request_to_add in json.loads(request_list.text)['requests']:
payload = json.dumps({
"external_id": request_to_add["display_id"],
"name": request_to_add["subject"],
"discovered_date": int(request_to_add["created_time"]["value"]),
"properties": {"manage_engine_id": int(request_to_add["display_id"])}
})
incident_added = requests.post(INCIDENT_URL, headers=headers, data=payload, verify=False)
5. Error Handling
Purpose: Gracefully handle exceptions to avoid script failure.
try:
except Exception as e:
print("CLIENT ERROR: {0}\n".format(e))
Best Practices
-
Security:
-
Scheduling:
-
Enhancements:
Full Code with Comments
import requests
import json
def main():
try:
ME_TOKEN_URL = "https://accounts.zoho.in/oauth/v2/token?refresh_token=1000.005fce...&grant_type=refresh_token"
response = requests.post(ME_TOKEN_URL, verify=False)
access_token = json.loads(response.text)["access_token"]
ME_headers = {
"Accept": "application/vnd.manageengine.v3+json",
"Authorization": "Zoho-oauthtoken " + access_token
}
INCIDENT_URL = "https://9.30.57.XX/rest/orgs/201/incidents"
soar_headers = {'Authorization': 'Basic NGVkMTBiZjAtZjU1MS00MTE5LWI0YzQtN2ViZDBjNThiOTBi...'}
incident_list = requests.get(INCIDENT_URL, headers=soar_headers, verify=False)
list_info = {
"list_info": {
"search_criteria": {
"field": "display_id",
"condition": "greater than",
"value": json.loads(incident_list.text)[0]['external_id']
}
}
}
params = {"input_data": json.dumps(list_info)}
request_list = requests.get("https://sdpondemand.manageengine.in/api/v3/requests",
headers=ME_headers, params=params, verify=False)
if request_list.json().get('requests'):
for req in request_list.json()['requests']:
payload = json.dumps({
"external_id": req["display_id"],
"name": req["subject"],
"discovered_date": int(req["created_time"]["value"]),
"properties": {"manage_engine_id": req["display_id"]}
})
requests.post(INCIDENT_URL, headers=soar_headers, data=payload, verify=False)
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
below images are from ManageEngine ITSM and IBM SOAR where ID 37 is synced with IBM SOAR.


Conclusion
This script automates one-way incident syncing from ManageEngine ITSM to IBM SOAR. By understanding API interactions and security practices, you can extend this to handle bidirectional syncs, attachments, or custom fields. Always test in a non-production environment before deployment.
Reach out to us if you need further guidance. Let’s elevate your security operations together!
Honey Gidwani honey.gidwani@ibm.com
Anuj Shrivastava - ashrivastava@in.ibm.com