> ## 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 FareHarbor

> Add one or more FareHarbor connections, validate your keys, and configure per-connection AI actions.

export const FareharborFlow = () => {
  const BEAM_COLOR = "#22c55e";
  const renderBeam = ({id, d, duration = 2.8, delay = 0, reverse = false}) => {
    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" strokeWidth="1.5" fill="none" strokeLinecap="round" />
        <path d={d} stroke={`url(#${id})`} strokeWidth="2" fill="none" strokeLinecap="round" />
        <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 OCX_X = 150, ROUTER_X = 420, FH_X = 720;
  const MID = 200, R = 44;
  const connections = [{
    label: "US · USD",
    y: 110
  }, {
    label: "NZ · NZD",
    y: 200
  }, {
    label: "EU · EUR",
    y: 290
  }];
  const b1 = `M ${OCX_X + R},${MID} L ${ROUTER_X - 30},${MID}`;
  const connBeams = connections.map((c, i) => ({
    id: `fh-c-${i}`,
    d: `M ${ROUTER_X + 30},${MID} C ${ROUTER_X + 80},${MID} ${FH_X - 80 - 40},${c.y} ${FH_X - 80},${c.y}`,
    delay: i * 0.4
  }));
  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 880 380" 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="FareHarbor flow: OpenCX routes each booking lookup to the correct FareHarbor connection based on currency or location">
          {renderBeam({
    id: "fh-1",
    d: b1,
    duration: 2.4,
    delay: 0
  })}
          {connBeams.map(b => renderBeam({
    id: b.id,
    d: b.d,
    duration: 3.0,
    delay: b.delay
  }))}

          <text x={(OCX_X + ROUTER_X) / 2} y={MID - 18} textAnchor="middle" className="fill-zinc-500 dark:fill-zinc-400" style={{
    font: "500 11px ui-sans-serif, system-ui, sans-serif"
  }}>action call</text>

          {}
          <g>
            <circle cx={OCX_X} cy={MID} r={R + 4} className="fill-none" stroke={BEAM_COLOR} strokeOpacity="0.25" strokeWidth="1.5">
              <animate attributeName="r" values={`${R + 2};${R + 12};${R + 2}`} 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={MID} r={R} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            <foreignObject x={OCX_X - 34} y={MID - 18} width="68" height="36">
              <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={MID + R + 22} 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={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"
  }}>AI agent</text>
          </g>

          {}
          <g>
            <polygon points={`${ROUTER_X - 30},${MID} ${ROUTER_X},${MID - 34} ${ROUTER_X + 30},${MID} ${ROUTER_X},${MID + 34}`} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            <text x={ROUTER_X} y={MID - 2} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "700 10.5px ui-sans-serif, system-ui, sans-serif"
  }}>auto-</text>
            <text x={ROUTER_X} y={MID + 10} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "700 10.5px ui-sans-serif, system-ui, sans-serif"
  }}>route</text>
            <text x={ROUTER_X} y={MID + 48} textAnchor="middle" className="fill-zinc-500 dark:fill-zinc-400" style={{
    font: "400 10px ui-sans-serif, system-ui, sans-serif"
  }}>by currency / brand</text>
          </g>

          {}
          {connections.map((c, i) => <g key={c.label}>
              <rect x={FH_X - 80} y={c.y - 22} width="160" height="44" rx="8" className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
              <circle cx={FH_X - 60} cy={c.y} r="10" fill={BEAM_COLOR} fillOpacity="0.18" stroke={BEAM_COLOR} strokeWidth="1.2" />
              <text x={FH_X - 60} y={c.y + 3.5} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "700 9px ui-sans-serif, system-ui, sans-serif"
  }}>{c.label.split("·")[1]?.trim()}</text>
              <text x={FH_X - 42} y={c.y - 2} className="fill-zinc-800 dark:fill-zinc-100" style={{
    font: "600 12px ui-sans-serif, system-ui, sans-serif"
  }}>Connection</text>
              <text x={FH_X - 42} y={c.y + 12} className="fill-zinc-500 dark:fill-zinc-400" style={{
    font: "400 10.5px ui-sans-serif, system-ui, sans-serif"
  }}>{c.label}</text>
            </g>)}

          <text x={FH_X} y={45} textAnchor="middle" className="fill-zinc-500 dark:fill-zinc-400" style={{
    font: "500 11px ui-sans-serif, system-ui, sans-serif"
  }}>FareHarbor · multi-connection</text>
        </svg>
      </div>
    </div>;
};

<FareharborFlow />

<Info>
  OpenCX supports **multiple FareHarbor connections per organization** — one per location, brand, or currency account. Each connection has its own API keys and its own AI action toggles.
</Info>

## Prerequisites

<AccordionGroup>
  <Accordion title="FareHarbor API partner account" icon="key">
    You need an **App Key** and a **User Key** issued by FareHarbor. These authenticate every request OpenCX makes to the FareHarbor API on your behalf. Contact FareHarbor to register as an API partner if you do not already have keys.
  </Accordion>

  <Accordion title="Currency-matched User Key" icon="dollar-sign">
    If you work with FareHarbor companies in different currencies (USD, NZD, EUR, etc.), you will have a separate **User Key for each currency**. Add one connection per currency — the AI will route each booking to the right connection automatically.
  </Accordion>
</AccordionGroup>

## Setup

<Steps>
  <Step title="Open the FareHarbor add-on">
    In your [OpenCX dashboard](https://platform.open.cx/settings/integrations), go to **Settings → Add-ons** and select **FareHarbor**.
  </Step>

  <Step title="Add a connection">
    Click **Add Connection**. If you already have connections configured, they are listed here — you can add more by clicking the button again.
  </Step>

  <Step title="Give the connection a name">
    Enter a **connection name** that reflects what this connection is — for example `Downtown Location`, `USD Account`, or `RealNZ`. The name is what your AI agent uses to disambiguate between connections during customer conversations, so pick something meaningful.

    <Tip>
      Pick names your **customers** would recognize, not internal codes. If a customer asks about a specific brand or region, the AI can match the request to the right connection without asking.
    </Tip>
  </Step>

  <Step title="Choose your environment">
    Toggle **Sandbox Mode** on for testing. This points the connection at FareHarbor's demo environment — no real bookings, no real charges.
  </Step>

  <Step title="Enter your API keys">
    Paste your **App Key** and **User Key**. OpenCX validates both keys immediately by calling the FareHarbor API to list your affiliated companies. If validation fails, double-check that the keys are correct and that the User Key matches the currency of at least one affiliated company.
  </Step>

  <Step title="Select a default company">
    After key validation succeeds, a dropdown appears with the companies affiliated with this User Key. Pick the company you want this connection to book for. If there is only one, it is selected automatically.

    You can also leave this as **Auto-detect** — the AI will use the first company returned by the FareHarbor API for this connection.
  </Step>

  <Step title="Click Test & Connect">
    OpenCX runs a final validation and saves the connection. The new connection appears in the list and is ready to use immediately.
  </Step>
</Steps>

## Managing connections

Each connection has its own settings panel, reached by clicking it in the connection list:

* **AI actions** — eight toggleable capabilities (list items, check availability, validate booking, create booking, get booking, cancel booking, generate payment link, send payment link via SMS). Each connection has its own toggles, so you can allow bookings on one location while keeping another read-only.
* **Disconnect** — removes just that connection. Other connections remain active.

## How the AI picks the right connection

When you have more than one connection, every FareHarbor tool call the AI makes includes a **location** parameter — which connection to use. The AI resolves this automatically whenever it can:

* If the customer mentions a specific company, tour, or region that maps to one connection, the AI uses it without asking.
* If the request is ambiguous (e.g. "what tours do you have?"), the AI asks the customer which location or brand they are interested in.

With a **single connection**, there is no `location` parameter and no disambiguation — behavior is identical to a single-connection setup.

## Disconnecting

To remove a single connection, open it from the connection list and click **Disconnect**. This removes:

* The stored API keys for that connection
* The AI action settings for that connection

Other connections, and your FareHarbor account itself, are not affected.

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Troubleshooting" icon="wrench" href="/integrations/fareharbor/troubleshooting">
    Connection failures, missing availability, SMS delivery.
  </Card>

  <Card title="Booking Flow" icon="route" href="/integrations/fareharbor/booking-flow">
    How each AI action works day-to-day.
  </Card>

  <Card title="Overview" icon="anchor" href="/integrations/fareharbor/overview">
    Capabilities, supported channels, and how it all fits together.
  </Card>

  <Card title="Chat" icon="comments" href="/integrations/fareharbor/channels/chat">
    Booking experience in chat widget, WhatsApp, and SMS.
  </Card>
</CardGroup>
