> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trychert.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bring Your Own Room

> You host the room; Chert joins it as the FaceTime gateway.

Your server creates a LiveKit room, runs your agent, and mints a separate participant token for Chert's FaceTime bridge. Your agent joins your own LiveKit project; Chert does not supply a hosted room for BYOR. To initiate a call from your backend, use the [Outbound guide](/facetime/outbound).

## Connect your own agent to a Chert-operated line

A **Chert-operated line** means Chert runs the FaceTime endpoint and media bridge. It does not require you to use a **Chert-managed Assistant**. If you already have a coaching agent, use BYOR: keep your agent and coaching logic in your own LiveKit project.

| Resource                                 | Who supplies it?                                                |
| ---------------------------------------- | --------------------------------------------------------------- |
| Provisioned FaceTime handle and bridge   | Chert                                                           |
| LiveKit project, room, and agent runtime | You                                                             |
| Agent participant token                  | Your backend, for your agent                                    |
| Separate bridge participant token        | Your backend, returned to Chert for this call                   |
| Caller audio and video tracks            | Chert's bridge publishes them into your room                    |
| Coaching, vision, and responses          | Your agent consumes the caller media and publishes its response |

You do not need to install or patch the open-source connector to use the Chert-operated service. These pages describe that service's BYOR contract; the open-source connector may have a different setup and feature set.

<Note>
  **No standing credential exchange**

  Keep your LiveKit API key and secret on your backend. Return only a short-lived, room-scoped bridge token. Chert seals these credentials for redemption by the authorized worker command and scrubs them on terminal cleanup.
</Note>

<a id="setup" />

## Set up inbound calls

1. Obtain a Chert project and provisioned FaceTime line. Confirm its worker is accepting calls with fresh readiness.
2. In **Workers & Lines → Integration**, configure your HTTPS webhook and assign the integration. Signing is optional and new integrations default to unsigned; enable signing and save the one-time secret on your backend for authenticated delivery.
3. Use `customer_decides` with no default Assistant. Assigning a published Assistant can select the managed route instead.
4. Prepare your LiveKit room and agent before the decision deadline. When signing is enabled, verify webhook signatures over the raw request body.

<a id="media" />

## Audio and video

For enabled inbound video calls, Chert bridges audio and video in both directions. Outbound currently supports audio only. Your agent receives the caller's microphone and camera as LiveKit tracks, and the caller receives your agent's audio and video in FaceTime.

| Direction                 | LiveKit media                        |
| ------------------------- | ------------------------------------ |
| Caller audio → your agent | Audio track: `facetime-remote-audio` |
| Caller video → your agent | Video track: `facetime-remote-video` |
| Your agent audio → caller | Your agent's published audio track   |
| Your agent video → caller | Your agent's published video track   |

For video calls, request `media: { audio: true, video: true }` and subscribe to both tracks from the bridge participant. Publish your agent's audio and video into the same room. Audio-only calls use `video: false` and do not publish caller video. A caller who turns off their camera will not provide live camera frames until they turn it back on.

<a id="token" />

## Token requirements

* Use a secure `wss://` LiveKit URL with no embedded credentials or fragment, and the default port or port 443.
* Mint a signed LiveKit JWT with a nonempty `sub` identifying the bridge, distinct from your agent's identity.
* Include integer Unix-second `nbf` and `exp` claims. The token must already be valid, unexpired, and have `exp - nbf ≤ 7200` seconds.
* Set `video.roomJoin`, `video.canPublish`, and `video.canSubscribe` to `true`, scoped to exactly one room through `video.room`.
* Supply a nonempty `remote_participant_identity` distinct from the bridge identity. This is required input, but the current worker selects the sole eligible audio publisher rather than enforcing an exact identity match.

Use a fresh bridge token for each call. Single-use redemption refers to Chert's credential handoff; a LiveKit JWT is not inherently a one-use token. Never log the token or include it in browser-facing call records.

<a id="inbound" />

## Inbound flow

1. FaceTime rings and Chert sends a `call.incoming` event, signed when your integration enables signing.
2. Verify the signature when enabled and respond with HTTP 2xx and an accept or decline body within the decision deadline (five seconds by default).
3. On acceptance, Chert authorizes the call and joins your room with the bridge token. Your agent must publish usable audio for the audio-ready gate.
4. Monitor call lifecycle events and release your own agent resources when the call ends or fails.

Example incoming event; IDs below are illustrative. Call information is nested under `data`.

```json theme={null}
{
  "event_id": "2e3f6bb2-567c-4ce9-94b6-2bdcc0538001",
  "type": "call.incoming",
  "created_at": "2026-09-19T19:00:00Z",
  "data": {
    "call_id": "c7161775-cd89-44e4-b1b1-93cc9c346b21",
    "line_id": "a74e0182-9e14-4f0d-a202-d97cd31f8412",
    "media": { "audio": true, "video": true },
    "from_handle_hash": null,
    "occurred_at": "2026-09-19T19:00:00Z"
  }
}
```

Accept with call-scoped credentials:

```json theme={null}
{
  "action": "accept",
  "livekit_url": "wss://your-project.livekit.cloud",
  "participant_token": "<fresh bridge JWT>",
  "remote_participant_identity": "your-agent"
}
```

To decline, return `{ "action": "decline" }`. Missing, late, or invalid decisions fail closed. A saved webhook URL alone does not prove the receiver works.

<a id="tracks" />

## Participants and tracks

Give the bridge a unique identity through the token's `sub`. Subscribe to that participant's published tracks using the LiveKit SDK. Do not assume an identity such as `facetime-caller` is assigned automatically.

The bridge publishes caller audio as `facetime-remote-audio` and caller video as `facetime-remote-video`. Subscribe to both tracks to hear and see the caller. Handle track publication, subscription, mute, and unpublish events, and inspect track kind rather than relying only on names.

For agent-to-caller media, the current worker requires exactly one eligible remote participant with one unmuted published audio track. Silent auxiliary participants may be present. Avoid duplicate audio publishers, including an agent voice track alongside a second avatar voice track. Video-only publication does not satisfy audio readiness.

### Example room and participant layout

For one demo call, your backend can create a room named `coaching-demo-001` and use this layout. The names are examples that you choose, not fixed Chert identities.

| Participant           | Identity     | Publishes                                           | Subscribes to          |
| --------------------- | ------------ | --------------------------------------------------- | ---------------------- |
| Your coaching agent   | `coach-001`  | Agent audio and video                               | Caller audio and video |
| Chert FaceTime bridge | `bridge-001` | `facetime-remote-audio` and `facetime-remote-video` | Agent audio and video  |

Mint two distinct tokens scoped to this room. Connect your agent using its own token. Mint Chert's bridge token with `sub: "bridge-001"` and the grants above, then return:

```json theme={null}
{
  "action": "accept",
  "livekit_url": "wss://your-project.livekit.cloud",
  "participant_token": "<JWT for bridge-001 in coaching-demo-001>",
  "remote_participant_identity": "coach-001"
}
```

In your agent, register track handlers before joining and enable subscriptions to both audio and video. Identify the caller participant by the bridge identity you chose. Feed its audio into your speech pipeline and its video frames into your vision or pose-analysis pipeline. Receiving a video track does not automatically enable vision in an audio-only agent; configure your agent to consume those frames. See LiveKit's [track subscription guide](https://docs.livekit.io/transport/media/subscribe/) and [raw media processing guide](https://docs.livekit.io/transport/media/raw-tracks/) for SDK examples.

Use a separate room and agent session for each simultaneous call so teams' media and coaching state remain isolated. A shared LiveKit project can contain those separate rooms.

## Concurrent demos

Each provisioned FaceTime line handles one active call. Several teams can reuse one line sequentially; simultaneous demos require separately provisioned, ready lines and enough capacity in your agent runtime. Confirm the number of assigned lines and their handles with Chert before publishing the demo schedule. See [capacity planning](/facetime/limits#capacity).

<a id="cleanup" />

## Ending and cleanup

Either party may hang up. To request an end through the API, send `POST /api/v1/calls/{id}/end` with project authentication. Chert releases the bridge and scrubs its stored room credentials during terminal cleanup. Call records and operational events can remain; token expiry does not mean all records disappear. Your service owns cleanup of its agent, dispatch, and room.
