Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsFlutterFlow API Call

Freshdesk

Connect FlutterFlow to Freshdesk using a FlutterFlow API Call with HTTP Basic Auth, where your API key is the username and the literal character X is the password — base64-encoded. Build API Groups for GET /tickets and PUT /tickets/{id} to create a mobile support-agent app. Route ticket writes through a Firebase Cloud Function proxy to keep the credential out of the compiled binary.

What you'll learn

  • How to find your Freshdesk API key and correctly base64-encode it for Basic Auth
  • How to create a Freshdesk API Group in FlutterFlow with the subdomain base URL
  • How to fetch tickets with requester details and SLA timestamps in a single call
  • How to update ticket status and priority from a Flutter action flow
  • How to handle Freshdesk's 400 requests-per-minute rate limit in your app
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read45 minutesCRM & SalesLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to Freshdesk using a FlutterFlow API Call with HTTP Basic Auth, where your API key is the username and the literal character X is the password — base64-encoded. Build API Groups for GET /tickets and PUT /tickets/{id} to create a mobile support-agent app. Route ticket writes through a Firebase Cloud Function proxy to keep the credential out of the compiled binary.

Quick facts about this guide
FactValue
ToolFreshdesk
CategoryCRM & Sales
MethodFlutterFlow API Call
DifficultyIntermediate
Time required45 minutes
Last updatedJuly 2026

Build a Mobile Support-Agent App with FlutterFlow and Freshdesk

Freshdesk is a popular customer support platform for companies managing support tickets across email, chat, and phone channels. Connecting FlutterFlow to Freshdesk lets you put ticket management in the hands of mobile support agents: list open tickets by priority, filter by SLA breach status, update a ticket's status or priority with a tap, and post private agent notes — all from a native iOS or Android app without opening a browser.

Freshdesk's REST v2 API uses HTTP Basic Auth where your API key is the username and the literal character X (not your password) is the password. This is a deliberate design choice by Freshdesk — the X is a placeholder that must be present exactly as-is. The full API is available on all paid Freshdesk plans; the free tier has limited access. The base URL pattern is https://{yoursubdomain}.freshdesk.com/api/v2, where {yoursubdomain} is the part before .freshdesk.com in your Freshdesk account URL. Freshdesk enforces a default rate limit of 400 API calls per minute across your account, so bulk operations require throttling.

The most important thing to get right before writing a single FlutterFlow widget is the Authorization header. FlutterFlow's API Call header fields accept plain text — they do not base64-encode values for you. You must compute base64({APIKEY}:X) yourself (any online base64 encoder works, or use the terminal command shown in Step 1) and paste the resulting string directly into the Authorization header as Basic {encodedValue}. Get this right and the integration is straightforward; get it wrong and every call returns 401 with no further explanation.

Integration method

FlutterFlow API Call

FlutterFlow connects to Freshdesk through the API Calls panel using Freshdesk's REST v2 API with Basic Auth. The Authorization header requires a base64-encoded string where your API key is the username and the literal character X is the password — FlutterFlow does not base64-encode for you, so you must pre-compute and paste the encoded value. For a shipped app, writes like updating ticket status or adding notes should route through a Firebase Cloud Function proxy since the Authorization header ships inside the compiled client.

Prerequisites

  • A Freshdesk account on a paid plan (the free tier has limited API access)
  • Your Freshdesk API key (found in Freshdesk → Profile settings → Your API key section on the right side)
  • Your Freshdesk subdomain (the part before .freshdesk.com in your Freshdesk URL)
  • A base64 encoder — any online tool works, or use a terminal: echo -n '{APIKEY}:X' | base64
  • A FlutterFlow project with at least one screen ready to display ticket data

Step-by-step guide

1

Find your Freshdesk API key and compute the Basic Auth header

Log in to your Freshdesk account. Click your avatar icon in the top-right corner and select Profile settings from the dropdown. On the right side of the Profile settings page, you will see a section called Your API Key with a long alphanumeric string. Click the copy icon to copy your API key. Now you need to compute the base64-encoded value for the Authorization header. Freshdesk Basic Auth is unusual: the API key is the username and the literal character X (uppercase, single character) is the password — not your Freshdesk password, not blank, but the specific character X. To compute the encoded value, take your API key and append :X to it. For example, if your API key is abc123def456, the string to encode is abc123def456:X. You can encode this at any online base64 encoder (search for 'base64 encode online'), or in a Mac/Linux terminal run: echo -n 'abc123def456:X' | base64. The result will be something like YWJjMTIzZGVmNDU2Olg=. You will use this encoded value in the next step. Keep a note of both your raw API key (for the Cloud Function) and the encoded value (for the direct API Group header). Verify the credential works by pasting the following into your browser address bar and checking for a JSON tickets response — you cannot use Basic Auth directly in a browser address bar, so use a tool like Postman, Insomnia, or the curl command: curl -u {APIKEY}:X https://{subdomain}.freshdesk.com/api/v2/tickets

auth_helper.sh
1# Compute the Basic Auth header value (Mac/Linux terminal)
2echo -n 'YOUR_API_KEY:X' | base64
3
4# Result example: WU9VUl9BUElfS0VZOlg=
5# Use in Authorization header as: Basic WU9VUl9BUElfS0VZOlg=
6
7# Test the credential (replace values):
8# curl -u YOUR_API_KEY:X https://yoursubdomain.freshdesk.com/api/v2/tickets

Pro tip: The #1 mistake with Freshdesk Basic Auth is using a blank string instead of X as the password. The header must be Basic base64(APIKEY:X) — not Basic base64(APIKEY:) and not Basic base64(APIKEY:yourpassword). If you see 401 errors, this is almost always the cause.

Expected result: You have the pre-computed Basic base64(APIKEY:X) string ready to paste into FlutterFlow, and you have confirmed it returns valid JSON when tested against the Freshdesk API.

2

Create a Freshdesk API Group in FlutterFlow

Open your FlutterFlow project. In the left navigation bar, click API Calls. Click + Add and select Create API Group. Name it Freshdesk. Set the Base URL to https://{yoursubdomain}.freshdesk.com/api/v2, replacing {yoursubdomain} with your actual Freshdesk subdomain. For example, if your Freshdesk URL is https://acmesupport.freshdesk.com, the base URL is https://acmesupport.freshdesk.com/api/v2. Now add a shared header to the API Group. Under the Headers section, click + Add Header. Set the key to Authorization and the value to Basic {your_encoded_string}, where {your_encoded_string} is the base64 value you computed in Step 1 — for example, Basic WU9VUl9BUElfS0VZOlg=. Add a second shared header: key Content-Type, value application/json. Click Save to save the API Group. All API Calls you add inside this group will automatically include the Authorization and Content-Type headers on every request. For a demo or internal tool with a limited audience, this shared header approach is acceptable. For a shipped app, you will replace this with a Cloud Function proxy in Step 5 — the encoded credential should not live inside the compiled FlutterFlow binary.

Pro tip: If you have multiple Freshdesk accounts (e.g. sandbox and production), create two separate API Groups — one named FreshdeskStaging and one FreshdeskProduction — with different base URLs and credentials. This makes it easy to switch environments during development and testing.

Expected result: The Freshdesk API Group appears in your API Calls panel with the correct base URL, Authorization header, and Content-Type header configured.

3

Add GET /tickets with include parameters and pagination

Inside the Freshdesk API Group, click + Add API Call. Name it GetTickets. Set the method to GET and the endpoint to /tickets. Click the Variables tab and add the following variables: page (String, default '1') and per_page (String, default '30'). Back in the endpoint, update it to /tickets?include=requester,stats&per_page={{per_page}}&page={{page}}. The include=requester,stats parameter is particularly important: it tells Freshdesk to embed the requester's name and email alongside each ticket, and to include SLA statistics like due_by and first_responded_at — all in a single API call. Without this include parameter, you would need a separate API call per ticket to fetch requester details, which would quickly exhaust your 400 calls/min budget. Note: Freshdesk defaults to returning 30 tickets per page. The maximum per_page value is 100. Set per_page to 100 in your app if you need to load more tickets per call. Click Response & Test. In the test form, leave the defaults, then click Test API. You should see a JSON array of ticket objects. Click Generate JSON Paths to auto-create path variables. Key paths to verify: $[:].id, $[:].subject, $[:].status, $[:].priority, $[:].requester.name, $[:].requester.email, $[:].stats.agent_responded_at, $[:].due_by. Status codes in Freshdesk: 2=Open, 3=Pending, 4=Resolved, 5=Closed. Priority codes: 1=Low, 2=Medium, 3=High, 4=Urgent. You will need to map these integers to display labels in your FlutterFlow widget using a conditional expression.

api_call_config.json
1{
2 "group": "Freshdesk",
3 "name": "GetTickets",
4 "method": "GET",
5 "endpoint": "/tickets?include=requester,stats&per_page={{per_page}}&page={{page}}",
6 "variables": [
7 { "name": "per_page", "type": "String", "default": "30" },
8 { "name": "page", "type": "String", "default": "1" }
9 ],
10 "response_json_paths": [
11 "$[:].id",
12 "$[:].subject",
13 "$[:].status",
14 "$[:].priority",
15 "$[:].requester.name",
16 "$[:].requester.email",
17 "$[:].due_by",
18 "$[:].stats.agent_responded_at"
19 ]
20}

Pro tip: To filter tickets by status or priority, add filter variables to the endpoint: /tickets?status={{status}}&priority={{priority}}&include=requester,stats&per_page={{per_page}}&page={{page}}. For complex queries like 'all escalated tickets', use the Freshdesk search endpoint /search/tickets?query="fr_escalated:true" instead — add a separate API Call named SearchTickets for this.

Expected result: GetTickets API Call is saved and returns a JSON array of tickets with requester names, email addresses, and SLA timestamps in a single response.

4

Bind ticket data to a ListView and add status filters

Open the screen in FlutterFlow where you want to display tickets. Add a Column containing a Row for filter chips (Open, Pending, Resolved) at the top, then a ListView below. Add a Page State variable called selectedStatus (Integer, default 2 for Open). Select the ListView widget and open its Backend Query. Click + Add Query → Freshdesk → GetTickets. Set per_page to '50' and page to '1'. Enable Trigger on Page Load. Inside the ListView's child template, add a Card widget. Inside the Card, add a Column with four Text widgets: one for $[:].subject bound to GetTickets.subject, one for $[:].requester.name, one for $[:].due_by (format the date string using FlutterFlow's date formatting function), and one for a status label. For the status label, use a Conditional Value: if status equals 2 display 'Open', if 3 display 'Pending', if 4 display 'Resolved'. Tint the label's background color accordingly — orange for Open, grey for Pending, green for Resolved. Add a priority badge in the corner of each card with a similar conditional: 1=Low (grey), 2=Medium (blue), 3=High (orange), 4=Urgent (red). For the filter chips at the top, wire each chip's tap action to set selectedStatus Page State to the corresponding integer, then re-trigger the GetTickets query. If the ListView shows no items but the API Call Test returns data, check that your JSON path starts with $[:]. (array root path) rather than $.data[:]. — Freshdesk returns a root-level array, not a data wrapper.

Pro tip: Freshdesk returns timestamps in ISO 8601 format (e.g. 2026-03-15T14:30:00Z). Use FlutterFlow's date formatting function in your widget binding to display these as human-readable strings like 'Mar 15, 2:30 PM'. The raw string will confuse users who are not developers.

Expected result: Your screen displays a filterable ListView of Freshdesk tickets with subject, requester name, SLA due date, and color-coded status and priority badges on each card.

5

Add PUT /tickets/{id} to update status and priority, proxied via Cloud Function

For any write operation — updating status, changing priority, or adding notes — the Authorization header credential must not live inside the compiled FlutterFlow app. The solution is a Firebase Cloud Function proxy that holds the credential server-side. Here is the full pattern: deploy the Cloud Function below to your Firebase project. It accepts a ticket ID, field updates, and an optional note body in the request body, then calls Freshdesk's API server-side using the stored credential. Once deployed (you will get a URL like https://us-central1-{project}.cloudfunctions.net/freshdeskProxy), create a new API Group in FlutterFlow named FreshdeskProxy with that function URL as the base URL. Add a PUT API Call named UpdateTicket with endpoint / (the function handles routing) and body variables for ticket_id (Integer), status (Integer, optional), priority (Integer, optional). Add a separate POST API Call named AddNote with body variables for ticket_id and body text and private (Boolean). In your app, wire the status-update button's action flow to FreshdeskProxy → UpdateTicket with the ticket's ID and the new status integer. Wire a private-note form's submit button to FreshdeskProxy → AddNote. The response structure mirrors Freshdesk's response so your existing JSON paths work for displaying the updated ticket. Note on rate limits: Freshdesk enforces 400 API calls per minute per account. For a small team of 3-5 agents refreshing the app every 30 seconds, this budget is easily exceeded. Add a minimum refresh interval of 60 seconds in your app and avoid triggering GetTickets queries on every scroll event.

index.js
1// Firebase Cloud Function: functions/index.js
2const functions = require('firebase-functions');
3const axios = require('axios');
4
5const FRESHDESK_KEY = functions.config().freshdesk.apikey;
6const FRESHDESK_DOMAIN = functions.config().freshdesk.subdomain;
7const BASE_URL = `https://${FRESHDESK_DOMAIN}.freshdesk.com/api/v2`;
8const AUTH_HEADER = 'Basic ' + Buffer.from(`${FRESHDESK_KEY}:X`).toString('base64');
9
10exports.freshdeskProxy = functions.https.onRequest(async (req, res) => {
11 res.set('Access-Control-Allow-Origin', '*');
12 if (req.method === 'OPTIONS') {
13 res.set('Access-Control-Allow-Methods', 'GET, PUT, POST');
14 res.set('Access-Control-Allow-Headers', 'Content-Type');
15 return res.status(204).send('');
16 }
17
18 const { action, ticket_id, status, priority, note_body, note_private } = req.body;
19 const headers = {
20 Authorization: AUTH_HEADER,
21 'Content-Type': 'application/json'
22 };
23
24 try {
25 if (action === 'update_ticket') {
26 const body = {};
27 if (status !== undefined) body.status = status;
28 if (priority !== undefined) body.priority = priority;
29 const r = await axios.put(`${BASE_URL}/tickets/${ticket_id}`, body, { headers });
30 return res.json(r.data);
31 }
32 if (action === 'add_note') {
33 const r = await axios.post(`${BASE_URL}/tickets/${ticket_id}/notes`, {
34 body: note_body,
35 private: note_private ?? true
36 }, { headers });
37 return res.json(r.data);
38 }
39 return res.status(400).json({ error: 'Unknown action' });
40 } catch (err) {
41 return res.status(err.response?.status || 500).json({ error: err.message });
42 }
43});
44
45// Deploy:
46// firebase functions:config:set freshdesk.apikey="YOUR_API_KEY" freshdesk.subdomain="yoursubdomain"
47// firebase deploy --only functions
48// If you'd prefer someone else handle this setup, RapidDev builds FlutterFlow integrations
49// like this every week — free scoping call at rapidevelopers.com/contact

Pro tip: Keep separate Cloud Functions for reads (GetTickets) and writes (UpdateTicket, AddNote) if you want to allow client-side reads for performance but enforce server-side proxy for all writes. Reads can be rate-limited client-side; writes need server-side protection of the credential.

Expected result: Tapping 'Mark as Resolved' or 'Add Note' in the app triggers the Cloud Function, which updates the Freshdesk ticket server-side. The Freshdesk credential is never visible in the compiled FlutterFlow app binary.

Common use cases

Mobile ticket-management app for support agents on the go

Build a FlutterFlow app where support agents can view their assigned tickets filtered by status and priority, see requester details and SLA timestamps alongside each ticket, and update ticket status or priority with a single tap. The app queries GET /tickets?include=requester,stats to pull contact info and SLA data in one call, reducing API usage against the 400/min limit.

FlutterFlow Prompt

A FlutterFlow screen showing a list of Freshdesk tickets assigned to the current agent, each card showing ticket subject, requester name, priority badge, and due date, with tap-to-update status functionality.

Copy this prompt to try it in FlutterFlow

Escalation monitoring dashboard for support managers

Build a manager-facing FlutterFlow screen that uses Freshdesk's search API to list all tickets where fr_escalated:true, sorted by created_at descending. Managers can see which tickets are at risk of breaching first-response SLA, tap into each ticket for full details, and post a private note to the responsible agent — all without logging into the Freshdesk web interface.

FlutterFlow Prompt

A FlutterFlow manager dashboard screen with a list of escalated Freshdesk tickets, each showing subject, SLA due time, and a button to post a private note to the agent.

Copy this prompt to try it in FlutterFlow

Customer self-service ticket status app

Build a simple FlutterFlow app where customers enter their email address to look up their open support tickets using GET /tickets?email={email}. Each ticket card shows the current status, subject, and last update time, giving customers visibility into their support requests without needing to reply to email threads.

FlutterFlow Prompt

A FlutterFlow screen with an email input field and a button that searches for Freshdesk tickets matching that email, then displays a list of results with status and last-update time.

Copy this prompt to try it in FlutterFlow

Troubleshooting

401 Unauthorized on every Freshdesk API Call

Cause: The most common cause is a wrong password in the Basic Auth encoding. Freshdesk requires the literal character X as the password — not blank, not your account password, exactly the letter X. The second most common cause is encoding APIKEY:X with extra whitespace or a newline character (common when copying from some base64 tools).

Solution: Re-compute your base64 string carefully: take your API key string, append :X (colon then uppercase X), and base64-encode the combined string with no trailing newline. In a Mac/Linux terminal: echo -n 'YOURAPIKEY:X' | base64. The -n flag removes the trailing newline that would otherwise corrupt the encoding. Paste the result into FlutterFlow as Basic {result} with no extra spaces. Also verify your Freshdesk subdomain is correct in the base URL.

HTTP 429 Too Many Requests from Freshdesk

Cause: You have exceeded Freshdesk's rate limit of 400 API calls per minute for your account. This commonly happens when multiple agents refresh the ticket list simultaneously, or when a bulk update loop fires many PUT requests in rapid succession.

Solution: Add a minimum refresh interval to your GetTickets query — set it to trigger on page load only, not on every state change. For bulk operations, add a delay between consecutive PUT /tickets/{id} calls using FlutterFlow's Wait action (set to at least 200ms between calls). For writes, the Cloud Function can add server-side throttling by batching multiple updates into a single function invocation that internally spaces out the Freshdesk API calls.

GET /tickets returns an empty array even though tickets exist in Freshdesk

Cause: By default, GET /tickets returns only tickets assigned to the user whose API key you are using. If the API key belongs to an agent with no assigned tickets, the response is an empty array. Also, Freshdesk filters by status by default — resolved and closed tickets are not returned without an explicit status filter.

Solution: To fetch all tickets visible to the authenticated agent regardless of assignment, add the filter parameter: /tickets?filter=all_tickets&include=requester,stats. To include resolved tickets, add status=resolved to the query. To search across all tickets in the account, use the search endpoint /search/tickets?query="priority:3" with a broad query string.

Private note posted via PUT shows as a public reply in Freshdesk

Cause: Public replies and private notes use different Freshdesk endpoints. A PUT to /tickets/{id}/notes with private: true creates a private note. A POST to /tickets/{id}/reply creates a public customer-facing reply. Using the wrong endpoint or omitting the private: true flag sends a response visible to the customer.

Solution: For agent-only private notes, use POST /tickets/{id}/notes with the body { "body": "your note text", "private": true }. For customer-facing replies, use POST /tickets/{id}/reply. Add two separate API Calls in FlutterFlow (or two actions in the Cloud Function) so agents cannot accidentally send a private note to the customer.

Best practices

  • Never paste the raw Freshdesk API key or the base64-encoded Basic Auth value into a FlutterFlow API Call header that ships in a production app — use the Firebase Cloud Function proxy pattern for all write operations.
  • Always use ?include=requester,stats in your GET /tickets calls to fetch contact name, email, and SLA timestamps in one request, reducing API call count against the 400/min rate limit.
  • Map Freshdesk's numeric status (2, 3, 4, 5) and priority (1, 2, 3, 4) codes to human-readable labels and color-coded badges in your FlutterFlow widgets using conditional expressions.
  • Use the Freshdesk search endpoint GET /search/tickets?query="{filter}" for complex filters like escalated tickets or high-priority open tickets, rather than fetching all tickets and filtering client-side.
  • Implement pagination with per_page=100 and page variables, and build a 'Load more' button that increments the page number and appends results to a running list in App State.
  • Add a minimum refresh interval (at least 60 seconds) to your ticket-list query to avoid multiple agents simultaneously hitting the 400 calls/min rate limit.
  • Distinguish clearly in your app's UI between private notes (agent-only) and public replies (visible to customer) — use different button labels and confirm dialogs to prevent agents from accidentally sending internal notes to customers.

Alternatives

Frequently asked questions

Why do I need to use X as the password in Freshdesk Basic Auth?

Freshdesk's API authentication design uses the API key as the HTTP Basic Auth username and the literal character X as the placeholder password. The X is simply a required non-empty string — it has no meaning other than satisfying the Basic Auth format that requires both a username and a password. This is documented in Freshdesk's API reference and is the same across all Freshdesk accounts.

Can I fetch tickets from all agents in the account, not just my own?

Yes — add the filter parameter to your GET /tickets endpoint: /tickets?filter=all_tickets. This returns all tickets visible to the authenticated user based on their role. Agents see their assigned tickets; admins see all tickets. The API key you use must belong to an account admin to see all tickets across all agents.

How do I handle Freshdesk's 400 API calls per minute rate limit in a multi-agent mobile app?

Design your app to be pull-based rather than continuously polling. Set ticket lists to refresh on screen load or on explicit pull-to-refresh gestures, not on a timer. For bulk updates, route them through a Firebase Cloud Function that spaces out the Freshdesk API calls with a 200ms+ delay between each request, keeping the total rate well below 400 per minute across all agents sharing the account credentials.

Can FlutterFlow display Freshdesk ticket conversations and attachments?

Yes. Add a GET /tickets/{id}/conversations API Call to fetch all replies and notes for a ticket. The response includes body_text (plain text content), attachments (array with attachment_url fields), and a private flag for each conversation item. In FlutterFlow, bind these to a second ListView inside a ticket detail screen. For attachments, use a Link widget bound to attachment_url so agents can tap to open the file in a browser.

Is there a Freshdesk free tier that includes API access?

Freshdesk offers a free tier (Sprout plan) with limited features, and API access on the free tier is restricted. For full REST API access including ticket updates, notes, and filtering, you will need a paid Freshdesk plan. Check Freshdesk's current pricing page for the latest plan details before building your integration.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.