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

# Collect Customer Satisfaction

> Send a CSAT survey after a conversation resolves, capture the 1–5 rating and comment, and see the trend in your reports.

export const CsatFlow = () => {
  const CUSTOMER_X = 90;
  const OPENCX_X = 400;
  const RATING_X = 710;
  const BASELINE = 130;
  const NODE_R = 42;
  const BEAM_COLOR = "#22c55e";
  const Beam = ({id, d, duration = 2.6, delay = 0, reverse = false}) => {
    const values = reverse ? {
      x1: "110%;-10%",
      x2: "120%;0%"
    } : {
      x1: "-10%;110%",
      x2: "0%;120%"
    };
    return <g>
        <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 beam1D = `M ${CUSTOMER_X + NODE_R},${BASELINE} C ${CUSTOMER_X + 140},${BASELINE + 50} ${OPENCX_X - 140},${BASELINE + 50} ${OPENCX_X - NODE_R},${BASELINE}`;
  const beam2D = `M ${OPENCX_X + NODE_R},${BASELINE} C ${OPENCX_X + 140},${BASELINE - 50} ${RATING_X - 140},${BASELINE - 50} ${RATING_X - NODE_R},${BASELINE}`;
  return <div className="csat-flow zendesk-flow not-prose my-8 w-full">
      <div className="csat-flow-frame 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="csat-flow-grid zendesk-flow-grid pointer-events-none absolute inset-0" aria-hidden />

        <svg viewBox="0 0 800 260" 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="CSAT flow: after the conversation resolves, OpenCX sends a rating survey and captures the score back into analytics">
          {}
          <Beam id="csat-beam-1" d={beam1D} duration={2.6} delay={0} />
          <Beam id="csat-beam-2a" d={beam2D} duration={2.6} delay={0.2} />
          <Beam id="csat-beam-2b" d={beam2D} duration={2.6} delay={1.5} reverse />

          {}
          <g className="fill-zinc-500 dark:fill-zinc-400" style={{
    font: "500 11px ui-sans-serif, system-ui, sans-serif"
  }}>
            <text x={(CUSTOMER_X + OPENCX_X) / 2} y={BASELINE + 70} textAnchor="middle">conversation</text>
            <text x={(OPENCX_X + RATING_X) / 2} y={BASELINE - 60} textAnchor="middle">survey · rating</text>
          </g>

          {}
          <g>
            <circle cx={CUSTOMER_X} cy={BASELINE} r={NODE_R} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            {}
            <g transform={`translate(${CUSTOMER_X - 14} ${BASELINE - 12})`} className="fill-none stroke-zinc-700 dark:stroke-zinc-200" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
              <path d="M 2,2 H 26 A 2,2 0 0 1 28,4 V 16 A 2,2 0 0 1 26,18 H 12 L 6,23 V 18 H 4 A 2,2 0 0 1 2,16 Z" />
              <circle cx="9" cy="10" r="1.2" fill="currentColor" stroke="none" />
              <circle cx="15" cy="10" r="1.2" fill="currentColor" stroke="none" />
              <circle cx="21" cy="10" r="1.2" fill="currentColor" stroke="none" />
            </g>
            <text x={CUSTOMER_X} y={BASELINE + NODE_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={OPENCX_X} cy={BASELINE} 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 + 10};${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={BASELINE} r={NODE_R} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            <foreignObject x={OPENCX_X - 32} y={BASELINE - 16} width="64" 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={OPENCX_X} y={BASELINE + 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={BASELINE + 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"
  }}>sends survey on resolve</text>
          </g>

          {}
          <g>
            <circle cx={RATING_X} cy={BASELINE} r={NODE_R} className="fill-white stroke-black/10 dark:fill-zinc-900 dark:stroke-white/10" strokeWidth="1" />
            <foreignObject x={RATING_X - 36} y={BASELINE - 12} width="72" height="24">
              <div className="flex h-full w-full items-center justify-center" style={{
    fontSize: "13px",
    letterSpacing: "0.5px"
  }}>
                <span>😡</span>
                <span>😞</span>
                <span>😐</span>
                <span>🙂</span>
                <span>😍</span>
              </div>
            </foreignObject>
            <text x={RATING_X} y={BASELINE + NODE_R + 22} textAnchor="middle" className="fill-zinc-700 dark:fill-zinc-200" style={{
    font: "600 12px ui-sans-serif, system-ui, sans-serif"
  }}>CSAT</text>
            <text x={RATING_X} y={BASELINE + 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"
  }}>1–5 rating</text>
          </g>
        </svg>
      </div>
    </div>;
};

<Tooltip tip="Customer Satisfaction — a short survey sent after a support conversation ends so you can score the experience." cta="See the API" href="/api-reference/csat">**CSAT**</Tooltip> runs once a conversation resolves, collects a 1–5 rating and an optional comment, and feeds back into your reports and workflows. The survey reaches the customer on the **same channel** the conversation lived on — an email conversation gets an email survey, an SMS conversation gets an SMS.

<CsatFlow />

<Info>
  CSAT is off by default. Enable it per channel from [Settings → CSAT](https://platform.open.cx/settings/csat).
</Info>

## How it works

* After a session is marked resolved, OpenCX waits the delay you chose and sends the survey on the same channel the customer used.
* The customer rates 1–5 (😡 😞 😐 🙂 😍) and can optionally leave a comment.
* The score lands back on the session, in your reports, on the webhook stream, and as a workflow trigger.

## Enable CSAT

<Steps>
  <Step title="Open Settings → CSAT">
    Head to [Settings → CSAT](https://platform.open.cx/settings/csat) in your OpenCX dashboard.
  </Step>

  <Step title="Turn on auto-send">
    Switch on **Auto-send CSAT after session closes** and pick a delay: **Now**, **5m**, **15m**, or **1h**. The survey fires that long after the session is marked resolved.
  </Step>

  <Step title="Pick your channels">
    Under **Channel Configuration**, enable the channels you want surveys to run on — Web Chat, Email, Phone, WhatsApp, SMS, Slack, API, or Web Voice. Each channel toggles independently.
  </Step>

  <Step title="Save">
    Click **Save Changes**. New resolutions from this point forward will trigger a survey on any enabled channel.
  </Step>
</Steps>

<Warning>
  Native CSAT auto-send is only available when your ticketing system is set to **Open**. If a third-party ticketing integration owns resolution in your org, trigger CSAT from a [workflow](#trigger-csat-from-a-workflow) instead.
</Warning>

## The rating scale

| Score | Emoji | Label     |
| ----- | ----- | --------- |
| 1     | 😡    | Terrible  |
| 2     | 😞    | Poor      |
| 3     | 😐    | Okay      |
| 4     | 🙂    | Good      |
| 5     | 😍    | Excellent |

Low scores (1–2) prompt the customer for a comment before they can skip — the extra context makes bad scores actionable.

## Supported channels

| Channel   | Delivery                                                               |
| --------- | ---------------------------------------------------------------------- |
| Web Chat  | In-widget rating card                                                  |
| Email     | Branded email with clickable emoji ratings                             |
| SMS       | Short text with a rating link                                          |
| WhatsApp  | WhatsApp message with a rating link                                    |
| Phone     | SMS follow-up with a rating link                                       |
| Web Voice | Email follow-up with a rating link                                     |
| Slack     | Thread reply with a rating link                                        |
| API       | Programmatic — trigger via API and collect the submission over webhook |

## Where responses land

* **Inbox.** Open the session in [Inbox](https://platform.open.cx/inbox) — the timeline shows a **CSAT requested** event and, once submitted, a **CSAT submitted** event with the selected emoji and comment.
* **Reports.** Filter and export scored sessions from [Reports → Data Export](https://platform.open.cx/reports/data-export) using the **CSAT Score** and **CSAT Comment** columns.
* **Webhooks.** Subscribe to `csat.requested` and `csat.score_submitted` to pipe scores into your data warehouse or an alerting channel — see the [CSAT API reference](/api-reference/csat).

## Trigger CSAT from a workflow

When auto-send doesn't fit — for example you only want surveys on sessions with positive sentiment, or you want branching logic on low scores — drive CSAT from the [workflow builder](https://platform.open.cx/workflows) instead.

* **Send CSAT Request** action — send the survey on demand for a specific session or conversation.
* **Generate CSAT Link** action — return the rating URL without sending anything, so you can paste it into a custom outbound message.
* **Get CSAT Score** action — branch on a submitted score. *If score \< 3, notify a supervisor and reopen the session.*
* **CSAT Score Submit** trigger — fires every time a customer submits a rating, with the score and comment in the payload.

## Good To Know

<AccordionGroup>
  <Accordion title="One survey per conversation" icon="arrows-rotate">
    OpenCX stores a single CSAT per session. If the survey is resent, the customer's latest rating replaces the earlier one so your reports stay clean.
  </Accordion>

  <Accordion title="The rating is tied to the contact" icon="user">
    Every submission is linked to the session's contact, so scores roll up against the right person in [Contacts](https://platform.open.cx/contacts) and in CSAT-by-agent reports.
  </Accordion>

  <Accordion title="Custom message" icon="pen-to-square">
    Add a **custom message** on Settings → CSAT to override the default survey prompt. It replaces the default intro line on every enabled channel.
  </Accordion>

  <Accordion title="Instagram and Messenger" icon="triangle-exclamation">
    Not supported yet — the survey won't send on Instagram or Messenger conversations even if other channels are enabled. For social conversations that bridge into the Web Chat widget, enable **Web Chat** as the fallback.
  </Accordion>
</AccordionGroup>

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Increase Automation" icon="chart-line" href="/resources/increase-automation">
    Pair low CSAT with handoff reasons to prioritize AI training.
  </Card>

  <Card title="Guaranteed Delivery" icon="paper-plane" href="/resources/guaranteed-delivery">
    Make sure the survey actually reaches the customer.
  </Card>

  <Card title="CSAT API Reference" icon="code" href="/api-reference/csat">
    List, filter, and read scores programmatically.
  </Card>

  <Card title="Contacts" icon="address-book" href="/contacts/manage-contacts">
    See every rating a contact has left, not just the latest.
  </Card>
</CardGroup>
