Okta → ManageEngine SDP / Workflow Templates

Prepared: April 2, 2026
0% complete

Okta Workflows → ManageEngine ServiceDesk Plus

Complete card-by-card templates for automating ticket creation, assignment, and team management in ManageEngine SDP Cloud from Okta Workflows.

Identity Provider: Okta
ITSM: ManageEngine SDP Cloud
Auth: Zoho OAuth 2.0 (US)
API: SDP V3 REST API
Region: US (accounts.zoho.com)
Prepared: April 2, 2026
Integration Architecture
Okta Event
Okta Workflow
Zoho OAuth
→ token →
HTTP Card
SDP Cloud API


Okta fires event  |  Workflow gets OAuth token  |  Calls SDP V3 API with form-encoded input_data

Setup Prerequisites

Cloud SDP (your setup): Authentication uses Zoho OAuth 2.0. Access tokens expire every 60 minutes, so Flow 0 handles automatic refresh. Complete these steps before building any workflow.

Step 1: Create Zoho OAuth Self Client

  1. Go to https://api-console.zoho.com/
  2. Click Add Client → choose Self Client
  3. Generate an authorization code with scopes:
    SDPOnDemand.requests.ALL,SDPOnDemand.setup.ALL
  4. Exchange the auth code for tokens (code expires in ~10 minutes):
    POST https://accounts.zoho.com/oauth/v2/token grant_type=authorization_code &client_id={{YOUR_CLIENT_ID}} &client_secret={{YOUR_CLIENT_SECRET}} &code={{AUTH_CODE}}
  5. Save the refresh_token from the response — you'll need it for the config table

Step 2: Create SDP_Config Table in Okta Workflows

Create this table before building any flows. It stores credentials so you never hardcode secrets in cards.

keyvalueNotes
base_urlhttps://sdpondemand.manageengine.comUS data center
client_idYour Zoho OAuth client IDFrom api-console.zoho.com
client_secretYour Zoho OAuth client secretFrom api-console.zoho.com
refresh_tokenYour Zoho refresh tokenFrom Step 1 exchange
access_token(initially empty)Managed by Flow 0
token_expires_at(initially empty)Managed by Flow 0
auth_modecloudYour deployment type

0 Flow 0: OAuth Token Refresh Helper

Helper flow (no trigger) that ensures a valid Zoho OAuth access token. Called by every other flow before making SDP API requests. Tokens expire every 60 minutes; this flow caches them with a 2-minute buffer.

Cards 1-3: Read Current Token State

Card 1: Tables > Read Row — Get Current Token

  • Table: SDP_Config
  • Where: key is access_token
  • Output: value (current access token)

Card 2: Tables > Read Row — Get Expiry

  • Table: SDP_Config
  • Where: key is token_expires_at
  • Output: value (ISO timestamp)

Card 3: Date > Now

  • Output: current_time (ISO timestamp)
Card 4: Branch — Is Token Still Valid?

Card 4: Branching > If/Else

  • Condition: card2.value is after card3.current_time
  • True path: Skip to Card 10 (return cached token)
  • False path: Continue to Card 5 (refresh needed)
Cards 5-8: Refresh the Token

Cards 5-7: Read OAuth Credentials from Table

  • Card 5: Read Row where key = client_id
  • Card 6: Read Row where key = client_secret
  • Card 7: Read Row where key = refresh_token

Card 8: HTTP > Raw Request — Call Zoho Token Endpoint

  • Method: POST
  • URL: https://accounts.zoho.com/oauth/v2/token
  • Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token &client_id={{card5.value}} &client_secret={{card6.value}} &refresh_token={{card7.value}}

Expected response:

{ "access_token": "1000.xxxx.yyyy", "token_type": "Bearer", "expires_in": 3600 }
Cards 9-10: Store & Return

Card 9a: Date > Add — Calculate Expiry

  • Add (expires_in - 120) seconds to card3.current_time
  • The 120-second buffer prevents using tokens that are about to expire

Card 9b: Tables > Update Row — Store Token

  • Where: key = access_token
  • Set: value = card8.body.access_token

Card 9c: Tables > Update Row — Store Expiry

  • Where: key = token_expires_at
  • Set: value = card9a.output

Card 10: Flow Control > Return

  • From True path (token valid): return card1.value
  • From False path (refreshed): return card8.body.access_token

Testing

  • Clear access_token and token_expires_at in SDP_Config table
  • Run flow — should fetch new token and populate both rows
  • Run again immediately — should return cached token (no Zoho call)
  • Verify token works: manual GET to /api/v3/requests

1 Flow 1: Create Ticket SDP API

Creates a ServiceDesk Plus request/ticket from an Okta event. The most common starting flow.

Trigger Options

TriggerWhen it firesGood for
Okta > User Assigned to ApplicationUser gets app assignedAccess request tickets
Okta > User DeactivatedUser is deactivatedOffboarding tickets
Okta > User CreatedNew user in OktaOnboarding tickets
Okta > User Added to GroupGroup membership changeRole-based tickets
API EndpointExternal webhook callCustom integrations

Template trigger: Okta > User Assigned to Application

Available outputs: user.id, user.profile.email, user.profile.firstName, user.profile.lastName, user.profile.department, target.displayName

Cards 1-5: Auth Setup

Card 1: Tables > Read Row — Get Base URL

  • Table: SDP_Config
  • Where: key is base_url
  • Output: https://sdpondemand.manageengine.com

Card 2: Flow Control > Call Flow — Get Token

  • Flow: 00-oauth-token-refresh
  • Output: access_token
Cards 6-8: Build & Send Request

Card 6: Compose — Build Ticket JSON

{ "request": { "subject": "Application Access: {{trigger.target.displayName}} for {{trigger.user.profile.firstName}} {{trigger.user.profile.lastName}}", "description": "Automated request: {{trigger.user.profile.firstName}} {{trigger.user.profile.lastName}} ({{trigger.user.profile.email}}) has been assigned to {{trigger.target.displayName}} in Okta. Please provision access in downstream systems.", "requester": { "email_id": "{{trigger.user.profile.email}}" }, "priority": { "name": "Normal" }, "category": { "name": "Access Management" }, "request_type": { "name": "Service Request" }, "group": { "name": "IT Help Desk" } } }

Customize: Change category, group, priority, and request_type to match your SDP configuration. Add "technician": {"name": "Jane Doe"} to auto-assign.

Card 7: Text > Encode URI Component + Compose

  1. Pass Card 6 output through Text > Encode URI Component
  2. Use Compose to prepend input_data=

Critical: The SDP V3 API does NOT accept raw JSON bodies. You must send Content-Type: application/x-www-form-urlencoded with the JSON inside input_data=. This is the #1 cause of "400 Bad Request" errors.

Card 8: HTTP > Raw Request — Create Ticket

  • Method: POST
  • URL: {{card1.value}}/api/v3/requests
  • Headers:
Content-Type: application/x-www-form-urlencoded Authorization: Zoho-oauthtoken {{card2.access_token}}
  • Body: {{card7.output}} (the input_data=... string)
Cards 9-11: Response Handling

Card 9: Branching > If/Else — Check Response

  • Condition: card8.statusCode equals 201
  • True: Card 10 (success)
  • False: Card 11 (error)

Card 10: Success — Log or Notify

  • Option A: Create Row in a Ticket_Log table with ticket_id, subject, user_email, created_at
  • Option B: Send Slack/Teams message with the new ticket ID

Card 11: Error Handling

  • Extract error from card8.body.response_status.messages
  • Send error notification via Slack/email
  • Consider: retry once on 401 (token may have expired mid-flow)

Simplified Version (Cloud, No Branching)

[Trigger: User Assigned to App][Call Flow 0: Get OAuth Token][Compose: Build JSON payload][Text: Encode URI Component on JSON][Compose: Prepend "input_data="][HTTP Raw Request] POST sdpondemand.manageengine.com/api/v3/requests Header: Authorization: Zoho-oauthtoken {{token}} Header: Content-Type: application/x-www-form-urlencoded Body: input_data={{encoded_json}} ↓ [If statusCode == 201 → log success, else → log error]

Testing

  • Create a test user in Okta (or use your own in sandbox)
  • Assign the test user to an application
  • Verify flow triggers and ticket appears in SDP
  • Check ticket has correct requester email, subject, category
  • Test error: use wrong API key, verify error handling fires

2 Flow 2: Assign / Reassign Ticket Helper

Updates an existing SDP ticket to assign or reassign it to a technician and/or support group. Typically called as a sub-flow from Flow 1 or Flow 4.

Input Parameters

ParameterTypeRequiredExample
request_idStringYes"123456"
technician_nameStringOne of these"Jane Doe"
technician_idStringOne of these"71000000099"
group_nameStringNo"Tier 2 Support"
Cards 1-6: Build Update Payload

Cards 1-2: Get Base URL + Auth Token

Same as Flow 1: read base_url from table, call Flow 0 for token.

Card 3: Branching — Use ID or Name?

  • Condition: input.technician_id is not empty
  • True: Build payload with id
  • False: Build payload with name

Cards 4a/4b: Compose — Build JSON

With ID (preferred):

{ "request": { "technician": { "id": "{{input.technician_id}}" }, "group": { "name": "{{input.group_name}}" } } }

With Name:

{ "request": { "technician": { "name": "{{input.technician_name}}" } } }

Cards 5-6: Encode + Form Body

  1. Text > Encode URI Component on the JSON
  2. Compose: prepend input_data=
Cards 7-9: Send & Return

Card 7: HTTP > Raw Request — Update Ticket

  • Method: PUT
  • URL: {{base_url}}/api/v3/requests/{{input.request_id}}
  • Headers: Content-Type: application/x-www-form-urlencoded + Authorization: Zoho-oauthtoken
  • Body: input_data=...

No separate "assign" endpoint exists. Assignment is just an update (PUT) on the request object. You can also assign at creation time by including technician in the Flow 1 payload.

Card 8: Check Response (statusCode == 200)

Card 9: Return

  • success: true/false
  • request_id: the input request_id
  • message: success or error text

Lookup Technician by Email (Optional Pre-Step)

If you only have the user's email from Okta, look up their SDP technician ID first:

GET {{base_url}}/api/v3/technicians?input_data={"list_info":{"search_criteria":[{"field":"email_id","condition":"is","value":"{{email}}"}]}} // Parse: body.technicians[0].id

Testing

  • Create a ticket manually or via Flow 1
  • Call this flow with the ticket ID and a valid technician name
  • Verify in SDP that the ticket is assigned to that technician
  • Test reassignment: call again with a different technician
  • Test with invalid technician name — verify error handling

3 Flow 3: Add User to Support Group SDP API

Adds a technician to a support group in SDP. Since the API uses full member replacement (not additive), this flow reads current members, appends the new one, and writes back the full list.

Destructive API: The PUT /support_groups/{id} endpoint replaces the entire members list. If you send only the new member, all existing members are removed. Cards 7-9 below handle this safely.

Prerequisite: Group Mapping Table

Create Okta_SDP_Group_Map table mapping Okta groups to SDP support group IDs:

okta_group_namesdp_group_idsdp_group_name
IT-HelpDesk301000000001IT Help Desk
Network-Ops301000000002Network Operations
Desktop-Support301000000003Desktop Support

Get your group IDs: GET /api/v3/support_groups — the id field in each result.

Cards 1-6: Lookup Group & Technician

Card 1: Tables > Read Row — Look Up SDP Group

  • Table: Okta_SDP_Group_Map
  • Where: okta_group_name is {{trigger.target.displayName}}
  • Output: sdp_group_id, sdp_group_name

Card 2: Branching — Group Mapping Exists?

  • True: Continue
  • False: Log "No SDP mapping for Okta group" and end flow

Not every Okta group needs an SDP mapping. This prevents errors for unmapped groups.

Cards 3-4: Auth + Base URL

Call Flow 0 for token, read base_url from table.

Card 5: HTTP > Raw Request — Look Up Technician by Email

GET {{base_url}}/api/v3/technicians?input_data={"list_info":{"search_criteria":[{"field":"email_id","condition":"is","value":"{{trigger.user.profile.email}}"}]}} // Save: body.technicians[0].id as new_tech_id

Card 6: Branching — Technician Found?

  • True: Continue
  • False: Log "User is not a technician in SDP" and end
Cards 7-9: Read Members, Check Dupes, Append

Card 7: HTTP > GET Current Group Members

GET {{base_url}}/api/v3/support_groups/{{sdp_group_id}} // Parse: body.support_group.members → array of {"id": "...", "name": "..."}

Card 8: List > Find — Already a Member?

  • Search members for item where id equals new_tech_id
  • Found: Log "Already in group" and end (prevents duplicate)
  • Not found: Continue

Card 9: List > Map + Append — Build Updated Array

  1. List > Map existing members to extract just IDs: {"id": "{{item.id}}"}
  2. List > Append: {"id": "{{new_tech_id}}"}
Cards 10-14: Update Group

Card 10: Compose — Build Update Payload

{ "support_group": { "members": {{card9.output}} } }

Cards 11-12: Encode + Form Body

Same pattern: URL-encode, prepend input_data=

Card 13: HTTP > PUT — Update Support Group

  • Method: PUT
  • URL: {{base_url}}/api/v3/support_groups/{{sdp_group_id}}

Card 14: Check Response (statusCode == 200)

  • Success: Log "Added {{email}} to {{group_name}}"
  • Error: Log error from response

Race condition risk: If two users are added to the same Okta group simultaneously, two flow executions could read the same member list, each append their user, and the second PUT overwrites the first addition.

Mitigations:

  • Use Flow Control > Wait to serialize writes to the same group
  • Add a periodic reconciliation flow that syncs Okta groups to SDP groups
  • Accept the small risk for low-frequency group changes

Testing

  • Add Okta-to-SDP group mapping in Okta_SDP_Group_Map table
  • Verify the user exists as a technician in SDP
  • Add the user to the mapped Okta group
  • Check SDP: user appears in the support group
  • Critical: Verify existing members are still present after update
  • Test duplicate: add same user again — should no-op
  • Test unmapped group — flow should exit cleanly

4 Flow 4: Onboarding Bundle Full Example

End-to-end example: when a new user is created in Okta, this flow creates an onboarding ticket and optionally adds the user to a support group based on department.

Trigger: Okta > User Created

Outputs: user.profile.email, firstName, lastName, department, title, manager

Prerequisite: Dept_Onboarding_Config Table

departmentsdp_categorysdp_groupsdp_priority
EngineeringOnboardingIT Help DeskHigh
SalesOnboardingIT Help DeskNormal
FinanceOnboardingIT Help DeskNormal
ExecutiveOnboardingExecutive SupportUrgent
Cards 1-6: Create Onboarding Ticket

Card 1: Read Department Config

  • Table: Dept_Onboarding_Config
  • Where: department is {{trigger.user.profile.department}}

Card 2: Branching — Department Mapped?

  • True: Use mapped values
  • False: Defaults: category=Onboarding, group=IT Help Desk, priority=Normal

Card 3: Auth Setup (Call Flow 0 + Read Base URL)

Card 4: Compose — Build Onboarding Ticket

{ "request": { "subject": "New Hire Onboarding: {{firstName}} {{lastName}} ({{department}})", "description": "New employee onboarding request.\n\nName: {{firstName}} {{lastName}}\nEmail: {{email}}\nDepartment: {{department}}\nTitle: {{title}}\n\nPlease complete:\n- [ ] Provision laptop\n- [ ] Create email/calendar access\n- [ ] Set up VPN credentials\n- [ ] Provision department-specific apps\n- [ ] Schedule IT orientation", "requester": { "email_id": "{{email}}" }, "priority": { "name": "{{card1.sdp_priority}}" }, "category": { "name": "{{card1.sdp_category}}" }, "group": { "name": "{{card1.sdp_group}}" }, "request_type": { "name": "Service Request" } } }

Cards 5-6: Encode, Build Form Body, POST to /api/v3/requests

Same pattern as Flow 1, Cards 7-8.

Cards 7-11: Chain Actions

Card 7: Save Ticket ID

Compose: extract card6.body.request.id as ticket_id

Card 8: Branching — Auto-Assign?

  • True: Call Flow 2 with ticket_id + technician info
  • False: Let SDP auto-assignment handle it

Card 9: Branching — Add to Support Group?

  • Condition: Department in [IT, Engineering, Support]
  • True: Call Flow 3 or add user to mapped Okta group
  • False: Skip

Card 10: (Optional) Parallel Actions

  • Path A: Slack/Teams welcome message
  • Path B: Calendar event for IT orientation

Card 11: Log to Onboarding_Log Table

  • user_email, department, ticket_id, sdp_group, created_at, status
[Okta: User Created][Read Dept Config][Get Auth (Flow 0)][Build Ticket JSON][Create Ticket via API][Save ticket_id] ↓ ↓ [Auto-assign? → Call Flow 2] [Add to team? → Call Flow 3][Log to Onboarding_Log]

Variations

Offboarding (User Deactivated)
  • Trigger: Okta > User Deactivated
  • Subject: "Offboarding: {{firstName}} {{lastName}}"
  • Checklist: Revoke access, collect equipment, disable accounts
  • Inverse of Card 9: Remove user from SDP group (filter instead of append)
App-Specific Provisioning
  • Trigger: Okta > User Assigned to Application
  • Subject: "Provision {{app.displayName}} for {{firstName}}"
  • Category: Map app names to SDP categories via lookup table

Testing

  • Create a test user in Okta with a mapped department
  • Verify ticket created with correct category, group, priority
  • Verify ticket description contains all user details
  • Test unmapped department — should use defaults
  • If auto-assign enabled, verify technician is set
  • Check Onboarding_Log table for the entry
  • Test duplicate user creation — should be idempotent

API Quick Reference

OperationMethodEndpointKey Fields
Create ticketPOST/api/v3/requestssubject (required), requester, priority, category, technician, group
Update/assign ticketPUT/api/v3/requests/{id}technician, group, status
Get ticketGET/api/v3/requests/{id}
List ticketsGET/api/v3/requestsUse input_data with list_info for filters
Update support groupPUT/api/v3/support_groups/{id}members array (full replacement!)
List techniciansGET/api/v3/techniciansFilter with search_criteria
List support groupsGET/api/v3/support_groupsFilter with search_criteria

Authentication (Cloud / US Region)

Authorization: Zoho-oauthtoken {{access_token}} Content-Type: application/x-www-form-urlencoded // Base URL: https://sdpondemand.manageengine.com // Token endpoint: https://accounts.zoho.com/oauth/v2/token // API console: https://api-console.zoho.com/

Okta Workflows Gotchas

1. The input_data Form Encoding

The SDP V3 API does NOT accept raw JSON. Every request must use application/x-www-form-urlencoded with JSON inside input_data=. Use the "Raw Request" card, not the standard HTTP POST card.

2. OAuth Token Refresh

Zoho access tokens expire every 60 minutes. Okta Workflows doesn't natively manage Zoho tokens. That's why Flow 0 exists — build it first and call it from every other flow.

3. Auth Header Format

Cloud uses Authorization: Zoho-oauthtoken <token> — note: not Bearer. This specific scheme is required.

4. Response Parsing

Successful creates return 201. Updates return 200. Errors return 400/401/500 with response_status.messages array.

5. Rate Limits

SDP Cloud: ~30-50 requests/minute depending on plan. Add a Wait card if you're bulk-creating tickets. Okta Workflows can fire many events rapidly during bulk provisioning.

Master Implementation Checklist

Setup

  • Create Zoho Self Client at api-console.zoho.com
  • Generate auth code with correct scopes
  • Exchange for refresh token
  • Create SDP_Config table in Okta Workflows
  • Populate table with client_id, client_secret, refresh_token, base_url

Build Flows

  • Build Flow 0: OAuth Token Refresh
  • Test Flow 0: verify token fetch and caching
  • Build Flow 1: Create Ticket
  • Test Flow 1: verify ticket creation in SDP
  • Build Flow 2: Assign Ticket
  • Test Flow 2: verify assignment and reassignment
  • Create Okta_SDP_Group_Map table
  • Build Flow 3: Add User to Team
  • Test Flow 3: verify member add with existing members preserved
  • Create Dept_Onboarding_Config table
  • Build Flow 4: Onboarding Bundle
  • Test Flow 4: end-to-end new user onboarding

Go Live

  • Enable flows in production
  • Monitor Flow History for errors in first 48 hours
  • Document any SDP category/group customizations