To integrate Freshdesk with Lovable, generate an API key from your Freshdesk agent profile, store it with your subdomain in Cloud → Secrets, then build Edge Functions that proxy calls to the Freshdesk API for tickets, contacts, agents, and companies. Freshdesk is the affordable Zendesk alternative with a free plan for 10 agents, making it ideal for startups building embedded customer support portals in their Lovable apps.
Add affordable customer support features to Lovable with Freshdesk's API
Freshdesk is the most cost-effective full-featured helpdesk for startups and growing companies. Its free plan supports up to 10 agents with email and phone ticketing, knowledge base, and full API access — making it an exceptional starting point for Lovable apps that need to embed customer support features without incurring significant tool costs. Freshdesk's API covers tickets, contacts, companies, agents, time tracking, and solution articles (knowledge base).
Authentication uses API key-based Basic Auth — simpler than OAuth2 and simpler than some token formats. The Freshdesk API uses HTTP Basic Auth where the username is your API key and the password is literally the string 'X' (a Freshdesk convention). The Edge Function encodes these using Base64 and sends the Authorization header as 'Basic base64(apiKey:X)'.
Your Freshdesk subdomain is the company name in your Freshdesk URL (yourcompany.freshdesk.com). All API calls go to https://yourcompany.freshdesk.com/api/v2/. The API is JSON-based with standard REST conventions.
For Lovable apps, the highest-value use cases are customer-facing support portals (users create and track tickets without leaving your app), agent productivity tools (internal ticket management UI without full Freshdesk access for light users), and automated ticket creation from app events (error reports, usage limit alerts, failed exports). Freshdesk's solution articles API also enables embedding your knowledge base directly in your app for self-service support.
Integration method
Freshdesk has no native Lovable connector. All support operations are proxied through Lovable Edge Functions that call the Freshdesk API v2. The API key and subdomain are stored in Cloud → Secrets and accessed via Deno.env.get(). Authentication uses HTTP Basic Auth with the API key as the username and 'X' as the password, which the Edge Function encodes into a Base64 Authorization header.
Prerequisites
- A Lovable project with Lovable Cloud enabled (Edge Functions require a deployed app)
- A Freshdesk account (free plan available for up to 10 agents at freshdesk.com — no credit card required)
- Your Freshdesk subdomain (the 'yourcompany' in yourcompany.freshdesk.com)
- Agent-level access to your Freshdesk account to access API key settings
Step-by-step guide
Generate a Freshdesk API key and identify your subdomain
Generate a Freshdesk API key and identify your subdomain
Freshdesk API authentication uses an API key associated with your agent account. Each agent has their own API key visible in their profile settings. To get your API key: log in to Freshdesk and click your profile picture in the top-right corner. Select 'Profile Settings' from the dropdown. Scroll to the bottom of the Profile Settings page to find the 'Your API Key' section. The API key is displayed (click the eye icon to show it). Copy the key. Your Freshdesk subdomain is the company name in your Freshdesk URL. When logged in, your URL looks like yourcompany.freshdesk.com. The subdomain is 'yourcompany'. This is required for constructing all API base URLs: https://yourcompany.freshdesk.com/api/v2/. Freshdesk's API authentication uses HTTP Basic Auth with a specific convention: the username is your API key, and the password is the literal string 'X' (just the letter X — it is not a typo; this is Freshdesk's API authentication convention). You base64-encode the combined string 'apiKey:X' and send it as the Authorization header. So the Authorization header value is: Basic base64(YOUR_API_KEY + ':X') In the Edge Function, this is: btoa(apiKey + ':X') Freshdesk also supports email + password authentication, but API key authentication is recommended for server-to-server integrations as it does not change when the user's password changes and can be revoked independently.
Pro tip: The Freshdesk API password 'X' is intentional — it is a Freshdesk convention for API key authentication where any non-empty password string works, but 'X' is standard in all Freshdesk documentation. Using an empty password will cause authentication failures.
Expected result: A Freshdesk API key is copied from Profile Settings. Your Freshdesk subdomain is noted (the company name part of your Freshdesk URL without .freshdesk.com).
Store credentials in Cloud Secrets
Store credentials in Cloud Secrets
Add Freshdesk credentials to Lovable's Cloud Secrets panel. In Lovable, click '+' → Cloud panel → Secrets tab. Add two secrets: - FRESHDESK_API_KEY — your agent API key from Freshdesk Profile Settings - FRESHDESK_SUBDOMAIN — your Freshdesk subdomain (just 'yourcompany', not the full URL) The Edge Function constructs the full API URL as: https://${FRESHDESK_SUBDOMAIN}.freshdesk.com/api/v2/ And the authorization header as: Basic ${btoa(FRESHDESK_API_KEY + ':X')} Store only the subdomain portion without protocol, dots, or the .freshdesk.com suffix. For yourcompany.freshdesk.com, the secret value is simply 'yourcompany'. For Freshdesk ticket priority values, the API uses numeric IDs: - 1 = Low - 2 = Medium (default) - 3 = High - 4 = Urgent For ticket status values: - 2 = Open - 3 = Pending - 4 = Resolved - 5 = Closed Store any Freshdesk agent IDs you want to use for auto-assignment as additional secrets (FRESHDESK_DEFAULT_AGENT_ID). Agent IDs are numeric and found in Freshdesk → Admin → Agents → click an agent to see their ID in the URL.
Pro tip: Freshdesk ticket priority and status fields use numeric IDs in the API, not string labels. Map your app's priority terms (low, medium, high, urgent) to the corresponding numeric IDs in your Edge Function rather than passing strings.
Expected result: FRESHDESK_API_KEY and FRESHDESK_SUBDOMAIN appear in Cloud → Secrets with masked values.
Create the Freshdesk Support API proxy Edge Function
Create the Freshdesk Support API proxy Edge Function
Build the Edge Function that handles Freshdesk operations. The function constructs the Base64 Basic Auth header from the stored credentials and proxies requests to the appropriate Freshdesk API endpoints. Freshdesk API v2 endpoints follow REST conventions. Key endpoints: - GET /api/v2/tickets — list tickets, supports filters: email, status, priority, assigned_to - GET /api/v2/tickets/{id} — get ticket with details - POST /api/v2/tickets — create ticket - PUT /api/v2/tickets/{id} — update ticket (status, priority, assignee) - GET /api/v2/tickets/{id}/conversations — get ticket conversation thread - POST /api/v2/tickets/{id}/reply — add reply to ticket - GET /api/v2/contacts — list contacts (for finding users by email) - POST /api/v2/contacts — create contact For filtering tickets by requester email: GET /api/v2/tickets?email=user@example.com. This returns all tickets where the requester's email matches. For creating tickets, the minimum required fields are: subject, description, email (requester's email), priority (numeric), and status (numeric). Setting status to 2 (Open) and priority to 2 (Medium) is a good default. Ticket creation accepts HTML in the description field for rich formatting, but plain text works for most use cases. Store the requester's email and name in the ticket's requester_id (by looking up or creating the Freshdesk contact first) or pass email and name directly — Freshdesk creates the contact automatically if it does not exist.
Create an Edge Function called freshdesk-api at supabase/functions/freshdesk-api/index.ts. Accept POST with { action, params }. Support actions: create_ticket, list_tickets, get_ticket, get_conversations, reply_to_ticket, update_ticket_status. Build the Base64 Basic Auth header from FRESHDESK_API_KEY + ':X' and FRESHDESK_SUBDOMAIN secrets. Return the Freshdesk API response. Include CORS headers.
Paste this in Lovable chat
1import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'23const corsHeaders = {4 'Access-Control-Allow-Origin': '*',5 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',6}78function fdHeaders() {9 const key = Deno.env.get('FRESHDESK_API_KEY')10 if (!key) throw new Error('FRESHDESK_API_KEY not configured')11 return {12 Authorization: `Basic ${btoa(key + ':X')}`,13 'Content-Type': 'application/json',14 }15}1617function fdUrl(path: string) {18 const sub = Deno.env.get('FRESHDESK_SUBDOMAIN')!19 return `https://${sub}.freshdesk.com/api/v2${path}`20}2122serve(async (req) => {23 if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders })2425 try {26 const { action, params = {} } = await req.json()27 const headers = fdHeaders()28 let resp: Response2930 switch (action) {31 case 'create_ticket':32 resp = await fetch(fdUrl('/tickets'), {33 method: 'POST',34 headers,35 body: JSON.stringify({36 subject: params.subject,37 description: params.description,38 email: params.requesterEmail,39 name: params.requesterName || '',40 priority: params.priority || 2,41 status: 2, // Open42 tags: params.tags || [],43 }),44 })45 break4647 case 'list_tickets':48 resp = await fetch(fdUrl(`/tickets?email=${encodeURIComponent(params.email || '')}&per_page=${params.perPage || 20}&page=${params.page || 1}&include=description`), { headers })49 break5051 case 'get_ticket':52 resp = await fetch(fdUrl(`/tickets/${params.ticketId}?include=description,conversations`), { headers })53 break5455 case 'get_conversations':56 resp = await fetch(fdUrl(`/tickets/${params.ticketId}/conversations`), { headers })57 break5859 case 'reply_to_ticket':60 resp = await fetch(fdUrl(`/tickets/${params.ticketId}/reply`), {61 method: 'POST',62 headers,63 body: JSON.stringify({64 body: params.body,65 user_id: params.userId,66 }),67 })68 break6970 case 'update_ticket_status':71 resp = await fetch(fdUrl(`/tickets/${params.ticketId}`), {72 method: 'PUT',73 headers,74 body: JSON.stringify({ status: params.status }),75 })76 break7778 default:79 return new Response(JSON.stringify({ error: 'Unknown action' }), {80 status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' },81 })82 }8384 const data = await resp.json()85 return new Response(JSON.stringify(data), {86 status: resp.ok ? 200 : resp.status,87 headers: { ...corsHeaders, 'Content-Type': 'application/json' },88 })89 } catch (error) {90 return new Response(JSON.stringify({ error: error.message }), {91 status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' },92 })93 }94})Pro tip: The Freshdesk 'conversations' endpoint returns all replies and notes for a ticket. Replies (public_conversation: true) are visible to the customer; notes (public_conversation: false or is_note: true) are internal agent notes. Filter appropriately when building a customer-facing portal.
Expected result: The freshdesk-api Edge Function deploys in Cloud → Edge Functions. Test with action 'list_tickets' and your own email — existing tickets should appear, or an empty array if none exist for that email.
Build the Freshdesk customer support portal in Lovable
Build the Freshdesk customer support portal in Lovable
With the Edge Function deployed, build the support portal UI using Lovable's chat. A complete customer support portal has three views: ticket submission form, ticket list, and ticket detail with conversation thread. For the ticket list, call list_tickets with the authenticated user's email on component mount. Freshdesk ticket statuses use numeric values in the API: convert them to labels in your React component (2 = 'Open', 3 = 'Pending', 4 = 'Resolved', 5 = 'Closed'). Color-code: open/pending in yellow-orange, resolved/closed in gray. For the ticket submission form, collect subject and description at minimum. If your app handles multiple product areas or issue types, add a category selector that maps to Freshdesk ticket tags or custom fields. On submission, call create_ticket and store the returned ticket ID in your Supabase database linked to the user's account. For the conversation thread view, call get_conversations with the selected ticket ID. Freshdesk returns conversations as an array where each item has a body (HTML), created_at, user_id, and from_email. Render as a chat-style thread. For customer-facing portals, filter out internal notes (is_note: true) to show only public replies. To allow users to reply to their tickets, use the reply_to_ticket action. Freshdesk requires the agent user ID for replies added via API — if you want users to add replies, set the user_id to your Freshdesk admin account ID (stored as FRESHDESK_BOT_AGENT_ID in secrets) and customize the reply body to include the user's name for context. RapidDev's team can help design production-grade support portal implementations with real-time updates via Freshdesk webhooks if you need ticket status changes to push to your app immediately.
Build a support portal page with three sections: a 'New Ticket' button that opens a form with subject and description fields, a ticket list table showing all the user's tickets with status badges (Open=yellow, Pending=blue, Resolved=green, Closed=gray), and a ticket detail drawer that opens when a ticket row is clicked showing the conversation thread. Use the freshdesk-api Edge Function with the current user's email for list_tickets and ticketId for get_conversations.
Paste this in Lovable chat
Pro tip: Freshdesk's ticket list endpoint returns a maximum of 100 tickets per page. For users with many old tickets, add pagination using the 'page' parameter. Start with the most recent tickets by using the default sort order (updated_at descending) — most users only care about recent activity.
Expected result: The support portal shows the user's ticket history. New tickets created via the form appear in Freshdesk within seconds and are visible in the Freshdesk agent workspace. The conversation thread renders correctly with public replies visible.
Common use cases
Customer support portal with ticket creation and history
Build a support section in your Lovable app where users can submit support tickets and view their existing ticket history without leaving your product. The Edge Function creates tickets in Freshdesk with the user's information and tracks the conversation thread. Users see open, pending, and resolved tickets with status badges, and can add replies to ongoing conversations — all within your branded interface.
Create an Edge Function called freshdesk-support that accepts { action, params } and supports: create_ticket (with requesterName, requesterEmail, subject, description, priority) and list_tickets (with requesterEmail). Use FRESHDESK_API_KEY and FRESHDESK_SUBDOMAIN from secrets with Base64 Basic Auth. Return the ticket ID on creation, and ticket list with status/subject/created_at on list. Build a support portal page in Lovable with a New Ticket form and ticket history table.
Copy this prompt to try it in Lovable
Automatic ticket creation from app error events
When users encounter errors or blocked workflows in your Lovable app, automatically create a Freshdesk ticket with the full error context. The Edge Function creates a ticket with the error message, user ID, browser info, and relevant app state as description text. Your support team has immediate context without requiring the user to describe the problem, reducing time-to-resolution significantly.
Create an Edge Function called freshdesk-auto-ticket that accepts { userEmail, userName, errorType, errorMessage, appPage, userAgent } and creates a Freshdesk support ticket using FRESHDESK_API_KEY and FRESHDESK_SUBDOMAIN from secrets. Set priority to 'high' for errors that block key workflows, 'normal' for general errors. Tag the ticket with 'auto-created' and 'error-report'. Return the ticket ID. Call this from my app's error boundary.
Copy this prompt to try it in Lovable
Agent productivity dashboard with ticket queue management
Build an internal support dashboard for your team that shows their Freshdesk ticket queue filtered by status and priority. Support agents can view ticket details, update statuses, and add replies from the Lovable dashboard. Managers see a queue overview with SLA compliance indicators. This reduces context-switching for agents who primarily work in your product rather than Freshdesk directly.
Create Edge Functions to fetch open Freshdesk tickets filtered by assignee and priority, get a specific ticket with its conversations, and update ticket status. Build a support agent dashboard in Lovable showing an open ticket queue table with subject, requester, priority badge, and time open. Add ticket detail panel showing conversation history and a reply form. Include status update dropdown for quick triage.
Copy this prompt to try it in Lovable
Troubleshooting
Freshdesk API returns 401 Unauthorized on every request
Cause: The Base64 encoding of the auth string is incorrect. Common mistake: encoding the API key alone instead of 'apiKey:X' with the literal ':X' suffix.
Solution: Verify the authorization header uses btoa(apiKey + ':X') — including the colon and capital X as the password. Also confirm FRESHDESK_API_KEY is set correctly in Cloud → Secrets and matches the key shown in your Freshdesk Profile Settings. The encoded string should look like 'Basic abc123...='.
1// Correct Freshdesk auth encoding:2Authorization: `Basic ${btoa(key + ':X')}` // ':X' is required, not optionalAPI returns 404 for all endpoints
Cause: The FRESHDESK_SUBDOMAIN contains the full domain (yourcompany.freshdesk.com) instead of just the subdomain (yourcompany).
Solution: Update FRESHDESK_SUBDOMAIN to contain only the company name portion. For yourcompany.freshdesk.com, the value should be 'yourcompany'. The Edge Function constructs the full URL from this — including the full domain creates double-domain URLs that return 404.
Ticket creation returns 422 Unprocessable Entity
Cause: Required fields are missing or have invalid values. Most commonly: subject is empty, description is empty, or priority/status use string values instead of numeric IDs.
Solution: Ensure subject and description are non-empty strings. Use numeric values for priority (1-4) and status (2-5). Freshdesk returns a detailed error object with field-level messages in the response body — check the error.errors array in the response to see which specific field is failing validation.
Ticket list returns empty array even though tickets exist in Freshdesk
Cause: The email filter is URL-encoded incorrectly, or the email does not match any contact's email in Freshdesk.
Solution: Verify you are URL-encoding the email in the query parameter: encodeURIComponent(params.email). Also check that the email exists as a contact or requester in Freshdesk — tickets created via email submissions may use a different email address than the one you are searching for. Test the API directly in Freshdesk's API explorer with your target email.
Best practices
- Encode Freshdesk API key auth as btoa(apiKey + ':X') — the ':X' suffix is a Freshdesk convention that is required for proper Basic Auth encoding.
- Store ticket IDs in your Supabase database when creating tickets so future lookups use direct ticket ID retrieval instead of email-filtered list queries.
- Filter conversation replies to show only public messages (is_note: false) in customer-facing portals — internal agent notes should not be visible to customers.
- Use numeric status codes (2-5) rather than string labels in API calls — convert to human-readable labels only in your React UI components.
- Add tags to auto-created tickets (e.g., 'auto-created', 'in-app') so your support team can distinguish programmatically-created tickets from customer-submitted ones in Freshdesk reports.
- Implement a rate limit for automatic ticket creation from error events — trigger tickets only for unique error types per user per hour, not for every occurrence of a repeated error.
- For customer-facing portals, show ticket descriptions from the initial ticket body rather than from the conversations array — the ticket description and the first conversation reply are different fields in Freshdesk.
Alternatives
Choose Zendesk if you need deeper customization, a larger app marketplace, and more sophisticated SLA management — Zendesk's enterprise feature set justifies its higher price for larger support operations.
Choose Intercom if you prioritize conversational in-app messaging over traditional ticket queues — Intercom's messenger-first approach suits modern SaaS products better for proactive support.
Choose Freshsales if you need CRM pipeline management alongside support — the Freshworks suite allows Freshdesk tickets to appear in Freshsales contact timelines for a unified customer view.
Frequently asked questions
Does Freshdesk's free plan support API access for Lovable integrations?
Yes — Freshdesk's free plan (Sprout) supports up to 10 agents with full API access. The free plan includes ticket management, basic knowledge base, email, and phone channels. API rate limits on the free plan are 1,000 API calls per hour per agent. For most Lovable integrations, this is more than sufficient. Paid plans (Growth at $18/agent/month) increase limits and add automation, custom roles, and SLA management.
Why does Freshdesk use 'X' as the API key password?
This is a Freshdesk convention for API key authentication that pre-dates modern token-based auth standards. HTTP Basic Auth requires both a username and password, but when using an API key, the password field is irrelevant. Freshdesk accepts any non-empty string as the password when using API keys, but 'X' is the value specified in all official documentation. Many developers use the API key as both username and password, which also works, but ':X' is the canonical form.
How do I create tickets from multiple Lovable app users without mixing up support requests?
When creating Freshdesk tickets, always pass the requester's email address in the 'email' field. Freshdesk creates a unique contact record for each email address and associates all tickets with that contact. In your Lovable app, use the authenticated user's email (from Supabase Auth) as the requester email. This ensures each user's ticket history is properly isolated and searchable by their email in Freshdesk.
Can I use Freshdesk alongside Freshsales in the same Lovable app?
Yes — both Freshdesk and Freshsales APIs use the same API key and subdomain authentication pattern. You can create separate Edge Functions for each product. Freshdesk subdomains end in .freshdesk.com and Freshsales subdomains end in .freshsales.io — store them separately as FRESHDESK_SUBDOMAIN and FRESHSALES_SUBDOMAIN. Building a unified customer view that shows both CRM data and support tickets for the same user email creates significant operational value for teams using both products.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation