LiveKit docs › Agent Server › Agent dispatch

---

# Agent dispatch

> Specifying how and when your agents are assigned to rooms.

## Dispatching agents

Dispatch is the process of assigning an agent to a room. LiveKit server manages this process as part of the [Server lifecycle](https://docs.livekit.io/agents/server/lifecycle.md). LiveKit optimizes dispatch for high concurrency and low latency, typically supporting hundreds of thousands of new connections per second with a max dispatch time under 150 ms.

Explicit dispatch is the recommended approach for most applications. It gives you full control over when and how agents join rooms and lets you pass job-specific metadata to each agent session.

## Dispatch name

The dispatch name is a unique identifier for an agent. Explicit dispatch uses it to route jobs to the right agent. It's the value of `agent_name` (Python) or `agentName` (Node.js) on `@server.rtc_session()` or `ServerOptions`.

The dispatch name is distinct from the agent's **display name** (`Participant.name`), which is set by the `name` parameter in `req.accept()` and shown to other participants in the room. The dispatch name targets the agent. The display name labels it. See [Request handler](https://docs.livekit.io/agents/server/options.md#request-handler) for setting the display name.

When you scaffold an agent with [`lk agent init`](https://docs.livekit.io/reference/developer-tools/livekit-cli/agent.md#init), the AGENT-NAME you provide becomes the dispatch name in the generated source. To change it later, edit the source code and redeploy.

To set the dispatch name in your server configuration:

**Python**:

In Python, set the agent name in the `@server.rtc_session` decorator:

```python
@server.rtc_session(agent_name="test-agent")
async def my_agent(ctx: JobContext):
    # Agent entrypoint code...

```

---

**Node.js**:

```ts
const opts = new ServerOptions({
  //...
  agentName: "test-agent",
});

```

With `agent_name` set, the agent is only assigned to rooms when explicitly dispatched using one of the following methods.

## Dispatch via API

You can explicitly dispatch an agent to a room using the `AgentDispatchService` [API](https://docs.livekit.io/reference/agents/agent-dispatch-service-api.md).

**Python**:

```python
import asyncio
from livekit import api

room_name = "my-room"
agent_name = "test-agent"

async def create_explicit_dispatch():
    lkapi = api.LiveKitAPI()
    dispatch = await lkapi.agent_dispatch.create_dispatch(
        api.CreateAgentDispatchRequest(
            agent_name=agent_name, room=room_name, metadata='{"user_id": "12345"}'
        )
    )
    print("created dispatch", dispatch)

    dispatches = await lkapi.agent_dispatch.list_dispatch(room_name=room_name)
    print(f"there are {len(dispatches)} dispatches in {room_name}")
    await lkapi.aclose()

asyncio.run(create_explicit_dispatch())

```

---

**Node.js**:

```ts
import { AgentDispatchClient } from 'livekit-server-sdk';

const roomName = 'my-room';
const agentName = 'test-agent';

async function createExplicitDispatch() {
  const agentDispatchClient = new AgentDispatchClient(process.env.LIVEKIT_URL, process.env.LIVEKIT_API_KEY, process.env.LIVEKIT_API_SECRET);

  // create a dispatch request for an agent named "test-agent" to join "my-room"
  const dispatch = await agentDispatchClient.createDispatch(roomName, agentName, {
    metadata: '{"user_id": "12345"}',
  });
  console.log('created dispatch', dispatch);

  const dispatches = await agentDispatchClient.listDispatch(roomName);
  console.log(`there are ${dispatches.length} dispatches in ${roomName}`);
}

```

---

**LiveKit CLI**:

```shell
lk dispatch create \
  --agent-name test-agent \
  --room my-room \
  --metadata '{"user_id": "12345"}'

```

---

**Ruby**:

```ruby
require "livekit"

room_name = "my-room"
agent_name = "test-agent"

client = LiveKit::AgentDispatchServiceClient.new(
  ENV["LIVEKIT_URL"],
  api_key: ENV["LIVEKIT_API_KEY"],
  api_secret: ENV["LIVEKIT_API_SECRET"],
)

dispatch = client.create_dispatch(
  room_name,
  agent_name,
  metadata: '{"user_id": "12345"}',
)

if dispatch.data
  puts "successfully dispatched agent #{dispatch.data.agent_name} to #{dispatch.data.room}"
else
  puts "created dispatch (error): #{dispatch}"
end

```

---

**Go**:

```go
func createAgentDispatch() {
	req := &livekit.CreateAgentDispatchRequest{
		Room:      "my-room",
		AgentName: "test-agent",
		Metadata:  "{\"user_id\": \"12345\"}",
	}
	dispatch, err := dispatchClient.CreateDispatch(context.Background(), req)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Dispatch created: %v\n", dispatch)
}

```

---

**Kotlin**:

```kotlin
import io.livekit.server.AgentDispatchServiceClient

val roomName = "my-room"
val agentName = "test-agent"

  fun createExplicitDispatch() {
      val client = AgentDispatchServiceClient.createClient(
          System.getenv("LIVEKIT_URL")!!.replace("wss", "https"),
          System.getenv("LIVEKIT_API_KEY")!!,
          System.getenv("LIVEKIT_API_SECRET")!!,
      )
      try {
          val response = client.createDispatch(
              room = roomName,
                  agentName = agentName,
                  metadata = """{"user_id": "12345"}""",
              ).execute().body()
          if (response != null) {
              println("successfully dispatched agent ${response.agentName} to ${response.room}")
          } else {
              println("created dispatch (error): ${response}")
          }
      } catch (e: Exception) {
          println("error creating dispatch: ${e.message}")
      }
  }

```

The room, `my-room`, is automatically created during dispatch if it doesn't already exist, and the agent server assigns `test-agent` to it.

### Job metadata

Explicit dispatch allows you to pass metadata to the agent, available in the `JobContext`. This is useful for including details such as the user's ID, name, or phone number.

The metadata field is a string. LiveKit recommends using JSON to pass structured data.

The [examples](#via-api) in the previous section demonstrate how to pass job metadata during dispatch.

For information on consuming job metadata in an agent, see the following guide:

- **[Job metadata](https://docs.livekit.io/agents/server/job.md#metadata)**: Learn how to consume job metadata in an agent.

## Dispatch from inbound SIP calls

Agents can be explicitly dispatched for inbound SIP calls. [SIP dispatch rules](https://docs.livekit.io/telephony/accepting-calls/dispatch-rule.md) can define one or more agents using the `room_config.agents` field.

LiveKit recommends explicit agent dispatch for SIP inbound calls rather than automatic agent dispatch as it allows multiple agents within a single project.

## Dispatch via access token

You can include one or more agent dispatch entries in a participant's access token. When the first participant connects and creates the room, LiveKit dispatches the specified agents.

> ℹ️ **Applied on room creation only**
> 
> Agent dispatch from the token only occurs when the room is first created. If the room already exists, the token's dispatch configuration is ignored. Use a unique room name per session or [dispatch via API](#via-api) for more control.

The following example creates a token that dispatches the `test-agent` agent to the `my-room` room:

**Python**:

```python
from livekit.api import (
  AccessToken,
  RoomAgentDispatch,
  RoomConfiguration,
  VideoGrants,
)

room_name = "my-room"
agent_name = "test-agent"

def create_token_with_agent_dispatch() -> str:
    token = (
        AccessToken()
        .with_identity("my_participant")
        .with_grants(VideoGrants(room_join=True, room=room_name))
        .with_room_config(
            RoomConfiguration(
                agents=[
                    RoomAgentDispatch(agent_name="test-agent", metadata='{"user_id": "12345"}')
                ],
            ),
        )
        .to_jwt()
    )
    return token

```

---

**Node.js**:

```ts
import { RoomAgentDispatch, RoomConfiguration } from '@livekit/protocol';
import { AccessToken } from 'livekit-server-sdk';

const roomName = 'my-room';
const agentName = 'test-agent';

async function createTokenWithAgentDispatch(): Promise<string> {
  const at = new AccessToken();
  at.identity = 'my-participant';
  at.addGrant({ roomJoin: true, room: roomName });
  at.roomConfig = new RoomConfiguration({
    agents: [
      new RoomAgentDispatch({
        agentName: agentName,
        metadata: '{"user_id": "12345"}',
      }),
    ],
  });
  return await at.toJwt();
}

```

---

**LiveKit CLI**:

The following example assumes the environment variables `LIVEKIT_API_KEY` and `LIVEKIT_API_SECRET` are set:

```shell
lk token create \
  --identity "my-participant" \
  --room "my-room" \
  --agent "test-agent" \
  --join

```

---

**Ruby**:

```ruby
require "livekit"

roomName = "my-room"
agentName = "test-agent"

def create_token_with_agent_dispatch(roomName:, agentName:)
  token = LiveKit::AccessToken.new(
    api_key: ENV["LIVEKIT_API_KEY"],
    api_secret: ENV["LIVEKIT_API_SECRET"],
    identity: "my-participant",
  )
  token.video_grant = LiveKit::VideoGrant.new(roomJoin: true, room: roomName)
  token.room_config = LiveKit::Proto::RoomConfiguration.new(
    agents: [
      LiveKit::Proto::RoomAgentDispatch.new(
        agent_name: agentName,
        metadata: '{"user_id": "12345"}',
      ),
    ],
  )
  token.to_jwt
end

```

---

**Go**:

```go
func createTokenWithAgentDispatch() (string, error) {
	at := auth.NewAccessToken(
		os.Getenv("LIVEKIT_API_KEY"),
		os.Getenv("LIVEKIT_API_SECRET"),
	).
		SetIdentity("my-participant").
		SetName("Participant Name").
		SetVideoGrant(&auth.VideoGrant{
			Room:     "my-room",
			RoomJoin: true,
		}).
		SetRoomConfig(&livekit.RoomConfiguration{
			Agents: []*livekit.RoomAgentDispatch{
				{
					AgentName: "test-agent",
					Metadata:  "{\"user_id\": \"12345\"}",
				},
			},
		})

	return at.ToJWT()
}

```

---

**Kotlin**:

```kotlin
import io.livekit.server.AccessToken
import io.livekit.server.RoomJoin
import io.livekit.server.RoomName
import livekit.LivekitAgentDispatch
import livekit.LivekitRoom.RoomConfiguration

val roomName = "my-room"
val agentName = "test-agent"

fun createTokenWithAgentDispatch(): String {
    val token = AccessToken(
        System.getenv("LIVEKIT_API_KEY")!!,
        System.getenv("LIVEKIT_API_SECRET")!!,
    )
    token.identity = "my-participant"
    token.addGrants(RoomJoin(true), RoomName(roomName))
    token.roomConfiguration = RoomConfiguration.newBuilder()
        .addAgents(
            LivekitAgentDispatch.RoomAgentDispatch.newBuilder()
                .setAgentName(agentName)
                .setMetadata("""{"user_id": "12345"}""")
                .build(),
        )
        .build()
    return token.toJwt()
}

```

## Automatic agent dispatch

> 🔥 **Caution**
> 
> Automatic dispatch is not recommended for most applications. It dispatches an agent to every new room, regardless of whether one is needed, and doesn't support passing metadata to the agent session. Use one of the explicit dispatch methods above instead.

When `agent_name` is not set, an agent is automatically dispatched to each new room. This can be useful for simple prototypes where every room requires the same agent.

---

This document was rendered at 2026-06-07T11:32:17.217Z.
For the latest version of this document, see [https://docs.livekit.io/agents/server/agent-dispatch.md](https://docs.livekit.io/agents/server/agent-dispatch.md).

To explore all LiveKit documentation, see [llms.txt](https://docs.livekit.io/llms.txt).