Freshdesk
"Freshdesk: ticket management, contact CRUD, automations, canned responses, REST API v2, webhooks, satisfaction surveys"
You are an expert in integrating Freshdesk for customer support ticket management, automation, and helpdesk operations. ## Key Points - Use the search/filter API (`/search/tickets`) with Freshdesk query syntax instead of fetching all tickets and filtering client-side; pagination caps at 30 results per page. - Store the Freshdesk ticket ID in your own database alongside internal case references so you can reconcile state across systems. - Leverage custom fields on tickets and contacts to push application-specific context (user plan, account ID, feature flags) so agents do not need to switch tools. - Freshdesk enforces strict rate limits (typically 50 requests/minute on lower plans). Always implement retry-after handling and batch operations where possible. - The `description` field on ticket creation expects HTML, not plain text. Passing raw text without wrapping it in `<p>` tags can lead to rendering issues in the agent dashboard.
skilldb get customer-support-services-skills/FreshdeskFull skill: 200 linesFreshdesk — Customer Support Integration
You are an expert in integrating Freshdesk for customer support ticket management, automation, and helpdesk operations.
Core Philosophy
Overview
Freshdesk is a cloud-based customer support platform by Freshworks that organizes support requests into tickets, supports multi-channel intake (email, phone, chat, social), and provides automation rules, SLA management, and a knowledge base. The REST API v2 allows full programmatic control over tickets, contacts, agents, groups, and canned responses.
Setup & Configuration
Install dependencies and configure the API client:
import axios, { AxiosInstance } from "axios";
interface FreshdeskConfig {
domain: string; // e.g. "yourcompany" for yourcompany.freshdesk.com
apiKey: string;
}
function createFreshdeskClient(config: FreshdeskConfig): AxiosInstance {
const client = axios.create({
baseURL: `https://${config.domain}.freshdesk.com/api/v2`,
auth: {
username: config.apiKey,
password: "X", // Freshdesk uses API key as username, password is always "X"
},
headers: { "Content-Type": "application/json" },
});
return client;
}
const freshdesk = createFreshdeskClient({
domain: process.env.FRESHDESK_DOMAIN!,
apiKey: process.env.FRESHDESK_API_KEY!,
});
// Verify connection
async function verifyConnection(): Promise<void> {
const response = await freshdesk.get("/agents/me");
console.log(`Connected as: ${response.data.contact.name}`);
}
Core Patterns
Create a Ticket
interface CreateTicketPayload {
subject: string;
description: string;
email: string;
priority: 1 | 2 | 3 | 4; // 1=Low, 2=Medium, 3=High, 4=Urgent
status: 2 | 3 | 4 | 5; // 2=Open, 3=Pending, 4=Resolved, 5=Closed
type?: string;
tags?: string[];
custom_fields?: Record<string, unknown>;
}
async function createTicket(payload: CreateTicketPayload): Promise<number> {
const response = await freshdesk.post("/tickets", payload);
return response.data.id;
}
// Example
const ticketId = await createTicket({
subject: "Cannot access billing page",
description: "User reports a 403 error when navigating to /billing.",
email: "customer@example.com",
priority: 3,
status: 2,
tags: ["billing", "access-error"],
});
List and Filter Tickets
async function listTickets(filter?: string): Promise<any[]> {
// filter uses Freshdesk query language
const endpoint = filter
? `/search/tickets?query="${encodeURIComponent(filter)}"`
: "/tickets";
const response = await freshdesk.get(endpoint);
return filter ? response.data.results : response.data;
}
// Get all high-priority open tickets
const urgent = await listTickets("priority:3 AND status:2");
// Get tickets updated in last 24 hours
const recent = await listTickets(
`updated_at:>'${new Date(Date.now() - 86400000).toISOString()}'`
);
Reply to a Ticket
async function replyToTicket(
ticketId: number,
body: string,
isPrivate: boolean = false
): Promise<void> {
await freshdesk.post(`/tickets/${ticketId}/reply`, {
body,
private: isPrivate, // private = internal note visible only to agents
});
}
Manage Contacts
async function upsertContact(email: string, data: {
name?: string;
phone?: string;
company_id?: number;
custom_fields?: Record<string, unknown>;
}): Promise<any> {
try {
const existing = await freshdesk.get(
`/contacts?email=${encodeURIComponent(email)}`
);
if (existing.data.length > 0) {
const contactId = existing.data[0].id;
const updated = await freshdesk.put(`/contacts/${contactId}`, data);
return updated.data;
}
} catch {
// Contact not found, create new
}
const created = await freshdesk.post("/contacts", { email, ...data });
return created.data;
}
Webhook Handler
import express, { Request, Response } from "express";
interface FreshdeskWebhookPayload {
ticket_id: number;
ticket_subject: string;
ticket_status: string;
ticket_priority: string;
triggered_event: string;
}
function setupWebhookHandler(app: express.Application): void {
app.post("/webhooks/freshdesk", (req: Request, res: Response) => {
const payload = req.body as FreshdeskWebhookPayload;
switch (payload.triggered_event) {
case "ticket_created":
handleNewTicket(payload);
break;
case "ticket_updated":
handleTicketUpdate(payload);
break;
default:
console.log(`Unhandled event: ${payload.triggered_event}`);
}
res.sendStatus(200);
});
}
Best Practices
- Use the search/filter API (
/search/tickets) with Freshdesk query syntax instead of fetching all tickets and filtering client-side; pagination caps at 30 results per page. - Store the Freshdesk ticket ID in your own database alongside internal case references so you can reconcile state across systems.
- Leverage custom fields on tickets and contacts to push application-specific context (user plan, account ID, feature flags) so agents do not need to switch tools.
Common Pitfalls
- Freshdesk enforces strict rate limits (typically 50 requests/minute on lower plans). Always implement retry-after handling and batch operations where possible.
- The
descriptionfield on ticket creation expects HTML, not plain text. Passing raw text without wrapping it in<p>tags can lead to rendering issues in the agent dashboard.
Anti-Patterns
Using the service without understanding its pricing model. Cloud services bill differently — per request, per GB, per seat. Deploying without modeling expected costs leads to surprise invoices.
Hardcoding configuration instead of using environment variables. API keys, endpoints, and feature flags change between environments. Hardcoded values break deployments and leak secrets.
Ignoring the service's rate limits and quotas. Every external API has throughput limits. Failing to implement backoff, queuing, or caching results in dropped requests under load.
Treating the service as always available. External services go down. Without circuit breakers, fallbacks, or graceful degradation, a third-party outage becomes your outage.
Coupling your architecture to a single provider's API. Building directly against provider-specific interfaces makes migration painful. Wrap external services in thin adapter layers.
Install this skill directly: skilldb add customer-support-services-skills
Related Skills
Crisp
"Crisp: live chat widget, chatbot scenarios, CRM contacts, campaigns, helpdesk, REST API, JavaScript SDK, webhooks"
Drift
"Drift: conversational marketing, live chat, chatbots, meeting scheduling, contact management, REST API, webhooks, JavaScript SDK"
Help Scout
"Help Scout: conversation management, mailbox API, customer profiles, Beacon widget, webhooks, Docs knowledge base, REST API v2"
Intercom
"Intercom: Messenger widget, conversations API, custom bots, product tours, help center, user/company data, events, webhooks, Node SDK"
LiveChat
"LiveChat: real-time chat, ticket system, agent management, chat archives, customer SDK, REST API v3, webhooks, rich messages"
Tawk.to
"Tawk.to: free live chat widget, JavaScript API, visitor monitoring, triggers, REST API, webhooks, customization"