{"intent":"webrtc-calling","title":"Browser-Based Calling with WebRTC","description":"Make and receive phone calls directly from the browser using Telnyx WebRTC. No plugins required — just open the HTML file and start calling.","category":"voice","difficulty":"beginner","time_to_run":"3 minutes","stack":["html","javascript","telnyx-webrtc"],"commands":[{"step":"Open the app","command":"Open index.html in your browser","expected_output":"Phone dialer UI appears"},{"step":"Enter credentials","command":"Enter your SIP credential and caller ID in the settings","expected_output":"Connected! Ready to make calls."},{"step":"Make a call","command":"Dial a phone number and click Call","expected_output":"Ringing... Connected when answered"}],"prerequisites":["Telnyx account","A Telnyx phone number configured for voice","A modern browser (Chrome, Firefox, Edge)","Telnyx SIP Connection credential"],"files":{"index.html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Telnyx WebRTC Phone</title>\n  <style>\n    * { margin: 0; padding: 0; box-sizing: border-box; }\n    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0f172a; color: #e2e8f0; display: flex; justify-content: center; align-items: center; min-height: 100vh; }\n    .phone { background: #1e293b; border-radius: 24px; padding: 32px; width: 360px; box-shadow: 0 25px 50px rgba(0,0,0,0.5); }\n    h1 { font-size: 20px; text-align: center; margin-bottom: 20px; color: #38bdf8; }\n    .status { text-align: center; font-size: 14px; padding: 8px; border-radius: 8px; margin-bottom: 16px; background: #334155; }\n    .status.connected { background: #065f46; color: #6ee7b7; }\n    .status.error { background: #7f1d1d; color: #fca5a5; }\n    .display { background: #0f172a; border-radius: 12px; padding: 16px; margin-bottom: 16px; font-size: 28px; text-align: center; letter-spacing: 2px; min-height: 60px; display: flex; align-items: center; justify-content: center; }\n    .keypad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 16px; }\n    .key { background: #334155; border: none; border-radius: 12px; color: #e2e8f0; font-size: 24px; padding: 16px; cursor: pointer; transition: background 0.15s; }\n    .key:hover { background: #475569; }\n    .key:active { background: #38bdf8; color: #0f172a; }\n    .actions { display: flex; gap: 8px; }\n    .btn { flex: 1; border: none; border-radius: 12px; padding: 16px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.15s; }\n    .btn-call { background: #22c55e; color: #052e16; }\n    .btn-call:hover { background: #16a34a; }\n    .btn-call:disabled { background: #334155; color: #64748b; cursor: not-allowed; }\n    .btn-hangup { background: #ef4444; color: #fff; }\n    .btn-hangup:hover { background: #dc2626; }\n    .btn-hangup:disabled { background: #334155; color: #64748b; cursor: not-allowed; }\n    .settings { margin-top: 16px; }\n    .settings summary { cursor: pointer; color: #94a3b8; font-size: 14px; }\n    .settings label { display: block; margin: 8px 0 4px; font-size: 12px; color: #94a3b8; }\n    .settings input { width: 100%; background: #0f172a; border: 1px solid #334155; border-radius: 8px; padding: 8px 12px; color: #e2e8f0; font-size: 14px; }\n    .settings input:focus { outline: none; border-color: #38bdf8; }\n  </style>\n</head>\n<body>\n  <div class=\"phone\">\n    <h1>📞 Telnyx WebRTC</h1>\n    <div id=\"status\" class=\"status\">Not connected</div>\n    <div class=\"display\" id=\"display\"></div>\n    <div class=\"keypad\">\n      <button class=\"key\" data-digit=\"1\">1</button>\n      <button class=\"key\" data-digit=\"2\">2</button>\n      <button class=\"key\" data-digit=\"3\">3</button>\n      <button class=\"key\" data-digit=\"4\">4</button>\n      <button class=\"key\" data-digit=\"5\">5</button>\n      <button class=\"key\" data-digit=\"6\">6</button>\n      <button class=\"key\" data-digit=\"7\">7</button>\n      <button class=\"key\" data-digit=\"8\">8</button>\n      <button class=\"key\" data-digit=\"9\">9</button>\n      <button class=\"key\" data-digit=\"*\">*</button>\n      <button class=\"key\" data-digit=\"0\">0</button>\n      <button class=\"key\" data-digit=\"#\">#</button>\n    </div>\n    <div class=\"actions\">\n      <button class=\"btn btn-call\" id=\"btnCall\" disabled>Call</button>\n      <button class=\"btn btn-hangup\" id=\"btnHangup\" disabled>Hang Up</button>\n    </div>\n    <details class=\"settings\">\n      <summary>⚙️ Connection Settings</summary>\n      <label for=\"sipUser\">SIP User</label>\n      <input type=\"text\" id=\"sipUser\" placeholder=\"sip_username\" />\n      <label for=\"sipPassword\">SIP Password</label>\n      <input type=\"password\" id=\"sipPassword\" placeholder=\"sip_password\" />\n      <label for=\"callerId\">Caller ID</label>\n      <input type=\"text\" id=\"callerId\" placeholder=\"+15551234567\" />\n    </details>\n  </div>\n  <script src=\"app.js\"></script>\n</body>\n</html>\n","app.js":"// Telnyx WebRTC Phone — Browser-based calling\n// Uses the TelnyxRTC SDK for WebRTC connectivity\n//\n// To use this app:\n// 1. Create a SIP Connection in the Telnyx portal\n// 2. Enter your SIP credentials in the settings panel\n// 3. Click \"Connect\" and start making calls\n\n(function () {\n  \"use strict\";\n\n  // UI Elements\n  const statusEl = document.getElementById(\"status\");\n  const displayEl = document.getElementById(\"display\");\n  const btnCall = document.getElementById(\"btnCall\");\n  const btnHangup = document.getElementById(\"btnHangup\");\n  const sipUserInput = document.getElementById(\"sipUser\");\n  const sipPasswordInput = document.getElementById(\"sipPassword\");\n  const callerIdInput = document.getElementById(\"callerId\");\n\n  let currentCall = null;\n  let client = null;\n\n  // ── Status Updates ──\n  function setStatus(text, type = \"\") {\n    statusEl.textContent = text;\n    statusEl.className = \"status\" + (type ? \" \" + type : \"\");\n  }\n\n  // ── Keypad Input ──\n  let dialedNumber = \"\";\n  document.querySelectorAll(\".key\").forEach((key) => {\n    key.addEventListener(\"click\", () => {\n      const digit = key.getAttribute(\"data-digit\");\n      dialedNumber += digit;\n      displayEl.textContent = dialedNumber;\n      // Send DTMF if in a call\n      if (currentCall && currentCall.state === \"active\") {\n        currentCall.dtmf(digit).catch(console.error);\n      }\n    });\n  });\n\n  // ── Initialize TelnyxRTC Client ──\n  async function connect() {\n    const sipUser = sipUserInput.value.trim();\n    const sipPassword = sipPasswordInput.value.trim();\n\n    if (!sipUser || !sipPassword) {\n      setStatus(\"Enter SIP credentials in settings\", \"error\");\n      return;\n    }\n\n    try {\n      setStatus(\"Connecting...\");\n\n      // Load TelnyxRTC SDK dynamically from CDN\n      if (!window.TelnyxRTC) {\n        await loadScript(\"https://unpkg.com/@telnyx/webrtc@2/cjs/telnyxrtc.js\");\n      }\n\n      const TelnyxRTC = window.TelnyxRTC.default || window.TelnyxRTC;\n\n      client = new TelnyxRTC({\n        login: sipUser,\n        password: sipPassword,\n      });\n\n      // Connection events\n      client.on(\"telnyx.socket.open\", () => {\n        setStatus(\"Connected\", \"connected\");\n        btnCall.disabled = false;\n      });\n\n      client.on(\"telnyx.socket.close\", () => {\n        setStatus(\"Disconnected\", \"error\");\n        btnCall.disabled = true;\n        btnHangup.disabled = true;\n      });\n\n      client.on(\"telnyx.socket.error\", (err) => {\n        setStatus(\"Connection error: \" + (err.message || err), \"error\");\n      });\n\n      // Call events\n      client.on(\"telnyx.notification\", (notification) => {\n        handleNotification(notification);\n      });\n\n      // Connect\n      client.connect();\n    } catch (err) {\n      setStatus(\"Failed to connect: \" + (err.message || err), \"error\");\n    }\n  }\n\n  // ── Handle Call Notifications ──\n  function handleNotification(notification) {\n    const { call } = notification;\n    if (!call) return;\n\n    switch (call.state) {\n      case \"ringing\":\n        // Incoming call\n        setStatus(\"Incoming call from \" + call.remoteCallerNumber);\n        currentCall = call;\n        btnCall.disabled = true;\n        btnHangup.disabled = false;\n        // Auto-answer for demo purposes\n        call.answer();\n        break;\n      case \"active\":\n        setStatus(\"In call\", \"connected\");\n        btnHangup.disabled = false;\n        break;\n      case \"hangup\":\n      case \"destroy\":\n        setStatus(\"Call ended\");\n        currentCall = null;\n        btnCall.disabled = false;\n        btnHangup.disabled = true;\n        dialedNumber = \"\";\n        displayEl.textContent = \"\";\n        break;\n    }\n  }\n\n  // ── Make Outbound Call ──\n  btnCall.addEventListener(\"click\", () => {\n    if (!client || !dialedNumber) return;\n\n    const callerId = callerIdInput.value.trim();\n    setStatus(\"Calling \" + dialedNumber + \"...\");\n\n    currentCall = client.newCall({\n      destinationNumber: dialedNumber,\n      callerNumber: callerId || dialedNumber,\n    });\n\n    btnCall.disabled = true;\n    btnHangup.disabled = false;\n  });\n\n  // ── Hang Up ──\n  btnHangup.addEventListener(\"click\", () => {\n    if (currentCall) {\n      currentCall.hangup();\n    }\n  });\n\n  // ── Auto-connect on credential input ──\n  sipPasswordInput.addEventListener(\"change\", connect);\n\n  // ── Helper: Load script dynamically ──\n  function loadScript(src) {\n    return new Promise((resolve, reject) => {\n      const script = document.createElement(\"script\");\n      script.src = src;\n      script.onload = resolve;\n      script.onerror = () => reject(new Error(\"Failed to load \" + src));\n      document.head.appendChild(script);\n    });\n  }\n})();\n"},"evaluate_command":"curl -s http://localhost:8080 | head -1","telnyx_products":["webrtc","call-control","phone-numbers"],"schema":"telnyx.starter-detail.v1","schema_version":"1.0","canonical_url":"https://telnyx.com/ai/starters/webrtc-calling.json","bias_disclosure":{"maintainer":"Telnyx","data_sources":["telnyx-product-docs"],"permission":"product-evaluation"}}