# Get work session by ID

> GET /v1/routing/work-sessions/{id} · Queues and routing API

Source: https://developer.kustomer.com/api/queues-and-routing/get-work-session-by-id

```http
GET https://{orgname}.api.kustomerapp.com/v1/routing/work-sessions/{id}
Authorization: Bearer <API key>
```

## Authorization

Requires any one of these roles: `org.permission.work_session.read`, `org.permission.routing.read`; or legacy `org.user.routing.read`. The API key or user must hold at least one of them. Legacy roles predate permission sets and still work.

Retrieves work sessions based on its unique ID.

## About Work sessions

A work session is one agent's shift in routing, from the moment they first set a routing status until they go offline. It tracks their status, how much capacity they have left, the work items they hold, and how long they spent in each status.

Routing decides which agent gets each conversation, and it only considers agents who have a live work session. A session is created the first time an agent sets a routing status, usually by signing in to routing in the agent desktop. It ends when they switch to a status of type `offline`: Kustomer sets `signedOutAt`, `active` becomes `false`, and the session is read-only from then on. The next status change starts a new session, so each user has at most one current session and a history of past ones.

#### Status and routability

Every session points to a [routing user status](https://developer.kustomer.com/api/queues-and-routing#routing-user-statuses), such as "Available", "Lunch" or "Training". Admins define those statuses; each one has a `statusType`, and that type is what routing acts on:

| `statusType` | Meaning |
| --- | --- |
| `available` | Ready for new work. The only type that can receive routed items. |
| `busy` | Set by the system while an agent is on a call or in wrap-up. Counts toward queue staffing, but receives nothing new. |
| `unavailable` | Signed in but not taking work, such as on a break. |
| `offline` | Signed out. Setting it ends the session. |

`routable` is `true` only while all three hold: the status type is `available`, there is capacity left, and no item is waiting for the agent to accept it.

#### Capacity

Capacity is how much work an agent can hold at once, set in [team routing settings](https://developer.kustomer.com/api/queues-and-routing#team-routing-settings). Each assigned [work item](https://developer.kustomer.com/api/queues-and-routing#work-items) uses capacity equal to its `itemSize`, and a paused item uses none. `totalCapacity` and `capacityRemaining` give the overall picture, and `capacity` breaks it down by work item priority as `{ priority, limit, remaining }`. `capacityStatus` summarizes it as `idle`, `not-at-capacity` or `at-capacity`.

#### Time tracking

Sessions are the source for agent utilization reporting. `totalAvailable`, `totalUnavailable` and `totalBusy` each hold `time` (milliseconds spent in that status type), `businessTime` (the same, counted only within the team's business hours) and `statusAt` (when the agent last entered it). The `…IdleCapacity`, `…NotAtCapacity` and `…AtCapacity` variants split that time by how loaded the agent was, and `totalTimeByStatus` breaks it down by individual status ID. `handledItemCount` and `handledConversationCount` count the work completed in the session.

#### Which endpoint to use

| To | Call |
| --- | --- |
| Read or change the signed-in user's own session | [`GET`](https://developer.kustomer.com/api/queues-and-routing/get-current-work-session) or [`PUT /v1/routing/work-sessions/current`](https://developer.kustomer.com/api/queues-and-routing/update-current-work-session) |
| See whether a specific agent is working right now | [`GET /v1/routing/users/{id}/work-sessions/current`](https://developer.kustomer.com/api/queues-and-routing/get-current-work-session-by-user-id) |
| Report on an agent's past shifts | [`GET /v1/routing/users/{id}/work-sessions`](https://developer.kustomer.com/api/queues-and-routing/get-work-sessions-by-user-id) |
| Find who is staffing a queue | [`GET /v1/routing/queues/{id}/work-sessions`](https://developer.kustomer.com/api/queues-and-routing/get-work-sessions-by-queue-id), with `routable=true` for agents who can take work now |
| List sessions across the org | [`GET /v1/routing/work-sessions`](https://developer.kustomer.com/api/queues-and-routing/get-work-sessions), with `active=true` for current sessions only |
| Read one session, past or current | [`GET /v1/routing/work-sessions/{id}`](https://developer.kustomer.com/api/queues-and-routing/get-work-session-by-id) |
| See what an agent is holding right now | [`GET /v1/routing/work-sessions/current/work-items`](https://developer.kustomer.com/api/queues-and-routing/get-work-items-in-current-work-session) for yourself, or [`GET /v1/routing/work-sessions/{id}/work-items`](https://developer.kustomer.com/api/queues-and-routing/get-work-items-by-work-session) for any session |
| Change another agent's status, as a supervisor | [`PUT /v1/routing/work-sessions/{id}`](https://developer.kustomer.com/api/queues-and-routing/update-work-session-by-id) |

To change a status, send the routing user status ID as `status`. Sending `team` moves the agent to that team's queues and capacity rules. `PUT /v1/routing/work-sessions/{id}` also accepts `rev`, the revision you last read; if the session changed since then, the request fails with `409 Conflict`, so read it again and retry. Updating a session that has already ended fails with `400`.

Related: [Routing user statuses](https://developer.kustomer.com/api/queues-and-routing#routing-user-statuses), [Work items](https://developer.kustomer.com/api/queues-and-routing#work-items), [Team routing settings](https://developer.kustomer.com/api/queues-and-routing#team-routing-settings)

### Recipes

#### 1. See who can take work in a queue right now

List the agents who are signed in to a queue, available and under capacity, for example to show live staffing on a wallboard.

**Step 1.** [`GET /v1/routing/queues/{id}/work-sessions`](https://developer.kustomer.com/api/queues-and-routing/get-work-sessions-by-queue-id.md). Ask for the queue's sessions with `routable=true`. Only sessions whose status type is `available`, with capacity left and nothing waiting to be accepted, come back. Each session's `relationships.user` is the agent, and `capacityRemaining` says how much more they can take.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/routing/queues/$QUEUE_ID/work-sessions?routable=true&pageSize=100" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/routing/queues/$QUEUE_ID/work-sessions --query routable=true --all \
  | jq -r '[.data.relationships.user.data.id, .data.attributes.capacityRemaining] | @tsv'
```

**Step 2.** [`GET /v1/users/{id}`](https://developer.kustomer.com/api/access-management/get-user-by-id.md). Sessions carry user IDs, not names. Look each user up once and cache the result, because names rarely change.

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

```bash title="kusty"
kusty api GET /v1/users/$USER_ID --fields attributes.displayName
```

> Drop `routable=true` and pass `statusType=busy` or `statusType=unavailable` to count agents who are signed in to the queue but can't take new work.

#### 2. Put an agent on a break as a supervisor

Change another agent's routing status, for example from a workforce-management tool, without overwriting a change they just made themselves.

**Step 1.** [`GET /v1/routing/statuses`](https://developer.kustomer.com/api/queues-and-routing/get-routing-user-statuses.md). Find the ID of the status to set. Statuses are defined per org, so match on `name` and check that its `statusType` is `unavailable`. Names are stored in lowercase, so compare against "break", not "Break".

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/routing/statuses?pageSize=100" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  | jq -r '.data[] | select(.attributes.name == "break" and .attributes.statusType == "unavailable") | .id'
```

```bash title="kusty"
kusty api GET /v1/routing/statuses --all \
  | jq -r 'select(.data.attributes.name == "break" and .data.attributes.statusType == "unavailable") | .data.id'
```

**Step 2.** [`GET /v1/routing/users/{id}/work-sessions/current`](https://developer.kustomer.com/api/queues-and-routing/get-current-work-session-by-user-id.md). Read the agent's current session for its `id` and `rev`. A `404` means the agent isn't signed in to routing, so there is nothing to change.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/routing/users/$USER_ID/work-sessions/current" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/routing/users/$USER_ID/work-sessions/current --fields id,attributes.rev
```

**Step 3.** [`PUT /v1/routing/work-sessions/{id}`](https://developer.kustomer.com/api/queues-and-routing/update-work-session-by-id.md). Send the new `status` with the `rev` you read. If the agent changed their own status in between, the API answers `409 Conflict`; read the session again and decide whether to retry.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/routing/work-sessions/$SESSION_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"status\": \"$BREAK_STATUS_ID\", \"rev\": $REV}"
```

```bash title="kusty"
kusty api PUT /v1/routing/work-sessions/$SESSION_ID \
  --field status=$BREAK_STATUS_ID --field rev:=$REV
```

#### 3. Total an agent's available time across past shifts

Add up how long an agent was available during business hours, the number behind utilization reports.

**Step 1.** [`GET /v1/routing/users/{id}/work-sessions`](https://developer.kustomer.com/api/queues-and-routing/get-work-sessions-by-user-id.md). Page through the agent's sessions. Each one is a shift; `signedInAt` and `signedOutAt` bound it, and the current shift has no `signedOutAt` yet.

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

```bash title="kusty"
kusty api GET /v1/routing/users/$USER_ID/work-sessions --all --output jsonl > sessions.jsonl
```

**Step 2.** Sum `totalAvailable.businessTime` over the sessions in your date range. It's in milliseconds and counts only time inside the team's business hours; use `totalAvailable.time` for wall-clock time.

```bash title="kusty"
jq -s 'map(.data.attributes.totalAvailable.businessTime // 0) | add / 3600000' sessions.jsonl
```

### Example object

A real record from [`GET /v1/routing/work-sessions/{id}`](https://developer.kustomer.com/api/queues-and-routing/get-work-session-by-id.md):

```json
{
  "id": "5deb98ac714a13011af86f56",
  "type": "work_session",
  "attributes": {
    "routable": false,
    "statusType": "unavailable",
    "workItemCount": 0,
    "pausedWorkItemCount": 0,
    "signedInAt": "2019-12-07T12:18:52.495Z",
    "capacity": [
      {
        "remaining": 10,
        "limit": 10,
        "priority": 1
      },
      {
        "remaining": 2,
        "limit": 2,
        "priority": 10
      }
    ],
    "capacityRemaining": 10,
    "totalCapacity": 10,
    "handledItemCount": 0,
    "handledConversationCount": 0,
    "totalAvailable": {
      "statusAt": "2019-12-07T12:18:52.495Z",
      "businessTime": 79,
      "time": 79
    },
    "totalAvailableIdleCapacity": {
      "businessTime": 79,
      "time": 79
    },
    "totalUnavailable": {
      "statusAt": "2019-12-07T12:18:52.574Z"
    },
    "totalTimeByStatus": {
      "5deb98ab714a13011af86f1f": {
        "statusAt": "2019-12-07T12:18:52.495Z",
        "businessTime": 79,
        "time": 79
      },
      "5deb98ab714a13011af86f22": {
        "statusAt": "2019-12-07T12:18:52.574Z"
      }
    },
    "capacityStatus": "idle",
    "updatedAt": "2019-12-07T12:18:52.574Z",
    "modifiedAt": "2019-12-07T12:18:52.574Z",
    "createdAt": "2019-12-07T12:18:52.495Z",
    "rev": 4,
    "lastRevision": {
      "handledItemCount": 0,
      "totalAvailable": {
        "statusAt": "2019-12-07T12:18:52.544Z"
      }
    }
  },
  "relationships": {
    "org": {
      "data": {
        "type": "org",
        "id": "55d7998f2d3f9f8800099aed"
      },
      "links": {
        "self": "/v1/orgs/55d7998f2d3f9f8800099aed"
      }
    },
    "user": {
      "data": {
        "type": "user",
        "id": "5dd7998f2d3f9f8800099aed"
      },
      "links": {
        "self": "/v1/users/5dd7998f2d3f9f8800099aed"
      }
    },
    "team": {
      "data": {
        "type": "team",
        "id": "55d79fff2d3f9f8800099aff"
      },
      "links": {
        "self": "/v1/teams/55d79fff2d3f9f8800099aff"
      }
    },
    "routingSettings": {
      "data": {
        "type": "team_routing_settings",
        "id": "5deb98ab714a13011af86f29"
      },
      "links": {
        "self": "/v1/routing/settings/5deb98ab714a13011af86f29"
      }
    },
    "status": {
      "data": {
        "type": "status",
        "id": "5deb98ab714a13011af86f22"
      },
      "links": {
        "self": "/v1/routing/statuses/5deb98ab714a13011af86f22"
      }
    },
    "modifiedBy": {
      "data": {
        "type": "user",
        "id": "5dd7998f2d3f9f8800099aed"
      },
      "links": {
        "self": "/v1/users/5dd7998f2d3f9f8800099aed"
      }
    },
    "queues": {
      "data": [
        {
          "type": "queue",
          "id": "5deb98ab714a13011af86f25"
        }
      ]
    }
  },
  "links": {
    "self": "/v1/routing/work-sessions/5deb98ac714a13011af86f56"
  }
}
```

Fields:

- `attributes.routable` (boolean): `true` only while the status type is `available`, capacity is left, and no item is waiting for the agent to accept it. Routing offers new work only to routable sessions.
- `attributes.statusType` (string): The type of the current routing status, which is what routing acts on.
- `attributes.workItemCount` (integer): Work items the agent holds right now, including paused ones.
- `attributes.pausedWorkItemCount` (integer): Held items that are paused. Paused items use no capacity.
- `attributes.signedInAt` (string): When the session started, the first time the agent set a routing status. Ended sessions also have `signedOutAt`.
- `attributes.capacity` (array): Capacity broken down by work item priority. Each entry is `{ priority, limit, remaining }`.
- `attributes.capacity[].remaining` (integer): How much more work of this priority the agent can take. Each item uses its `itemSize`.
- `attributes.capacityRemaining` (integer): Overall capacity left. When it reaches `0`, the session stops being routable.
- `attributes.totalCapacity` (integer): Overall capacity, from the team's routing settings.
- `attributes.handledItemCount` (integer): Work items completed in this session.
- `attributes.totalAvailable` (object): Time spent in `available` statuses. `time` is milliseconds; `businessTime` counts only the team's business hours; `statusAt` is when the agent last became available.
- `attributes.totalTimeByStatus` (object): The same time totals keyed by routing status ID, for reports by individual status such as "Lunch".
- `attributes.capacityStatus` (string): A one-word summary of load: `idle`, `not-at-capacity` or `at-capacity`.
- `attributes.rev` (integer): The revision. Send it back on `PUT` to fail with `409 Conflict` if the session changed since you read it.
- `relationships.user` (object): The agent this session belongs to.
- `relationships.team` (object): The team whose queues and capacity rules apply. Change it by sending `team` on `PUT`.
- `relationships.status` (object): The current [routing user status](https://developer.kustomer.com/api/queues-and-routing#routing-user-statuses). Change it by sending `status` on `PUT`.
- `relationships.queues` (object): The queues the agent is signed in to and can receive work from.

### Relationships

- Points to **Users** via `relationships.user`
- Points to **Teams** via `relationships.team`
- Points to **Team routing settings** via `relationships.routingSettings`
- Points to **Routing user statuses** via `relationships.status`
- Points to **Queues** via `relationships.queues` (list)
- Referenced by **Work items** via `relationships.workSession`: The work session holding this work item. While the item is assigned and not paused, it uses `itemSize` of the session's capacity.
- Referenced by **Work items** via `relationships.routedToSession`: The work session this work item was last offered to.

### Permissions

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

## Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Work Session ID |

## Responses

### 200

OK

- `data` (object, required)
  - `id` (string, required)
  - `type` (string, required)
  - `attributes` (object, required)
    - `routable` (boolean, required)
    - `statusType` (string, required)
    - `workItemCount` (integer, required)
    - `pausedWorkItemCount` (integer, required)
    - `signedInAt` (string, required)
    - `capacity` (array<object>, required)
    - `capacityRemaining` (integer, required)
    - `totalCapacity` (integer, required)
    - `handledItemCount` (integer, required)
    - `handledConversationCount` (integer, required)
    - `totalAvailable` (object, required)
    - `totalAvailableIdleCapacity` (object, required)
    - `totalUnavailable` (object, required)
    - `totalTimeByStatus` (object, required)
    - `capacityStatus` (string, required)
    - `updatedAt` (string, required)
    - `modifiedAt` (string, required)
    - `createdAt` (string, required)
    - `rev` (integer, required)
    - `lastRevision` (object, required)
  - `relationships` (object, required)
    - `org` (object, required)
    - `user` (object, required)
    - `team` (object, required)
    - `routingSettings` (object, required)
    - `status` (object, required)
    - `modifiedBy` (object, required)
    - `queues` (object, required)
  - `links` (object, required)
    - `self` (string, required)

```json
{
  "data": {
    "id": "5deb98ac714a13011af86f56",
    "type": "work_session",
    "attributes": {
      "routable": false,
      "statusType": "unavailable",
      "workItemCount": 0,
      "pausedWorkItemCount": 0,
      "signedInAt": "2019-12-07T12:18:52.495Z",
      "capacity": [
        {
          "remaining": 10,
          "limit": 10,
          "priority": 1
        },
        {
          "remaining": 2,
          "limit": 2,
          "priority": 10
        }
      ],
      "capacityRemaining": 10,
      "totalCapacity": 10,
      "handledItemCount": 0,
      "handledConversationCount": 0,
      "totalAvailable": {
        "statusAt": "2019-12-07T12:18:52.495Z",
        "businessTime": 79,
        "time": 79
      },
      "totalAvailableIdleCapacity": {
        "businessTime": 79,
        "time": 79
      },
      "totalUnavailable": {
        "statusAt": "2019-12-07T12:18:52.574Z"
      },
      "totalTimeByStatus": {
        "5deb98ab714a13011af86f1f": {
          "statusAt": "2019-12-07T12:18:52.495Z",
          "businessTime": 79,
          "time": 79
        },
        "5deb98ab714a13011af86f22": {
          "statusAt": "2019-12-07T12:18:52.574Z"
        }
      },
      "capacityStatus": "idle",
      "updatedAt": "2019-12-07T12:18:52.574Z",
      "modifiedAt": "2019-12-07T12:18:52.574Z",
      "createdAt": "2019-12-07T12:18:52.495Z",
      "rev": 4,
      "lastRevision": {
        "handledItemCount": 0,
        "totalAvailable": {
          "statusAt": "2019-12-07T12:18:52.544Z"
        }
      }
    },
    "relationships": {
      "org": {
        "data": {
          "type": "org",
          "id": "55d7998f2d3f9f8800099aed"
        },
        "links": {
          "self": "/v1/orgs/55d7998f2d3f9f8800099aed"
        }
      },
      "user": {
        "data": {
          "type": "user",
          "id": "5dd7998f2d3f9f8800099aed"
        },
        "links": {
          "self": "/v1/users/5dd7998f2d3f9f8800099aed"
        }
      },
      "team": {
        "data": {
          "type": "team",
          "id": "55d79fff2d3f9f8800099aff"
        },
        "links": {
          "self": "/v1/teams/55d79fff2d3f9f8800099aff"
        }
      },
      "routingSettings": {
        "data": {
          "type": "team_routing_settings",
          "id": "5deb98ab714a13011af86f29"
        },
        "links": {
          "self": "/v1/routing/settings/5deb98ab714a13011af86f29"
        }
      },
      "status": {
        "data": {
          "type": "status",
          "id": "5deb98ab714a13011af86f22"
        },
        "links": {
          "self": "/v1/routing/statuses/5deb98ab714a13011af86f22"
        }
      },
      "modifiedBy": {
        "data": {
          "type": "user",
          "id": "5dd7998f2d3f9f8800099aed"
        },
        "links": {
          "self": "/v1/users/5dd7998f2d3f9f8800099aed"
        }
      },
      "queues": {
        "data": [
          {
            "type": "queue",
            "id": "5deb98ab714a13011af86f25"
          }
        ]
      }
    },
    "links": {
      "self": "/v1/routing/work-sessions/5deb98ac714a13011af86f56"
    }
  }
}
```

### 401

Unauthorized

### 403

Forbidden

### 404

Not Found

## Example request

```bash
curl --request GET \
  --url 'https://api.kustomerapp.com/v1/routing/work-sessions/5f184e74c61792001af42e49' \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```
