<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Code and Trust</title>
    <description>The latest articles on DEV Community by Code and Trust (@codeandtrust).</description>
    <link>https://dev.to/codeandtrust</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3975408%2F1b5ceb1d-9d6b-4a94-9af6-5980ee798a54.png</url>
      <title>DEV Community: Code and Trust</title>
      <link>https://dev.to/codeandtrust</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codeandtrust"/>
    <language>en</language>
    <item>
      <title>How to Give Your Self-Hosted AI Agent Inbound Phone Calls (OpenClaw + Twilio)</title>
      <dc:creator>Code and Trust</dc:creator>
      <pubDate>Tue, 09 Jun 2026 07:27:31 +0000</pubDate>
      <link>https://dev.to/codeandtrust/how-to-give-your-self-hosted-ai-agent-inbound-phone-calls-openclaw-twilio-43k4</link>
      <guid>https://dev.to/codeandtrust/how-to-give-your-self-hosted-ai-agent-inbound-phone-calls-openclaw-twilio-43k4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cross-posted from the Code and Trust blog. Canonical: &lt;a href="https://www.codeandtrust.com/blog/openclaw-phone-calls" rel="noopener noreferrer"&gt;codeandtrust.com/blog/openclaw-phone-calls&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your self-hosted OpenClaw agent can answer emails, send Slack messages, and query your calendar — but if someone calls your business number, the agent is nowhere to be found. This guide shows how to fix that with a 50-line Twilio webhook and an open-source bridge called &lt;strong&gt;&lt;a href="https://github.com/CODEANDTRUST/clawcall" rel="noopener noreferrer"&gt;clawcall&lt;/a&gt;&lt;/strong&gt; (MIT).&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: realtime voice mode can't use gateway tools
&lt;/h2&gt;

&lt;p&gt;OpenClaw's &lt;code&gt;voice-call&lt;/code&gt; plugin has a &lt;code&gt;realtime&lt;/code&gt; mode that provides excellent conversational feel — sub-second latency, barge-in support. But until recently (&lt;a href="https://github.com/openclaw/openclaw/issues/71262" rel="noopener noreferrer"&gt;#71272&lt;/a&gt;), realtime mode ran as an isolated audio session that bypassed the gateway's tool registry entirely. Ask it to check your calendar or send a message, and it would politely decline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;clawcall takes a different approach:&lt;/strong&gt; route the audio through the gateway's normal agent turn pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inbound call → Twilio → STT → chat.send() → agent (full tool access) → TTS → Twilio → caller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent turn is a standard &lt;code&gt;chat.send&lt;/code&gt; message to your gateway, so every skill, tool, and memory lookup works exactly as it does in your Telegram or Discord channel. The only difference is that input arrives as transcribed audio and output leaves as synthesized speech.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A running OpenClaw gateway (any version with the chat API)&lt;/li&gt;
&lt;li&gt;A Twilio account with a phone number&lt;/li&gt;
&lt;li&gt;Node.js 18+ (or Bun)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ngrok&lt;/code&gt; or a public URL for local dev&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup in 5 minutes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Clone clawcall&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/CODEANDTRUST/clawcall
&lt;span class="nb"&gt;cd &lt;/span&gt;clawcall
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Configure environment&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPENCLAW_GATEWAY_URL=http://localhost:4000
OPENCLAW_API_KEY=your-key
TWILIO_ACCOUNT_SID=ACxxxxxxx
TWILIO_AUTH_TOKEN=your-auth-token
STT_PROVIDER=deepgram        # or whisper
TTS_PROVIDER=elevenlabs      # or openai-tts
PORT=3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Expose with ngrok&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the &lt;code&gt;https://xxxx.ngrok.io&lt;/code&gt; URL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Wire Twilio&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your Twilio console → Phone Numbers → your number → Voice Configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Incoming call webhook:&lt;/strong&gt; &lt;code&gt;https://xxxx.ngrok.io/call/incoming&lt;/code&gt; (HTTP POST)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Start the bridge&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call your Twilio number. The bridge answers, transcribes your speech with Deepgram (or Whisper), sends it to your OpenClaw gateway as a chat message, streams the text response through your TTS provider, and plays the audio back to you. If you ask the agent to check your calendar or search memory, it does — because it's a real gateway turn.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the tool-access problem is solved
&lt;/h2&gt;

&lt;p&gt;The key is that &lt;code&gt;chat.send&lt;/code&gt; goes through the full agent runtime, not a sidecar realtime session. The gateway schedules a turn, runs tool calls, awaits results, and returns a response — exactly as it would for a text message. clawcall just wraps this in audio I/O.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// core of the bridge (simplified)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/call/incoming&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transcript&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;stt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audioUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c1"&gt;// STT&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transcript&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// full agent turn&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;tts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;              &lt;span class="c1"&gt;// TTS&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;twiml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                                         &lt;span class="c1"&gt;// back to Twilio&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Production checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Move ngrok to a real public URL (Railway, Render, Fly.io — all work)&lt;/li&gt;
&lt;li&gt;[ ] Add &lt;code&gt;X-Twilio-Signature&lt;/code&gt; validation (&lt;code&gt;twilio.validateRequest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;[ ] Set &lt;code&gt;OPENCLAW_AGENT_ID&lt;/code&gt; to route calls to a specific agent persona&lt;/li&gt;
&lt;li&gt;[ ] Add a per-call session key if you want conversation memory scoped to the call&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/CODEANDTRUST/clawcall" rel="noopener noreferrer"&gt;clawcall on GitHub&lt;/a&gt; — MIT, PRs welcome&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.codeandtrust.com/blog/openclaw-phone-calls" rel="noopener noreferrer"&gt;Full guide with architecture diagram&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Related: &lt;a href="https://github.com/openclaw/openclaw/issues/71262" rel="noopener noreferrer"&gt;openclaw/openclaw#71262&lt;/a&gt; — the upstream issue this approach addresses&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://www.codeandtrust.com" rel="noopener noreferrer"&gt;Code and Trust&lt;/a&gt; — AI agent infrastructure for businesses.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>twilio</category>
      <category>selfhosted</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
