Skip to content

Tool calling

There are two ways to integrate tool calling with Runway Characters. Both let the model decide when to invoke a named tool during a realtime Session. They differ in where the handler runs and how results participate in the conversation.

Client events are one-way, fire-and-forget signals from the Avatar into your frontend. You declare tools when you create the Session; the Avatar can invoke them during the call. Invocations arrive over the WebRTC data channel, and your app reacts—for example updating React state or showing a UI overlay. No return value is sent back into the conversation.

Good for driving local UI: captions, trivia boards, highlights, game state, or any on-device effect that doesn’t need a server round trip.

// Shared tool definitions — import the same module on server and client
import { clientTool, type ClientEventsFrom } from '@runwayml/avatars-react/api';
export const showCaption = clientTool('show_caption', {
description: 'Show a short caption overlay',
args: {} as { text: string },
});
export const tools = [showCaption];
export type AppEvents = ClientEventsFrom<typeof tools>;
// Server: pass tools when creating the Session
await client.realtimeSessions.create({
model: 'gwm1_avatars',
avatar: { type: 'custom', avatarId },
tools,
});
// Client: subscribe inside AvatarCall / AvatarSession
import { useClientEvent } from '@runwayml/avatars-react';
function CaptionOverlay() {
const caption = useClientEvent<AppEvents, 'show_caption'>('show_caption');
return caption ? <p>{caption.text}</p> : null;
}

See the nextjs-client-events example for a full working demo.

Backend RPC is for tools whose implementation runs on your server and whose return values should feed back into the conversation—so the model can speak from real data rather than only from what runs in the browser. Use it when you need server-side auth, access to private systems, or structured results that shape what the Avatar says next.

The @runwayml/avatars-node-rpc package joins the Session as a hidden participant and routes incoming tool requests to your handler functions.

import { createRpcHandler } from '@runwayml/avatars-node-rpc';
const handler = await createRpcHandler({
apiKey: process.env.RUNWAYML_API_SECRET!,
sessionId,
tools: {
fetch_order_status: async (args) => {
const order = await db.orders.find(String(args.orderId));
return { status: order.status, eta: order.eta };
},
},
onConnected: () => console.log('Connected'),
onDisconnected: () => console.log('Disconnected'),
onError: (err) => console.error(err),
});
await handler.close();

Create a Session the same way as in Building your integration, then attach the RPC handler with your API key and Session ID (or pre-fetched credentials—see the package README).

Client eventsBackend RPC
Runs inBrowser (React SDK)Your Node server process
Typical useLocal UI, overlays, on-device effectsAuthenticated backends; results that shape the conversation
Result handlingOne-way signals into your UIReturn values are sent back so the model can use them on the next turn

You can combine both: client events for immediate surface updates, and backend RPC when the Avatar needs trusted data before it speaks.