# ManageEngine

- Tool: FlutterFlow
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: July 2026

## TL;DR

Connect FlutterFlow to ManageEngine by first picking the specific product — ServiceDesk Plus (ITSM tickets), Endpoint Central (device management), or OpManager (network monitoring) — since each has its own API base URL and auth method. Cloud products use Zoho OAuth (Zoho-oauthtoken header); on-prem uses a technician API key. ServiceDesk Plus has a unique quirk: many endpoints expect request data as a URL-encoded 'input_data' parameter, not a plain JSON body.

## ManageEngine Is Not One Product — Pick Your API First

ManageEngine is a brand, not a single product. Zoho's IT management division encompasses roughly 60 different tools: ServiceDesk Plus for IT ticketing, Endpoint Central (formerly Desktop Central) for device patch management, OpManager for network monitoring, Log360 for SIEM, and many more. Each product has its own REST API, its own base URL, and in many cases its own authentication system. The most important first step when connecting FlutterFlow to ManageEngine is deciding which product's API you're integrating with — 'the ManageEngine API' doesn't exist as a concept.

For most non-technical founders building with FlutterFlow, the goal is a mobile IT service desk: **ServiceDesk Plus** is the product to target. Its API lets you read and update tickets, check SLA status, approve requests, and view asset records. The real-world use-case is a technician on a factory floor or hospital ward approving IT tickets or updating asset status from a phone, without needing to open a browser on a laptop.

Authentication splits along deployment type. ManageEngine Cloud (hosted by Zoho, SaaS) uses Zoho OAuth 2.0 — you register an OAuth application in the Zoho API Console, get a client ID and secret, and the user authorizes via a Zoho login flow. The resulting access token goes in the `Authorization: Zoho-oauthtoken [token]` header. On-premise ManageEngine (running on your own server) historically uses a simpler API key system — a technician key generated in the ManageEngine product admin panel, sent in a `TECHNICIAN_KEY` header or as an `authtoken` query parameter. The exact mechanism varies by product version — verify with your ManageEngine administrator. ServiceDesk Plus Standard plans start from approximately $10–12 per technician per month for Cloud (verify current pricing at manageengine.com), with free editions available for small teams.

## Before you start

- A ManageEngine product account — ServiceDesk Plus, Endpoint Central, or OpManager — and clarity on whether it is the Cloud (Zoho-hosted) or on-premise version
- For Cloud products: a Zoho Developer Console account (accounts.zoho.com → Developer Console) to register an OAuth 2.0 client application and obtain a client ID and client secret
- For on-premise products: a technician API key from your ManageEngine admin (generated in Administration → API settings of the specific product)
- The API base URL for your specific product and deployment (ServiceDesk Plus Cloud: https://sdpondemand.manageengine.com/api/v3/; on-premise varies by hostname)
- A FlutterFlow account (any plan — API Calls work on all plans) and basic familiarity with the API Calls panel and JSON Path

## Step-by-step guide

### 1. Choose your ManageEngine product and find its API base URL

The first and most important step is picking the right ManageEngine product and locating its API documentation. Every product has a different base URL and in many cases a different authentication model. Here are the most common ones:

**ServiceDesk Plus Cloud:** `https://sdpondemand.manageengine.com/api/v3/`
**ServiceDesk Plus On-Premise:** `https://your-sdp-server:8080/api/v3/` (port may vary)
**ServiceDesk Plus (US data center):** `https://sdpondemand.manageengine.com/api/v3/` (same; confirm datacenter-specific URLs in Zoho documentation)
**Endpoint Central Cloud:** varies — check the Endpoint Central Cloud documentation for the current API hostname
**OpManager On-Premise:** `https://your-opmanager-server:8060/api/json/`

For ServiceDesk Plus, verify the correct URL in your SDP account: go to Admin → API → API Configuration to see the exact API URL configured for your instance.

For on-premise products, the hostname is your internal server's address — `https://your-server.yourcompany.com/` or an IP address. This means the API is only reachable from devices on your corporate network or VPN. A published FlutterFlow web app accessed from outside the corporate network will time out on all API calls. Native mobile builds on a VPN-connected device work fine.

Once you have confirmed the base URL in your browser (the Swagger/API docs page or a simple GET to the base URL should return something), note it for the next step.

**Expected result:** You have the correct API base URL for your specific ManageEngine product and deployment type, and you know whether it's Cloud (Zoho OAuth) or on-premise (API key).

### 2. Set up authentication: Zoho OAuth (Cloud) or API key (on-premise)

Authentication differs completely between Cloud and on-premise deployments.

**For Cloud products — Zoho OAuth 2.0:**
1. Go to api-console.zoho.com → Add Client → Server-based Applications
2. Set the redirect URI to a URI you control (for a mobile app, use a custom scheme like `myapp://oauth/callback` or your Firebase function URL)
3. Save and note the Client ID and Client Secret
4. Perform the OAuth authorization code flow to get an initial access token and refresh token. The Zoho OAuth flow is a browser-based login at `https://accounts.zoho.com/oauth/v2/auth?client_id=...&scope=SDPOnDemand.requests.ALL&response_type=code&redirect_uri=...`
5. Exchange the code for tokens via a POST to `https://accounts.zoho.com/oauth/v2/token`
6. Store the access token and refresh token securely — the Client Secret must live ONLY in your backend (Firebase Cloud Function). Never add it to FlutterFlow.

Zoho OAuth access tokens expire in approximately 1 hour. The refresh cycle must happen server-side: your Firebase Cloud Function holds the client secret and refresh token, refreshes when needed, and returns a fresh access token to your FlutterFlow app via a simple API call. In FlutterFlow's API Group, the `Authorization` header value will be `Zoho-oauthtoken [token from App Variable]`, updated each session by your refresh function.

**For on-premise products — Technician API Key:**
1. Log into the ManageEngine product as an admin → Administration → API → Generate New Key (exact path varies by product)
2. Copy the key — store it as a FlutterFlow App Constant
3. In the API Group, add header: `TECHNICIAN_KEY: [App Constant: ME_TECHNICIAN_KEY]`

On-premise API keys are simpler but tied to a specific user account's permissions. Use a dedicated read-only service account rather than a real technician's credentials.

```
// Firebase Cloud Function: Zoho OAuth token refresher
// Called by FlutterFlow to get a fresh access token
const functions = require('firebase-functions');
const axios = require('axios');

exports.getZohoToken = functions.https.onCall(async (data, context) => {
  const params = new URLSearchParams({
    refresh_token: process.env.ZOHO_REFRESH_TOKEN,
    client_id: process.env.ZOHO_CLIENT_ID,
    client_secret: process.env.ZOHO_CLIENT_SECRET,
    grant_type: 'refresh_token',
  });
  const resp = await axios.post(
    'https://accounts.zoho.com/oauth/v2/token',
    params.toString(),
    { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
  );
  return { access_token: resp.data.access_token };
});
```

**Expected result:** You have a working authentication mechanism — either a Zoho OAuth flow with token refresh via Cloud Function (for Cloud products) or a technician API key stored as a FlutterFlow App Constant (for on-premise).

### 3. Create the ManageEngine API Group in FlutterFlow

Open FlutterFlow → API Calls in the left nav → + Add → Create API Group.

Set:
- **Group Name:** Name it after the specific product, e.g., `ServiceDeskPlus` or `OpManager` — not just 'ManageEngine' (they have different base URLs)
- **Base URL:** The product-specific URL from Step 1

Add headers based on your auth type:

**Cloud (Zoho OAuth):**
- `Authorization` → `Zoho-oauthtoken [Page State: currentZohoToken]` or `Zoho-oauthtoken [App Variable: zohoAccessToken]`
Note: The prefix is `Zoho-oauthtoken`, not `Bearer` — this is Zoho's custom format.

**On-premise (API key):**
- `TECHNICIAN_KEY` → `[App Constant: ME_TECHNICIAN_KEY]`

For both cases, also add:
- `Accept` → `application/json`
- `Content-Type` → `application/json`

For Zoho OAuth, the token value changes each session after the refresh Cloud Function runs. Use a FlutterFlow App State variable (App State → + Add State Variable → `zohoAccessToken` of type String) to store the current token. Your app's initialization action calls the Firebase Cloud Function, gets a fresh token, and sets it in the App State variable before any ManageEngine API Call fires.

Click Save on the API Group.

```
{
  "api_group_name": "ServiceDeskPlus",
  "base_url": "https://sdpondemand.manageengine.com/api/v3/",
  "headers": [
    {
      "name": "Authorization",
      "value": "Zoho-oauthtoken [App State: zohoAccessToken]"
    },
    {
      "name": "Accept",
      "value": "application/json"
    },
    {
      "name": "Content-Type",
      "value": "application/json"
    }
  ]
}
```

**Expected result:** The ServiceDeskPlus (or OpManager) API Group appears in the API Calls panel with the product-specific base URL and the correct auth header format.

### 4. Add a GET Tickets call and handle ServiceDesk Plus's input_data pattern

ServiceDesk Plus has a distinctive API pattern that differs from most REST APIs: many endpoints expect request parameters as a URL-encoded `input_data` parameter containing a JSON string, rather than as a standard JSON request body or standard query parameters.

For a simple GET request (fetching a list of tickets), this is straightforward. Add an API Call:
- **Call Name:** `GetOpenTickets`
- **Method:** GET
- **Endpoint:** `requests`

For the ServiceDesk Plus v3 API, add query parameters to filter the results. Add a Variable `input_data` of type String, and include it as a URL query parameter:
`requests?input_data=[input_data]`

The variable's value should be a URL-encoded JSON string. For example, to fetch only open tickets:
```
{"list_info":{"row_count":25,"start_index":0,"search_criteria":[{"field":"status.name","condition":"is","value":"Open"}],"sort_field":"created_time","sort_order":"desc"}}
```

In FlutterFlow, set the variable's default value to this JSON string (not URL-encoded — FlutterFlow will handle URL encoding automatically when you mark the parameter as a query variable). Click Test in the Response & Test tab.

A successful response wraps tickets in a `requests` array. Add JSON Paths:
- `ticket_id` → `$.requests[*].id`
- `ticket_subject` → `$.requests[*].subject`
- `ticket_status` → `$.requests[*].status.name`
- `ticket_priority` → `$.requests[*].priority.name`
- `ticket_created` → `$.requests[*].created_time.value`

Bind these to a ListView with one list item widget per ticket, showing subject, priority badge, and status.

```
{
  "call_name": "GetOpenTickets",
  "method": "GET",
  "endpoint": "requests?input_data=[input_data]",
  "variables": [
    {
      "name": "input_data",
      "type": "String",
      "default": "{\"list_info\":{\"row_count\":25,\"start_index\":0,\"search_criteria\":[{\"field\":\"status.name\",\"condition\":\"is\",\"value\":\"Open\"}],\"sort_field\":\"created_time\",\"sort_order\":\"desc\"}}"
    }
  ],
  "json_paths": [
    { "name": "ticket_id", "path": "$.requests[*].id" },
    { "name": "ticket_subject", "path": "$.requests[*].subject" },
    { "name": "ticket_status", "path": "$.requests[*].status.name" },
    { "name": "ticket_priority", "path": "$.requests[*].priority.name" },
    { "name": "ticket_created", "path": "$.requests[*].created_time.value" }
  ]
}
```

**Expected result:** The GetOpenTickets call returns a 200 response with a 'requests' array. The ListView displays open tickets with subject, status, and priority. The input_data pattern works correctly with URL-encoded JSON.

### 5. Route ticket write operations through a Cloud Function and test on device

For read-only operations (viewing ticket lists, checking device patch status, monitoring alarms), the API Group setup from the previous steps is sufficient. For write operations — updating a ticket status, adding a work note, acknowledging a monitoring alarm, approving a service request — the authentication credential must be more privileged, and privileged credentials must not ship in the client.

For Zoho Cloud products, the access token your Firebase Cloud Function refreshes is already living server-side. Route write calls through a second Firebase function endpoint that accepts the ticket ID and new status from FlutterFlow, makes the ManageEngine API call with the full Zoho OAuth credential server-side, and returns the result. This way your Zoho client secret and refresh token never appear in the Flutter binary.

For on-premise API key auth, the technician API key in App Constants is compiled into the binary. For internal team apps where this is acceptable, direct write calls can work — but for any app distributed beyond your own device, use the same Cloud Function proxy pattern.

A typical write API Call to ServiceDesk Plus (updating ticket status) uses the `input_data` pattern on a PUT endpoint:
```
PUT /api/v3/requests/{request_id}
input_data={"request":{"status":{"name":"Resolved"}}}
```

With the Action Flow Editor in FlutterFlow: wire a 'Close Ticket' button → Show Confirm Dialog → on Confirm → Call Firebase Function 'updateTicketStatus' (passing ticket ID and 'Resolved' as parameters) → on success → refresh the ListView.

Test all API Calls in FlutterFlow's Test mode first, then run on a physical device using the FlutterFlow mobile test app (iOS/Android) to confirm the real network path works, especially for on-premise products where VPN connectivity must be verified on device.

```
// Firebase Cloud Function: update ManageEngine SDP ticket status
const functions = require('firebase-functions');
const axios = require('axios');

exports.updateTicketStatus = functions.https.onCall(async (data, context) => {
  const { ticketId, newStatus, zohoToken } = data;
  const inputData = JSON.stringify({
    request: { status: { name: newStatus } }
  });
  const resp = await axios.put(
    `https://sdpondemand.manageengine.com/api/v3/requests/${ticketId}`,
    `input_data=${encodeURIComponent(inputData)}`,
    {
      headers: {
        Authorization: `Zoho-oauthtoken ${zohoToken}`,
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    }
  );
  return resp.data;
});
```

**Expected result:** Write operations (ticket updates, alarm acknowledgments) work through a Firebase Cloud Function without any privileged credentials appearing in the FlutterFlow client. The full read/write ticket management flow is tested and working on a physical device.

## Best practices

- Always pick a specific ManageEngine product before configuring FlutterFlow — 'ManageEngine' covers ~60 different tools with different API base URLs and authentication models.
- Use Zoho OAuth for Cloud products and store the Zoho client secret and refresh token exclusively in Firebase Cloud Function environment variables — never in FlutterFlow App Constants or Custom Action Dart.
- For on-premise API key authentication, create a dedicated read-only service account with the minimum required permissions rather than using a real technician's credentials.
- Store on-premise technician API keys as FlutterFlow App Constants (Settings & Integrations → App Settings → App Constants) and use them only for internal team apps where the binary exposure risk is acceptable.
- Use the correct Authorization header format for Zoho Cloud: 'Zoho-oauthtoken [token]' — not 'Bearer' — this is a Zoho-specific format that differs from standard OAuth 2.0 Bearer tokens.
- For ServiceDesk Plus, expect the input_data URL-encoded JSON pattern on many endpoints — test the pattern in Hoppscotch before adding it to FlutterFlow to verify the filter syntax.
- Route all write operations (ticket updates, alarm acknowledgments) through a Firebase Cloud Function regardless of auth type — restricts blast radius if a credential is ever compromised.
- For on-premise ManageEngine, clearly communicate to app users that VPN or corporate network access is required — document this in app onboarding so users aren't confused by connection failures.

## Use cases

### Mobile IT ticket approval app for technicians

A healthcare network's IT department uses ServiceDesk Plus Cloud for their helpdesk. Technicians often work across multiple buildings without laptops. A FlutterFlow app lets them view assigned tickets, update ticket status, add notes, and approve service requests from their phones using Zoho OAuth authentication. The app fetches open tickets via the SDP API, displays them with priority and SLA status, and sends status updates via PATCH calls routed through a Firebase Cloud Function that holds the Zoho client secret.

Prompt example:

```
Build an IT ticket list screen that fetches open tickets assigned to the current technician from ServiceDesk Plus Cloud API, shows ticket ID, subject, priority, and SLA status, sorted by priority. Include a button to open each ticket's detail screen with a status update dropdown.
```

### Device patch compliance dashboard for IT managers

A manufacturing company's IT manager needs to see which endpoints (Windows PCs, laptops) have missing critical patches across four production facilities. A FlutterFlow dashboard app connects to Endpoint Central's API, fetches devices with missing patches above a severity threshold, and displays a floor-by-floor compliance summary. The app uses a read-only technician API key for the on-premise Endpoint Central instance, accessible via the corporate VPN.

Prompt example:

```
Create a patch compliance screen that queries the Endpoint Central API for devices with 'Critical' severity missing patches, groups results by location, and shows a compliance percentage badge per group — green above 95%, yellow 80-95%, red below 80%.
```

### Network alarm monitor for on-call engineers

A telecom company uses OpManager to monitor hundreds of network devices. Their on-call engineers need to check active alarms from their phones during off-hours incidents. A FlutterFlow app queries OpManager's alarm API, displays active alarms sorted by severity, and allows the engineer to acknowledge an alarm from the app. The integration uses OpManager's on-premise API key auth and is accessible only when the engineer is on the corporate VPN.

Prompt example:

```
Build an alarms screen that fetches all unacknowledged network alarms from the OpManager API, displays device name, alarm message, severity, and time, sorted by severity descending. Add an 'Acknowledge' button that sends a POST to the OpManager alarm acknowledgment endpoint.
```

## Troubleshooting

### 401 Unauthorized when calling ServiceDesk Plus Cloud API

Cause: The Zoho OAuth access token has expired (they expire approximately every 1 hour) or the wrong header format is being used ('Bearer' instead of 'Zoho-oauthtoken').

Solution: Verify the Authorization header format is exactly 'Zoho-oauthtoken [token]' — not 'Bearer'. If the token is expired, your Firebase Cloud Function for Zoho token refresh needs to run before the API Call. Add an app initialization action that calls the refresh function and stores the new token in the App State variable before any ManageEngine screens load.

### API Call returns data but looks wrong — wrong tickets, wrong schema, unexpected fields

Cause: You may be calling the wrong ManageEngine product's API endpoint, or the API version (v2 vs v3) differs from what the documentation shows for your account tier.

Solution: Confirm the base URL against your specific product and account configuration. In ServiceDesk Plus, go to Admin → API → API Configuration to see the exact API URL. Verify the API version — v3 is current for SDP Cloud; some older on-premise installations still use v2. Check with your ManageEngine administrator if the response schema doesn't match the documentation.

### ServiceDesk Plus API Call returns 400 or unexpected error when trying to filter tickets

Cause: The input_data parameter is malformed — either the JSON inside it is invalid, it's not URL-encoded correctly, or the search_criteria structure doesn't match the SDP API v3 format.

Solution: Validate the JSON string in the input_data variable using a JSON validator before URL-encoding. The search_criteria structure must follow SDP's documented format exactly — field names like 'status.name' are SDP-specific and differ from generic REST patterns. Test the raw API call in Hoppscotch (web-based REST client) with the correct Content-Type and input_data before adding it to FlutterFlow.

### The app works in FlutterFlow test mode but fails on mobile with connection timeout

Cause: For on-premise ManageEngine products, the server is behind a corporate firewall or VPN. FlutterFlow's web-based test mode may run from your browser while you're on-network, but the mobile device on cellular can't reach the internal server.

Solution: Connect the test device to the corporate WiFi or enable VPN before testing. For production deployments, consider whether all app users will have VPN access. If not, expose the ManageEngine API through a reverse proxy with appropriate authentication, or migrate to ManageEngine Cloud.

### Zoho OAuth refresh token stops working after a few days

Cause: Zoho refresh tokens can expire if they haven't been used within a certain period, or if the OAuth scope was modified, or if the user revoked app permissions in their Zoho account.

Solution: Re-run the initial Zoho OAuth authorization flow to obtain a fresh refresh token. Store the new refresh token in your Firebase Cloud Function environment variables. For long-running production apps, monitor refresh failures in your Cloud Function logs and alert on failures so you can re-authorize before users see authentication errors.

## Frequently asked questions

### Do I need different FlutterFlow API Groups for different ManageEngine products?

Yes — each ManageEngine product has its own API base URL and potentially different authentication. If your app needs data from both ServiceDesk Plus (ITSM tickets) and OpManager (network alarms), create two separate API Groups in FlutterFlow: one for each product with the corresponding base URL and auth header. They cannot share a single API Group because their URLs and response schemas differ entirely.

### Why does ServiceDesk Plus use 'input_data' instead of a standard JSON body?

ServiceDesk Plus's API design predates modern REST conventions, and many of its endpoints encode request parameters as URL-form-encoded data rather than JSON request bodies. The 'input_data' parameter contains a URL-encoded JSON string, which is then parsed server-side. This is a legacy pattern from earlier SDP versions that has been carried forward. The v3 API has improved this for some endpoints, but many filtering and search endpoints still use the input_data pattern. Always check the SDP API v3 documentation for the specific endpoint you're targeting.

### How do I handle Zoho OAuth token expiration in a production FlutterFlow app?

Zoho access tokens expire after approximately 1 hour. In a production app, your Firebase Cloud Function should check if the current token is close to expiring (Zoho access tokens include an 'expires_in' field in the refresh response) and proactively refresh it before it expires. In FlutterFlow, call the refresh function when the app launches or when an API Call returns a 401, then retry the failed call with the new token. Store the expiry time in App State alongside the token so the app can refresh proactively rather than reactively.

### Can the FlutterFlow app work offline with ManageEngine data?

FlutterFlow doesn't have a built-in offline data sync mechanism for API Call data. You can implement a cache pattern by writing fetched ticket and alarm data to Firebase Firestore when online, and reading from Firestore when offline. The ManageEngine API calls then serve as the data source for the online sync; Firestore serves as the offline cache. This requires more architecture but gives technicians access to recently viewed ticket data when they lose connectivity.

### Is it safe to use a ManageEngine on-premise API key in a FlutterFlow app distributed to multiple users?

App Constants in FlutterFlow are compiled into the Flutter binary, which means a technically capable person can extract them from a distributed APK. For an internal team app distributed to trusted IT technicians via MDM or direct APK install, this risk is generally acceptable — the token is scoped to one technician account's permissions. For any app distributed through public app stores or to external users, route all ManageEngine API calls through a Firebase Cloud Function that holds the credentials server-side, and authenticate users to the function using Firebase Authentication before granting API access.

---

Source: https://www.rapidevelopers.com/flutterflow-integrations/manageengine
© RapidDev — https://www.rapidevelopers.com/flutterflow-integrations/manageengine
