Summary and recommendation
The Freshservice REST API (v2) uses HTTP Basic Auth with an API key as the username and any non-empty string as the password; there is no OAuth 2.0 option for server-to-server calls. The API key is scoped to the generating agent's permissions, so integrations should use a dedicated admin-level service account key.
Agents and Requesters are entirely separate API resources - /api/v2/agents and /api/v2/requesters - and operations on one object type do not affect the other.
Rate limits are enforced per account (not per key), meaning multiple integrations sharing one Freshservice account draw from the same bucket: 50 req/min on Starter up to 400 req/min on Enterprise, with HTTP 429 and a Retry-After header on breach.
Pagination is offset-based using page and per_page (max 100); there is no cursor, so large datasets require sequential page iteration.
API quick reference
| Has user API | Yes |
| Auth method | HTTP Basic Auth using API key as username and any string as password (e.g., 'X'). API key found under Profile Settings in Freshservice. |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Pro or Enterprise (SCIM provisioning available via marketplace apps on Pro+; native SCIM endpoint on Enterprise) |
Authentication
Auth method: HTTP Basic Auth using API key as username and any string as password (e.g., 'X'). API key found under Profile Settings in Freshservice.
Setup steps
- Log in to Freshservice as an admin.
- Navigate to Profile Settings (top-right avatar menu).
- Copy the API Key displayed on the page.
- Use the API key as the username in HTTP Basic Auth; use any non-empty string (e.g., 'X') as the password.
- Base64-encode 'apikey:X' and pass as Authorization: Basic
header.
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique system-generated ID | auto | read-only | Used in URL path for updates/deletes |
| first_name | string | First name of the agent/requester | required | optional | |
| last_name | string | Last name | optional | optional | |
| string | Primary email address; must be unique | required | optional | Used as login identifier | |
| phone | string | Phone number | optional | optional | |
| mobile_phone_number | string | Mobile phone number | optional | optional | |
| job_title | string | Job title | optional | optional | |
| department_ids | array[integer] | IDs of departments the user belongs to | optional | optional | Agents use department_ids; requesters also support department_ids |
| location_id | integer | ID of the user's location | optional | optional | |
| role_ids | array[integer] | Agent role IDs assigned (agents only) | required for agents | optional | At least one role required when creating an agent |
| group_ids | array[integer] | Agent group memberships (agents only) | optional | optional | |
| occasional | boolean | Whether agent is an occasional (non-full-time) agent | optional | optional | Defaults to false |
| active | boolean | Whether the user account is active | auto (true) | optional | Set false to deactivate |
| time_zone | string | User's time zone (e.g., 'Eastern Time (US & Canada)') | optional | optional | |
| language | string | Preferred language code (e.g., 'en') | optional | optional | |
| custom_fields | object | Key-value map of custom field values | optional | optional | Field keys are account-specific |
| created_at | datetime | ISO 8601 timestamp of creation | auto | read-only | |
| updated_at | datetime | ISO 8601 timestamp of last update | auto | auto | |
| reporting_manager_id | integer | ID of the user's reporting manager (requesters) | optional | optional | Requester-specific field |
| vip_user | boolean | Whether requester is flagged as VIP | optional | optional | Requester-specific field |
Core endpoints
List Agents
- Method: GET
- URL:
https://<domain>.freshservice.com/api/v2/agents - Watch out for: Returns only active agents by default. Use ?active=false to list deactivated agents.
Request example
GET /api/v2/agents?per_page=100&page=1
Authorization: Basic <base64(apikey:X)>
Response example
{
"agents": [
{"id": 1, "first_name": "Jane", "email": "jane@acme.com", "active": true}
]
}
Create Agent
- Method: POST
- URL:
https://<domain>.freshservice.com/api/v2/agents - Watch out for: roles array is required; each entry needs role_id and assignment_scope. Creating an agent consumes a licensed seat.
Request example
POST /api/v2/agents
Content-Type: application/json
{
"first_name": "Jane",
"email": "jane@acme.com",
"roles": [{"role_id": 1, "assignment_scope": "entire_helpdesk"}]
}
Response example
{
"agent": {
"id": 42,
"email": "jane@acme.com",
"active": true,
"created_at": "2024-01-10T10:00:00Z"
}
}
Update Agent
- Method: PUT
- URL:
https://<domain>.freshservice.com/api/v2/agents/{id} - Watch out for: Uses PUT (full-style), but only supplied fields are updated. Email cannot be changed via API if SSO is enabled.
Request example
PUT /api/v2/agents/42
Content-Type: application/json
{
"job_title": "Senior Engineer",
"department_ids": [3]
}
Response example
{
"agent": {
"id": 42,
"job_title": "Senior Engineer",
"updated_at": "2024-06-01T12:00:00Z"
}
}
Deactivate Agent
- Method: DELETE
- URL:
https://<domain>.freshservice.com/api/v2/agents/{id} - Watch out for: DELETE deactivates the agent (soft delete), not permanent removal. The agent record is retained.
Request example
DELETE /api/v2/agents/42
Authorization: Basic <base64(apikey:X)>
Response example
HTTP 204 No Content
List Requesters
- Method: GET
- URL:
https://<domain>.freshservice.com/api/v2/requesters - Watch out for: Requesters and Agents are separate objects. An agent is not returned in /requesters and vice versa.
Request example
GET /api/v2/requesters?per_page=100&page=1
Authorization: Basic <base64(apikey:X)>
Response example
{
"requesters": [
{"id": 101, "first_name": "Bob", "email": "bob@acme.com"}
]
}
Create Requester
- Method: POST
- URL:
https://<domain>.freshservice.com/api/v2/requesters - Watch out for: Email must be unique across both agents and requesters in the account.
Request example
POST /api/v2/requesters
Content-Type: application/json
{
"first_name": "Bob",
"email": "bob@acme.com",
"department_ids": [2]
}
Response example
{
"requester": {
"id": 101,
"email": "bob@acme.com",
"active": true
}
}
Convert Requester to Agent
- Method: PUT
- URL:
https://<domain>.freshservice.com/api/v2/requesters/{id}/convert_to_agent - Watch out for: Consumes an agent license seat. Roles must be assigned separately after conversion.
Request example
PUT /api/v2/requesters/101/convert_to_agent
Authorization: Basic <base64(apikey:X)>
Response example
{
"agent": {
"id": 101,
"email": "bob@acme.com",
"active": true
}
}
Get Agent by ID
- Method: GET
- URL:
https://<domain>.freshservice.com/api/v2/agents/{id} - Watch out for: Returns 404 if the agent ID belongs to a deactivated agent and the account has purged it.
Request example
GET /api/v2/agents/42
Authorization: Basic <base64(apikey:X)>
Response example
{
"agent": {
"id": 42,
"email": "jane@acme.com",
"active": true,
"role_ids": [1]
}
}
Rate limits, pagination, and events
- Rate limits: Rate limits are plan-based and applied per Freshservice account. Limits are enforced per minute. When exceeded, the API returns HTTP 429.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: Response headers include X-RateLimit-Total, X-RateLimit-Remaining, X-RateLimit-Used-CurrentRequest, and Retry-After on 429 responses.
- Pagination method: offset
- Default page size: 30
- Max page size: 100
- Pagination pointer: page and per_page
| Plan | Limit | Concurrent |
|---|---|---|
| Starter | 50 requests/min | 0 |
| Growth | 100 requests/min | 0 |
| Pro | 200 requests/min | 0 |
| Enterprise | 400 requests/min | 0 |
- Webhooks available: Yes
- Webhook notes: Freshservice supports webhooks via Automation rules (Workflow Automator and Event-based rules). Webhooks can be triggered on ticket, agent, or requester events by configuring an HTTP action in the automator.
- Alternative event strategy: Polling the /agents and /requesters endpoints with updated_since filter for change detection.
- Webhook events: ticket.created, ticket.updated, agent.created, agent.updated, requester.created, requester.updated
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Pro or Enterprise (SCIM provisioning available via marketplace apps on Pro+; native SCIM endpoint on Enterprise)
Endpoint: https://scim.freshservice.com/scim/v2
Supported operations: GET /Users, GET /Users/{id}, POST /Users, PUT /Users/{id}, PATCH /Users/{id}, DELETE /Users/{id}, GET /Groups, POST /Groups, PUT /Groups/{id}, PATCH /Groups/{id}, DELETE /Groups/{id}
Limitations:
- SSO must be configured before enabling SCIM provisioning.
- SCIM is accessed via marketplace apps for Okta, Azure AD (Entra ID), and OneLogin; not a generic open SCIM endpoint on lower plans.
- SCIM-provisioned users are created as requesters by default; agent promotion requires manual action or API.
- Custom attribute mapping is limited to fields supported by the IdP connector.
- Deprovisioning via SCIM deactivates the user but does not permanently delete the record.
Common scenarios
Three scenarios cover the majority of programmatic identity lifecycle work against Freshservice. For provisioning, POST to /api/v2/requesters with first_name, last_name, email, department_ids, and job_title.
if the user also needs agent access, follow with PUT /api/v2/requesters/{id}/convert_to_agent - this immediately consumes a billable seat, and roles must be assigned in a subsequent PUT /api/v2/agents/{id}. For deprovisioning, locate the user via GET /api/v2/agents?
email= or GET /api/v2/requesters? email=, then issue DELETE on the appropriate resource; DELETE is a soft deactivation - the record and email address are retained, so re-provisioning the same user requires reactivation rather than a new record.
For incremental sync into an external identity graph, use GET /api/v2/agents? updated_since=
active=false to capture deactivated agents, which are excluded from the default response.
Provision a new employee as a Freshservice requester
- POST /api/v2/requesters with first_name, last_name, email, department_ids, job_title.
- Capture the returned requester id for future updates.
- Optionally PATCH custom_fields to populate HR-specific attributes.
- If the employee is also IT staff, call PUT /api/v2/requesters/{id}/convert_to_agent and then PUT /api/v2/agents/{id} to assign roles.
Watch out for: Email must not already exist in the account. Converting to agent consumes a license seat immediately.
Deprovision a departing employee
- Identify the user: GET /api/v2/agents?email=user@acme.com or GET /api/v2/requesters?email=user@acme.com.
- If agent: DELETE /api/v2/agents/{id} to deactivate.
- If requester: DELETE /api/v2/requesters/{id} to deactivate.
- Verify deactivation by checking active: false in a subsequent GET.
Watch out for: DELETE is a soft deactivation. The record persists and the email remains reserved; re-provisioning the same email requires reactivation, not a new record.
Incremental sync of agent changes to an external directory
- Store the timestamp of the last successful sync.
- GET /api/v2/agents?updated_since=
&per_page=100&page=1. - Iterate pages until the response returns fewer records than per_page.
- For each returned agent, upsert into the external directory using the Freshservice id as the key.
- Update the stored last_sync_timestamp to the current time.
Watch out for: updated_since filters by the agent's updated_at field. Deactivated agents are not returned unless ?active=false is also passed; run a separate pass for deactivated users.
Why building this yourself is a trap
Several API behaviors create silent failure modes worth flagging explicitly. GET /api/v2/agents returns only active agents by default - omitting ?active=false means deprovisioned users are invisible to any sync that does not account for this, leaving stale identities undetected in a downstream identity graph.
Email addresses must be globally unique across both agents and requesters in the same account; a provisioning call will fail if the address exists on either object type, and the error surface is not always obvious.
The PUT /api/v2/agents/{id} endpoint behaves as a partial update despite its method - only supplied fields are modified - but email cannot be changed via API when SSO is enabled, which can cause silent no-ops in automated update flows.
Finally, SCIM provisioning at https://scim.freshservice.com/scim/v2 requires SSO to be configured first, is gated to Pro or Enterprise plans, and provisions users as requesters by default; agent promotion is not handled by the SCIM flow and requires a separate API call.
Automate Freshservice workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.