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

# Sunshine Conversations

> Route chat, SMS, WhatsApp, phone, and social handoffs from OpenCX straight into the Zendesk Agent Workspace.

export const ZendeskSunshineFlow = () => {
  const CHANNEL_X = 80;
  const ZENDESK_X = 470;
  const OPENCX_X = 830;
  const MID_Y = 190;
  const NODE_R = 44;
  const CH_R = 22;
  const BEAM_COLOR = "#22c55e";
  const channels = [{
    key: "widget",
    label: "Web widget",
    y: 40
  }, {
    key: "whatsapp",
    label: "WhatsApp",
    y: 95
  }, {
    key: "sms",
    label: "SMS",
    y: 150
  }, {
    key: "messenger",
    label: "Messenger",
    y: 205
  }, {
    key: "instagram",
    label: "Instagram",
    y: 260
  }, {
    key: "phone",
    label: "Phone",
    y: 315
  }];
  const Beam = ({id, d, duration = 3.2, delay = 0, reverse = false, color = BEAM_COLOR}) => {
    const values = reverse ? {
      x1: "110%;-10%",
      x2: "120%;0%"
    } : {
      x1: "-10%;110%",
      x2: "0%;120%"
    };
    return <g>
        <path d={d} stroke="currentColor" strokeOpacity="0.16" 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={color} stopOpacity="0" />
            <stop offset="45%" stopColor={color} stopOpacity="1" />
            <stop offset="55%" stopColor={color} stopOpacity="1" />
            <stop offset="100%" stopColor={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 Glyph = ({kind}) => {
    const common = {
      fill: "currentColor"
    };
    switch (kind) {
      case "widget":
        return <g {...common}>
            <path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h11A1.5 1.5 0 0 1 15 2.5v7A1.5 1.5 0 0 1 13.5 11H9l-3 3v-3H2.5A1.5 1.5 0 0 1 1 9.5v-7z" />
          </g>;
      case "whatsapp":
        return <g {...common}>
            <path d="M8 1a7 7 0 0 0-6.03 10.55L1 15l3.55-.93A7 7 0 1 0 8 1Zm3.78 9.6c-.16.45-.94.86-1.31.89-.33.03-.76.04-1.22-.08a11 11 0 0 1-1.11-.4 8.6 8.6 0 0 1-3.3-2.88c-.25-.32-.65-.86-.65-1.64 0-.78.4-1.16.55-1.32.14-.16.3-.2.4-.2h.3c.1 0 .24 0 .37.28l.55 1.31c.05.1.08.21.02.33a1.23 1.23 0 0 1-.19.3l-.28.32c-.1.1-.19.22-.08.41.1.2.45.75.98 1.22.66.58 1.22.77 1.42.86.2.1.3.08.42-.05.12-.13.48-.56.61-.76.13-.2.26-.17.44-.1.18.06 1.15.55 1.35.65.2.1.33.15.38.24.05.1.05.5-.11.95Z" />
          </g>;
      case "sms":
        return <g {...common}>
            <path d="M3 1.5A1.5 1.5 0 0 0 1.5 3v8A1.5 1.5 0 0 0 3 12.5h2V15l2.8-2.5H13A1.5 1.5 0 0 0 14.5 11V3A1.5 1.5 0 0 0 13 1.5H3Zm1 4.75a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0Zm3.25 0a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0Zm3.25 0a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0Z" />
          </g>;
      case "messenger":
        return <g {...common}>
            <path d="M8 1C4.13 1 1 3.9 1 7.48c0 2.02 1 3.8 2.6 5v2.52l2.37-1.3c.64.17 1.32.27 2.03.27 3.87 0 7-2.9 7-6.49C15 3.9 11.87 1 8 1Zm.72 8.7-1.82-1.93-3.55 1.94 3.9-4.14 1.87 1.94 3.5-1.94-3.9 4.13Z" />
          </g>;
      case "instagram":
        return <g {...common}>
            <path d="M4.5 1.5A3 3 0 0 0 1.5 4.5v7a3 3 0 0 0 3 3h7a3 3 0 0 0 3-3v-7a3 3 0 0 0-3-3h-7Zm7.5 2.25a.75.75 0 1 1 0 1.5.75.75 0 0 1 0-1.5ZM8 4.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7Zm0 1.5a2 2 0 1 0 0 4 2 2 0 0 0 0-4Z" />
          </g>;
      case "phone":
        return <svg x="0" y="0" width="14" height="14" viewBox="0 0 20 20" fill="currentColor">
            <path d="M2 3.5A1.5 1.5 0 0 1 3.5 2h1.148a1.5 1.5 0 0 1 1.465 1.175l.716 3.223a1.5 1.5 0 0 1-1.052 1.767l-.933.267c-.41.117-.643.555-.48.95a11.5 11.5 0 0 0 6.254 6.254c.395.163.833-.07.95-.48l.267-.933a1.5 1.5 0 0 1 1.767-1.052l3.223.716A1.5 1.5 0 0 1 18 14.352V15.5a1.5 1.5 0 0 1-1.5 1.5H15c-1.149 0-2.263-.15-3.326-.43A13.022 13.022 0 0 1 2.43 7.326 13.019 13.019 0 0 1 2 4V3.5Z" />
          </svg>;
      default:
        return null;
    }
  };
  const controlOffset = 120;
  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 900 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="Zendesk Sunshine Conversations flow: messaging channels land in the Zendesk Agent Workspace where OpenCX and human reps respond">
          {}
          {channels.map((ch, i) => {
    const startX = CHANNEL_X + CH_R;
    const startY = ch.y;
    const endX = ZENDESK_X - NODE_R;
    const endY = MID_Y;
    const d = `M ${startX},${startY} C ${startX + controlOffset},${startY} ${endX - controlOffset},${endY} ${endX},${endY}`;
    return <Beam key={`ch-${ch.key}`} id={`zs-beam-ch-${i}`} d={d} duration={3.2} delay={i * 0.35} />;
  })}

          {}
          {(() => {
    const d = `M ${ZENDESK_X + NODE_R},${MID_Y} C ${ZENDESK_X + 140},${MID_Y - 40} ${OPENCX_X - 140},${MID_Y - 40} ${OPENCX_X - NODE_R},${MID_Y}`;
    return <>
                <Beam id="zs-beam-out" d={d} duration={2.8} delay={0} />
                <Beam id="zs-beam-back" d={d} duration={2.8} delay={1.4} reverse />
              </>;
  })()}

          {}
          {channels.map(ch => <g key={ch.key}>
              <circle cx={CHANNEL_X} cy={ch.y} r={CH_R} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
              <g transform={`translate(${CHANNEL_X - 7} ${ch.y - 7})`} className="text-zinc-700 dark:text-zinc-200">
                <Glyph kind={ch.key} />
              </g>
              <text x={CHANNEL_X + CH_R + 8} y={ch.y + 4} className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "500 11.5px ui-sans-serif, system-ui, sans-serif"
  }}>{ch.label}</text>
            </g>)}

          {}
          <g>
            <circle cx={ZENDESK_X} cy={MID_Y} r={NODE_R} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            <foreignObject x={ZENDESK_X - 24} y={MID_Y - 24} width="48" height="48">
              <div className="flex h-full w-full items-center justify-center">
                <img src="/images/integrations/zendesk.svg" alt="" className="block h-8 w-8 object-contain dark:hidden" />
                <img src="/images/integrations/zendesk-dark.svg" alt="" className="hidden h-8 w-8 object-contain dark:block" />
              </div>
            </foreignObject>
            <text x={ZENDESK_X} y={MID_Y + NODE_R + 22} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "600 12px ui-sans-serif, system-ui, sans-serif"
  }}>Zendesk</text>
            <text x={ZENDESK_X} y={MID_Y + NODE_R + 36} textAnchor="middle" className="fill-zinc-500 dark:fill-zinc-400" style={{
    font: "400 10.5px ui-sans-serif, system-ui, sans-serif"
  }}>Sunshine · Agent Workspace</text>
          </g>

          {}
          <g>
            <circle cx={OPENCX_X} cy={MID_Y} r={NODE_R + 4} className="fill-none" stroke={BEAM_COLOR} strokeOpacity="0.25" strokeWidth="1.5">
              <animate attributeName="r" values={`${NODE_R + 2};${NODE_R + 12};${NODE_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={OPENCX_X} cy={MID_Y} r={NODE_R} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            <foreignObject x={OPENCX_X - 34} y={MID_Y - 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={OPENCX_X} y={MID_Y + NODE_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={OPENCX_X} y={MID_Y + NODE_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 · human reps</text>
          </g>
        </svg>
      </div>
    </div>;
};

Sunshine Conversations is Zendesk's real-time messaging layer. Use this path for every channel **except email** — the web chat widget, SMS, WhatsApp, Messenger, Instagram, and phone handoffs. Conversations land in the Zendesk <Tooltip tip="Zendesk's unified UI where agents handle tickets and live messaging side by side.">Agent Workspace</Tooltip> as live chats your reps pick up with full context attached.

<ZendeskSunshineFlow />

<Info>
  Sunshine Conversations is included with the **Zendesk Suite** plan. If you're
  on Support Professional, use the [Ticketing
  API](/integrations/zendesk/ticketing-api) path for email handoff and contact
  Zendesk to upgrade for real-time channels.
</Info>

## Before you start

<AccordionGroup>
  <Accordion title="Zendesk Suite plan with Sunshine provisioned" icon="circle-check">
    Open [**Admin Center → Apps and integrations → APIs → Conversations API**](https://admin.zendesk.com/apps/integrations/apis/conversations-api). If the section is missing, Sunshine isn't provisioned on your account — contact Zendesk.
  </Accordion>

  <Accordion title="Admin access in Zendesk" icon="user-shield">
    You'll create an App key in the Conversations API section and note your{" "}

    <Tooltip tip="The Sunshine app your messaging channels live under. Shown in the Conversations API admin view.">
      App ID
    </Tooltip>

    . Agent roles don't have access.
  </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="Create a Sunshine App key">
    In Zendesk, open [**Admin Center → Apps and integrations → APIs → Conversations API**](https://admin.zendesk.com/apps/integrations/apis/conversations-api).

    1. Click **Create API key**.
    2. Label it `OpenCX`.
    3. **Copy the Key ID and Secret** — Zendesk only shows the Secret once.
    4. Note the **App ID** displayed at the top of the page.

    <Warning>
      **Generate an App key, not an Integration key.** The Key ID starts with `app_` for an App key. Integration keys are scoped to a single Sunshine integration and will fail authentication against OpenCX. If your Key ID doesn't start with `app_`, delete it and create a new key from the **Conversations API** section (not from inside a specific integration).
    </Warning>

    <Warning>
      Lose the Secret? Delete the key and generate a new one. Zendesk does not let you retrieve the Secret after the dialog closes.
    </Warning>
  </Step>

  <Step title="Turn on multi-conversations for the Sunshine app">
    OpenCX creates a fresh Sunshine conversation for every chat session. Sunshine's default `personal` conversation mode caps each end user at **one** open conversation, so the second `POST /v2/apps/{appId}/conversations` for the same user fails and the session never reaches the Agent Workspace. Turn multi-conversations on once, in Zendesk, before you wire up OpenCX.

    1. In Admin Center, go to **Channels → Messaging and social → Messaging**.
    2. Click **Manage settings** at the top of the page.
    3. Under **Web Widget and Mobile SDKs**, expand **Multi-conversations** and click **Set up multi-conversations**.
    4. Click **Turn on multi-conversations for your account** and select **every** Web Widget, iOS SDK, and Android SDK channel OpenCX will answer on.
    5. **Save settings.**

    See [Allowing multiple conversations for your end users](https://support.zendesk.com/hc/en-us/articles/8008427696410-Allowing-multiple-conversations-for-your-end-users) for the full Zendesk walkthrough.

    <Warning>
      **This change is irreversible.** Once enabled, end users keep access to their conversations list forever — there's no toggle back to the single-conversation experience. You can later remove the **New conversation** button from a channel, but the list itself stays. Read [Zendesk's considerations](https://support.zendesk.com/hc/en-us/articles/8008427696410-Allowing-multiple-conversations-for-your-end-users#topic_yhy_vjy_1dc) before flipping the switch.
    </Warning>

    <Info>
      **Pick every Web/iOS/Android channel.** Turning multi-conversations on for one channel updates the underlying Sunshine app for **all** Web/iOS/Android channels — channels you leave unselected just lose the **New conversation** button. Selecting all of them keeps the end-user experience consistent and avoids surprises when you add the next channel. Social channels (WhatsApp, Messenger, Instagram) don't support multi-conversations and aren't affected by this setting.
    </Info>

    <Tip>
      Prefer to flip it without leaving your terminal? Same effect via the Conversations API:

      ```bash theme={"dark"}
      curl -X PATCH "https://api.smooch.io/v2/apps/{APP_ID}" \
        -u "{KEY_ID}:{SECRET}" \
        -H "Content-Type: application/json" \
        -d '{"settings": {"multiConvoEnabled": true}}'
      ```

      Same irreversibility caveat applies. Confirm with `GET /v2/apps/{APP_ID}` — `settings.multiConvoEnabled` should come back `true`.
    </Tip>
  </Step>

  <Step title="Open Zendesk Messaging in OpenCX">
    In OpenCX, go to [**Settings →
    Integrations**](https://platform.open.cx/settings/integrations), find
    **Zendesk**, and click **Zendesk Messaging**.
  </Step>

  <Step title="Enter your Sunshine credentials">
    | Field              | Value                                                                                                                                                                                                                                                                         |
    | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | **Subdomain**      | The `acme` part of `acme.zendesk.com`. Same subdomain used for Ticketing.                                                                                                                                                                                                     |
    | **App ID**         | The Sunshine <Tooltip tip="A unique identifier for the Sunshine app that owns your messaging integrations.">App ID</Tooltip> from the Conversations API page.                                                                                                                 |
    | **Key ID**         | The ID from the App key you just created — starts with `app_`.                                                                                                                                                                                                                |
    | **Secret**         | The secret you copied.                                                                                                                                                                                                                                                        |
    | **Integration ID** | The ID of the **OpenCX** Conversations integration in Zendesk (see warning below). Find it under [**Admin Center → Apps and integrations → Integrations → Conversations integrations**](https://admin.zendesk.com/apps/integrations/integrations/conversations-integrations). |

    <Warning>
      **The Conversations integration MUST be named exactly `opencx`** (lowercase). This name is how OpenCX's switchboard wiring identifies which integration owns each conversation, and it's also how the AI agent shows up in Zendesk's AI Agents Marketplace (next step). Any other spelling — `OpenCX`, `open-cx`, `opencx-prod`, etc. — and routing will silently break.

      If you've already created the integration with a different name, delete it from **Conversations integrations** and recreate it with the exact name `opencx`, then copy the new Integration ID here.
    </Warning>

    Click **Verify & Save**.
  </Step>

  <Step title="Provide a Zendesk API token for user merge">
    Sunshine uses a separate identity model from Zendesk's support API. To prevent the same person from showing up as two separate <Tooltip tip="A contact record on the support side of Zendesk. Sunshine tracks messaging contacts under a different identity; OpenCX merges the two so reports and ticket ownership aren't split.">Zendesk users</Tooltip>, OpenCX merges Sunshine messaging contacts with matching Zendesk support users.

    In the **Zendesk API credentials** panel on the same page, reuse the credentials from the [Ticketing API](/integrations/zendesk/ticketing-api) setup. The merge endpoint uses the same token. If you skipped Ticketing, generate a token for this purpose alone — user search is free on every Zendesk plan, and no webhook is needed.

    <Info>
      **What OpenCX writes on every Sunshine conversation** — `metadata.opencx_org_id`, `metadata.opencx_contact_id`, and `metadata.opencx_session_id`. Your reps don't see these fields directly, but they're reachable via the Conversations API for debugging and analytics. The session ID is the trace key back to the exact OpenCX conversation.
    </Info>
  </Step>

  <Step title="Copy your messaging webhook URL">
    After saving, OpenCX displays a **Messaging Webhook URL**. Keep it handy —
    you'll paste it into Zendesk next.
  </Step>

  <Step title="Register the webhook in Sunshine">
    In Zendesk's **Conversations API** admin, open the **Integrations / Webhooks** panel for your app and add a new webhook.

    | Field          | Value                                                                               |
    | -------------- | ----------------------------------------------------------------------------------- |
    | **Target URL** | Paste the OpenCX messaging webhook URL.                                             |
    | **Triggers**   | `conversation:message` (required). `conversation:read` optional, for read receipts. |
    | **Secret**     | Leave blank — OpenCX validates by signed URL.                                       |

    Save the webhook.
  </Step>

  <Step title="Pick the channels OpenCX should answer on">
    Saving credentials only wires up the integration — Zendesk still needs to be told which channels the OpenCX AI agent should actually respond on. This is done from Zendesk's AI Agents admin (not from OpenCX).

    1. In your Zendesk Admin Center, go to **AI → AI agents → AI agents**, then click the **Marketplace bots** tab. (Direct admin URLs are subdomain-scoped, so we can't link them — see Zendesk's [Managing third-party bots in Admin Center](https://support.zendesk.com/hc/en-us/articles/5064149334426-Managing-third-party-bots-in-Admin-Center) for the canonical walkthrough.)
    2. Find the **OpenCX** entry in the list of marketplace bots and open it.
    3. Under **Channels**, pick every channel you want the AI agent to handle — specific WhatsApp numbers, the Web Widget, Facebook Messenger pages, Instagram accounts, SMS numbers, etc. To make OpenCX the first responder for *every* messaging channel instead, use the entry's **Options → Assign default** menu — see [Setting an advanced AI agent as the default responder](https://support.zendesk.com/hc/en-us/articles/8357757911834-Setting-an-advanced-AI-agent-as-the-default-responder-for-a-messaging-channel).
    4. Save.

    Only channels enabled here are routed to OpenCX. Anything you leave off keeps Zendesk's default routing (usually straight to the Agent Workspace).

    <Tip>
      **Roll out gradually with a test channel.** Don't flip every production WhatsApp number and your live Web Widget over to the AI agent on day one. Add a single low-stakes channel first — for example a dedicated test WhatsApp number, a sandbox Messenger page, or a staging copy of your Web Widget — and enable only that one in the Marketplace. Run real conversations through it for a few days, watch the [Inbox](https://platform.open.cx/inbox) and your handoff rate, then expand to the rest of your channels once you're happy.
    </Tip>
  </Step>

  <Step title="Set Conversation control to Release control">
    The previous step makes OpenCX the *first* responder. This step makes sure OpenCX *takes back* the conversation after a human agent solves the ticket — otherwise repeat customers stay routed to Zendesk's Agent Workspace or `zd-answerBot` instead of the AI.

    `Conversation control` is an **account-wide Sunshine Conversations switchboard setting** — not a per-bot setting. It lives on the Messaging setup page, not inside the OpenCX marketplace bot entry.

    1. In Zendesk Admin Center, go to **Channels → Messaging and social → Messaging**, then open your messaging configuration. (The direct URL is subdomain-scoped: `https://<your-subdomain>.zendesk.com/admin/channels/messaging_and_social/messaging/setup`.)
    2. Expand the **Conversation control** section.
    3. Select **Release control**.
    4. Save.

    <Warning>
      **`Release control` is required for the per-channel default responder to take effect on handback.** With the default `Pass control`, when an agent solves+closes a ticket, Sunshine `passControl`'s the conversation to whatever `nextSwitchboardIntegrationId` is wired on the Agent Workspace integration — usually Zendesk's own `zd-answerBot`. With `Release control`, Sunshine clears the active integration on close, and the customer's next message gets re-assigned via the per-channel `defaultResponder` you set in the previous step (= OpenCX). This is the difference between "AI takes back" and "answer bot intercepts."

      Sources:

      * Zendesk's [About Sunshine Conversations in Zendesk Suite](https://support.zendesk.com/hc/en-us/articles/5514406080538) — section *"Configuring conversational control"*: *"Expand the Conversation control section and select your control option."*
      * Zendesk's [Setting an advanced AI agent as the default responder for a messaging channel](https://support.zendesk.com/hc/en-us/articles/8357757911834-Setting-an-advanced-AI-agent-as-the-default-responder-for-a-messaging-channel) — *"The Conversation control setting must be set to **Release control** for per-channel responders to work."*
    </Warning>
  </Step>

  <Step title="Shorten the Solved→Closed window (recommended)">
    Sunshine only hands control back to OpenCX when a Zendesk ticket transitions from **Solved** to **Closed** — not on Solved alone. By default Zendesk waits **4 days** between the two. Until close fires, any customer who returns inside that window stays routed to the Agent Workspace, not the AI.

    To make handback near-instant, do **one** of:

    * **Edit the built-in automation:** in [**Admin Center → Objects and rules → Business rules → Automations**](https://admin.zendesk.com/objects-rules/rules/automations), open `Close ticket 4 days after status is set to solved` and drop the timeframe to \~1 hour (or whatever close-window your team needs for re-opens).
    * **Add a close-on-solve trigger:** create a trigger that closes any ticket whose status changes to Solved. Handback then fires the instant an agent solves.

    Reference: [Managing conversation handoff and handback](https://support.zendesk.com/hc/en-us/articles/4408824482586-Managing-conversation-handoff-and-handback) — section *"Modifying the time between the solved and closed states"*.

    <Info>
      Skipping this step doesn't break the integration — it just means returning customers within the close window land on the Agent Workspace instead of the AI. If your reps and customers are fine with re-engaging via the live agent for a few days post-solve, the default 4-day automation is acceptable.
    </Info>
  </Step>

  <Step title="Verify end to end">
    Run a full handoff + handback cycle on a channel you enabled.

    1. **Handoff:** send a test message, request a human, confirm the conversation appears in the Zendesk Agent Workspace as a live chat. Reply from Agent Workspace and confirm it lands back on the original channel within a couple of seconds.
    2. **Solve and close:** solve the ticket from Agent Workspace, then close it (manually, or wait for your trigger / shortened automation from the previous step to fire).
    3. **Handback:** from the same end user, send a fresh message on the same channel. Confirm the AI responds — not the live agent and not Zendesk's `zd-answerBot`.

    <Tip>
      To pinpoint a routing failure on step 3, inspect the inbound webhook payload in [Admin Center → Apps and integrations → Conversations API → Webhooks → Activity](https://admin.zendesk.com/apps/integrations/apis/conversations-api). The `activeSwitchboardIntegration.name` field should be `opencx`. If it's `zd-answerBot`, your **Conversation control** is still on `Pass control` — re-check the previous step (set on the Messaging setup page, not the bot entry). If it's `zd-agentWorkspace`, the prior ticket hasn't transitioned from Solved to Closed yet — re-check the Solved→Closed window step.
    </Tip>

    If any step fails, jump to [Troubleshooting](/integrations/zendesk/troubleshooting#messaging-sunshine-issues).
  </Step>
</Steps>

## Sunshine channels handle off as live chats, not tickets

For channels routed through Sunshine — WhatsApp, Messenger, Instagram, web chat, SMS, and phone — handoff does **not** create a Zendesk ticket. The conversation appears in the Agent Workspace as a live chat. Ticket-based behavior (AI Fields auto-fill, tag application, agent-reply sync) described on the [Ticketing API](/integrations/zendesk/ticketing-api) page applies to email only.

## Turn on AI agent tickets if you want ticket ids before handoff

By default, an AI-handled messaging conversation has **no Zendesk ticket** until it's handed off to a human. Zendesk creates the ticket at escalation, and that's the moment a ticket id appears in the OpenCX [Inbox](https://platform.open.cx/inbox) sidebar. A conversation the AI resolves on its own never gets a ticket — so its ticket id in OpenCX stays empty. With the default configuration that's expected, not a bug.

If you want **every** conversation — including AI-resolved ones — to have a Zendesk ticket and a ticket id in OpenCX, turn on Zendesk's **AI agent tickets** feature:

1. In Zendesk, go to **Admin Center → Channels → Messaging and social → Messaging** and click **Manage settings**.
2. Expand **AI agent conversations as tickets in Agent Workspace** and turn it on.
3. Save.

With the setting on, Zendesk creates a read-only ticket from the first message of every AI conversation (it becomes a regular, editable ticket at handoff), and OpenCX stamps its ticket id on the session automatically — your reps also get full visibility into ongoing AI conversations from the Agent Workspace.

<Info>
  Zendesk turned AI agent tickets on by default for all accounts on **May 4,
  2026** — but accounts using Zendesk's own **AI agents - Advanced** add-on are
  exempt and may still have it off. If your AI-only conversations aren't
  producing tickets, check the setting. References: [Understanding AI agent
  tickets for AI agent–only
  conversations](https://support.zendesk.com/hc/en-us/articles/9204149016346-Understanding-AI-agent-tickets-for-AI-agent-only-conversations),
  [general availability
  announcement](https://support.zendesk.com/hc/en-us/articles/9727051305498-Announcing-the-general-availability-of-AI-agent-conversations-as-tickets-in-Support-and-Agent-Workspace),
  [default-on
  schedule](https://support.zendesk.com/hc/en-us/articles/10143404078234-Announcing-AI-agent-tickets-turned-on-by-default-for-specific-Pods).
</Info>

## Rotating Sunshine credentials

Create a new App key in Zendesk, paste it into OpenCX, then delete the old key. The <Tooltip tip="An internal token OpenCX uses to sign the messaging webhook URL. Regenerated only when you disconnect and reconnect.">webhook signing key</Tooltip> is unaffected by App key rotation.

## Disconnecting

Click **Disconnect** on the Zendesk Messaging card in [Settings → Integrations](https://platform.open.cx/settings/integrations). Then delete the webhook in the Conversations API admin. Active conversations finish out on their current channel; no data is deleted from Zendesk.

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Channels" icon="grid-2" href="/integrations/zendesk/channels/widget">
    Per-channel implementation details for Widget, WhatsApp, SMS, Phone, and
    Email.
  </Card>

  <Card title="Ticketing API" icon="envelope" href="/integrations/zendesk/ticketing-api">
    The companion path for email handoff.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/integrations/zendesk/troubleshooting">
    Agent Workspace not receiving messages, duplicate users.
  </Card>
</CardGroup>
