Ingest Freshdesk tickets, replies, private notes, and attachments into CortexDB.
Freshdesk Connector
Captures Freshdesk tickets, their full conversation thread (public replies + private notes), and their attachments as CortexDB experiences. Each item keeps its real source timestamp, so the support timeline is preserved.
Two ways to run this connector:
- Run it yourself (Free + paid) —
pip install cortexdb-connectorsthencortexdb-sync sync freshdesk. The connector reads your token from~/.cortexdb/state.json(written bycortexdb init) or from env vars. - Managed sync (Starter and up) — CortexDB runs the same connector as a worker on its infrastructure. Configure it once from your dashboard; CortexDB handles scheduling, retries, backfill, and idempotency.
Upgrade to 0.2.20. Earlier versions silently lost data: a ticket that reached 10 conversations stopped ingesting new replies forever, a rate-limited detail fetch stored the ticket with no body, and attachments were dropped entirely. See Upgrading from 0.2.19 or earlier.
1. Prepare credentials in Freshdesk
Freshdesk → Profile settings → Your API Key. The connector authenticates as
HTTP Basic with the API key as the username and X as the password.
You also need your Freshdesk domain, e.g. acme.freshdesk.com.
2. Configure in your CortexDB dashboard
- Sign in at cortexdb.ai/login.
- Settings → Connectors → Add Connector → Freshdesk.
- Paste the domain and the API key.
- Set the scope template. Default:
org:<your-org>/source:freshdesk. - Click Start sync.
What gets written
| Event | Episode type | Notes |
|---|---|---|
| Ticket | issue | Subject + description; content is prefixed [FreshDesk ticket #N] |
| Public reply | comment | Prefixed [FreshDesk reply on ticket #N]; tagged reply |
| Private note | comment | Prefixed [FreshDesk note on ticket #N]; tagged note |
| Ticket or reply attachment | document | Uploaded to /v1/blobs and ingested as content.kind="blob_ref", so the server's content processors extract and index the file |
Every episode carries thread_id = "freshdesk:ticket:<id>", an entity reference of
type ticket, and metadata with ticket_id, status, priority and a
source_url back to the agent view.
Visibility
All Freshdesk episodes are stored restricted — support data is customer-facing and
sensitive. Public replies and private notes land at the same visibility; the
distinction is carried by the reply / note tag and the content prefix.
Idempotency and history
Tickets and conversations use a content-versioned key:
ticket:<id>:<updated_at>:<content-digest>
conv:<id>:<created_at>:<content-digest>
freshdesk:att:<attachment_id>
Each edited state is retained as a new version rather than overwriting the last one.
The content digest is what makes edited replies and merged tickets work: a
Freshdesk merge moves conversations to the primary ticket without changing their id
or created_at, so a timestamp-only key would collide and the corrected attribution
could never be recorded.
Attachments key on the stable attachment id, so a re-synced ticket replays instead of re-uploading the bytes.
Real-time delivery (webhook)
Freshdesk automations don't sign their payloads, so the receiver authenticates on a static header you configure on the rule.
export FRESHDESK_WEBHOOK_SECRET=<a long random string>
cortexdb-sync serve --port 8081
Then in Freshdesk → Admin → Workflows → Automations, add a Trigger webhook
action pointing at POST https://<your-host>/webhooks/freshdesk/events with the
header Authorization: Bearer <the same secret>.
The webhook path builds its episode through the same normalizer as polling, so a
ticket seen by both serve and sync produces one key and one stored copy at one
visibility — running both is safe.
Run it yourself
pip install cortexdb-connectors
# Step 1: get a CortexDB token + actor (one-time, free tier)
pip install cortexdb-cli
cortexdb init
# Step 2: supply the connector's third-party credentials
export FRESHDESK_DOMAIN=acme.freshdesk.com # required
export FRESHDESK_API_KEY=... # required
export FRESHDESK_INCLUDE_CONVERSATIONS=1 # 0 = tickets only, bodies still ingested
export FRESHDESK_INCLUDE_ATTACHMENTS=1 # 0 = skip attachment blobs
# Step 3: one-shot sync, or `watch` for a poll loop
cortexdb-sync sync freshdesk
cortexdb-sync watch freshdesk --interval 300
cortexdb-sync reads your CortexDB token + actor from ~/.cortexdb/state.json
automatically. Cursor state is persisted in ~/.cortexdb/sync_state.json, so
re-running picks up where the last cycle left off.
FRESHDESK_INCLUDE_CONVERSATIONS=0 drops only the reply and note episodes. The
ticket description is always ingested — before 0.2.20 this flag silently discarded
ticket bodies too.
Limits and behaviour worth knowing
- Attachments over 32 MiB are recorded metadata-only: the filename survives in the episode text, the bytes are not uploaded.
- Rate limits are handled: ticket-detail and conversation requests retry 429 and
5xx responses with
Retry-After, up to 5 attempts. - A ticket that cannot be fetched holds the cursor and is retried rather than
being stored partially. After 5 consecutive failed cycles it is dead-lettered, and
cortexdb-sync statusreports it. - Deletions are not propagated from the poll path. A reply deleted in Freshdesk remains in CortexDB as retained history.
Upgrading from 0.2.19 or earlier
0.2.20 fixed several silent data-loss paths. The one that most likely affects an existing install:
Tickets that reached 10 conversations stopped ingesting. The thread was read from a preview array capped at 10 entries, so once a ticket had 10 replies, every later reply, note and resolution was invisible — while the sync kept reporting success. What was lost was the newest activity: the diagnosis and the fix.
To backfill an affected desk, clear the connector's cursor and re-sync:
cortexdb-sync status # note the current cursor first
cortexdb-sync sync freshdesk --since 2020-01-01T00:00:00Z
Re-ingest is safe — idempotency keys dedupe anything already stored.
Because keys now carry a content digest, the first sync after upgrading records one new version per currently-visible ticket state. Steady-state dedupe is unchanged.