Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 137 additions & 1 deletion livekit-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,140 @@

The official server API crate for [LiveKit](https://livekit.com).

Use this crate to generate access tokens and invoke LiveKit server APIs for agent dispatch, ingress, egress, SIP, and more.
Use this crate to generate access tokens and invoke LiveKit server APIs for rooms, egress, ingress, SIP, agent dispatch, and more.
Comment on lines 2 to +5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Missing changeset file required by repository rules

No changeset file was added for this PR (livekit-api/README.md change), violating the repository's mandatory changeset requirement.

Impact: The PR cannot be properly tracked for release notes and version bumping.

Changeset requirement from AGENTS.md

AGENTS.md states: "Every PR needs a changeset" and "Changeset must list any crates which need to be bumped stemming from the change." Since the README is referenced in livekit-api/Cargo.toml:8 via readme = "README.md", it is part of the published crate and a changeset documenting at least a patch bump for livekit-api should be included. Changesets are created via knope document-change or manually in /.changeset.

(Refers to lines 1-5)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


## Server API

`LiveKitApi` is a single entry point to every server API, exposing each service through an accessor (`room()`, `egress()`, `ingress()`, `sip()`, `agent_dispatch()`, `connector()`).

```rust,no_run
use livekit_api::services::LiveKitApi;
use livekit_api::services::room::CreateRoomOptions;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let lk = LiveKitApi::with_api_key("https://my.livekit.host", "my-key", "my-secret");

let room = lk
.room()
.create_room(
"my-room",
CreateRoomOptions { empty_timeout: 600, max_participants: 20, ..Default::default() },
)
.await?;

println!("created room {}", room.name);
Ok(())
}
```

Individual service clients (`RoomClient`, `SIPClient`, etc.) can also be created directly with the same constructors.

### Authentication

The server API supports two modes of operation:

- **API key & secret** — recommended for backend use. `LiveKitApi::new(host)` reads the key and secret from the `LIVEKIT_API_KEY` and `LIVEKIT_API_SECRET` environment variables; `LiveKitApi::with_api_key(host, key, secret)` takes them explicitly instead. Either way, a short-lived token is signed for each request.
- **Access token** — for client-side use where the API secret must not be exposed. `LiveKitApi::with_token(host, token)` sends a pre-signed [access token](https://docs.livekit.io/frontends/reference/tokens-grants/) verbatim on every request; its grants must cover the calls you make.

### Agent dispatch

Explicitly dispatch an agent into a room (see [Agent dispatch](https://docs.livekit.io/agents/server/agent-dispatch/)):

```rust,no_run
use livekit_api::services::LiveKitApi;
use livekit_protocol as proto;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let lk = LiveKitApi::with_api_key("https://my.livekit.host", "my-key", "my-secret");

lk.agent_dispatch()
.create_dispatch(proto::CreateAgentDispatchRequest {
room: "my-room".to_owned(),
agent_name: "my-agent".to_owned(),
..Default::default()
})
.await?;
Ok(())
}
```

### Error handling

Service methods return `ServiceResult<T>` (`Result<T, ServiceError>`). A failed server call is a `ServiceError::Twirp(ServerError)`; when the server returns a structured error it is `ServerError::Twirp(ServerErrorCode)`, which carries the error code and message. (`ServerError`'s former `TwirpError`/`TwirpErrorCode`/`TwirpResult` type names remain as deprecated aliases.)

```rust,no_run
use livekit_api::services::LiveKitApi;

#[tokio::main]
async fn main() {
let lk = LiveKitApi::with_api_key("https://my.livekit.host", "my-key", "my-secret");
match lk.room().delete_room("my-room").await {
Ok(_) => {}
Err(e) => eprintln!("delete_room failed: {e}"),
}
}
```

### Handling SIP call errors

When a SIP call fails (e.g. the callee is busy or declines), the server attaches a SIP status to the error. `SipCallError::from_error` decodes it from a returned error, exposing the SIP status code and reason:

```rust,no_run
use livekit_api::services::sip::CreateSIPParticipantOptions;
use livekit_api::services::{LiveKitApi, SipCallError};

#[tokio::main]
async fn main() {
let lk = LiveKitApi::with_api_key("https://my.livekit.host", "my-key", "my-secret");

let result = lk
.sip()
.create_sip_participant(
"ST_trunk".to_owned(),
"+15105550100".to_owned(),
"my-room".to_owned(),
CreateSIPParticipantOptions {
wait_until_answered: Some(true),
..Default::default()
},
None,
)
.await;
Comment on lines +95 to +105

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 SIP example function takes owned String parameters rather than &str

The create_sip_participant example at livekit-api/README.md:95-104 passes .to_owned() strings because the actual function signature at livekit-api/src/services/sip.rs:650-656 takes String parameters (not &str). AGENTS.md recommends "Functions should generally accept &str/&[T] rather than String/Vec<T>". This is a pre-existing API design concern (not introduced by this README PR), but worth noting if the SIP client API is ever revised — switching to &str / impl Into<String> would make the API more ergonomic and consistent with other service methods like RoomClient::create_room and RoomClient::delete_room which already accept &str.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


if let Err(err) = result {
if let Some(sip) = SipCallError::from_error(&err) {
eprintln!("{sip}"); // e.g. "SIP call failed: 486 Busy Here (resource_exhausted)"
if sip.sip_status_code() == Some(486) {
// callee is busy
}
}
}
}
```

## Access tokens

Access tokens are generated with `AccessToken`:

```rust,no_run
use livekit_api::access_token::{AccessToken, VideoGrants};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let token = AccessToken::with_api_key("my-key", "my-secret")
.with_identity("participant-identity")
.with_name("Participant Name")
.with_grants(VideoGrants {
room_join: true,
room: "my-room".to_owned(),
..Default::default()
})
.to_jwt()?;

println!("{token}");
Ok(())
}
```

By default, tokens expire 6 hours after generation. Override this with `.with_ttl(duration)`.
Loading