# CTI (CSTA) Overview

> Drive third-party call control from an ISV app over the engine-native ECMA CSTA verb API: one connection, standard verbs, live events.

You want your own application to *drive* calls on the platform. Examples: an
agent workstation, a CRM screen-pop, or an ISV integration. The application can
originate calls, hold and transfer them, set an agent ready or not-ready, or
act as the routing brain that decides where a call goes. That is
**third-party call control**. FirstByte Voice exposes it as an
**engine-native ECMA CSTA verb API**. Your app speaks it over the same
connection as the rest of the SDK.

You do not proxy these actions through the portal, and you do not scrape a UI.
Your app opens a control leg directly to the engine and authenticates once. It
then issues standard CSTA operations, such as `makeCall`, `holdCall`,
`singleStepTransfer`, and `setAgentState`, as short request/reply lines. State
changes come back to you as a live event stream.

## What CSTA is

**ECMA CSTA** (Computer-Supported Telecommunications Applications,
standardized as **ECMA-269**) is the long-established vendor-neutral API for
third-party call control. It defines the operations that an external
application uses to observe and manipulate calls and agents on a telephony
system: make a call, clear a connection, hold, retrieve, transfer, consult,
set agent state, and register as a route point. The application does not own
the media itself.

FirstByte implements those operations as **wire verbs** on a control leg
into the engine. The verb names map one-to-one to CSTA operations. If you
drove CSTA before, the model is familiar. Your app sends a verb and gets a
reply. It separately subscribes to a stream of CSTA events that describe what
happened to the calls and agents that you touched.

> **NOTE:**
> This API is the **engine twin** of the routing engine that the operator
> console uses for its own agent workstation. Both surfaces speak the same
> verbs. The console drives its built-in workstation. This API is for **your**
> external ISV or SDK client. There is no behavioral fork. It is the same call
> control, exposed for third parties.

## When to use it

  - **Third-party call control** — Your backend or app must originate, hold, transfer, or clear live calls
    programmatically. It does not run the AI pipeline; it *controls* the call.
  - **Agent workstations** — You build a custom softphone or agent desktop. You must set agent work
    state (ready / not-ready / after-call-work) and pop calls on screen.
  - **Route point / ACD brain** — Your app registers as a route point and makes the routing decision. It
    receives route requests and selects the destination, ACD-style.
  - **CRM / ISV integration** — You integrate an existing contact-center or CRM product. You want a
    standards-based CTI link, not a proprietary one.

If you instead want the AI runtime to *handle* a call (ASR/LLM/TTS,
speech-to-speech), use the [Runtime](/modalities/voice/runtime/overview), not
CTI. CTI sits above the media plane and moves calls and agents around.

## Connect

The CTI control leg runs on the engine over one of two transports. Both carry
the same length-prefixed, correlated request/reply lines. Pick the one that
fits your client.

  
    Open a secure WebSocket to the engine with the subprotocol **`cti`**. This
    is the simplest client to write. It works from any language with a
    WebSocket library.

    ```
    wss://<workspace-id>.webrtc.firstbyte.fast/    (subprotocol: cti)
    ```
  
  
    Open a WebTransport session to the path **`/cti`**. This is CTI-over-QUIC,
    on the same QUIC engine that carries the rest of the SDK. Use this when
    you already run the transport stack and want the control leg to share it.

    ```
    https://<workspace-id>.webrtc.firstbyte.fast/cti   (WebTransport)
    ```
  

The framing is identical on both transports. Each message is a
length-prefixed line of the form `<verb>\t<args-json>`. Every request carries
a correlation id, so its reply can be matched. **The very first line that you
send must be `auth`.** The connection stays unauthenticated until `auth`
succeeds, and the engine refuses every other verb before that.

  1. **Mint a CTI token**
Ask the control-plane API for a short-lived signed CTI token for the
    tenant, with the scopes that your app needs (see below). Treat it like any
    bearer credential. It is short-lived by design. Refresh and reconnect
    before it expires.
  2. **Open the control leg**
Connect over `cti` (wss) or WebTransport `/cti` as above.
  3. **Authenticate on the first line**
Send `auth` with the token as the first request. On success, the reply
    confirms the connection and the granted scopes. On failure, the engine
    closes the connection.

    ```text First line
    auth	{"jwt":"<cti-token>"}
    ```
  4. **Drive verbs and subscribe to events**
Issue call-control, agent-state, or routing verbs. Subscribe to the CSTA
    event track (below) to observe state changes.

## Auth and scopes

Authorization is a **short-lived signed CTI token** that the control-plane API
mints. It carries a **`scopes`** claim. The claim gates which families of
verbs the connection may use. The engine **refuses** a verb whose scope is not
present in the token. Request only the scopes that your app needs.

- **`call_control`** (`scope`) — The call-control verbs: `makeCall`, `clearConnection`, `holdCall`, `retrieveCall`, `singleStepTransfer`, `consultationCall`, `transferCall`, `cancelConsultation`, `generateDigits`.

- **`agent_state`** (`scope`) — Agent work-state control: `setAgentState` (login / logout / ready / not-ready with an aux reason / after-call-work).

- **`routing`** (`scope`) — Route-point registration: `routeRegister`, to receive route requests and select the destination.

Beyond the scope check, the engine **tenant-checks per-call verbs against the
call** that they name. A token for one tenant cannot act on another tenant's
call, even with the correct scope. Scopes are additive. A token can hold any
combination.

## Events ride a durable track

Verbs are **request/reply**. You send an operation and get an
acknowledgement. The resulting *state changes* arrive separately as **CSTA
events** on a durable **`cti/<tenant>`** track. Your SDK subscribes to that
track directly. Because the track is durable, a client that reconnects can
resume the event stream. It does not miss what happened while it was away.

The event stream reports the CSTA lifecycle of the calls and agents that you
touch:

| Event | Meaning |
| --- | --- |
| `delivered` | A call is offered / ringing at a device. |
| `established` | A connection answered — the parties are talking. |
| `held` / `retrieved` | A connection was placed on hold or taken back. |
| `transferred` | A transfer completed. |
| `cleared` | A connection was released / hung up. |
| `failed` | The operation or call failed. |

The pattern is: **issue a verb, then watch the event track** for the resulting
state. For example, `makeCall` acknowledges immediately. You learn that the
far end rings from `delivered`, and that it answered from `established`.

## The verb families

  - **[Call Control](/modalities/voice/cti/call-control)** — Originate, clear, hold/retrieve, blind and consultative transfer, consult,
    and DTMF: `makeCall`, `holdCall`, `singleStepTransfer`, and the rest.
  - **[Agent State](/modalities/voice/cti/agent-state)** — Log agents in and out. Move them between ready, not-ready (with an aux
    reason), and after-call-work with `setAgentState`.
  - **[Routing & Events](/modalities/voice/cti/routing-events)** — Register as a route point with `routeRegister` and subscribe to the
    `cti/<tenant>` CSTA event track.

> **NOTE:**
> **Client surface today.** CTI is a wire protocol that you speak from any
> WebSocket or WebTransport client. There is no dedicated single-purpose
> polyglot CTI SDK wrapper yet. The verbs, framing, auth, and event track
> documented here are the contract. Build against them directly, or over the
> transport that the rest of the SDK already gives you.

## Related

  - **[High Availability](/modalities/voice/high-availability)** — Run more than one engine node, so the CTI control plane and call routing
    survive a node failure. CTI pairs with the enterprise HA story.
  - **[Deployment Models](/modalities/voice/concepts/deployment-models)** — See where the engine runs (managed cloud or on-prem) and how the control
    leg reaches it.
  - **[On-Prem Quickstart](/modalities/voice/quickstart/on-prem)** — Stand up the engine in your own environment, the common home for an
    enterprise CTI integration.
  - **[Transport to Telephony](/modalities/voice/transport-telephony/overview)** — Learn how calls enter and leave over SIP — the calls that your CTI app
    then controls.
