Skip to Content

Freshservice

Arcade does not offer a default Freshservice , and cannot offer one. Freshworks OAuth endpoints embed your organization domain, so there is no global Freshworks authorization server for Arcade to register a shared application against. Every Freshservice deployment brings its own OAuth application and configures a custom provider, as described below.

The Freshservice enables tools and to call Freshservice APIs on behalf of a user. Each user authorizes once, and every call runs under that ’s own token and their own Freshservice permissions.

Freshservice is not Freshdesk

Freshworks ships both products, and they are separate services with separate APIs, separate scopes, and separate Arcade toolkits:

  • Freshservice is IT service management: tickets, changes, problems, requesters, the service catalog, and the solutions knowledge base. This page covers Freshservice.
  • Freshdesk is a customer support desk. It has its own and toolkit.

Neither supersedes the other. Configure the one that matches the product your team runs.

What’s documented here

This page is for platform operators who configure Freshservice auth for an organization. It describes how to register a Freshworks OAuth app, how to configure the matching custom provider in Arcade, and which scopes to request.

This is used by:

  • The Arcade Freshservice toolkit, which provides read-only for tickets, requesters, changes, problems, solutions, and the service catalog
  • Your app code that needs to call Freshservice APIs
  • Your custom tools that need to call Freshservice APIs

Prerequisites

  • A Freshservice , and an administrator on the Freshworks organization that owns it
  • An Arcade with access to the Arcade Dashboard 

Create a Freshworks OAuth app

When using your own app credentials, make sure you configure your to use a custom user verifier. Without this, your end-users will not be able to use your app or in production.

You create OAuth apps in the Freshworks developer portal, not in the Freshworks administrator console. That console carries only , security, organization, and audit log settings, and creates no OAuth credentials.

Open the developer portal

Go to the Freshworks developer portal and enter your organization URL, for example https://acme-1012508551095650304.myfreshworks.com.

Create an OAuth credential

Open Connectivity and in your developer profile, select the OAuth Credentials tab, and create a credential.

Fill in the application details

Supply an application name of three words or fewer, a description, and your redirect URL. Arcade generates the redirect URL when you create the provider, so you can return and fill this in after the next section.

Select scopes

Select the scopes your integration requests, per product. See scopes below for the set the Arcade Freshservice toolkit uses.

Copy the credentials

Copy the Client ID and Client Secret. You need both in the next steps.

Get your Freshworks organization domain

Your OAuth endpoints are unique to your Freshworks organization. The domain looks like:

TEXT
acme-1012508551095650304.myfreshworks.com

Freshworks appends a numeric organization id, so the domain is not your company name alone. Read the value off the URL you are redirected to when you sign in to your organization, rather than constructing it.

Your authorization and token endpoints are:

TEXT
https://<your-freshworks-org-domain>/org/oauth/v2/authorize https://<your-freshworks-org-domain>/org/oauth/v2/token

Both endpoints require the /org/ path segment, and leaving it out fails in a way that looks like success. Without /org/, both URLs return 200 because they fall through to the admin interface’s catch-all route, so the provider looks correctly configured and fails only at consent time. With /org/, the authorize URL redirects to your login page and the token URL returns 401, which is the correct behavior for these endpoints. Freshworks’ own documentation shows /org/ on the authorize URL but omits it from the token URL.

Get your Freshservice subdomain

Your Freshservice subdomain is the value before the .freshservice.com part of your Freshservice URL. If your Freshservice domain is https://acme.freshservice.com, your subdomain is acme.

Your Freshworks organization domain and your Freshservice subdomain are separate values and are not guaranteed to match. Arcade needs both: the organization domain routes the authorization, and the subdomain routes the API call to https://<subdomain>.freshservice.com/api/v2.

Set the Freshservice subdomain secret

Set the FRESHSERVICE_SUBDOMAIN secret in the Arcade Dashboard .

This secret is a routing address, not a credential. It names the Freshservice tenant to call. Authorization stays entirely with the user’s OAuth token, so Arcade stores no static .

Configure Freshservice auth

Access the Arcade Dashboard

Navigate to the Arcade Dashboard  OAuth Providers page.

  • Click Add OAuth Provider in the top right corner.
  • Click the Custom Provider tab at the top.

Enter the provider details

  • ID: freshservice
  • Description: <your description>
  • Client ID: <your client id>
  • Client Secret: <your client secret>
  • Authorization Endpoint: https://<your-freshworks-org-domain>/org/oauth/v2/authorize
  • Token Endpoint: https://<your-freshworks-org-domain>/org/oauth/v2/token
  • Refresh Token Endpoint: https://<your-freshworks-org-domain>/org/oauth/v2/token

Leave the remaining settings as they are. The OAuth 2.0 provider reference describes each one.

Register the redirect URL

Note the Redirect URL that Arcade generates, and add it to your Freshworks OAuth credential.

Create the provider

Click to create the provider.

Access tokens issued by Freshworks are valid for 30 minutes and refresh tokens for 365 days. Arcade refreshes the access token for you.

Scopes

The Arcade Freshservice toolkit is read-only and requests nine scopes:

TEXT
freshservice.tickets.view freshservice.tickets.conversations.view freshservice.tickets.tasks.view freshservice.requesters.view freshservice.solutions.view freshservice.service_catalog.view freshservice.changes.view freshservice.problems.view freshservice.departments.view

Scopes are granular per sub-resource

A parent scope does not imply its children. freshservice.tickets.view does not cover ticket conversations or ticket tasks, which need freshservice.tickets.conversations.view and freshservice.tickets.tasks.view. The same split applies to changes.notes.view, changes.tasks.view, problems.notes.view, and others.

An integration that requests only parent scopes reaches the consent screen and then fails at runtime on the sub-resource call, presenting as a permissions error rather than a missing declaration.

The scope registry is irregular

Freshworks publishes no scope reference, and the catalog does not follow a consistent view and manage pair per resource. Read the authoritative list from your credential’s Scopes and permissions panel in the developer portal.

Three gaps are worth knowing before you scope a read-only integration:

  • cannot be read. freshservice.agents.manage exists, but freshservice.agents.view does not. agents.fields.view and agents.roles.view return field and role definitions rather than agent records. A read-only grant cannot resolve an agent’s name, so a ticket’s assignee is not resolvable without granting a write scope.
  • Groups have no read scope. Groups are spelled agentgroups with no underscore, and only freshservice.agentgroups.manage exists.
  • The service catalog’s list endpoints need a write scope. freshservice.service_catalog.view does not grant /service_catalog/items, /service_catalog/items/{id}, or /service_catalog/categories, which return 403 under the read scope and 200 under freshservice.service_catalog.edit. The search endpoint, /service_catalog/items/search, does work under the read scope, which is what the Arcade toolkit uses.

Each of these forces the same choice: accept reduced capability, or grant a write scope to perform a read. The Arcade Freshservice toolkit takes the first option and stays read-only.

Authorization errors are reported as invalid_request, never invalid_scope, and the error page carries only an opaque error id. A wrong scope, a wrong redirect URI, and a wrong client id are indistinguishable by error code. Change one variable at a time when debugging.

Trim your credential

The consent screen validates each requested scope string against a global Freshworks registry rather than against the scopes selected on your credential. Reaching consent therefore proves the scope name exists, not that your credential declares it.

Select only the scopes your integration requests. A token carries only what the authorization request asks for, so a broadly selected credential does not widen a narrow token. The credential itself still stands as an app authorized to do everything selected on it, which is what a security review reads.

Use Freshservice auth in app code

Use the Freshservice you created to get a token for Freshservice APIs. See authorizing agents with Arcade to understand how this works.

Python
from arcadepy import Arcade client = Arcade() # Automatically finds the `ARCADE_API_KEY` env variable # Start the authorization process auth_response = client.auth.start( user_id="{arcade_user_id}", provider="freshservice", scopes=["freshservice.tickets.view"], ) if auth_response.status != "completed": print("Please complete the authorization challenge in your browser:") print(auth_response.url) # Wait for the authorization to complete auth_response = client.auth.wait_for_completion(auth_response) token = auth_response.context.token # Do something interesting with the token...

Arcade adds the scopes already held by a connection to every new authorization request. A follow-up authorization for one extra scope re-requests everything the already granted, so there is no narrow incremental consent.

Use Freshservice auth in custom tools

If the Arcade Freshservice toolkit does not meet your needs, you can author your own custom tools that call Freshservice APIs.

Use the OAuth2() auth class to declare that a requires authorization with Freshservice, and requires_secrets to declare the subdomain. Arcade populates the token for you:

Python
from typing import Annotated, Any import httpx from arcade_mcp_server import Context, tool from arcade_mcp_server.auth import OAuth2 @tool( requires_auth=OAuth2(id="freshservice", scopes=["freshservice.tickets.view"]), requires_secrets=["FRESHSERVICE_SUBDOMAIN"], ) async def list_open_tickets( context: Context, ) -> Annotated[dict[str, Any], "Open tickets from Freshservice"]: """List open tickets from Freshservice.""" token = context.get_auth_token_or_empty() subdomain = context.get_secret("FRESHSERVICE_SUBDOMAIN") url = f"https://{subdomain}.freshservice.com/api/v2/tickets" headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json", } async with httpx.AsyncClient() as client: response = await client.get(url, headers=headers) response.raise_for_status() return {"tickets": response.json()}
Last updated on