# Update satisfaction survey order

> PUT /v1/satisfaction/order · Settings and configurations API

Source: https://developer.kustomer.com/api/settings-and-configurations/update-satisfaction-survey-order

```http
PUT https://{orgname}.api.kustomerapp.com/v1/satisfaction/order
Authorization: Bearer <API key>
```

## Authorization

Requires any one of these roles: `org.permission.satisfaction.update`; or legacy `org.admin.satisfaction.write`. The API key or user must hold at least one of them. Legacy roles predate permission sets and still work.

Updates the display order of satisfaction surveys for the organization.

The `satisfactionForms` array may contain up to 500 survey IDs and defines the order in which enabled surveys are evaluated for a channel.

## About Satisfaction surveys

Satisfaction surveys (CSAT) ask customers to rate their experience after a conversation. A survey defines the question, rating scale, channel and audience; each time one is sent, Kustomer creates a satisfaction response that tracks delivery and stores the customer's rating and comments.

CSAT measures how customers feel about the help they got. Admins build surveys in the Kustomer admin settings; customers answer them in the chat widget or in a message on email, SMS and other channels; agents and managers see the results on the conversation and in reporting. The API splits this into two objects: the survey (type `satisfaction`), which is configuration, and the satisfaction response (type `satisfaction_response`), which is one survey sent to one customer about one conversation.

#### Surveys

Key survey fields:

- `channel`: where the survey is sent, such as `email`, `sms`, `chat`, `voice`, `facebook` or `whatsapp`. `from` picks the sending account for that channel.
- `formType`: always `csat`.
- `scale.options`: how many rating choices the customer sees, `2`, `3`, `5` or `10`, with optional `labelLow` and `labelHigh`.
- `introduction` and `ratingPrompt`: the text around the rating question. `questions` adds follow-up questions, each either a free-text `response` or a `property` question tied to a conversation attribute named in `property`.
- `delayTime`: how long after the conversation closes to send the survey, in milliseconds. Chat surveys are offered right away.
- `criteria`: audience rules in the same `and`/`or` format as queue rules. Omit it on update to keep the current rules; send `null` or empty lists to clear them.

Each channel can have up to 30 enabled surveys, and creating or enabling one beyond that returns `422`. Disabled surveys don't count.

#### Which survey a conversation gets

When a conversation is marked done, or a chat ends, Kustomer looks at the enabled surveys for the channel of the conversation's latest message and keeps those whose `criteria` match. If several match, the one with the most criteria rules wins, and the survey order breaks ties. If the conversation is reopened before a scheduled survey goes out, the survey is canceled.

#### Responses

| `status` | Meaning |
| --- | --- |
| `scheduled` | Created and waiting for `scheduledFor`. |
| `offered` | Sent to the customer (`sentAt`). |
| `rated` | The customer gave a rating. |
| `commented` | The customer also left a comment or answered follow-up questions. |
| `unresponded` | No answer was received. |
| `canceled` | Not sent, for example because the conversation reopened. |

`rating` is the customer's raw choice on the survey's scale, and `score` is the derived result used for CSAT reporting, `1` for a satisfied rating and `0` otherwise. `answers` holds follow-up answers, and responses stop accepting changes after `lockedAt`. Each response links to its `satisfaction` survey, `customer` and `conversation`.

#### Which endpoint to use

| To | Call |
| --- | --- |
| List the org's surveys | [`GET /v1/satisfaction`](https://developer.kustomer.com/api/core-resources/get-satisfactions) |
| Create a survey | [`POST /v1/satisfaction`](https://developer.kustomer.com/api/settings-and-configurations/create-a-satisfaction-survey) |
| Change, enable or disable a survey | [`PUT /v1/satisfaction/{id}`](https://developer.kustomer.com/api/settings-and-configurations/update-a-satisfaction-survey) |
| Set the survey order used to break ties | [`PUT /v1/satisfaction/order`](https://developer.kustomer.com/api/settings-and-configurations/update-satisfaction-survey-order) |
| Read one customer's response and rating | [`GET /v1/satisfaction-responses/{id}`](https://developer.kustomer.com/api/core-resources/get-satisfaction-response) |

To find responses in bulk, search conversations by the `conversation_satisfaction_*` attributes, such as rating, score or status. If you run your own chat UI, record a rating with the [chat satisfaction response endpoint](https://developer.kustomer.com/api/chat/update-a-chat-satisfaction-response). `PUT /v1/satisfaction/order` takes the full ordered list of survey IDs in `satisfactionForms`, up to 500.

Related: [Server chat API](https://developer.kustomer.com/docs/chat/server-chat-api), [Expressions reference](https://developer.kustomer.com/docs/workflows/expressions-reference), [Search](https://developer.kustomer.com/docs/concepts/search)

### Recipes

#### 1. Read the CSAT result for a conversation

Show a conversation's survey rating and comments in your own tool, such as a CRM or QA dashboard.

**Step 1.** [`GET /v1/conversations/{id}`](https://developer.kustomer.com/api/core-resources/get-conversation-by-id.md). Read the [conversation](https://developer.kustomer.com/api/core-resources#conversations). Its `satisfactionLevel` has the response's `status`, `rating` and `score`, and the satisfaction response ID in `formResponse`. If there's no `formResponse`, there's no response to read yet.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/conversations/$CONVERSATION_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
RESPONSE_ID=$(kusty api GET /v1/conversations/$CONVERSATION_ID \
  | jq -r '.data.attributes.satisfactionLevel.formResponse // empty')
```

**Step 2.** [`GET /v1/satisfaction-responses/{id}`](https://developer.kustomer.com/api/core-resources/get-satisfaction-response.md). Read the response for the full detail: when it was sent, the follow-up `answers`, and the survey it came from. `rating` is the customer's choice on the survey's scale, and `score` is `1` for a satisfied rating and `0` otherwise.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/satisfaction-responses/$RESPONSE_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/satisfaction-responses/$RESPONSE_ID \
  --fields attributes.status,attributes.rating,attributes.score,attributes.answers,relationships.satisfaction
```

#### 2. Report CSAT for a date range

Work out the share of satisfied customers for last month, overall and per survey, for a weekly report.

**Step 1.** [`POST /v1/customers/search`](https://developer.kustomer.com/api/core-resources/create-customer-search.md). [Search](https://developer.kustomer.com/api/core-resources#search) for conversations with an answered survey: `conversation_satisfaction_status` of `rated` or `commented`, and `conversation_satisfaction_created_at`, when the survey response was created, in the window. For a quick number, run it with `pageSize=1` as shown, run it again with `{"conversation_satisfaction_score": {"equals": 1}}` added, and divide the second `meta.total` by the first.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/customers/search?pageSize=1&trackTotalHits=true" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "queryContext": "conversation",
    "and": [
      {"conversation_satisfaction_status": {"in": ["rated", "commented"]}},
      {"conversation_satisfaction_created_at": {"gte": "2026-09-01T00:00:00Z"}},
      {"conversation_satisfaction_created_at": {"lt": "2026-10-01T00:00:00Z"}}
    ]
  }' | jq '.meta.total'
```

```bash title="kusty"
kusty api POST /v1/customers/search --query pageSize=500 --all --output jsonl \
  --data @csat-september.json > rated.jsonl
```

**Step 2.** To break the result down, export the matches and group them by survey locally. Each conversation's `satisfactionLevel` carries the `form` (survey ID) and `score`.

```bash title="kusty"
jq -s 'map(.data.attributes.satisfactionLevel)
  | group_by(.form)
  | map({survey: .[0].form, responses: length, csat: ((map(.score) | add) / length * 100 | round)})' rated.jsonl
```

> One search can only page through its first 10,000 matches. For a busy org, split the range into shorter windows and combine the results.

#### 3. Launch a new survey and give it priority

Add a survey for a channel, put it ahead of existing ones in the tie-break order, and only then turn it on.

**Step 1.** [`POST /v1/satisfaction`](https://developer.kustomer.com/api/settings-and-configurations/create-a-satisfaction-survey.md). Create the survey. It starts disabled unless you send `enabled: true`, which gives you time to set its order first. `formType` and `scale.options` are required.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/satisfaction" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "SMS survey",
    "formType": "csat",
    "channel": "sms",
    "scale": {"options": 5, "type": "number", "labelLow": "Terrible", "labelHigh": "Amazing"},
    "ratingPrompt": "How satisfied are you with your interaction?",
    "delayTime": 3600000
  }'
```

```bash title="kusty"
SURVEY_ID=$(kusty api POST /v1/satisfaction --data @sms-survey.json | jq -r '.data.id')
```

**Step 2.** [`GET /v1/satisfaction`](https://developer.kustomer.com/api/core-resources/get-satisfactions.md). List every survey. The list comes back in the current order, with new surveys added at the end.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/satisfaction?pageSize=100" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/satisfaction --all | jq -r '.data.id' > survey-order.txt
```

**Step 3.** [`PUT /v1/satisfaction/order`](https://developer.kustomer.com/api/settings-and-configurations/update-satisfaction-survey-order.md). Send the full list with the new survey first. It must include every survey in the org, or the request fails with `400`. The order only matters when several enabled surveys with the same number of criteria rules match a conversation.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/satisfaction/order" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"satisfactionForms\": [\"$SURVEY_ID\", \"$OTHER_SURVEY_ID\"]}"
```

```bash title="kusty"
ORDER=$( (echo "$SURVEY_ID"; grep -v "$SURVEY_ID" survey-order.txt) | jq -R . | jq -sc .)
kusty api PUT /v1/satisfaction/order --field "satisfactionForms:=$ORDER"
```

**Step 4.** [`PUT /v1/satisfaction/{id}`](https://developer.kustomer.com/api/settings-and-configurations/update-a-satisfaction-survey.md). Turn it on. Leaving `criteria` out keeps the survey's current audience rules. Enabling fails with `422` if the channel already has 30 enabled surveys.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/satisfaction/$SURVEY_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"enabled": true}'
```

```bash title="kusty"
kusty api PUT /v1/satisfaction/$SURVEY_ID --field enabled:=true
```

### Example object

A real record from [`POST /v1/satisfaction`](https://developer.kustomer.com/api/settings-and-configurations/create-a-satisfaction-survey.md):

```json
{
  "type": "satisfaction",
  "id": "5daf7921124be4f2960cb197",
  "attributes": {
    "name": "Satisfaction Form for Email",
    "formType": "csat",
    "enabled": true,
    "channel": "email",
    "description": "This survey will be sent when email conversations are marked done.",
    "from": {
      "channel": "email",
      "account": "support@test.mail.kustomerapp.com",
      "app": "postmark"
    },
    "delayTime": 0,
    "scale": {
      "options": 5,
      "type": "number",
      "labelLow": "Terrible",
      "labelHigh": "Amazing"
    },
    "introduction": "Tell us about your experience.",
    "ratingPrompt": "How satisfied are you with your interaction?",
    "questions": [],
    "criteria": {
      "and": [],
      "or": []
    },
    "createdAt": "2019-08-15T14:25:30.715Z",
    "updatedAt": "2022-07-11T19:52:48.296Z",
    "modifiedAt": "2022-07-11T19:52:48.296Z"
  },
  "relationships": {
    "org": {
      "links": {
        "self": "/v1/orgs/57f29863a1dbf61100e6aa92"
      },
      "data": {
        "type": "org",
        "id": "57f29863a1dbf61100e6aa92"
      }
    },
    "createdBy": {
      "links": {
        "self": "/v1/users/58190b991f2932100010d683"
      },
      "data": {
        "type": "user",
        "id": "58190b991f2932100010d683"
      }
    },
    "modifiedBy": {
      "links": {
        "self": "/v1/users/58190b991f2932100010d683"
      },
      "data": {
        "type": "user",
        "id": "58190b991f2932100010d683"
      }
    }
  }
}
```

Fields:

- `attributes.name` (string): The survey's name in the admin settings. Like the other text fields, it can be a snippet reference.
- `attributes.formType` (string): Always `csat`.
- `attributes.enabled` (boolean): Whether Kustomer sends the survey. New surveys are disabled unless you send `enabled` set to `true`, and each channel can have at most 30 enabled surveys.
- `attributes.channel` (string): The channel the survey goes out on, such as `email`, `sms`, `chat`, `voice`, `facebook` or `whatsapp`. Kustomer matches it against the channel of the conversation's latest message.
- `attributes.description` (string): Notes for admins about when the survey is used.
- `attributes.from` (object): The account the survey is sent from, as `{ channel, app, account }`, for example a support address for email.
- `attributes.from.account` (string): The sending address or account on the survey's channel, such as `support@example.com`.
- `attributes.delayTime` (number): How long after the conversation is marked done to send the survey, in milliseconds. It defaults to 24 hours (`86400000`); chat surveys are offered right away.
- `attributes.scale` (object): The rating question's scale. It's required when you create a survey, with at least `options`.
- `attributes.scale.options` (number): How many rating choices the customer sees, `2`, `3`, `5` or `10`.
- `attributes.scale.type` (string): How the choices are drawn, `emoji`, `number`, `thumb` or `custom`.
- `attributes.scale.labelLow` (string): The label at the low end of the scale, such as "Terrible".
- `attributes.scale.labelHigh` (string): The label at the high end of the scale, such as "Amazing".
- `attributes.introduction` (string): Text shown before the rating question. It can be a snippet reference such as `[[sn.org.csat.intro]]`, filled in with the customer's language.
- `attributes.ratingPrompt` (string): The rating question itself, such as "How satisfied are you with your interaction?".
- `attributes.questions` (array): Follow-up questions shown after the rating. Each has a `type` and a `prompt`, where `type` is `response` for free text or `property` for a question tied to the conversation attribute named in `property`.
- `attributes.criteria` (object): Audience rules, in the same `and`/`or` format as queue rules, that decide which conversations on the channel get this survey. On `PUT`, omit it to keep the current rules, or send `null` or empty lists to clear them.

### Relationships

- Points to **Customers** via `relationships.customer`: The customer a satisfaction response was sent to. Each response, read with `GET /v1/satisfaction-responses/{id}`, links to one customer, one conversation and the survey it came from.
- Points to **Conversations** via `relationships.conversation`: The conversation a satisfaction response is about. The conversation's `satisfactionLevel` holds the response ID in `formResponse`, along with its `status`, `rating` and `score`.

### Permissions

Fewest roles for a read-only integration: `org.permission.satisfaction.read`. For every endpoint: `org.permission.satisfaction.read`, `org.permission.satisfaction.update`, `org.permission.satisfaction.create`.

## Request body

Content type: `application/json` (required)

- `satisfactionForms` (array<string<object-id>>, required): Ordered list of satisfaction survey IDs. (max items 500; unique items)

Example:

```json
{
  "satisfactionForms": [
    "string"
  ]
}
```

## Responses

### 200

OK

- `data` (object, required)
  - `type` (string): One of: `satisfaction_order`.
  - `attributes` (object)
    - `createdAt` (string<date-time>)
    - `updatedAt` (string<date-time>)
  - `relationships` (object)
    - `org` (object)
    - `satisfactionForms` (array<object>)
    - `modifiedBy` (object)

```json
{
  "data": {
    "type": "satisfaction_order",
    "attributes": {
      "createdAt": "2019-08-15T14:25:30.715Z",
      "updatedAt": "2022-07-11T19:52:48.296Z"
    },
    "relationships": {
      "org": {
        "links": {
          "self": "/v1/orgs/57f29863a1dbf61100e6aa92"
        },
        "data": {
          "type": "org",
          "id": "57f29863a1dbf61100e6aa92"
        }
      },
      "satisfactionForms": [
        {
          "links": {
            "self": "/v1/satisfaction/5daf7921124be4f2960cb197"
          },
          "data": {
            "type": "satisfaction",
            "id": "5daf7921124be4f2960cb197"
          }
        }
      ],
      "modifiedBy": {
        "links": {
          "self": "/v1/users/58190b991f2932100010d683"
        },
        "data": {
          "type": "user",
          "id": "58190b991f2932100010d683"
        }
      }
    }
  }
}
```

### 400

Bad Request

### 401

Unauthorized

### 403

Forbidden

## Example request

```bash
curl --request PUT \
  --url 'https://api.kustomerapp.com/v1/satisfaction/order' \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "satisfactionForms": [
    "string"
  ]
}'
```
