# Create sub-status

> POST /v1/sub-statuses · Core resources API

Source: https://developer.kustomer.com/api/core-resources/create-sub-status

```http
POST https://{orgname}.api.kustomerapp.com/v1/sub-statuses
Authorization: Bearer <API key>
```

## Authorization

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

Creates a new sub-status.

## About Sub-statuses

A sub-status adds detail inside one of the three main statuses, open, snoozed or done, such as "Waiting on warehouse" under snoozed or "Resolved by refund" under done. Admins define them, and agents pick one when they change a conversation's or custom object's status.

Every conversation, and every custom object (KObject), has a `status` of `open`, `snoozed` or `done`. Those three values drive Kustomer's behavior: what appears in queues and searches, when snoozes wake up, and when SLAs and CSAT surveys run. They can't be renamed or extended. Sub-statuses are how an org adds its own vocabulary on top without changing that behavior. Each sub-status belongs to exactly one parent status, and the record stores the chosen sub-status ID in its `subStatus` attribute. Agents choose sub-statuses from the status menu in the agent desktop, and you can search conversations by them with the `conversation_sub_status` attribute.

#### Key fields

| Field | Meaning |
| --- | --- |
| `label` | The name agents see. |
| `description` | What the sub-status is for. Required when you create one. |
| `parentStatus` | `open`, `snoozed` or `done`. |
| `resourceType` | `conversation` or `kobject`. Can't be changed after creation. |
| `default` | Whether this is the sub-status applied automatically for its parent status. |

Sub-statuses for custom objects belong to one Klass. When you create one with `resourceType: "kobject"`, also send `parentId` (the Klass ID) or `parentName` (the Klass name), and filter lists the same way.

#### Defaults and status changes

Each parent status has at most one default sub-status per resource type (per Klass for custom objects). The first sub-status you create for a parent status becomes its default automatically, and setting `default: true` on another one moves the default to it.

When a conversation's `status` changes and the request doesn't include a `subStatus`, Kustomer sets the default sub-status for the new status, or clears it if there's no default. If you do send a `subStatus`, it must be an existing sub-status ID, and on a `PUT` it must also belong to the conversation's new status, or the update fails with `404`. Send `subStatus: null` to clear it.

#### Which endpoint to use

| To | Call |
| --- | --- |
| List the sub-statuses agents can choose | [`GET /v1/sub-statuses`](https://developer.kustomer.com/api/core-resources/get-sub-statuses), with `resource` (required) and optionally `parentId` or `parentName` |
| Look up one or more sub-statuses by ID, for example to show a conversation's `subStatus` label | [`GET /v1/sub-statuses/{id}`](https://developer.kustomer.com/api/core-resources/get-sub-status-by-id), with comma-separated IDs for several |
| Create a sub-status | [`POST /v1/sub-statuses`](https://developer.kustomer.com/api/core-resources/create-sub-status) |
| Rename, redescribe or make one the default | [`PUT /v1/sub-statuses/{id}`](https://developer.kustomer.com/api/core-resources/update-sub-status) |
| Retire a sub-status | [`DELETE /v1/sub-statuses/{id}`](https://developer.kustomer.com/api/core-resources/delete-sub-status) |

Deleting is a soft delete: the response returns the sub-status with its deletion recorded, and records that already use it keep the ID in `subStatus`. Reading sub-statuses requires read access to the matching resource type, conversations or custom objects, and writing them requires admin permission.

Related: [Conversations and messages](https://developer.kustomer.com/docs/concepts/conversations-and-messages), [Snoozes](https://developer.kustomer.com/api/core-resources#snoozes), [Custom objects](https://developer.kustomer.com/docs/concepts/custom-objects)

### Recipes

#### 1. Close a conversation with a resolution reason

When your system resolves an issue, such as issuing a refund, mark the conversation done with the matching sub-status so reports show why it closed.

**Step 1.** [`GET /v1/sub-statuses`](https://developer.kustomer.com/api/core-resources/get-sub-statuses.md). List the conversation sub-statuses and find the one under `done` with the label you want. Labels can change, so cache the ID by label and refresh it now and then rather than hard-coding it.

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

```bash title="kusty"
SUB_STATUS_ID=$(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.** [`PUT /v1/conversations/{id}`](https://developer.kustomer.com/api/core-resources/update-conversation.md). Send `status` and `subStatus` together. The sub-status must sit under the new status, or the update fails with `404`. If you leave `subStatus` out, Kustomer applies the default sub-status for `done`, if there is one.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/conversations/$CONVERSATION_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"status\": \"done\", \"subStatus\": \"$SUB_STATUS_ID\"}"
```

```bash title="kusty"
kusty api PUT /v1/conversations/$CONVERSATION_ID \
  --field status=done \
  --field subStatus=$SUB_STATUS_ID
```

#### 2. Report on conversations by sub-status label

Turn the sub-status IDs stored on conversations into readable labels for a report or export.

**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 the conversations to report on, keeping only the fields you need.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/customers/search?pageSize=500" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"queryContext": "conversation", "and": [{"conversation_status": {"equals": "done"}}, {"conversation_updated_at": {"gte": "2026-10-01T00:00:00Z"}}]}'
```

```bash title="kusty"
kusty api POST /v1/customers/search --query pageSize=500 --all --output jsonl \
  --data '{"queryContext": "conversation", "and": [{"conversation_status": {"equals": "done"}}, {"conversation_updated_at": {"gte": "2026-10-01T00:00:00Z"}}]}' \
  > done.jsonl
```

**Step 2.** [`GET /v1/sub-statuses/{id}`](https://developer.kustomer.com/api/core-resources/get-sub-status-by-id.md). Look up every distinct `subStatus` in one request by passing the IDs comma-separated.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/sub-statuses/$ID_1,$ID_2,$ID_3" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
IDS=$(jq -r '.data.attributes.subStatus // empty' done.jsonl | sort -u | paste -sd, -)
kusty api GET /v1/sub-statuses/$IDS \
  | jq '[.data] | flatten | map({(.id): .attributes.label}) | add' > labels.json
```

**Step 3.** Count conversations per label locally.

```bash title="kusty"
jq -r --slurpfile labels labels.json '$labels[0][.data.attributes.subStatus] // "(none)"' done.jsonl \
  | sort | uniq -c | sort -rn
```

#### 3. Add sub-statuses to a custom object Klass

Give a custom object, such as returns, its own sub-statuses so agents can track where each record stands.

**Step 1.** [`GET /v1/klasses`](https://developer.kustomer.com/api/core-resources/get-klasses.md). Find the [Klass](https://developer.kustomer.com/api/core-resources#klasses) ID by name.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/klasses?name=return" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
KLASS_ID=$(kusty api GET /v1/klasses --query name=return | jq -r '.data[0].id')
```

**Step 2.** [`POST /v1/sub-statuses`](https://developer.kustomer.com/api/core-resources/create-sub-status.md). Create each sub-status with `resourceType: "kobject"` and the Klass as `parentId`. `description` is required. The first one you create under a parent status becomes its default.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/sub-statuses" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{
    \"label\": \"Awaiting item\",
    \"description\": \"The customer hasn't shipped the item back yet.\",
    \"parentStatus\": \"snoozed\",
    \"resourceType\": \"kobject\",
    \"parentId\": \"$KLASS_ID\"
  }"
```

```bash title="kusty"
kusty api POST /v1/sub-statuses \
  --field "label=Awaiting item" \
  --field "description=The customer hasn't shipped the item back yet." \
  --field parentStatus=snoozed \
  --field resourceType=kobject \
  --field parentId=$KLASS_ID
```

**Step 3.** [`PUT /v1/sub-statuses/{id}`](https://developer.kustomer.com/api/core-resources/update-sub-status.md). To make a different sub-status the default for its parent status, set `default: true` on it. The previous default stops being the default.

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

```bash title="kusty"
kusty api PUT /v1/sub-statuses/$SUB_STATUS_ID --field default:=true
```

**Step 4.** [`GET /v1/sub-statuses`](https://developer.kustomer.com/api/core-resources/get-sub-statuses.md). Check the result by listing the Klass's sub-statuses with `resource=kobject` and the same `parentId`.

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

```bash title="kusty"
kusty api GET /v1/sub-statuses --query resource=kobject --query parentId=$KLASS_ID --all \
  --fields id,attributes.label,attributes.parentStatus,attributes.default
```

### Example object

A real record from [`GET /v1/sub-statuses/{id}`](https://developer.kustomer.com/api/core-resources/get-sub-status-by-id.md):

```json
{
  "type": "conversation_sub_status",
  "id": "567229511e4a481f0062f7fd",
  "attributes": {
    "label": "Waiting for Customer",
    "description": "Conversation is waiting for customer response",
    "parentStatus": "open",
    "resourceType": "conversation",
    "default": false,
    "createdAt": "2026-03-14T15:09:26.535Z",
    "updatedAt": "2026-03-14T15:09:26.535Z"
  },
  "links": {
    "self": "/v1/sub-statuses/567229511e4a481f0062f7fd"
  }
}
```

Fields:

- `type` (string): `conversation_sub_status` or `kobject_sub_status`, following `resourceType`.
- `attributes.label` (string): The name agents see in the status menu, up to 256 characters.
- `attributes.description` (string): What the sub-status is for, to help agents pick the right one. Required on create.
- `attributes.parentStatus` (string): The main status this sub-status sits under: `open`, `snoozed` or `done`. When you change a conversation's `status` and send a `subStatus` in the same `PUT`, the sub-status must sit under the new status.
- `attributes.resourceType` (string): Whether the sub-status is for conversations (`conversation`) or custom objects (`kobject`). It's set on create and can't be changed.
- `attributes.default` (boolean): Whether Kustomer applies this sub-status when a conversation moves to its parent status without a `subStatus` in the request. Each parent status has at most one default, so setting `true` here takes it from the current one.

### Relationships

- Points to **Klasses** via `relationships.parentId`: The Klass a custom object sub-status belongs to, sent as `parentId` (or `parentName`) on create and used to filter `GET /v1/sub-statuses`. Records of that Klass can use its sub-statuses; conversation sub-statuses have no Klass.

### Permissions

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

## Request body

Content type: `application/json` (required)

- `label` (string, required): Display name for the sub-status
- `description` (string): Description of the sub-status
- `parentStatus` (string, required): Parent status (open, done, snoozed) One of: `open`, `done`, `snoozed`.
- `resourceType` (string, required): Resource type (conversation or kobject) One of: `conversation`, `kobject`.
- `default` (boolean): Whether this is the default sub-status for the parent status

Example:

```json
{
  "label": "Waiting for Customer",
  "description": "Conversation is waiting for customer response",
  "parentStatus": "open",
  "resourceType": "conversation",
  "default": false
}
```

## Responses

### 201

Created

- `data` (object)
  - `type` (string)
  - `id` (string)
  - `attributes` (object)
    - `label` (string)
    - `description` (string)
    - `parentStatus` (string)
    - `resourceType` (string)
    - `default` (boolean)
    - `createdAt` (string<date-time>)
    - `updatedAt` (string<date-time>)
  - `links` (object)
    - `self` (string)

```json
{
  "data": {
    "type": "conversation_sub_status",
    "id": "567229511e4a481f0062f7fd",
    "attributes": {
      "label": "Waiting for Customer",
      "description": "Conversation is waiting for customer response",
      "parentStatus": "open",
      "resourceType": "conversation",
      "default": false,
      "createdAt": "2026-03-14T15:09:26.535Z",
      "updatedAt": "2026-03-14T15:09:26.535Z"
    },
    "links": {
      "self": "/v1/sub-statuses/567229511e4a481f0062f7fd"
    }
  }
}
```

### 400

Bad Request

### 401

Unauthorized

### 403

Forbidden

## Example request

```bash
curl --request POST \
  --url 'https://api.kustomerapp.com/v1/sub-statuses' \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "label": "Waiting for Customer",
  "description": "Conversation is waiting for customer response",
  "parentStatus": "open",
  "resourceType": "conversation",
  "default": false
}'
```
