call.created on the webhook or WebSocket your agent already uses for messages, and carries the voice and camera over WebRTC. Your backend runs the agent; it registers nothing for calls.
Answer a call
Whencall.created arrives with status: "ringing", join the room for call.id within 32 seconds. Joining is the answer: the call becomes in-progress. A call nobody joins ends no-answer.
Each package below joins the room and runs the WebRTC connection for you, so your code never handles SDP, ICE or TURN credentials. Choose the one that matches your agent’s runtime.
- TypeScript
- Python
- Pipecat
- LiveKit
Install the SDK with the WebRTC packages it lists as optional peer dependencies. Add
node-webcodecs only if your agent sends or receives video.Show video or Rive in a call
Your agent can show the person something while it talks. It sends live video, or it names a Rive file that the person’s phone draws and your agent drives. When video from your agent is arriving, the phone shows the video; otherwise it shows the Rive file.Video
Publish a camera track and capture frames into it withtransport.publishTrack(LocalVideoTrack) and VideoSource.captureFrame. Video calls covers frame formats, rates and bitrates. Show a talking avatar turns your agent’s speech into a lip-synced face with Simli.
Rive
Set a.riv file on your agent’s Contact Card, then open the call’s rive channel with transport.rive() to set View Model values, fire triggers and switch files, timed to your agent’s speech. Show Rive in a call walks through it.
Decline a call
To decline a ringing call, end it without joining. A call your agent ends while it rings becomesno-answer.
End a call
End the call for both sides withend() on the transport (transport.end() in Pipecat), with relay.calls.end(callId), or with POST /v1/calls/{callId}/end. An answered call becomes completed. close() in TypeScript and aclose() in Python only clean up locally; the call goes on until someone ends it.
The call room
The room is a WebSocket atGET /v1/calls/{callId}/room, opened with your Agent Token. It carries only signaling: JSON frames with WebRTC session descriptions. Audio and video travel over WebRTC, never through this socket.
- Send
{"type":"join"}when the socket opens. Relay answers withiceServersand thenroomState. - Send an
offerthat publishes one track namedaudio. Relay answers withanswer. The first time your camera turns on, send anotherofferthat adds a track namedvideo. - Send
connectedwhen your peer connection is connected. - Relay sends an
offerfor each of the person’s tracks. Reply withanswer.
The call in the chat
Relay writes one system message into the chat withis_system_message: true and system_event.type: "call". The marker exists from the moment the call is placed; call.status carries its state. The event’s actor is the caller, its subject is the callee, and its call object carries its current state.
parts[0].value is a plain sentence such as “Atlas called you” or “Atlas called you with no answer”. The Relay app shows it as a call bubble on the caller’s side. Agents receive it like any other system message and should not reply to it.
- Preview
- JSON
The Call object states
status uses Twilio’s call status words. ringing and in-progress are live. The other five are terminal and set ended_at.

