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.
| Fact | Value |
|---|---|
| Tool | Freshdesk |
| Category | Communication |
| Method | REST API Resource |
| Difficulty | Intermediate |
| Time required | 20 minutes |
| Last updated | April 2026 |
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.
Integration method
Freshdesk connects to Retool via a REST API Resource using Basic Auth with the Freshdesk API key as the username and the literal letter 'X' as the password. The base URL uses your Freshdesk subdomain (e.g., https://yourcompany.freshdesk.com/api/v2). All requests are proxied server-side through Retool, keeping credentials secure and eliminating CORS issues.
Prerequisites
- 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
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.
Pro tip: If you're building a shared Retool tool, use the API key from a Freshdesk admin account. Agent-level keys may not have permission to view or modify tickets assigned to other agents.
Expected result: You have your Freshdesk API key and know your subdomain URL (e.g., 'yourcompany' from yourcompany.freshdesk.com).
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.
Pro tip: The password field must be 'X' exactly — not empty, not null, just the letter X. Freshdesk's API key authentication requires a non-empty password field, but ignores its value.
Expected result: The Freshdesk API resource is created. GET /agents/me returns your agent profile with name, email, and role.
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.
1// Transformer for Freshdesk tickets with SLA status2const tickets = data || [];3const now = new Date();45const STATUS_MAP = { 2: 'Open', 3: 'Pending', 4: 'Resolved', 5: 'Closed' };6const PRIORITY_MAP = { 1: 'Low', 2: 'Medium', 3: 'High', 4: 'Urgent' };78function getSLAStatus(due_by) {9 if (!due_by) return 'No SLA';10 const due = new Date(due_by);11 const diffMs = due - now;12 if (diffMs < 0) return 'Breached';13 if (diffMs < 3600000) return 'At Risk'; // within 1 hour14 return 'Compliant';15}1617return tickets.map(ticket => ({18 id: ticket.id,19 subject: ticket.subject,20 status: STATUS_MAP[ticket.status] || ticket.status,21 priority: PRIORITY_MAP[ticket.priority] || ticket.priority,22 requester_name: ticket.requester?.name || 'Unknown',23 requester_email: ticket.requester?.email || '',24 agent_id: ticket.responder_id,25 group_id: ticket.group_id,26 created_at: new Date(ticket.created_at).toLocaleDateString(),27 due_by: ticket.due_by ? new Date(ticket.due_by).toLocaleString() : 'None',28 sla_status: getSLAStatus(ticket.due_by),29 tags: (ticket.tags || []).join(', '),30 fr_escalated: ticket.fr_escalated || false,31 url: `https://YOUR_SUBDOMAIN.freshdesk.com/helpdesk/tickets/${ticket.id}`32}));Pro tip: Freshdesk search query syntax uses field:value pairs joined by AND/OR. String values need quotes: status:2 AND agent_id:1234. Date ranges use colon syntax: created_at:>2024-01-01.
Expected result: The getTickets query returns tickets with SLA status calculated, displaying correctly in a Table with color-coded SLA badges.
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.
1// Transformer for agents dropdown2const agents = data || [];3return agents.map(agent => ({4 label: agent.contact?.name || agent.contact?.email || `Agent ${agent.id}`,5 value: agent.id,6 email: agent.contact?.email || '',7 role: agent.role_ids?.[0] || '',8 ticket_scope: agent.ticket_scope // 1=Global, 2=Group, 3=Restricted9}));Pro tip: Cache getAgents and getGroups queries for 10 minutes. Agent lists are stable reference data and don't need to reload on every query refresh.
Expected result: Agent and group dropdowns are populated. Selecting a ticket and clicking Assign updates the ticket in Freshdesk and refreshes the Table.
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.
1// updateTicketStatus request body2{3 "status": {{ select_newStatus.value }},4 "priority": {{ select_newPriority.value }},5 "responder_id": {{ select_newAgent.value || 'null' }},6 "group_id": {{ select_newGroup.value || 'null' }},7 "tags": {{ JSON.stringify((textInput_tags.value || '').split(',').map(t => t.trim()).filter(Boolean)) }}8}Pro tip: When adding notes via the API, set private: true for internal notes visible only to agents. Set private: false (or omit it) for notes visible to the customer. Public replies go to the separate /reply endpoint.
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.
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.
1// Compute SLA metrics from ticket data2const tickets = getTickets.data || [];3const now = new Date();45const breached = tickets.filter(t => t.sla_status === 'Breached');6const atRisk = tickets.filter(t => t.sla_status === 'At Risk');7const compliant = tickets.filter(t => t.sla_status === 'Compliant');89// Average resolution time in hours (for resolved tickets with stats)10const resolvedTickets = tickets.filter(t => t.stats?.resolved_at && t.created_at);11const avgResolutionHours = resolvedTickets.length > 012 ? resolvedTickets.reduce((sum, t) => {13 const created = new Date(t.created_at);14 const resolved = new Date(t.stats.resolved_at);15 return sum + (resolved - created) / 3600000;16 }, 0) / resolvedTickets.length17 : 0;1819return {20 breached_count: breached.length,21 at_risk_count: atRisk.length,22 compliant_count: compliant.length,23 sla_compliance_rate: tickets.length > 024 ? ((compliant.length / tickets.length) * 100).toFixed(1) + '%' : 'N/A',25 avg_resolution_hours: avgResolutionHours.toFixed(1)26};Pro tip: Freshdesk's search API is more powerful than the standard /tickets endpoint for complex filtering. Use it for SLA-specific queries like finding all tickets where fr_escalated:true.
Expected result: An SLA monitoring dashboard showing breach counts, at-risk tickets, and compliance rate. Managers can immediately identify tickets that need urgent attention.
Common 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.
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.
Copy this prompt to try it in Retool
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.
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.
Copy this prompt to try it in Retool
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.
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.
Copy this prompt to try it in Retool
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.
1// In your getTickets query, add parameters:2// per_page: 1003// 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\\\"\".
1// Correct Freshdesk search query syntax:2// Integers (no quotes): status:2, priority:3, agent_id:12343// Strings (double quotes): type:"Question", tag:"billing"4// Dates: created_at:>2024-01-01, due_by:<2024-02-015// Combined: "status:2 AND priority:4 AND agent_id:1234"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.
Alternatives
Zendesk has more complex subdomain-based URL routing and supports embedding Retool as a Zendesk sidebar app, while Freshdesk has simpler API auth (API key directly) and straightforward REST endpoints.
Intercom combines live chat, messaging, and ticketing in one platform — choose it over Freshdesk if proactive customer messaging and in-app chat are more important than ticket routing.
Freshsales is part of the same Freshworks ecosystem as Freshdesk but focuses on CRM and sales pipeline — connect both in Retool to build a unified customer view combining support tickets with sales data.
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].
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation