Grouper Freshservice requester provisioner
The info on this page applies to Grouper v4 and above.
External System
Grouper Freshservice external system
Links
Freshservice URL: https://somedomainname.freshservice.com
Overview
The Freshservice Requester Provisioner manages requester users, requester groups, and group memberships in a Freshservice instance via the Freshservice REST API v2.
The provisioning type is membershipObjects, meaning Grouper independently manages three object types in Freshservice:
Entities — Freshservice requester users (people who submit tickets)
Groups — Freshservice requester groups
Memberships — Associations between requester users and requester groups
Because Freshservice assigns its own numeric IDs to users and groups, the provisioner uses group and entity link (target attribute value caches) to track the Freshservice-assigned IDs for each Grouper object.
The provisioner class is edu.internet2.middleware.grouper.app.freshServiceRequester.FreshRequesterProvisioner.
You must track Freshservice agents in your Grouper registry and subtract them as provisionable from your requester provisioner.
Freshservice has a SCIM app that as of Feb 2026 does not follow the SCIM spec and does not work. It is recommended to use this provisioner instead.
Provisioning attributes
Advice
Provisioning type is hardcoded to membershipObjects
Use group and entity link (since there are uuids in the target for groups and entities that need to be looked up)
Requester group attributes. API
Grouper Attribute Name | Type | Required? | Freshservice API Field | Description |
|---|---|---|---|---|
| String (though its numeric) | Yes |
| Freshservice-assigned group ID. Read-only / select-only. Do not translate this from Grouper. Configure it as a target attribute and cache it in an attribute value cache so the provisioner can look up the group by ID on subsequent runs. |
| String | Yes |
| The display name of the requester group in Freshservice. Typically translated from the Grouper group |
| String | No |
| The description of the requester group. Typically translated from the Grouper group |
Requester (user) attributes. API
The provisioner supports the following built-in entity attributes. These appear in the dropdown when configuring target entity attributes in the UI. Deleting an entity through the provisioner performs a soft-delete (deactivation), not a permanent removal.
Grouper Attribute Name | Type | Required? | Freshservice API Field | Description |
|---|---|---|---|---|
| String (though its numeric) | Yes |
| Freshservice-assigned user ID. Read-only / select-only. Do not translate from Grouper. Cache it in an attribute value cache for linking. |
| String | Yes (for create) |
| First name of the requester in Freshservice. |
| String | Yes (for create) |
| Last name of the requester in Freshservice. |
| String | Yes |
| Primary email address. Note: the Freshservice API field is |
| String | No |
| External identifier (e.g., institutional ID, pennkey). Can be used as an entity matching attribute for lookups. |
| String | No |
| The user's job title. |
| String | No |
| The user's work phone number. |
| Long | No |
| The Freshservice department ID. The API uses an array ( |
| Long | No |
| Freshservice ID of the user's reporting manager (must be an existing requester). |
| String | No |
| The user's address. |
Custom Fields
Freshservice supports custom fields on requester users. These are arbitrary fields defined in your Freshservice instance's admin settings under Admin → User Management → User Fields (requester fields)
In Grouper, custom fields are represented as entity attributes with the prefix customField_. For example, if your Freshservice instance has a custom field called pennkey, configure it in Grouper as customField_pennkey.
Supported Custom Field Value Types
Type | Notes |
|---|---|
| Text values |
| Whole numbers (integral). Internally normalized to |
| True/false values |
Decimal numbers (Float/Double) are not supported as custom field values and will cause a runtime exception.
Custom Field Configuration Example
Custom fields do not appear in the entity attribute name dropdown in the UI. You must type the attribute name manually using the customField_<fieldName> convention. The <fieldName> portion must exactly match the custom field name defined in Freshservice. Set the attribute name with an EL:
${'customField_theName'}It is easiest to just have "text" type field, but if you must have numeric, make sure the value type on the custom field in Grouper is Long (recommended)
Custom Fields as Search/Matching Attributes
Custom fields can be used for entity searching. When searching by a custom field, the Freshservice query uses the format:
GET /api/v2/requesters?query=<fieldName>:'value' (for String values)
GET /api/v2/requesters?query=<fieldName>:12345 (for numeric values)
Entity and Group Matching
Matching tells the provisioner how to find existing Freshservice objects that correspond to Grouper objects. This is critical for the provisioner to correctly link Grouper groups/entities to their Freshservice counterparts.
Entity Matching
Generally you should have id as the first search/match attribute. Then if you can have externalId or a customField with is opaque and unchanging, that is best to change email changes. Otherwise use id/email. The entity matching system supports searching by the following attribute names:
Searching/Matching Attribute | Freshservice Lookup Method | Notes |
|---|---|---|
|
| Direct lookup by Freshservice-assigned ID. Fastest method. |
|
| Search by primary email address. Most common for initial matching. |
|
| Search by external identifier. |
|
| Search by any custom field for text. The |
|
| Search by any custom field for long (integer). The |
If multiple requesters match a query, the provisioner throws an exception. Ensure your matching attributes have unique values. Freshservice agents (users with is_agent=true) are automatically excluded from all search results.
CRUD Operations
The provisioner supports the following operations. Use customizeEntityCrud, customizeGroupCrud, and customizeMembershipCrud to enable fine-grained control over which operations are allowed.
Object | Operation | Supported? | Notes |
|---|---|---|---|
Entity (User) | Retrieve all | Yes | Retrieves all active requesters (not agents) |
Retrieve one | Yes | By id, email, externalId, or customField_* | |
Insert | Yes | Upsert behavior: reactivates and updates if email already exists | |
Update | Yes | GET-then-PUT: only changed fields are sent | |
Delete | Yes | Soft delete (deactivation), not permanent removal | |
Group | Retrieve all | Yes | Excludes rule_based groups |
Retrieve one | Yes | By id (direct) or name (client-side filter) | |
Insert | Yes | 409 = already exists | |
Update | Yes | GET-then-PUT | |
Delete | Yes | Permanent deletion | |
Membership | Retrieve by group | Yes | Lists all members of a requester group |
Insert | Yes | Adds user to group | |
Delete | Yes | Removes user from group |
Behavioral Notes
Entity Create is Upsert
When creating a new requester user, the provisioner first checks if a user with the same primary_email already exists in Freshservice (including deactivated users). If a match is found:
If the existing user is deactivated, the provisioner reactivates them first (
PUT /api/v2/requesters/{id}/reactivate).Then the provisioner updates the user with all the provisioned attribute values.
Only if no matching email is found does the provisioner perform a true POST create.
Entity Delete is Soft Delete
When the provisioner deletes an entity, it calls DELETE /api/v2/requesters/{id}, which deactivates the requester in Freshservice rather than permanently removing them. The user remains in Freshservice in a deactivated state and can be reactivated later.
A separate GDPR "forget" operation (DELETE /api/v2/requesters/{id}/forget) exists in the Freshservice API for permanent deletion, but it is not used by the provisioner's standard delete flow.
Entity Update is GET-then-PUT
When updating a requester user, the provisioner:
GETs the current state of the user from Freshservice.
Deep-copies the response to a mutable JSON object.
Strips read-only fields:
id,created_at,has_logged_in,is_agent,updated_at,work_schedule_id,department_names,location_name.Overlays only the fields that changed (from the provisioning object changes).
PUTs the modified object back.
This approach ensures that Freshservice attributes not managed by Grouper are preserved.
Agents are Excluded
Freshservice distinguishes between requesters (users who submit tickets) and agents (IT staff who handle tickets). This provisioner only manages requesters. Users with is_agent=true are automatically filtered out of all retrieval and search operations.
Rule-Based Groups are Excluded
Freshservice "rule-based" groups (identified by type: "rule_based" in the API response) cannot be managed via the API. These groups are automatically excluded from all group retrieval operations.
Department IDs
The Freshservice API models department associations as an array (department_ids), supporting multiple departments per user. However, the Grouper provisioner models this as a single departmentId (Long). When reading from Freshservice, only the first department ID in the array is used. When writing, the single value is wrapped in a one-element array. There is an example GSH daemon to manage departments in Freshservice.
You need a table to store the departments locally (with their ID), and you can join to that in your entity attribute SQL resolver for the provisioner. Here is sample code that you can tailor to your environment:
Pagination
All list operations (retrieve all users, groups, memberships) use paginated API calls. The page size is controlled by the pageSize property on the external system. The Freshservice API maximum is 100 items per page and defaults to 100 so you shouldn't need to adjust it.
Example configuration