# How to Integrate Retool with Freshdesk

- Tool: Retool
- Difficulty: Intermediate
- Time required: 20 minutes
- Last updated: April 2026

## TL;DR

Connect Retool to Freshdesk by creating a REST API Resource with your Freshdesk subdomain URL and API key encoded as Basic Auth (API key as username, 'X' as password). Build a custom ticket management dashboard with filtering, status updates, SLA tracking, and agent management that moves faster than Freshdesk's native interface for high-volume support teams.

## Build a Custom Freshdesk Ticket Management Panel in Retool

Freshdesk's native interface is built for individual agents working through their queues. But support managers and operations teams often need views that Freshdesk doesn't provide easily: all overdue tickets across all agents, SLA breach rate by product category, agent workload comparison, or a bulk status-update tool for closing out resolved tickets. Retool lets you build these custom views against Freshdesk's REST API.

Freshdesk's API v2 provides comprehensive access to tickets, contacts, companies, agents, groups, and canned responses. Authentication is straightforward: your API key goes in the username field of Basic Auth, and the literal character 'X' serves as the password — Freshdesk ignores the password when an API key is used. The base URL includes your Freshdesk subdomain.

The most valuable Retool use case for Freshdesk is building an agent-facing ticket management panel that shows tickets filtered and sorted in ways Freshdesk's native views don't support. Operations managers can see real-time SLA compliance, identify bottlenecks, reassign tickets in bulk, and add internal notes to multiple tickets at once — all without navigating through Freshdesk's multi-page UI.

## Before you start

- A Freshdesk account with at least one agent and existing tickets
- Your Freshdesk subdomain URL (e.g., yourcompany.freshdesk.com from the URL when logged in)
- A Freshdesk API key (found in Freshdesk → Profile Settings → Your API Key at the bottom right)
- A Retool account with permission to create Resources
- Basic familiarity with Retool's query editor and Table component

## Step-by-step guide

### 1. Find your Freshdesk API key and subdomain

In Freshdesk, click your profile picture or avatar in the top-right corner. Select Profile Settings from the dropdown menu. On the Profile Settings page, scroll down to the bottom-right section. You'll see a field labeled Your API Key with a masked value and a clipboard/copy button. Click to copy your API key.

Freshdesk API keys are account-specific to each agent — they provide API access with the same permissions as that agent's Freshdesk role. For a Retool integration shared across your team, use the API key from an admin account so the integration has access to all tickets, all agents, and all Freshdesk settings.

Your Freshdesk subdomain is visible in your browser URL when logged in: it's the part before .freshdesk.com. For example, if you access Freshdesk at https://acme.freshdesk.com, your subdomain is 'acme'. This subdomain goes into your Retool resource's base URL.

Note: Freshdesk's Basic Auth scheme is unique — the API key serves as the username, and the password can be any value (the convention is to use 'X'). Freshdesk ignores the password field when an API key is provided as the username.

**Expected result:** You have your Freshdesk API key and know your subdomain URL (e.g., 'yourcompany' from yourcompany.freshdesk.com).

### 2. Create the Freshdesk REST API Resource in Retool

In Retool, navigate to the Resources tab and click Add Resource. Select REST API from the connector list.

Name: Freshdesk API

Base URL: https://YOUR_SUBDOMAIN.freshdesk.com/api/v2

Replace YOUR_SUBDOMAIN with your actual Freshdesk subdomain. Include /api/v2 in the base URL.

Authentication: Select Basic Auth. In the Username field, enter your Freshdesk API key. In the Password field, enter X (the literal letter X — this is Freshdesk's documented Basic Auth convention when using API keys).

For better security, store the API key in Configuration Variables first: Settings → Configuration Variables → create FRESHDESK_API_KEY → mark as secret. Then use {{ retoolContext.configVars.FRESHDESK_API_KEY }} as the Username field value.

Add a Content-Type header: Key: Content-Type, Value: application/json.

Click Create Resource. Test immediately with GET /agents/me — this should return your agent profile if the authentication is working. If you get 401, the API key or auth format is incorrect.

**Expected result:** The Freshdesk API resource is created. GET /agents/me returns your agent profile with name, email, and role.

### 3. Query tickets with filtering and pagination

Create a getTickets query. Set method to GET and path to /tickets. Freshdesk's ticket list endpoint supports filtering via query parameters:

- filter: open (predefined filters — also: 'resolved', 'spam', 'watching')
- order_by: created_at (also: updated_at, status, priority, due_by)
- order_type: desc
- page: {{ pagination.offset / 30 + 1 }} (Freshdesk paginates at 30 tickets per page)
- include: description,requester,stats

The include parameter is crucial — by default Freshdesk returns minimal ticket data. Adding requester gives you the customer's name and email. Adding stats gives first_responded_at and resolved_at timestamps for SLA calculations.

For more complex filtering (by agent, group, tag, or date range), use the Freshdesk search API: GET /search/tickets?query=\"status:2 AND priority:3\" (Freshdesk uses numeric values: status 2=open, 3=pending, 4=resolved; priority 1=low, 2=medium, 3=high, 4=urgent).

Create a transformer to calculate SLA status for each ticket. Freshdesk provides due_by (response due date) and fr_due_by (first response due) fields.

```
// Transformer for Freshdesk tickets with SLA status
const tickets = data || [];
const now = new Date();

const STATUS_MAP = { 2: 'Open', 3: 'Pending', 4: 'Resolved', 5: 'Closed' };
const PRIORITY_MAP = { 1: 'Low', 2: 'Medium', 3: 'High', 4: 'Urgent' };

function getSLAStatus(due_by) {
  if (!due_by) return 'No SLA';
  const due = new Date(due_by);
  const diffMs = due - now;
  if (diffMs < 0) return 'Breached';
  if (diffMs < 3600000) return 'At Risk'; // within 1 hour
  return 'Compliant';
}

return tickets.map(ticket => ({
  id: ticket.id,
  subject: ticket.subject,
  status: STATUS_MAP[ticket.status] || ticket.status,
  priority: PRIORITY_MAP[ticket.priority] || ticket.priority,
  requester_name: ticket.requester?.name || 'Unknown',
  requester_email: ticket.requester?.email || '',
  agent_id: ticket.responder_id,
  group_id: ticket.group_id,
  created_at: new Date(ticket.created_at).toLocaleDateString(),
  due_by: ticket.due_by ? new Date(ticket.due_by).toLocaleString() : 'None',
  sla_status: getSLAStatus(ticket.due_by),
  tags: (ticket.tags || []).join(', '),
  fr_escalated: ticket.fr_escalated || false,
  url: `https://YOUR_SUBDOMAIN.freshdesk.com/helpdesk/tickets/${ticket.id}`
}));
```

**Expected result:** The getTickets query returns tickets with SLA status calculated, displaying correctly in a Table with color-coded SLA badges.

### 4. Fetch agents and groups for assignment dropdowns

To enable ticket reassignment and filtering, fetch agents and groups. Create a getAgents query: GET /agents with page: 1 and per_page: 100. The response is an array of agent objects with id, contact.name, contact.email, and role_ids.

Create a getGroups query: GET /groups. Response includes id, name, and description. Groups in Freshdesk are used to organize agents by product, skill, or geography.

Set both queries to run on page load (they're reference data that changes rarely). Bind them to Select components for your ticket filtering and assignment forms:
- A Group dropdown (select_group) for filtering tickets by team
- An Agent dropdown (select_agent) for filtering and reassigning

For the ticket reassignment feature, create an updateTicket query: PATCH /tickets/{{ table1.selectedRow.data.id }} with body:
{
  "responder_id": {{ select_agent.value }},
  "group_id": {{ select_group.value }}
}

Set this to manual trigger and add an Assign button that fires it. On success, refresh getTickets to update the Table.

Add a cache of 5 minutes to the getAgents and getGroups queries — these lists rarely change and caching them avoids redundant API calls every time the dashboard loads.

```
// Transformer for agents dropdown
const agents = data || [];
return agents.map(agent => ({
  label: agent.contact?.name || agent.contact?.email || `Agent ${agent.id}`,
  value: agent.id,
  email: agent.contact?.email || '',
  role: agent.role_ids?.[0] || '',
  ticket_scope: agent.ticket_scope // 1=Global, 2=Group, 3=Restricted
}));
```

**Expected result:** Agent and group dropdowns are populated. Selecting a ticket and clicking Assign updates the ticket in Freshdesk and refreshes the Table.

### 5. Add ticket update and note actions

Build out the ticket detail and action panel. When a user clicks a ticket row in the Table, show a detail Container on the right with:
- Ticket subject and description (fetched from GET /tickets/{{ table1.selectedRow.data.id }}?include=description to get the full description text)
- Status and Priority Select dropdowns pre-populated from the selected row
- Agent and Group dropdowns
- A TextArea for adding an internal note or public reply

Create three action queries:

1. updateTicketStatus: PATCH /tickets/{{ table1.selectedRow.data.id }}
Body: { "status": {{ select_status.value }}, "priority": {{ select_priority.value }} }

2. addInternalNote: POST /tickets/{{ table1.selectedRow.data.id }}/notes
Body: { "body": "{{ textArea_note.value }}", "private": true, "user_id": {{ retoolContext.currentUser.id || 1 }} }

3. addPublicReply: POST /tickets/{{ table1.selectedRow.data.id }}/reply
Body: { "body": "{{ textArea_reply.value }}" }

For canned responses, fetch them with GET /canned_responses and display them in a dropdown. When a canned response is selected, populate the reply TextArea with its content using a component state setter event handler.

```
// updateTicketStatus request body
{
  "status": {{ select_newStatus.value }},
  "priority": {{ select_newPriority.value }},
  "responder_id": {{ select_newAgent.value || 'null' }},
  "group_id": {{ select_newGroup.value || 'null' }},
  "tags": {{ JSON.stringify((textInput_tags.value || '').split(',').map(t => t.trim()).filter(Boolean)) }}
}
```

**Expected result:** The ticket management panel supports updating status/priority, reassigning agents, and adding internal notes or public replies — all from Retool without switching to Freshdesk.

### 6. Build the SLA monitoring and reporting view

Create a dedicated SLA monitoring tab in your Retool app. For SLA data, Freshdesk provides ticket-level SLA fields: fr_due_by (first response due), due_by (resolution due), fr_escalated (first response escalated), and stats.reopened_at.

For the SLA overview, use summary Stat components:
- Breached: {{ getTickets.data.filter(t => t.sla_status === 'Breached').length }} (red background)
- At Risk: {{ getTickets.data.filter(t => t.sla_status === 'At Risk').length }} (yellow background)
- Compliant: {{ getTickets.data.filter(t => t.sla_status === 'Compliant').length }} (green background)

For the SLA breach trend chart, you'd need to query Freshdesk's Reporting API (available on Pro+ plans) or pull historical ticket data and compute breach rates yourself.

For historical performance, create a query that fetches tickets resolved in the last 30 days: GET /search/tickets?query=\"status:4 AND resolved_at:>{{ new Date(Date.now() - 30*24*60*60*1000).toISOString().split('T')[0] }}\"

Compute resolution time by subtracting created_at from stats.resolved_at for each ticket. Display average resolution time by agent, group, and priority in a bar chart.

For teams building comprehensive customer support analytics combining Freshdesk with your internal database, RapidDev's team can help architect the multi-resource Retool solution.

```
// Compute SLA metrics from ticket data
const tickets = getTickets.data || [];
const now = new Date();

const breached = tickets.filter(t => t.sla_status === 'Breached');
const atRisk = tickets.filter(t => t.sla_status === 'At Risk');
const compliant = tickets.filter(t => t.sla_status === 'Compliant');

// Average resolution time in hours (for resolved tickets with stats)
const resolvedTickets = tickets.filter(t => t.stats?.resolved_at && t.created_at);
const avgResolutionHours = resolvedTickets.length > 0
  ? resolvedTickets.reduce((sum, t) => {
      const created = new Date(t.created_at);
      const resolved = new Date(t.stats.resolved_at);
      return sum + (resolved - created) / 3600000;
    }, 0) / resolvedTickets.length
  : 0;

return {
  breached_count: breached.length,
  at_risk_count: atRisk.length,
  compliant_count: compliant.length,
  sla_compliance_rate: tickets.length > 0
    ? ((compliant.length / tickets.length) * 100).toFixed(1) + '%' : 'N/A',
  avg_resolution_hours: avgResolutionHours.toFixed(1)
};
```

**Expected result:** An SLA monitoring dashboard showing breach counts, at-risk tickets, and compliance rate. Managers can immediately identify tickets that need urgent attention.

## Best practices

- Use a Freshdesk admin account's API key for shared Retool tools — admin keys have global ticket scope, while agent keys may only access tickets in their assigned groups.
- Always use the literal character 'X' as the password in Freshdesk Basic Auth — this is documented by Freshdesk and the correct convention for API key authentication.
- Store the Freshdesk API key in Retool Configuration Variables (Settings → Configuration Variables) marked as secret, not hardcoded in the resource configuration.
- Use the include=requester,stats parameter on ticket queries to get customer information and SLA timestamps in a single request, avoiding separate lookup queries.
- For ticket search, use Freshdesk's search API (/search/tickets?query=...) instead of the standard /tickets endpoint when you need complex multi-field filtering.
- Implement pagination with per_page=100 (Freshdesk's maximum) and server-side pagination in the Table component — support teams often have thousands of open tickets.
- Cache getAgents and getGroups queries for 10 minutes since agent rosters change infrequently — this significantly reduces API calls on a high-traffic internal dashboard.
- For bulk operations across many tickets, run updates sequentially with a brief delay rather than in parallel — Freshdesk enforces rate limits of 400 API calls per minute per account.

## Use cases

### Build an SLA compliance monitoring dashboard

Create a Retool dashboard showing all Freshdesk tickets with SLA status — breached, at risk, and compliant. Sort by urgency and filter by agent, group, and product. Support managers can immediately see which customers need priority attention and reassign tickets to clear the queue.

Prompt example:

```
Build a Retool SLA dashboard showing all open Freshdesk tickets with SLA status. Show ticket ID, subject, customer email, agent assigned, priority, time since creation, SLA due date, and SLA status (breached/at-risk/compliant). Use color coding: red for breached, yellow for at-risk, green for compliant. Add filters for agent and group. Include a Reassign button for selected tickets.
```

### Build an agent workload and performance report

Build a Retool panel showing per-agent ticket metrics: total open tickets, average first response time, average resolution time, and SLA breach rate. Helps support managers balance workload across the team and identify agents who need coaching or additional resources.

Prompt example:

```
Build a Retool agent performance panel showing each Freshdesk agent with their open ticket count, tickets resolved this week, average response time, and SLA breach percentage. Show a bar chart comparing open tickets per agent. Add a Table of tickets for each agent when their row is selected.
```

### Build a bulk ticket management tool

Create a Retool tool where support managers can select multiple tickets from a Table and perform bulk operations: change status, reassign to a different agent, add a tag, or post a standard response. This replaces manually updating tickets one by one in Freshdesk.

Prompt example:

```
Build a Retool bulk ticket management tool with a multiselect Table of open Freshdesk tickets. Add a right panel with Status dropdown, Agent dropdown, and a Tag input. Include a Bulk Update button that applies changes to all selected rows using a looped query. Show progress and success notification.
```

## Troubleshooting

### 401 Unauthorized despite correct API key and X password

Cause: The API key may be from a restricted agent account, or the password field contains something other than the literal character 'X'.

Solution: Verify the password field is exactly 'X' — not empty, not 'x' (lowercase), not 'none'. Also confirm the API key is from a Freshdesk admin account if you need full access. Agent-level keys may not have permission to access all tickets. Regenerate the API key in Freshdesk Profile Settings if needed.

### Ticket list returns only 30 results even though there are hundreds of open tickets

Cause: Freshdesk paginates ticket list responses at 30 per page by default, with a maximum of 100 per page.

Solution: Add page and per_page query parameters to your query. Set per_page to 100 (maximum) and increment page to paginate. For the Table component, enable server-side pagination with page size set to 100. Alternatively, use Freshdesk's search API which can return results with full filtering control.

```
// In your getTickets query, add parameters:
// per_page: 100
// page: {{ Math.floor(table1.pagination.offset / 100) + 1 }}
```

### PATCH ticket update returns 403 Forbidden

Cause: The API key being used belongs to an agent who doesn't have permission to update the ticket, either because of their role or because the ticket is assigned to a different group than the agent has access to.

Solution: Use an admin API key for write operations. In Freshdesk, admin accounts have 'Global' ticket scope and can update any ticket. Agent accounts may have 'Group' or 'Restricted' scope which limits which tickets they can modify.

### Search API returns 400 Bad Request with 'Invalid search query'

Cause: Freshdesk's search query syntax is strict — string values need double quotes, boolean values use true/false without quotes, and numeric status/priority values use integers.

Solution: Check the search query format. Valid examples: status:2 for open tickets (integer, no quotes), type:"Question" (string in double quotes), created_at:>2024-01-01 for date filters. In Retool queries, escape the double quotes: \"status:2 AND type:\\\"Question\\\"\".

```
// Correct Freshdesk search query syntax:
// Integers (no quotes): status:2, priority:3, agent_id:1234
// Strings (double quotes): type:"Question", tag:"billing"
// Dates: created_at:>2024-01-01, due_by:<2024-02-01
// Combined: "status:2 AND priority:4 AND agent_id:1234"
```

## Frequently asked questions

### Does Retool have a native Freshdesk connector?

No, Retool does not have a dedicated native connector for Freshdesk. You connect via a REST API Resource using Freshdesk's API key Basic Auth. The setup takes about 20 minutes and gives full access to all Freshdesk v2 API endpoints including tickets, contacts, agents, and reports.

### What is Freshdesk's rate limit for API calls?

Freshdesk allows 400 API calls per minute per account for the standard plan. Higher-tier plans (Pro and Enterprise) have higher limits. If you exceed the rate limit, you'll receive a 429 Too Many Requests response with a Retry-After header indicating when to retry. For high-frequency dashboards, implement caching and avoid polling faster than every 30 seconds.

### Can I create tickets in Freshdesk from Retool?

Yes. Use POST /tickets with a JSON body containing subject, description, email (customer email), status (2=Open), and priority (1-4). You can also set responder_id (agent ID), group_id, type, and tags. Creating tickets from Retool is useful for building internal tools that automatically log issues from your database or monitoring systems into Freshdesk.

### How do I get a requester's full contact history in my Retool dashboard?

Fetch the requester's contact record: GET /contacts/{id} (where id comes from the ticket's requester_id field). This returns full contact details including all tickets. To get all tickets for a contact, use the search API: GET /search/tickets?query=\"requester_id:{contactId}\" or filter the ticket list with requester_id. Display this in a side panel next to the selected ticket for a full customer history view.

### Does Freshdesk's API support filtering by date range?

Yes. Use the search API with date range filters: GET /search/tickets?query=\"created_at:>2024-01-01 AND created_at:<2024-02-01\". The standard /tickets endpoint has limited date filtering — use the search API for complex date-based queries. Dates must be in YYYY-MM-DD format. For the last N days, compute the date with: new Date(Date.now() - N*24*60*60*1000).toISOString().split('T')[0].

---

Source: https://www.rapidevelopers.com/retool-integrations/freshdesk
© RapidDev — https://www.rapidevelopers.com/retool-integrations/freshdesk
