Set up phone calls
With a Session Initiation Protocol (SIP) trunk or a session border controller
(SBC) account, the device answers real phone calls. The brain’s SIP front-end
answers the call, taking it from a separate gateway service, teaport-sip.
Before you begin
Section titled “Before you begin”You need:
- A device with teaport installed. See Install teaport.
- A SIP trunk or SBC account, with its host, domain, username, and password.
- The gateway program. The installer creates the
teaport-sipservice, but it does not build or install the gateway: that is a separate program in its own repository, and the service runs~/teaport-sip/build/teaport-sip. - Access to the device as the user the teaport services run as.
Turn on telephony
Section titled “Turn on telephony”Telephony ships off. The installer lays down the teaport-sip service, gated on
a configuration file that does not exist, so nothing starts and no SIP port is
bound. The teaport sip configure wizard turns it on.
To configure the line, follow these steps:
-
On the device, start the wizard:
Terminal window teaport sip configure -
Answer its prompts: the registrar or SBC host, the domain, the username, and the password.
-
Wait for the test registration. The wizard registers from a throwaway port, never port 5060, so it leaves a running gateway untouched. When the trunk answers
200 OK, the wizard writes~/.config/teaport/teaport-sip.confwith mode600, which holds the SIP password, and enables the service. If the registration fails, the wizard writes nothing and telephony stays off.
Run the wizard as the user the services run as. As root, add sudo -E: a plain
sudo resets HOME, so the wizard writes a configuration the gateway never
reads, and it refuses to.
Turn it on without prompts
Section titled “Turn it on without prompts”To pass the answers on the command line, for example from a script:
teaport sip configure --host SBC_HOST --domain SIP_DOMAIN --user SIP_USER \ --password SIP_PASSWORD --yesReplace the following:
SBC_HOST: the registrar or SBC host.SIP_DOMAIN: the SIP domain of the trunk.SIP_USER: the username the trunk assigns you.SIP_PASSWORD: the trunk password.
Use an existing gateway configuration
Section titled “Use an existing gateway configuration”To adopt a gateway .conf that a device ran by hand, or one carried over from
another device:
teaport sip configure --conf CONF_PATHReplace CONF_PATH with the file to adopt. The wizard test-registers through
the same path, then installs the file as it is, except for four keys the
services own: sip_port becomes 5060, uds_path becomes the socket the brain
listens on, register becomes true, and auto_answer becomes false, because
the brain answers the call, after a couple of rings. If a gateway or SIP
front-end launched by hand is still running, the command refuses and names it:
the service it is about to enable needs port 5060 and the socket.
Change the trunk later
Section titled “Change the trunk later”A re-run, for a new password or a new registrar, rewrites only the keys the
wizard owns: the trunk’s id_uri, registrar_uri, username, password, and
realm, and the four service keys. Every other line of the installed
configuration survives: the source filter, sip_source_filter with its
sip_source_allow list, nameserver, aec, comments, and keys the wizard
does not know. Before the test registration, the wizard lists by name the
settings it keeps. To keep nothing and write a fresh template, run
teaport sip configure --fresh.
Check the line
Section titled “Check the line”teaport sip status reports the gateway service, the brain’s SIP front-end, the
configuration, including whether the running gateway confirms the source filter
in it, and the registration from this run:
teaport sip statusTo follow the gateway’s journal:
teaport logs sip -fThe call’s own lines are in the brain’s journal, teaport logs brain.
Keep the line running
Section titled “Keep the line running”The line survives reboots and crashes on its own. The service is enabled and restarts with a backoff that does not hammer the registrar, and the brain reconnects to the gateway whenever the gateway comes back. The two restart independently: a brain restart, from an update or a configuration change, leaves the call at the gateway, which hands it to the restarted brain, and the caller hears a short “sorry, I lost you for a moment”. The brain picks up a restarted gateway within a couple of seconds.
To restart the gateway, for example after editing its configuration by hand:
teaport sip restartThe command waits for the trunk’s 200 OK; the brain reconnects on its own.
The gateway cancels echo for the caller. teaport sip aec prints the current
acoustic echo cancellation (AEC) setting:
teaport sip aecThe flags teaport sip aec off and teaport sip aec on flip aec= in the
configuration and restart the gateway:
teaport sip aec offteaport sip aec onTo turn telephony off:
teaport sip disableThe command stops and disables the gateway. The configuration stays at
~/.config/teaport/teaport-sip.conf; add --purge to remove it too. Run the
wizard again to turn telephony back on.
The configuration is the gateway’s own key=value format: registrar_uri,
id_uri, username, password, uds_path, aec, auto_answer, and more.
Editing it by hand is fine; run teaport sip restart afterwards. Until you do,
teaport sip status and teaport doctor don’t show the source filter as
confirmed, because the gateway still runs the configuration it started with.
Nothing about the line lives in /tmp: the configuration is in
~/.config/teaport, the socket in /run/teaport, and the logs in journald.
A call during a conversation
Section titled “A call during a conversation”The brain runs every front-end in one process, and the engine serves one conversation at a time; the session arbiter decides which one. A phone call and a Talk session are peers there. See Architecture.
The brain answers the call, not the gateway. The delay is a setting,
SIP_ANSWER_AFTER_SECS, a couple of rings, and the brain builds the call’s
pipeline and words its greeting during the ring, so the greeting plays as
soon as the caller is connected. See the settings reference.
While the call rings, the agent asks the live conversation, in the conversation’s language: “Someone’s calling me — Ann Smith. Should I step away for a moment?” The agent uses the caller’s name when the call carries a real one. A placeholder such as “WIRELESS CALLER” does not count. Otherwise the agent says the last four digits of the number: “Someone’s calling me — a number ending in 8 5 0 0…”. The device’s screen shows the whole number. When the call carries no name or number, the agent asks “Someone’s calling me. Should I step away…”.
The question has three outcomes:
- Yes: the agent says “I’ll take the call — back in a moment.” The conversation goes on hold: its connection and everything said in it are kept, and it hears nothing while the call is up. After the call, the conversation resumes with “Sorry about that — where were we?”
- No: the call is never answered. The caller hears it ring until they give up.
- No answer:
TEAPORT_CALL_PROMPT_SECS, 8 seconds by default, after the question starts, and at least 3 seconds after it ends, the call is taken by default (TEAPORT_CALL_PROMPT_DEFAULT); it can instead keep ringing. If someone is still talking at that point, the agent waits up to 5 seconds more. The caller hears it ring the whole time, and many carriers send an unanswered call to voicemail after 20 to 30 seconds. With nobody answering, the call is picked up about 12 to 14 seconds after it rings.
An answer counts only after the question has been heard: played to the end, or cut after the user heard at least half of “Should I step away for a moment?”. While the question is still being said, the agent takes no answer. If talk in the room cuts the question before its last part, the agent takes no answer yet. When the user stops speaking, the agent asks the question again, one time only. After that second time, the agent takes the user’s answer even if the question was cut again. Without an answer, the no-answer default applies. The waiting time still counts from the ring, so with the question asked twice and nobody answering, the call is picked up about 16 to 18 seconds after it rings.
With TEAPORT_TOOL_ANSWER_PHONE_CALL=0, nobody is asked. A Talk session hears
“Sorry, a phone call is coming in and I have to take it…” and closes; a
conversation at the device keeps it, and the caller hears the busy line.
A call that is already connected is not asked about, because the caller waits on
a silent line: a configuration with auto_answer=true, or a call that was up
when the brain restarted. The brain takes the call and holds the conversation.
While a call is live, the brain refuses a Talk client with
“Sorry, I’m on a phone call right now…”, and the gateway turns away a
second call with 486 Busy Here; the caller’s phone handles that, as voicemail
or a busy tone.
To dedicate a device to the phone, turn off the other front-ends, not the brain, which is what answers calls: the local audio bridge, the Discord bridge, and Talk in OpenClaw.
Play typing while the agent works
Section titled “Play typing while the agent works”A question that goes to the agent can take 15 to 35 seconds to answer, and
sometimes minutes. Talk sessions and conversations at the device play a typing
sound through the wait. A phone call plays none unless you turn the typing on
for calls with SIP_THINKING_SOUND. To change the setting and apply it, see
Configure teaport.
Three other settings decide the typing on calls as well:
TEAPORT_THINKING_SOUND=0turns the typing off everywhere, calls included.- With
SIP_HALF_DUPLEXon,SIP_THINKING_SOUNDhas no effect. That setting drops the caller’s audio while the typing plays, so a caller who keeps talking through the wait couldn’t stop it. - In agent-first mode,
TEAPORT_AGENT_FIRST, calls play the typing whateverSIP_THINKING_SOUNDandSIP_HALF_DUPLEXsay.
On a call, the typing plays only after the line has been quiet for
TEAPORT_THINKING_GRACE_S, 1.5 seconds by default, and it stops when the
caller speaks. The exception is agent-first mode with SIP_HALF_DUPLEX on:
there the brain doesn’t hear the caller while the typing plays, the same as
while the agent speaks. An OLED face on the device doesn’t type along to a
call.
Before you turn the typing on for calls, listen to it on a call. The typing is made at 24 kHz, and a phone line carries 8 kHz audio, which keeps little of the typing’s click.
Record calls
Section titled “Record calls”Phone calls are not recorded unless you turn recording on. One setting,
TEAPORT_CALL_RECORDING, decides what a call leaves behind:
off, the default: nothing of the call is kept.audio: both sides of the call in one stereo WAV file, the caller on the left channel and the agent on the right.transcript: a text file of the caller’s and the agent’s lines, timed from the start of the call. A reply the caller cut off is written as far as they heard it.both: the two files.
Talk sessions and the room mic are never recorded by this setting.
Recording-consent law differs by place. Most states in the United States need the consent of one party to the call, California and about a dozen others need everyone’s, and other countries have their own rules. You choose the setting and are responsible for it; this page is not legal advice.
Each call’s files are named by the time the call started and its call id, for
example 20261102-141503-abc123.wav, in TEAPORT_CALL_RECORDING_DIR
(~/teaport-recordings by default). Recording starts when the caller is told,
before the greeting; a call the brain can’t hear isn’t recorded. If the brain
restarts during a call, the rest of the call goes to files beside the first ones
(.r1.wav, .r1.txt), and the caller hears the notice again.
With recording on, a call starts with “This call is being recorded.” in the
call’s language, before the greeting. The agent doesn’t listen while the notice
plays, so the caller can’t talk over it. To turn the notice off, set
TEAPORT_CALL_RECORDING_NOTICE=0. Either way, the agent knows the call is
recorded and says so if the caller asks.
Recordings older than TEAPORT_CALL_RECORDING_KEEP_DAYS (30 by default) are
deleted, and then the oldest until the directory fits in
TEAPORT_CALL_RECORDING_MAX_MB (2048 by default). The check runs as each call
starts. See the settings reference.
Test the phone path without a line
Section titled “Test the phone path without a line”brain/tests/fake_sip_gateway.py stands in for the gateway on the brain’s side
of the socket. It binds the socket, rings and answers a call from a caller ID
you choose, plays a WAV as the caller in real time, records what the caller
hears, and hangs up when the bot has answered, partway through the answer, or
after a number of seconds. A brain that drops mid-call is handed the call again
when it reconnects, as the real gateway does. Nothing registers and no SIP port
is bound, so it works on a device whose trunk account is registered somewhere
else. It needs only the system python3, run from a checkout that contains it.
The brain needs nothing: its SIP front-end looks for the socket every 2 seconds,
TEAPORT_SIP_SOCKET, /run/teaport/teaport-sip.sock by default, and takes
calls from whatever serves it.
To test the phone path, follow these steps:
-
From a checkout that contains
tools/sip-line-guardandbrain/tests/fake_sip_gateway.py, guard the real gateway for the session; it must not start and register while the fake is up:Terminal window sudo tools/sip-line-guard onThe guard is a runtime drop-in whose condition cannot hold, so the service skips every start of it, and the drop-in is gone at the next boot. The command ends with
GUARDED: teaport-sip.service cannot start. -
Prove that the guard holds, without starting anything. The check exits 0 when the drop-in is loaded, the guard condition fails, and the gateway is not running:
Terminal window tools/sip-line-guard status -
Create the socket directory and a directory for the recordings. The socket directory is the gateway’s runtime directory, absent while the service is off:
Terminal window sudo install -d -o teaspoon -g teaspoon -m 0750 /run/teaportinstall -d -m 0700 ~/fake-sip -
Start the fake gateway. It waits up to 120 seconds for the brain, which connects within about 2 seconds:
Terminal window python3 brain/tests/fake_sip_gateway.py --socket /run/teaport/teaport-sip.sock \--allow-live-path --from +15551234567 --wav brain/test/question.wav \--record ~/fake-sip/bot.wav --record-stereo ~/fake-sip/both.wav \--summary-json ~/fake-sip/summary.jsonThe call rings in from
+15551234567; replace it with any caller ID. The caller’s audio,question.wav, asks: “What is the capital of France, and is it a large city?” -
Follow the call from the brain’s side, in a second shell:
Terminal window journalctl -u teaport-brain -fThe front-end logs
SIP front-end: connected to the gateway at ...when it connects. -
End the fake; it removes the socket when it exits. Then clean up:
Terminal window sudo rmdir /run/teaportsudo tools/sip-line-guard off
The recordings and the result land in ~/fake-sip:
bot.wav: what the caller heard.both.wav: the caller on the left channel and the bot on the right.summary.json: whether the call was answered, when the greeting and the answer started and ended, the answer’s latency measured from the end of the WAV, who hung up, and the control messages the brain sent.
The fake exits 1 when the call was not answered, there was no greeting, there was no answer to the WAV, or the protocol was violated: an audio frame that is not exactly 640 bytes, a datagram over the gateway’s 2048-byte read, or unparseable control.
If brain.env sets TEAPORT_SIP_SOCKET to another path, serve that path
instead; it must not be empty, because empty turns the SIP front-end off. The
fake refuses to bind a path that something already listens on, compares real
paths, so a symlink such as /var/run does not pass, and without
--allow-live-path it refuses the default path as well.
Any 16-bit WAV works as the caller’s audio; other sample rates and stereo are
converted to the 16 kHz mono the gateway speaks. For a call that exercises a
tool, question_host_status.wav asks for the host status.
What’s next
Section titled “What’s next”These pages cover the rest:
- CLI reference, for every
teaportcommand and option. - Settings reference, for the settings that tune a
call, such as
SIP_ANSWER_AFTER_SECS. - Architecture, for the session arbiter.