# Update a shortcut

> PUT /v1/shortcuts/{id} · Settings and configurations API

Source: https://developer.kustomer.com/api/settings-and-configurations/update-a-shortcut

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

## Authorization

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

Updates a shortcut by ID.

## About Shortcuts

A shortcut is a saved reply that agents insert into a conversation instead of typing the same answer again. Its text can include placeholders such as the customer's first name, and it can also update the conversation, for example by adding a tag or marking it done.

Shortcuts are an org's library of reusable replies, often called canned responses. Admins create them in the Kustomer admin settings or through these endpoints, and agents search for one by name while writing a reply in the agent desktop. Inserting a shortcut fills in its placeholders for the current conversation and puts the text in the agent's reply, where they can edit it before sending. Shortcuts are organized into categories, and a shortcut can be limited to some teams or users.

#### Text and placeholders

The reply text is `draft.text`, up to 10,240 characters of Markdown. It can contain two kinds of placeholder:

| Placeholder | Example | Filled in with |
| --- | --- | --- |
| Handlebars variables | `{{customer.firstName}}`, `{{conversation.name}}`, `{{currentUser.displayName}}` | Data about the customer, the conversation, and the agent who inserts the shortcut. |
| Snippet references | `[[sn.org.billing.refund_policy]]` | The current text of that [snippet](https://developer.kustomer.com/api/settings-and-configurations#snippets), so one edit to the snippet updates every shortcut that uses it. |

Both are filled in when the agent inserts the shortcut, not when you save it. Kustomer does check that `draft.text` is a valid Handlebars template when you save it. Snippet references also work in a shortcut's `name`. When you read shortcuts, Kustomer replaces references in the name with text in the `lang` language; pass `translate=false` to get the stored values with the `[[...]]` references left as they are, which is what you want before editing and saving a shortcut.

#### Conversation actions

`conversation` describes changes to make to the conversation when the agent uses the shortcut. Each entry has an `operator` and a `value`: `tags`, `assignedUsers` and `assignedTeams` take `append`, `replace` or `remove` with a list of IDs; `name`, `status` (`open` or `done`) and `subStatus` take `replace` or `remove`; `snooze` and `queue` take `replace`; and `custom` sets conversation custom attributes.

#### Categories and access

Categories form a tree. Every org has one root category, which can't be deleted, and a shortcut or category created without `parent` goes into the root. A category with shortcuts or subcategories in it can't be deleted (`409`), so move or delete its contents first. `accessTeams` and `accessUsers` (up to 10 IDs each) make a shortcut private to those teams and users; with neither, everyone in the org can use it.

#### Which endpoint to use

| To | Call |
| --- | --- |
| List or search shortcuts | [`GET /v1/shortcuts`](https://developer.kustomer.com/api/settings-and-configurations/get-all-shortcuts), with `name` for a case-insensitive name prefix, `tagId`, `appCreated` or `deleted` |
| Create, read, update or delete a shortcut | [`POST /v1/shortcuts`](https://developer.kustomer.com/api/settings-and-configurations/create-a-shortcut), [`GET`](https://developer.kustomer.com/api/settings-and-configurations/get-a-shortcut), [`PUT`](https://developer.kustomer.com/api/settings-and-configurations/update-a-shortcut), [`DELETE /v1/shortcuts/{id}`](https://developer.kustomer.com/api/settings-and-configurations/delete-a-shortcut) |
| List categories | [`GET /v1/shortcuts/categories`](https://developer.kustomer.com/api/settings-and-configurations/get-shortcut-categories) |
| Create, read, update or delete a category | [`POST /v1/shortcuts/categories`](https://developer.kustomer.com/api/settings-and-configurations/create-a-shortcut-category), [`GET`](https://developer.kustomer.com/api/settings-and-configurations/get-a-shortcut-category), [`PUT`](https://developer.kustomer.com/api/settings-and-configurations/update-a-shortcut-category), [`DELETE /v1/shortcuts/categories/{id}`](https://developer.kustomer.com/api/settings-and-configurations/delete-a-shortcut-category) |

Deleting a shortcut is a soft delete: it disappears from lists unless you pass `deleted=true` or `deleted=all`, and `PUT` with `deleted: false` restores it. A deleted shortcut keeps its name, and names must be unique in the org, so restore it rather than creating a new one with the same name. An org can have up to 6,000 shortcuts that aren't deleted. Shortcuts installed by an app (`appCreated=true`) are managed by that app and can't be edited through these endpoints. Lists default to 100 per page, up to 1,000.

Related: [Snippets](https://developer.kustomer.com/api/settings-and-configurations#snippets), [Conversations](https://developer.kustomer.com/api/core-resources#conversations), [Drafts](https://developer.kustomer.com/api/core-resources#drafts)

### Recipes

#### 1. Sync a canned reply from another system

Keep a shortcut in step with a reply maintained elsewhere, such as a help desk you're migrating from, creating it the first time and updating or restoring it after that.

**Step 1.** [`GET /v1/shortcuts`](https://developer.kustomer.com/api/settings-and-configurations/get-all-shortcuts.md). Look the shortcut up by name. `name` is a case-insensitive prefix match, so compare the full name yourself before you trust a result, and pass `translate=false` to see the stored text rather than one with snippet references filled in.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/shortcuts?name=Replacement%20shipped&translate=false" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/shortcuts --query "name=$NAME" --query translate=false --all \
  | jq -r --arg n "$NAME" 'select(.data.attributes.name == $n) | .data.id'
```

**Step 2.** [`GET /v1/shortcuts`](https://developer.kustomer.com/api/settings-and-configurations/get-all-shortcuts.md). If nothing matched, check the deleted shortcuts with `deleted=true` before creating one. Names stay taken after a delete, so creating a shortcut with a deleted shortcut's name fails.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/shortcuts?name=Replacement%20shipped&deleted=true&translate=false" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/shortcuts --query "name=$NAME" --query deleted=true --query translate=false --all \
  | jq -r --arg n "$NAME" 'select(.data.attributes.name == $n) | .data.id'
```

**Step 3.** [`PUT /v1/shortcuts/{id}`](https://developer.kustomer.com/api/settings-and-configurations/update-a-shortcut.md). If you found one, send the new `draft`. `PUT` changes only the fields you send, and adding `deleted: false` restores a deleted shortcut, back in its old category if that still exists and in the root category if not.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/shortcuts/$SHORTCUT_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"draft": {"text": "Hi {{customer.firstName}}, your replacement has shipped."}, "deleted": false}'
```

```bash title="kusty"
kusty api PUT /v1/shortcuts/$SHORTCUT_ID \
  --data '{"draft": {"text": "Hi {{customer.firstName}}, your replacement has shipped."}, "deleted": false}'
```

**Step 4.** [`POST /v1/shortcuts`](https://developer.kustomer.com/api/settings-and-configurations/create-a-shortcut.md). If neither lookup found it, create it. Without `parent` it goes into the root category. Kustomer checks that `draft.text` is a valid Handlebars template and rejects the request if it isn't.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/shortcuts" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"name": "Replacement shipped", "draft": {"text": "Hi {{customer.firstName}}, your replacement has shipped."}}'
```

```bash title="kusty"
kusty api POST /v1/shortcuts \
  --data '{"name": "Replacement shipped", "draft": {"text": "Hi {{customer.firstName}}, your replacement has shipped."}}'
```

> Store the shortcut ID next to the reply in your system after the first sync, so later runs can go straight to the `PUT`. Shortcuts installed by an app can't be changed through these endpoints.

#### 2. Make a shortcut that closes the conversation with a reason

Give agents one shortcut that sends a closing reply and marks the conversation done with the right sub-status, so resolutions are reported consistently.

**Step 1.** [`GET /v1/sub-statuses`](https://developer.kustomer.com/api/core-resources/get-sub-statuses.md). Find the [sub-status](https://developer.kustomer.com/api/core-resources#sub-statuses) to apply. Ask for conversation sub-statuses and keep the one under `done` with the label you want.

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

```bash title="kusty"
kusty api GET /v1/sub-statuses --query resource=conversation --all \
  | jq -r 'select(.data.attributes.parentStatus == "done" and .data.attributes.label == "Resolved by refund") | .data.id'
```

**Step 2.** [`POST /v1/shortcuts`](https://developer.kustomer.com/api/settings-and-configurations/create-a-shortcut.md). Create the shortcut with a `conversation` block. Each entry is an `operator` and a `value`: here `status` is replaced with `done` and `subStatus` with the ID you found. The changes are applied when an agent uses the shortcut, not when you save it.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/shortcuts" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{
    \"name\": \"Refund issued, closing\",
    \"draft\": {\"text\": \"Your refund is on its way. Thanks for your patience!\"},
    \"conversation\": {
      \"status\": {\"operator\": \"replace\", \"value\": \"done\"},
      \"subStatus\": {\"operator\": \"replace\", \"value\": \"$SUB_STATUS_ID\"}
    }
  }"
```

```bash title="kusty"
kusty api POST /v1/shortcuts \
  --field "name=Refund issued, closing" \
  --field "draft.text=Your refund is on its way. Thanks for your patience!" \
  --field 'conversation.status:={"operator": "replace", "value": "done"}' \
  --field "conversation.subStatus:={\"operator\": \"replace\", \"value\": \"$SUB_STATUS_ID\"}"
```

> `tags`, `assignedUsers` and `assignedTeams` take `append`, `replace` or `remove` with a list of IDs, so a shortcut can also tag the conversation or hand it to a team.

#### 3. File shortcuts into a new category

Group related shortcuts, such as everything about billing, so agents can browse them together.

**Step 1.** [`POST /v1/shortcuts/categories`](https://developer.kustomer.com/api/settings-and-configurations/create-a-shortcut-category.md). Create the category. Without `parent` it goes directly under the org's root category; send another category's ID as `parent` to nest it.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/shortcuts/categories" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"name": "Billing"}'
```

```bash title="kusty"
kusty api POST /v1/shortcuts/categories --field name=Billing --fields id
```

**Step 2.** [`GET /v1/shortcuts`](https://developer.kustomer.com/api/settings-and-configurations/get-all-shortcuts.md). Find the shortcuts to move, for example every one whose name starts with "Billing".

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/shortcuts?name=billing&translate=false&pageSize=1000" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/shortcuts --query name=billing --query translate=false --all \
  | jq -r '.data.id' > shortcut-ids.txt
```

**Step 3.** [`PUT /v1/shortcuts/{id}`](https://developer.kustomer.com/api/settings-and-configurations/update-a-shortcut.md). Send the category ID as `parent` on each one. Nothing else about the shortcut changes.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/shortcuts/$SHORTCUT_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"parent\": \"$CATEGORY_ID\"}"
```

```bash title="kusty"
while read -r id; do
  kusty api PUT /v1/shortcuts/$id --field parent=$CATEGORY_ID --fields id
done < shortcut-ids.txt
```

> To remove a category later, move or delete everything in it first: deleting a category that still holds shortcuts or subcategories fails with `409`. The root category can't be deleted.

### Example object

A real record from [`PUT /v1/shortcuts/{id}`](https://developer.kustomer.com/api/settings-and-configurations/update-a-shortcut.md):

```json
{
  "id": "5828cb1c7894621a0031d021",
  "type": "shortcut",
  "attributes": {
    "name": "Replacement Shipped",
    "draft": {
      "text": "Thank you for your patience - we have shipped a replacement. It should arrive in 6-10 business days."
    },
    "createdAt": "2016-11-13T20:20:44.725Z",
    "modifiedAt": "2016-11-13T20:26:00.262Z",
    "updatedAt": "2016-11-13T20:26:00.262Z"
  },
  "relationships": {
    "org": {
      "links": {
        "self": "/v1/orgs/57bb5d2e32e83a130052e94e"
      },
      "data": {
        "type": "org",
        "id": "57bb5d2e32e83a130052e94e"
      }
    },
    "createdBy": {
      "data": {
        "type": "user",
        "id": "57bb5d2e32e83a130052e94f"
      },
      "links": {
        "self": "/v1/users/57bb5d2e32e83a130052e94f"
      }
    },
    "modifiedBy": {
      "data": {
        "type": "user",
        "id": "57bb5d2e32e83a130052e94f"
      },
      "links": {
        "self": "/v1/users/57bb5d2e32e83a130052e94f"
      }
    },
    "parent": {
      "data": {
        "type": "shortcut_category",
        "id": "57bb5d2e32e83a130052e94a"
      },
      "links": {
        "self": "/v1/shortcuts/categories/57bb5d2e32e83a130052e94a"
      }
    }
  },
  "links": {
    "self": "/v1/shortcuts/5828cb1c7894621a0031d021"
  }
}
```

Fields:

- `attributes.name` (string): The name agents search for when they insert the shortcut. It must be unique in the org, including deleted shortcuts, and it can contain snippet references, which reads replace with text unless you pass `translate=false`.
- `attributes.draft` (object): The reply the shortcut inserts. `PUT` replaces the whole `draft` object, so send `text` again whenever you change it.
- `attributes.draft.text` (string): Up to 10,240 characters of Markdown, with Handlebars variables such as `{{customer.firstName}}` and snippet references such as `[[sn.org.billing.refund_policy]]`. Placeholders are filled in when an agent inserts the shortcut, but the template must be valid when you save it.
- `attributes.modifiedAt` (string): When a user last changed the shortcut.
- `relationships.parent` (object): The category the shortcut is filed in, a `shortcut_category` ID. Send `parent` on `POST` or `PUT` to file it; leave it out on `POST`, or send `parent: null` on `PUT`, to put it in the root category.

### Relationships

- Points to **Teams** via `relationships.accessTeams` (list): The teams a shortcut is limited to, from its `accessTeams` list of up to 10 team IDs. With no teams or users listed, everyone in the org can use the shortcut.
- Points to **Users** via `relationships.accessUsers` (list): The users a shortcut is limited to, from its `accessUsers` list of up to 10 user IDs, in addition to any teams in `accessTeams`.

### Permissions

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

## Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Shortcut ID. |

## Request body

Content type: `application/json` (required)

- `name` (string): (min length 1; max length 128)
- `parent` (string<object-id>, nullable)
- `draft` (object)
  - `text` (string): (max length 10240)
  - `template` (string<object-id>)
- `payload` (object)
- `accessTeams` (array<string>): (max items 10; unique items)
- `accessUsers` (array<string>): (max items 10; unique items)
- `conversation` (object)
  - `tags` (object)
    - `operator` (string): One of: `append`, `replace`, `remove`.
    - `value` (array<tag_name>): (max items 20; unique items)
  - `name` (object)
    - `operator` (string): One of: `replace`, `remove`.
    - `value` (string): (max length 256)
  - `status` (object)
    - `operator` (string): One of: `replace`, `remove`.
    - `value` (string): One of: `open`, `done`.
  - `subStatus` (object)
    - `operator` (string): One of: `replace`, `remove`.
    - `value` (string<object-id>)
  - `snooze` (object)
    - `operator` (string): One of: `replace`.
    - `value` (string<object-id>)
  - `queue` (object)
    - `operator` (string): One of: `replace`.
    - `value` (oneOf)
      - One of:
        - object
        - object
  - `assignedUsers` (object)
    - `operator` (string): One of: `append`, `replace`, `remove`.
    - `value` (array<any>): (max items 20; unique items)
  - `assignedTeams` (object)
    - `operator` (string): One of: `append`, `replace`, `remove`.
    - `value` (array<any>): (max items 20; unique items)
  - `custom` (object)
- `deleted` (boolean)

Example:

```json
{
  "name": "example_name",
  "parent": "string",
  "draft": {
    "text": "Example text",
    "template": "string"
  },
  "payload": {},
  "accessTeams": [
    "string"
  ],
  "accessUsers": [
    "string"
  ]
}
```

## Responses

### 200

OK

- `data` (object, required)
  - `id` (string, required)
  - `type` (string, required)
  - `attributes` (object, required)
    - `name` (string, required)
    - `draft` (object, required)
    - `createdAt` (string, required)
    - `modifiedAt` (string, required)
    - `updatedAt` (string, required)
  - `relationships` (object, required)
    - `org` (object, required)
    - `createdBy` (object, required)
    - `modifiedBy` (object, required)
    - `parent` (object, required)
  - `links` (object, required)
    - `self` (string, required)

```json
{
  "data": {
    "id": "5828cb1c7894621a0031d021",
    "type": "shortcut",
    "attributes": {
      "name": "Replacement Shipped",
      "draft": {
        "text": "Thank you for your patience - we have shipped a replacement. It should arrive in 6-10 business days."
      },
      "createdAt": "2016-11-13T20:20:44.725Z",
      "modifiedAt": "2016-11-13T20:26:00.262Z",
      "updatedAt": "2016-11-13T20:26:00.262Z"
    },
    "relationships": {
      "org": {
        "links": {
          "self": "/v1/orgs/57bb5d2e32e83a130052e94e"
        },
        "data": {
          "type": "org",
          "id": "57bb5d2e32e83a130052e94e"
        }
      },
      "createdBy": {
        "data": {
          "type": "user",
          "id": "57bb5d2e32e83a130052e94f"
        },
        "links": {
          "self": "/v1/users/57bb5d2e32e83a130052e94f"
        }
      },
      "modifiedBy": {
        "data": {
          "type": "user",
          "id": "57bb5d2e32e83a130052e94f"
        },
        "links": {
          "self": "/v1/users/57bb5d2e32e83a130052e94f"
        }
      },
      "parent": {
        "data": {
          "type": "shortcut_category",
          "id": "57bb5d2e32e83a130052e94a"
        },
        "links": {
          "self": "/v1/shortcuts/categories/57bb5d2e32e83a130052e94a"
        }
      }
    },
    "links": {
      "self": "/v1/shortcuts/5828cb1c7894621a0031d021"
    }
  }
}
```

### 400

Bad Request

### 401

Unauthorized

### 403

Forbidden

## Example request

```bash
curl --request PUT \
  --url 'https://api.kustomerapp.com/v1/shortcuts/6026aba3fcca5f03e5541cd8' \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "example_name",
  "parent": "string",
  "draft": {
    "text": "Example text",
    "template": "string"
  },
  "payload": {},
  "accessTeams": [
    "string"
  ],
  "accessUsers": [
    "string"
  ]
}'
```
