> ## Documentation Index
> Fetch the complete documentation index at: https://docs.open.cx/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect Freshchat

> Generate a Freshchat API token, enter your credentials in OpenCX, register the webhook in Freshchat, and pick a routing mode.

export const FreshchatFlow = () => {
  const BEAM_COLOR = "#22c55e";
  const renderBeam = ({id, d, duration = 2.8, delay = 0, reverse = false, opacityMult = 1}) => {
    const values = reverse ? {
      x1: "110%;-10%",
      x2: "120%;0%"
    } : {
      x1: "-10%;110%",
      x2: "0%;120%"
    };
    return <g key={id}>
        <path d={d} stroke="currentColor" strokeOpacity={0.18 * opacityMult} strokeWidth="1.5" fill="none" strokeLinecap="round" />
        <path d={d} stroke={`url(#${id})`} strokeWidth="2" fill="none" strokeLinecap="round" strokeOpacity={opacityMult} />
        <defs>
          <linearGradient id={id} gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="0" y2="0">
            <stop offset="0%" stopColor={BEAM_COLOR} stopOpacity="0" />
            <stop offset="45%" stopColor={BEAM_COLOR} stopOpacity="1" />
            <stop offset="55%" stopColor={BEAM_COLOR} stopOpacity="1" />
            <stop offset="100%" stopColor={BEAM_COLOR} stopOpacity="0" />
            <animate attributeName="x1" values={values.x1} dur={`${duration}s`} begin={`${delay}s`} repeatCount="indefinite" />
            <animate attributeName="x2" values={values.x2} dur={`${duration}s`} begin={`${delay}s`} repeatCount="indefinite" />
          </linearGradient>
        </defs>
      </g>;
  };
  const CUST_X = 70, FC_X = 290, FORK_X = 500, OCX_X = 760, AGENT_X = 760;
  const MID = 180, R = 40;
  const AI_Y = 110, AGENT_Y = 250;
  const b1 = `M ${CUST_X + R},${MID} L ${FC_X - R},${MID}`;
  const b2 = `M ${FC_X + R},${MID} L ${FORK_X - 22},${MID}`;
  const bAI = `M ${FORK_X + 22},${MID} C ${FORK_X + 80},${MID} ${OCX_X - R - 80},${AI_Y} ${OCX_X - R},${AI_Y}`;
  const bAIr = `M ${OCX_X - R},${AI_Y} C ${OCX_X - R - 80},${AI_Y + 30} ${FORK_X + 80},${MID + 30} ${FORK_X + 22},${MID}`;
  const bAgent = `M ${FORK_X + 22},${MID} C ${FORK_X + 80},${MID} ${AGENT_X - 50 - 60},${AGENT_Y} ${AGENT_X - 50},${AGENT_Y}`;
  return <div className="zendesk-flow not-prose my-8 w-full">
      <div className="zendesk-flow-frame relative w-full overflow-hidden rounded-md border border-black/10 bg-white px-4 py-6 dark:border-white/10 dark:bg-zinc-950">
        <div className="zendesk-flow-grid pointer-events-none absolute inset-0" aria-hidden />
        <svg viewBox="0 0 860 340" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" className="relative z-[1] block w-full text-zinc-500 dark:text-zinc-400" role="img" aria-label="Freshchat flow: Customer chats land in Freshchat, then a binary routing-mode fork sends each conversation to either OpenCX or a human agent">
          {renderBeam({
    id: "fc-1",
    d: b1,
    duration: 2.4,
    delay: 0
  })}
          {renderBeam({
    id: "fc-2",
    d: b2,
    duration: 2.4,
    delay: 0.4
  })}
          {renderBeam({
    id: "fc-ai",
    d: bAI,
    duration: 2.6,
    delay: 0.8
  })}
          {renderBeam({
    id: "fc-air",
    d: bAIr,
    duration: 2.6,
    delay: 2.1,
    reverse: true
  })}
          {renderBeam({
    id: "fc-ag",
    d: bAgent,
    duration: 2.6,
    delay: 1.4
  })}

          <g className="fill-zinc-500 dark:fill-zinc-400" style={{
    font: "500 10.5px ui-sans-serif, system-ui, sans-serif"
  }}>
            <text x={(FORK_X + OCX_X) / 2} y={AI_Y - 12} textAnchor="middle">Auto-handle all</text>
            <text x={(FORK_X + AGENT_X) / 2} y={AGENT_Y + 28} textAnchor="middle">Assigned only</text>
          </g>

          {}
          <g>
            <circle cx={CUST_X} cy={MID} r={R} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            <g transform={`translate(${CUST_X - 10} ${MID - 10})`} className="fill-zinc-700 dark:fill-zinc-200">
              <path d="M 10 1 C 4.5 1 0 5 0 10 c 0 2 1 4 2.6 5.5 L 2 19 l 3.8 -1.4 c 1.3 0.5 2.7 0.7 4.2 0.7 c 5.5 0 10 -4 10 -9 S 15.5 1 10 1 Z" fill="currentColor" />
            </g>
            <text x={CUST_X} y={MID + R + 22} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "600 12px ui-sans-serif, system-ui, sans-serif"
  }}>Customer</text>
          </g>

          {}
          <g>
            <circle cx={FC_X} cy={MID} r={R} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            <foreignObject x={FC_X - 20} y={MID - 20} width="40" height="40">
              <div className="flex h-full w-full items-center justify-center">
                <img src="/images/integrations/freshchat.svg" alt="" className="h-7 w-7 object-contain" />
              </div>
            </foreignObject>
            <text x={FC_X} y={MID + R + 22} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "600 12px ui-sans-serif, system-ui, sans-serif"
  }}>Freshchat</text>
            <text x={FC_X} y={MID + R + 36} textAnchor="middle" className="fill-zinc-500 dark:fill-zinc-400" style={{
    font: "400 10.5px ui-sans-serif, system-ui, sans-serif"
  }}>live chat</text>
          </g>

          {}
          <g>
            <polygon points={`${FORK_X - 22},${MID} ${FORK_X},${MID - 26} ${FORK_X + 22},${MID} ${FORK_X},${MID + 26}`} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            <text x={FORK_X} y={MID - 2} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "700 10px ui-sans-serif, system-ui, sans-serif"
  }}>routing</text>
            <text x={FORK_X} y={MID + 10} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "700 10px ui-sans-serif, system-ui, sans-serif"
  }}>mode</text>
          </g>

          {}
          <g>
            <circle cx={OCX_X} cy={AI_Y} r="44" className="fill-none" stroke={BEAM_COLOR} strokeOpacity="0.25" strokeWidth="1.5">
              <animate attributeName="r" values="42;52;42" dur="3.6s" repeatCount="indefinite" />
              <animate attributeName="stroke-opacity" values="0.35;0;0.35" dur="3.6s" repeatCount="indefinite" />
            </circle>
            <circle cx={OCX_X} cy={AI_Y} r="40" className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            <foreignObject x={OCX_X - 30} y={AI_Y - 16} width="60" height="32">
              <div className="flex h-full w-full items-center justify-center">
                <img src="/logo-light.svg" alt="" className="block h-full w-full object-contain dark:hidden" />
                <img src="/logo.svg" alt="" className="hidden h-full w-full object-contain dark:block" />
              </div>
            </foreignObject>
            <text x={OCX_X} y={AI_Y + 56} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "600 12px ui-sans-serif, system-ui, sans-serif"
  }}>OpenCX</text>
            <text x={OCX_X} y={AI_Y + 70} textAnchor="middle" className="fill-zinc-500 dark:fill-zinc-400" style={{
    font: "400 10.5px ui-sans-serif, system-ui, sans-serif"
  }}>AI picks up every thread</text>
          </g>

          {}
          <g>
            <rect x={AGENT_X - 50} y={AGENT_Y - 24} width="100" height="48" rx="10" className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" strokeDasharray="3 3" />
            <g transform={`translate(${AGENT_X - 12} ${AGENT_Y - 12})`} className="fill-none stroke-zinc-700 dark:stroke-zinc-200" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
              <circle cx="12" cy="8" r="5" />
              <path d="M 2 24 C 2 16 22 16 22 24" />
            </g>
            <text x={AGENT_X} y={AGENT_Y + 40} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "600 12px ui-sans-serif, system-ui, sans-serif"
  }}>Default agent</text>
            <text x={AGENT_X} y={AGENT_Y + 54} textAnchor="middle" className="fill-zinc-500 dark:fill-zinc-400" style={{
    font: "400 10.5px ui-sans-serif, system-ui, sans-serif"
  }}>Freshchat routes first</text>
          </g>
        </svg>
      </div>
    </div>;
};

The Freshchat integration uses a <Tooltip tip="A long-lived credential generated in your Freshchat Admin Settings under Integrations & APIs. Bound to the admin that created it.">Freshchat API access token</Tooltip> to read and write conversations in your workspace, plus a webhook you register in Freshchat that pushes events back to OpenCX.

<FreshchatFlow />

<Info>
  Setup takes about 10 minutes. You need admin access in both Freshchat and your
  OpenCX organization.
</Info>

## Before you start

<AccordionGroup>
  <Accordion title="Admin access in Freshchat" icon="user-shield">
    Only Freshchat admins can generate API tokens and register webhooks. Agent roles cannot reach the **Admin Settings → Integrations & APIs** page.
  </Accordion>

  <Accordion title="Pick the default agent" icon="user-headset">
    The AI posts replies in Freshchat as an agent you designate. Customers see
    that agent's name and avatar on every AI message, so create a dedicated agent
    (for example *OpenCX AI*) rather than reusing a person's account. The agent
    must exist in Freshchat before setup — OpenCX resolves it by email.
  </Accordion>

  <Accordion title="Owner or admin in OpenCX" icon="key">
    Required to save integration settings in [Settings → Integrations](https://platform.open.cx/settings/integrations).
  </Accordion>
</AccordionGroup>

## Setup

<Steps>
  <Step title="Generate a Freshchat API access token">
    In Freshchat, open **Admin Settings → Integrations & APIs** and create a new API token.

    1. Label it `OpenCX` (or anything recognizable).
    2. Make sure the token has conversation management permissions.
    3. **Copy the token immediately** — Freshchat hides it after you close the dialog.

    See [Freshchat's API documentation](https://support.freshchat.com/en/support/solutions/articles/50000000306-freshchat-apis) for the latest path if Freshchat moves it.

    <Warning>
      The token is bound to the admin that created it. If that admin is deactivated or loses permissions, the integration stops working.
    </Warning>
  </Step>

  <Step title="Open Freshchat in OpenCX">
    In your OpenCX dashboard, go to [**Settings →
    Integrations**](https://platform.open.cx/settings/integrations) and select
    **FreshChat**.
  </Step>

  <Step title="Enter your credentials">
    | Field                   | Value                                                                                                                                                                                                                                                             |
    | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | **Domain**              | Your Freshchat workspace domain — for example `yourcompany.freshchat.com`. Include the full domain, do not prefix with `https://`.                                                                                                                                |
    | **Access Token**        | The API token you just copied.                                                                                                                                                                                                                                    |
    | **Default Agent Email** | The email of the Freshchat <Tooltip tip="The Freshchat agent OpenCX posts replies as, and the email OpenCX resolves to assign conversations to the AI in assigned-only mode.">default agent</Tooltip> the AI posts as. The agent must already exist in Freshchat. |

    Click **Test & Save**. OpenCX calls the Freshchat API with your credentials before saving — a wrong domain or bad token returns an error and nothing persists.

    <Warning>
      The agent email is **not** checked at save time — OpenCX resolves it later, when it first assigns or replies. A typo here fails silently, so double-check the email matches the agent in Freshchat exactly.
    </Warning>
  </Step>

  <Step title="Pick a routing mode">
    The **Auto Handle All Incoming Chats** toggle controls which conversations the AI picks up.

    <AccordionGroup>
      <Accordion title="Auto-handle all incoming chats (toggle on)" icon="bolt">
        The AI claims every unassigned Freshchat conversation by assigning it to the default agent, then replies. Use this when the AI is the front door to your chat support.
      </Accordion>

      <Accordion title="Assigned-only (toggle off)" icon="filter">
        The AI only replies to conversations Freshchat has already assigned to the default agent email. Use this when your existing Freshchat routing rules decide which chats go to the AI — for example, business-hours routing or team-based assignment.
      </Accordion>
    </AccordionGroup>

    You can change modes anytime. New conversations follow the current setting; in-flight conversations keep their current assignment.
  </Step>

  <Step title="Choose the handoff strategy">
    Choose what OpenCX should do in Freshchat after the AI hands off to a human.

    | Strategy                                | What happens                                                                                          | When to use it                                                                                     |
    | --------------------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
    | **Unassign conversation**               | OpenCX removes the AI/default agent from the conversation.                                            | Use this when your team works from the unassigned queue or manually triages handoffs.              |
    | **Freshchat automation**                | OpenCX sets the handoff marker property, then your Freshchat assignment rules route the conversation. | Use this when routing should stay inside Freshchat rules, queues, or automations.                  |
    | **Assign to another Freshchat agent**   | OpenCX assigns the conversation directly to the **Handoff Agent Email** you enter.                    | Use this for a fixed fallback agent or small teams without routing automation.                     |
    | **Assign to a Freshchat support group** | OpenCX sets the handoff marker, then moves the conversation into the **Handoff Group** you pick.      | Use this when a team queue should receive handoffs, but Freshchat automation rules are not needed. |

    Picking a strategy reveals its configuration field:

    * **Assign to another Freshchat agent** — enter the **Handoff Agent Email** of the human agent who receives handoffs. It must be a different agent from the default (AI) agent email.
    * **Assign to a Freshchat support group** — pick the **Handoff Group** from the dropdown, loaded live from your Freshchat workspace.

    <Info>
      Group handoffs land with status **new**, not assigned — so if the group uses <Tooltip tip="Freshchat's auto-assignment engine that distributes incoming conversations across the agents in a group.">IntelliAssign</Tooltip>, Freshchat still picks the agent within the group. OpenCX routes to the queue; Freshchat routes to the person.
    </Info>

    For the marker-based strategies — **Freshchat automation** and **Assign to a Freshchat support group** — create a checkbox conversation property in Freshchat (the agent-assignment strategy doesn't need it):

    | Field                 | Value                  |
    | --------------------- | ---------------------- |
    | **Label**             | `OpenCX handed off`    |
    | **Internal/API name** | `cf_opencx_handed_off` |
    | **Type**              | Checkbox               |

    <Warning>
      Some Freshchat roles can view conversation properties but cannot create or edit them. If you cannot add the `cf_opencx_handed_off` property, ask a Freshchat admin to create it, or select an existing editable custom checkbox property that your routing rules can safely use as the handoff marker.
    </Warning>

    If you use Freshchat automation or support group handoff, update your Freshchat rules so conversations with the marker set are **not** routed back to the OpenCX/default AI agent.
  </Step>

  <Step title="Copy your webhook URL">
    After saving, OpenCX displays a **Webhook URL** unique to your organization. Keep this tab open — you'll paste it into Freshchat in the next step.

    <Warning>
      The webhook URL contains a signed token tied to your organization. Anyone with it can post events into your OpenCX pipeline. Treat it like a password.
    </Warning>
  </Step>

  <Step title="Register the webhook in Freshchat">
    In Freshchat, open **Admin Settings → Integrations & APIs → Webhooks** and add a new webhook.

    | Field                   | Value                                                                                                  |
    | ----------------------- | ------------------------------------------------------------------------------------------------------ |
    | **Endpoint URL**        | Paste the OpenCX webhook URL.                                                                          |
    | **Events to subscribe** | **Message Create**, **Conversation Assignment**, **Conversation Resolution**, **Conversation Reopen**. |

    <Warning>
      Each unchecked event breaks a specific flow: without **Message Create** the AI never sees customer messages; without **Conversation Assignment** the assigned-only routing mode never wakes the AI; without **Conversation Resolution** closures in Freshchat don't sync back to OpenCX; without **Conversation Reopen** reopened chats stay closed in OpenCX.
    </Warning>

    Save the webhook.
  </Step>

  <Step title="Send a test message">
    Open your Freshchat widget as an end user and send a message. Within a few seconds:

    1. A new session appears in your [OpenCX Inbox](https://platform.open.cx/inbox) under the **Web** channel.
    2. The AI's reply appears in the Freshchat conversation as the default agent.

    <Info>
      **The session back-links to Freshchat.** Open the session from your inbox — the Freshchat conversation URL is stored on it so you can jump straight back into Freshchat to audit the rep view.
    </Info>

    If the session doesn't appear, jump to [Troubleshooting](/integrations/freshchat/troubleshooting).
  </Step>
</Steps>

## Rotating the API token

Freshchat tokens are long-lived but admins sometimes rotate them for compliance. To rotate:

1. Generate a new token in **Admin Settings → Integrations & APIs**.
2. Paste it into [Settings → Integrations → FreshChat](https://platform.open.cx/settings/integrations) and click **Test & Save**.
3. Revoke the old token in Freshchat.

The webhook URL does not change when you rotate the token — the webhook is signed with a separate key tied to your organization.

## Disconnecting

In OpenCX, open the Freshchat integration and click **Disconnect**. Then in Freshchat, delete the webhook you created. Conversations already exchanged stay in your OpenCX inbox; no new Freshchat messages flow in, and the AI stops posting in Freshchat.

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Conversation flow" icon="diagram-project" href="/integrations/freshchat/conversations">
    How inbound messages match to sessions, how handoff routes the chat, how
    resolutions sync.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/integrations/freshchat/troubleshooting">
    Credentials failing, webhook silent, handoff note missing.
  </Card>

  <Card title="Overview" icon="comment" href="/integrations/freshchat/overview">
    What the Freshchat integration does and how it fits with your team.
  </Card>

  <Card title="Human handoff" icon="user-group" href="/handoff/introduction">
    Global handoff rules and office hours.
  </Card>
</CardGroup>
