# CSTA Agent State

> Drive agent work state from your app: login, ready, not-ready with aux reasons, and after-call-work. The ACD then delivers only to agents who can take work.

Tell the platform whether an agent can take the next call. `setAgentState` is
the one CSTA verb that moves an agent through the work states that a contact
center runs on: **logged on**, **ready**, **not-ready** (with a reason),
**after-call-work**, and **logged off**. The distributor offers queued calls
only to agents who are **ready**. This verb therefore gates delivery. Drive it
from your own agent desktop, a workforce-management integration, or a
supervisor tool over the engine-native CSTA control leg. That is the same
connection that your app already uses for
[call control](/modalities/voice/cti/call-control) and
[routing](/modalities/voice/cti/routing-events).

This is the ECMA CSTA (ECMA-269) *Set Agent State* operation, exposed as the
wire verb `setAgentState`. It needs the **`agent_state`** scope on your CTI
token. The engine refuses the verb for a token without that scope. See
[CTI Overview](/modalities/voice/cti/overview) for how to connect and mint a
scoped token.

## The agent workstation model

An **agent** is a routable identity (a skill member) that the ACD can offer
calls to. A **workstation** is the endpoint that the agent answers on: a
browser softphone or a real SIP deskphone. `login` associates the two. It puts
the agent on shift at a workstation and makes them eligible for the skills
that they belong to. Everything after that is a work-state change on that
logged-on agent.

The distributor keeps a live view of every agent's state. It picks only from
the **ready** pool. When you move an agent out of `ready`, the distributor
removes them from selection immediately. When you move them back in, they
become a candidate on the next pass. That is the whole contract between this
verb and [the built-in ACD](/modalities/voice/transport-telephony/pbx-acd).

| Wire `state`       | CSTA agent mode        | Offered ACD work? | When you set it                                         |
|--------------------|------------------------|-------------------|---------------------------------------------------------|
| `login`            | Logged On              | After → `ready`   | Agent starts a shift and binds to a workstation         |
| `ready`            | Ready / Available      | **Yes**           | Agent is available for the next queued call             |
| `not-ready`        | Not Ready (+ aux)      | No                | Break, lunch, training, coaching — carry an `auxReason` |
| `after-call-work`  | Work Not Ready (ACW)   | No                | Wrap-up (disposition, notes) after a call clears        |
| `logout`           | Logged Off             | No                | End of shift — removed from all skills                  |

> **NOTE:**
> `login` makes the agent eligible. It does **not** by itself start call
> delivery. Set `ready` explicitly when the agent is actually at the keyboard.
> This mirrors real ACD ergonomics: "logged on" and "available" are distinct.
> An agent can log in, read handover notes, and only then go ready.

## `setAgentState`

- **`agentId`** (`string`, required) — The agent identity to move. This is the ACD agent id (a skill member), not   the workstation or SIP AOR. The engine tenant-checks it against your token's   workspace.

- **`state`** (`enum`, required) — One of `login`, `logout`, `ready`, `not-ready`, `after-call-work`.

- **`auxReason`** (`string`) — A reason code for **`not-ready`** (e.g. `lunch`, `break`, `training`,   `meeting`). Free-form and reporting-only. It does not change routing. It   labels *why* the agent is unavailable, so supervisors and wallboards can   break down idle time. The engine ignores it for the other states.

Every verb is request/reply and correlated. You get an acknowledgement for
each call, or a typed error (wrong scope, unknown agent, agent not logged
on). State transitions that the distributor observes also appear as CSTA
events on the `cti` event track. Subscribe to it if you build a supervisor
view (see [Routing & Events](/modalities/voice/cti/routing-events)).

## Move an agent through a shift

The control leg speaks length-prefixed, tab-separated `<verb>\t<args-json>`
lines. The first line authenticates with your scoped CTI token. You can send
those frames directly, or use the CTI client from your SDK, which wraps them.

  1. **Log the agent on at their workstation**
Bind the agent to the endpoint that they will answer on. After this, they
    belong to their skills, but the distributor does not yet offer them calls.
  2. **Go ready**
Put the agent into the delivery pool. The distributor can now offer them
    the next queued call for one of their skills.
  3. **Step away with a reason**
On a break, set `not-ready` with an `auxReason`. The distributor stops
    call offers. The reason code drives idle-time reporting.
  4. **Wrap up after a call**
When a call clears, set `after-call-work`. The agent can then disposition
    the call, and no new call lands mid-wrap-up. Return to `ready` when done.
  5. **Log off at end of shift**
`logout` removes the agent from every skill and frees the workstation.

**Wire (cti control leg):**
```text
# Each line: <verb>\t<args-json>, length-prefixed, reply correlated by request.
setAgentState	{"agentId":"agent-1007","state":"login"}
setAgentState	{"agentId":"agent-1007","state":"ready"}
setAgentState	{"agentId":"agent-1007","state":"not-ready","auxReason":"lunch"}
setAgentState	{"agentId":"agent-1007","state":"after-call-work"}
setAgentState	{"agentId":"agent-1007","state":"ready"}
setAgentState	{"agentId":"agent-1007","state":"logout"}
```

**TypeScript:**
```ts
import { Cti } from "clutchcall_connect/cti";

// `token` carries the `agent_state` scope (see the CTI Overview).
const cti = await Cti.connect({
  url: "wss://<workspace-id>.webrtc.firstbyte.fast/cti",
  token,
});

const agentId = "agent-1007";

await cti.setAgentState({ agentId, state: "login" });
await cti.setAgentState({ agentId, state: "ready" });

// Break — carry a reason for reporting.
await cti.setAgentState({ agentId, state: "not-ready", auxReason: "lunch" });

// A call just cleared: wrap up, then go ready again.
await cti.setAgentState({ agentId, state: "after-call-work" });
await cti.setAgentState({ agentId, state: "ready" });

await cti.setAgentState({ agentId, state: "logout" });
```

**Python:**
```python
from clutchcall-agents.cti import Cti

cti = Cti.connect(
    url="wss://<workspace-id>.webrtc.firstbyte.fast/cti",
    token=token,  # scope: agent_state
)

agent_id = "agent-1007"

cti.set_agent_state(agent_id=agent_id, state="login")
cti.set_agent_state(agent_id=agent_id, state="ready")

cti.set_agent_state(agent_id=agent_id, state="not-ready", aux_reason="lunch")

cti.set_agent_state(agent_id=agent_id, state="after-call-work")
cti.set_agent_state(agent_id=agent_id, state="ready")

cti.set_agent_state(agent_id=agent_id, state="logout")
```

## How state drives delivery

When a call queues to a skill, the distributor picks from that skill's
**ready** members. It uses the skill's algorithm: expected-agent-delay /
most-idle-agent, round-robin, and the other Avaya-style options described in
[PBX & ACD](/modalities/voice/transport-telephony/pbx-acd). An agent in
`not-ready`, `after-call-work`, or `logout` is never a candidate. The
distributor does not offer a second call to an agent who is already on one.
If a picked agent does not accept in time, the offer re-queues (RONA), and
the picker moves on. Honest state is what keeps queues flowing.

> **NOTE:**
> The platform does not force agents into `after-call-work` for you. Your
> desktop sets it explicitly after a call clears. If you want automatic
> wrap-up, set `after-call-work` from your `cleared` event handler on the
> [CSTA event track](/modalities/voice/cti/routing-events). Then transition
> back to `ready` when the agent finishes.

> **NOTE:**
> **Two surfaces, one model.** This verb is the engine twin of the state control
> built into the Contact Center console at `agent.firstbyte.fast`. The console
> drives its own agents through exactly these states. Use the CSTA verb when an
> **external** app (your CRM desktop, a WFM tool, an ISV integration) owns the
> agent's presence instead. The console and the verb API move the same agents
> through the same distributor. See
> [Contact-center platforms](/modalities/voice/build/contact-center-platforms).

> **TIP:**
> The same ready / not-ready model gates **text** work too. The system routes
> chats from the embedded chat widget (`<clutchcall-chat>`) to a human
> agent only while that agent is `ready`. One ready agent can carry several
> concurrent chats. A single presence state therefore governs an agent across
> voice and chat.

## Honest status

> **NOTE:**
> - **`setAgentState` over the CSTA control leg** — shipped, with `agent_state`
>   scope enforcement. It is verified to move agents across nodes in a
>   multi-node deployment.
> - **`login` / `ready` / `not-ready` / `after-call-work` / `logout`** — all
>   drive the distributor's ready pool as described.
> - **`auxReason`** — accepted and carried into reporting. It is a label, not a
>   routing input. A reason code never changes *which* agent is picked.
> - **Automatic ACW entry on call clear** — not automatic. Set it from your
>   `cleared` handler if you want it (see the note above).

## Related

  - **[CTI Overview](/modalities/voice/cti/overview)** — Connect the control leg, mint a scoped CTI token, and read the framing.
  - **[Routing & Events](/modalities/voice/cti/routing-events)** — Register as a route point and subscribe to the CSTA event track.
  - **[Call Control](/modalities/voice/cti/call-control)** — Make, hold, transfer, consult, DTMF, and clear the calls that agents
    handle.
  - **[PBX & ACD](/modalities/voice/transport-telephony/pbx-acd)** — Learn about skills, queues, and how the distributor picks from the ready
    pool.
  - **[Contact-center platforms](/modalities/voice/build/contact-center-platforms)** — Build a full agent desktop on top of the CSTA verbs.
  - **[High availability](/modalities/voice/high-availability)** — Keep agent state and routing alive across node failures.
