Overview
Request → approval → Workflow Automator Web Request → app checks addresses in Microsoft 365 → applies the change in Exchange Online → private note, status, optional reply.
Before you start
- Freshservice with the Service Catalog and Workflow Automator (Web Request step).
- Microsoft 365 with Exchange Online (cloud mailboxes; on-premises Exchange isn't supported).
- A Microsoft 365 Global Administrator for the one-time Entra setup (about 10 minutes).
- A Freshservice agent API key for an agent who can edit the relevant tickets.
- About 30 minutes for the full setup.
Step 1 – Microsoft Entra app registration
Open entra.microsoft.com as a Global Administrator.
- a. App registrations → New registration: any name (e.g. "Freshservice M365 Permissions"), "Accounts in this organizational directory only", no redirect URI → Register.
- b. API permissions → Add a permission → APIs my organization uses → "Office 365 Exchange Online" → Application permissions → Exchange.ManageAsApp → Add permissions → Grant admin consent (green tick).
- c. Roles & admins (under Entra ID, not inside the app registration) → Exchange Recipient Administrator → Add assignments → select the app → Active, Permanently assigned → Assign. Can take up to an hour.
- d. Sign-in credential – choose one. Client secret: Certificates & secrets → New client secret → copy the Value (not the Secret ID); shown once. Note the expiry date.
- Certificate (recommended): create a key and certificate, upload the public certificate (.crt/.cer) under Certificates → Upload certificate, note the Thumbprint and expiry. Upload the .crt/.cer; the .key.pem goes into the app, then delete it.
- e. Copy the Directory (tenant) ID and Application (client) ID from the app’s Overview, and the tenant domain ending in .onmicrosoft.com (Entra ID → Domain names).
Mac/Linux
openssl req -x509 -newkey rsa:2048 -nodes -days 730 -subj "/CN=Freshservice M365 Permissions" -keyout freshservice-m365.key.pem -out freshservice-m365.crtWindows – PowerShell 7
$rsa = [System.Security.Cryptography.RSA]::Create(2048)
$req = [System.Security.Cryptography.X509Certificates.CertificateRequest]::new('CN=Freshservice M365 Permissions', $rsa, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
$cert = $req.CreateSelfSigned((Get-Date).AddMinutes(-5), (Get-Date).AddYears(2))
[IO.File]::WriteAllBytes("$PWD/freshservice-m365.cer", $cert.Export('Cert'))
"-----BEGIN PRIVATE KEY-----`n" + [Convert]::ToBase64String($rsa.ExportPkcs8PrivateKey(), 'InsertLineBreaks') + "`n-----END PRIVATE KEY-----" | Set-Content -NoNewline "$PWD/freshservice-m365.key.pem"
$cert.ThumbprintStep 2 – Freshservice API key
Admin → Agents → the agent → Permissions → enable API key; copy it from Profile settings → Your API key.
Step 3 – Service item (the request form)
Admin → Service Catalog → New Item "Mailbox & calendar access".
Fields: Action (Dropdown: Grant, Remove), Permission (Dropdown or multi-select, e.g. Full Access, Send As, Send on Behalf, Calendar - Reviewer, Calendar - Editor, Calendar - Delegate (incl. private)), User needing access, Target mailbox.
User and mailbox fields can be a Freshservice user picker (Dropdown → Select from Data Source → All Users) or a single-line text field for an email address; the app recognises both. A picked person becomes their work email (primary first, then secondary emails). You can use both on one form (e.g. picker + text field) – list both names in the settings, picker first.
Optional: separate items for mailbox and calendar access, each with its own wording and approval.
Internal field names: the label in lower case with underscores (e.g. "User needing access" → user_needing_access). Check them by raising a test request and opening https://yourcompany.freshservice.com/api/v2/tickets/<ticket_id>/requested_items while signed in.
Step 4 – Install the app and fill in the settings
| 1 | Microsoft 365 connection | Cloud, tenant ID, tenant domain, client ID, secret or certificate → Test connection |
|---|---|---|
| 2 | Freshservice connection | yourcompany.freshservice.com + API key → Test connection & load groups |
| 3 | How the app starts | Allow workflows, copy the webhook secret – shown only until you save; only apply approved requests; optional trigger groups/statuses |
| 4 | Service item fields | Comma-separated names allowed |
| 5 | Permissions | Turn off what you never want automated; your own wording |
| 6 | Rules | Protected mailboxes, tenant domains only, change limit, dry run |
| 7 | After the app runs | Statuses for success/warnings/failure, fallback group, reply to requester |
| 8 | Mailbox & calendar options | Outlook auto-mapping, calendar invitations |
| 9 | Alerts & monitoring | Result field, Teams/Slack/Power Automate webhook, expiry date, test alert |
| 10 | Recent runs | Review recent results |
Step 5 – Workflow Automator
Admin → Workflow Automator → New Automator → Module: Tickets, Event Based Workflow. Event: Service Request is Raised. Condition: Requested items includes any → your item(s). Approval block.
On Approved, a Web Request: Request Type POST; Endpoint = the webhook URL (app settings, section 3, after installing); Credentials → API key, Key = X-Webhook-Secret, Value = the webhook secret; Header Content-Type: application/json. Save and Activate.
Workflows don't notify apps directly, so the Web Request is what starts the app.
Optional body fields for workflows without a service item: "action", "permission", "mailbox", "user"; add "dry_run": true to test a workflow.
Body
{"ticket_id": "{{ticket.id}}"}Step 6 – Test with Dry run
Turn on Dry run in section 6, raise a test service request and approve it. Check the private note for the intended changes and any warnings; no permissions are changed.
When the result is correct, turn off Dry run and repeat the request. Check Exchange Online and the ticket note; Outlook can take up to an hour to reflect access.
What the results look like
The private note lists each change with ✅/❌, names and addresses, warnings and how to retry. The app updates statuses, assigns the fallback group when needed, and can reply to the requester.
Optional M365 result field values: Processing, Succeeded, Succeeded with warnings, Failed, Blocked, Dry run.
Alerts & monitoring
Alerts when the app stops working (once, then daily), when it recovers, on rejected workflow calls, optionally on every failed request; reminders 30/14/7/1 days before the secret or certificate expires; daily health check.
Updating vs reinstalling
An update keeps all settings, secrets, the webhook URL and secret (open the settings and click Save). Uninstalling and reinstalling creates a new webhook URL and secret and needs the settings again.
Renewing credentials
Create the new secret/certificate before the old one expires (both work meanwhile), enter it in section 1, update the expiry date, save, then delete the old one in Entra.
Troubleshooting
| “This request isn't approved” | Add an Approval step or untick “Only apply approved requests”. |
|---|---|
| “… was not found in Microsoft 365” | Typo or the person has left – use the suggestion. |
| “… has no email address in this Microsoft 365 tenant” | Add their work email to their Freshservice profile. |
| “Exchange returned 403” | Assign the Exchange Recipient Administrator role (allow up to an hour). |
| “Webhook rejected” | Copy the endpoint and secret again. |
| “Not attempted: time limit” | Trigger again – changes already made are kept. |
| The Freshservice domain must be your yourcompany.freshservice.com address | Enter the freshservice.com domain. |
| "Not tested" on the settings page during an app update | Expected, save and test again afterwards. |
| Couldn't be checked in Microsoft 365 right now | Exchange was busy, try again. |
| User … is a guest/distribution group/shared mailbox | Give access to the person or a mail-enabled security group. |
| Nothing happens when a request is raised | Is the workflow activated and does it include the Web Request with the right URL and secret? |