Freshdesk ExecModule
Overview
FreshdeskModule connects ValkyrAI workflows to Freshdesk API v2 through the native map I/O ExecModule ABI. It manages bounded ticket lifecycles, replies and notes, and discovers conversations, contacts, companies, and groups for downstream agent workflows.
The connector follows the useful ticket and contact patterns in n8n's Freshdesk integration while keeping credentials in IntegrationAccount, enforcing one fixed tenant, bounding every request and response, retrying only read operations, and requiring explicit confirmation before ticket deletion.
Usage
- Create a dedicated Freshdesk agent with only the ticket and directory access the workflow needs.
- Copy that agent's Freshdesk API key into the encrypted
IntegrationAccount.apiKeySecureField. - Store only the tenant subdomain in
IntegrationAccount.accountId, for exampleacme-support. - Set the account to
READYand bind it throughExecModuleConfig.authConfig.integrationAccount. - Start with
list_groups,list_contacts, orlist_companiesto discover stable IDs. - Create, update, reply to, annotate, or delete tickets with bounded typed inputs.
Workflow payloads cannot supply credentials, authorization headers, or an arbitrary provider URL.
Inputs
| Name | Type | Requirement | Default | Constraints |
|---|---|---|---|---|
operation | string | Required | None | One of the fifteen operations below. |
subdomain | string | Optional | IntegrationAccount.accountId | One DNS label, 1–63 characters. |
ticketId | string | Ticket-specific operations | None | Positive integer. |
contactId | string | get_contact | None | Positive integer. |
companyId | string | get_company; optional ticket assignment | None | Positive integer. |
groupId | string | get_group; optional ticket assignment | None | Positive integer. |
subject | string | Typed create | None | At most 255 characters. |
email | string | Create unless requesterId is supplied | None | Requester email, at most 320 characters. |
commentText | string | Create, reply, or note | None | Plain text, 1–100,000 characters. |
publicComment | boolean | Optional for add_note | true | false creates a private note. |
requesterId | string | Optional | None | Existing requester contact ID. |
assigneeId | string | Optional | None | Freshdesk responder agent ID. |
priority | string | Optional | None | low, medium, high, or urgent; mapped to API codes 1–4. |
ticketStatus | string | Optional | None | open, pending, resolved, or closed; mapped to API codes 2–5. |
ticketType | string | Optional | None | question, incident, problem, feature request, or refund. |
tags | string array or JSON | Optional | None | At most 100 non-empty values of at most 255 characters. |
customFields | object or JSON | Optional | None | At most 100 entries and 256 KiB. |
ticket | object or JSON | Optional | None | Allowlisted provider fields only; at most 256 KiB. |
query | string | search_tickets | None | One-line Freshdesk search expression, 1–1,024 characters. |
afterCursor | string | Optional | 1 | Positive page number from a prior output, at most 300. |
limit | integer | Optional | 50 | 1–10,000 emitted items. |
returnAll | boolean | Optional | false | Continues until exhaustion, page 300, or 10,000 items. |
confirmDelete | boolean | delete_ticket | false | Must be exactly true before a DELETE is sent. |
The ticket object accepts only subject, description, html_description, email, requester_id, responder_id, group_id, company_id, product_id, email_config_id, priority, status, source, type, tags, custom_fields, cc_emails, due_by, and fr_due_by. Typed inputs override corresponding fields. create_ticket requires a description plus either an email or requester ID; update_ticket rejects empty mutations.
Outputs
| Name | Type | When present | Description |
|---|---|---|---|
status | string | Always | success or error. |
operation | string | Always | Normalized operation name. |
attempts | integer | Always | Provider attempts consumed across all pages. |
httpStatus | integer | Provider responded | Last HTTP status. |
data | object | Single-resource or mutation success | Provider resource normalized into the native output shape. |
id / url | scalar / string | Provider supplies them | Convenience resource fields. |
resourceStatus | integer | Ticket response supplies status | Freshdesk ticket state without overwriting execution status. |
items / count | array / integer | List or search success | Bounded resources and emitted count. |
hasMore | boolean | List or search success | Whether the next bounded page may exist. |
nextCursor | string | Another page may exist | Next Freshdesk page number. |
requestId | string | Provider supplies one | Trace reference for support. |
error | object | Failure | Safe code, message, optional HTTP status, and retryable flag. |
API keys and Basic authorization values are redacted from provider failures, outputs, logs, and workflow events.
IntegrationAccount Requirements
| Setting | Requirement |
|---|---|
| Provider | Freshdesk agent API access |
accountName | Human-readable automation identity |
accountId | Freshdesk tenant subdomain, such as acme-support |
apiKey | Encrypted SecureField containing the Freshdesk API key |
status | READY or an unset legacy status; CLOSED and ERROR fail closed |
Freshdesk API v2 uses HTTP Basic authentication with the API key as the username and X as the password. The module constructs that header internally; the workflow never receives either value. API-key rotation remains an IntegrationAccount lifecycle responsibility.
Configuration
{
"version": "1.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:freshdesk-support-ops"
},
"retryPolicy": {
"maxAttempts": 3,
"backoffStrategy": "EXPONENTIAL",
"initialDelayMs": 1000,
"maxDelayMs": 60000
},
"executionConfig": {"timeoutMs": 30000},
"payloadConfig": {
"parameters": "{\"operation\":\"search_tickets\",\"query\":\"status:2 AND priority:4\",\"limit\":50}"
}
}
The account reference is symbolic. Persisted workflows bind the generated relationship, never a plaintext API key.
Operations
| Operation | Freshdesk behavior | Side effect |
|---|---|---|
list_tickets | Lists visible tickets with bounded page pagination. | Read-only; safe retries. |
get_ticket | Reads one ticket by ID. | Read-only; safe retries. |
search_tickets | Runs a bounded Freshdesk ticket search. | Read-only; safe retries. |
create_ticket | Creates one ticket from allowlisted fields. | New ticket and possible notifications; one attempt. |
update_ticket | Updates one ticket with supplied allowlisted fields. | Mutation and possible notifications; one attempt. |
delete_ticket | Soft-deletes one ticket after explicit confirmation. | Destructive but restorable under provider policy; one attempt. |
reply_ticket | Adds one public ticket reply. | Conversation and possible notifications; one attempt. |
add_note | Adds one public or private note. | Conversation/audit mutation; one attempt. |
list_conversations | Lists replies and notes for one ticket. | Read-only; safe retries. |
list_contacts / get_contact | Discovers requester contacts. | Read-only; safe retries. |
list_companies / get_company | Discovers customer companies. | Read-only; safe retries. |
list_groups / get_group | Discovers support groups. | Read-only; safe retries. |
Errors and Failure Modes
| Code | Typical cause | Retryable | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Missing ID, description/requester, confirmation, invalid tenant/page/JSON, unsupported field/value, or empty update. | No | Correct the named input; no unsafe request was sent. |
UNSUPPORTED_OPERATION | Unknown operation. | No | Select a documented operation. |
INTEGRATION_ACCOUNT_REQUIRED | No bound account. | No | Bind a Freshdesk IntegrationAccount. |
INTEGRATION_ACCOUNT_NOT_READY | Account is closed or in error. | No | Repair or reconnect the account. |
FRESHDESK_HTTP_400 | Invalid field, value, search syntax, or provider constraint. | No | Correct the bounded provider error. |
FRESHDESK_HTTP_401 | API key is missing, revoked, or invalid. | No | Rotate or reconnect the IntegrationAccount. |
FRESHDESK_HTTP_403 | Agent lacks resource or operation authority. | No | Grant the minimum required role permission. |
FRESHDESK_HTTP_404 | ID is wrong, deleted, or invisible. | No | Re-run discovery under the same account. |
FRESHDESK_HTTP_429 | Account or endpoint rate limit is exhausted. | Yes for reads | Honor Retry-After and reduce concurrency. |
FRESHDESK_HTTP_5xx | Transient provider failure. | Yes for reads | Retry reads; reconcile writes before manual replay. |
NETWORK_ERROR | Timeout, DNS, TLS, or connectivity failure. | Yes for reads | Verify connectivity; inspect Freshdesk before repeating writes. |
RESPONSE_TOO_LARGE | Response exceeded 5 MiB. | No | Lower the limit or narrow the search. |
INVALID_PROVIDER_RESPONSE | A list/search response did not contain an array. | No | Use requestId and verify API compatibility. |
Example
Create an urgent escalation after a governed workflow validates the customer impact:
{
"operation": "create_ticket",
"subject": "Production onboarding is blocked",
"commentText": "Customer cannot complete activation after the verified redirect step.",
"email": "ops@example.com",
"priority": "urgent",
"groupId": "9001",
"tags": ["activation", "customer-blocked"],
"customFields": {"cf_incident_reference": "INC-2026-0811"}
}
Expected result:
{
"status": "success",
"operation": "create_ticket",
"attempts": 1,
"httpStatus": 201,
"id": 12345,
"resourceStatus": 2,
"data": {"id": 12345, "status": 2, "priority": 4}
}
Notes
- Pagination: ordinary list endpoints use
pageandper_pagewith at most 100 items. Search uses Freshdesk's 30-result page contract. The module stops at provider exhaustion, page 300, the requested limit, or 10,000 emitted items. - Rate limits: read operations may retry HTTP 408, 429, 500, 502, 503, and 504 and network failures, honoring numeric or RFC-1123
Retry-After. Mutations never retry automatically. - API limits: structured inputs are capped at 256 KiB and responses at 5 MiB. Ticket descriptions are capped at 100,000 characters and list/search output at 10,000 items.
- Idempotency: reads are repeatable for a fixed provider state. Create, update, reply, note, and delete operations are single-attempt because a timeout can follow a committed mutation. Reconcile the ticket or conversation before replaying a write.
- Destructive behavior:
delete_ticketrequiresconfirmDelete=trueand uses Freshdesk's ticket DELETE endpoint. Permanent contact deletion, bulk deletion, and attachment deletion are not exposed. - API behavior: ticket creation can trigger dispatch rules, email notifications, automations, SLA timers, and webhooks configured in Freshdesk. Those effects are provider-owned and can continue after the request returns.
- External verification: deterministic local tests cover Basic auth, tenant routing, request bodies, page controls, retries, mutation non-retry, delete confirmation, redaction, validation, and metadata discovery. Live roles, plan limits, automations, email delivery, and tenant-specific custom fields require separately authorized Freshdesk credentials and are not exercised in repository tests.
- Deferred operations: attachments, ticket merge/restore/archive, bulk jobs, contact/company mutations, agents, roles, solutions, satisfaction ratings, time entries, products, SLA policies, automations, custom objects, and webhooks are deferred.
- Functional references: n8n Freshdesk source and Freshdesk API v2.