{"intent":"sms-api","title":"Send and Receive SMS","description":"Build an SMS application that sends messages via the Telnyx Messaging API and receives inbound messages through a webhook endpoint. Perfect for notifications, alerts, and two-way messaging.","category":"messaging","difficulty":"beginner","time_to_run":"5 minutes","stack":["node.js","typescript","express","telnyx-sdk"],"commands":[{"step":"Clone and install","command":"mkdir sms-api && cd sms-api && cp -r ../starter/* . && npm install","expected_output":"added 25 packages in 3s"},{"step":"Configure environment","command":"cp .env.example .env && edit .env with your API key and phone number","expected_output":".env file created"},{"step":"Run the server","command":"npx tsx server.ts","expected_output":"SMS server listening on http://localhost:3000"},{"step":"Send a test SMS","command":"curl -X POST http://localhost:3000/send -H 'Content-Type: application/json' -d '{\"to\":\"+15559876543\",\"text\":\"Hello from Telnyx!\"}'","expected_output":"{\"success\":true,\"message_id\":\"msg_...\"}"}],"prerequisites":["Telnyx account with messaging enabled","Node.js 24+","A Telnyx phone number with messaging capability","A public URL for webhook (use ngrok for local development)"],"files":{"server.ts":"import express from \"express\";\nimport Telnyx from \"telnyx\";\nimport { verify as cryptoVerify } from \"crypto\";\n\n// Load environment variables\nconst TELNYX_API_KEY = process.env.TELNYX_API_KEY;\nconst TELNYX_NUMBER = process.env.TELNYX_NUMBER;\nconst TELNYX_PUBLIC_KEY = process.env.TELNYX_PUBLIC_KEY || \"\";\nconst PORT = parseInt(process.env.PORT || \"3000\", 10);\n\nif (!TELNYX_API_KEY || !TELNYX_NUMBER) {\n  console.error(\"Missing required environment variables. Check .env file.\");\n  process.exit(1);\n}\n\nconst telnyx = new Telnyx(TELNYX_API_KEY);\nconst app = express();\napp.use(express.json());\n\n// Verify Telnyx webhook signatures using Ed25519\nfunction verifyWebhookSignature(payload: string, signature: string): boolean {\n  if (!TELNYX_PUBLIC_KEY) {\n    console.warn(\"TELNYX_PUBLIC_KEY not set — skipping webhook verification\");\n    return true;\n  }\n  try {\n    const signatureBytes = Buffer.from(signature, \"utf-8\");\n    const payloadBytes = Buffer.from(payload, \"utf-8\");\n    const publicKeyBytes = Buffer.from(TELNYX_PUBLIC_KEY, \"base64\");\n    return cryptoVerify(null, payloadBytes, publicKeyBytes, signatureBytes);\n  } catch {\n    return false;\n  }\n}\n\n// Health check\napp.get(\"/health\", (_req, res) => {\n  res.json({ status: \"ok\", number: TELNYX_NUMBER });\n});\n\n// Send an SMS\napp.post(\"/send\", async (req, res) => {\n  try {\n    const { to, text } = req.body;\n\n    if (!to || !text) {\n      res.status(400).json({ error: \"Missing 'to' or 'text' in request body\" });\n      return;\n    }\n\n    const message = await telnyx.messages.create({\n      from: TELNYX_NUMBER,\n      to,\n      text,\n    });\n\n    res.json({\n      success: true,\n      message_id: message.id,\n      status: message.status,\n    });\n  } catch (error: any) {\n    console.error(\"Error sending SMS:\", error.message || error);\n    res.status(500).json({ error: \"Failed to send message\", details: error.message });\n  }\n});\n\n// Receive inbound SMS via webhook\napp.post(\"/webhook\", (req, res) => {\n  try {\n    const signature = req.headers[\"telnyx-signature-ed25519\"] as string;\n    const timestamp = req.headers[\"telnyx-timestamp\"] as string;\n\n    if (signature && TELNYX_PUBLIC_KEY) {\n      const payload = timestamp + JSON.stringify(req.body);\n      if (!verifyWebhookSignature(payload, signature)) {\n        res.status(401).json({ error: \"Invalid signature\" });\n        return;\n      }\n    }\n\n    const { data } = req.body;\n    const { event_type } = data;\n\n    if (event_type === \"message.received\") {\n      const { from, to, text } = data.payload;\n      console.log(`📥 SMS received from ${from} to ${to}: ${text}`);\n\n      // Process the inbound message here\n      // Example: auto-reply, store in database, trigger workflow\n    }\n\n    // Always respond 200 to Telnyx webhooks quickly\n    res.status(200).json({ received: true });\n  } catch (error) {\n    console.error(\"Webhook processing error:\", error);\n    res.status(200).json({ received: true }); // Still 200 to prevent retries\n  }\n});\n\napp.listen(PORT, () => {\n  console.log(`SMS server listening on http://localhost:${PORT}`);\n  console.log(`Health check: http://localhost:${PORT}/health`);\n  console.log(`Webhook URL: http://localhost:${PORT}/webhook`);\n  console.log(\"\\nSet your webhook URL in the Telnyx portal:\");\n  console.log(`  https://portal.telnyx.com/#/app/phone-numbers`);\n});\n","package.json":"{\n  \"name\": \"sms-api\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"engines\": {\n    \"node\": \">=24\"\n  },\n  \"scripts\": {\n    \"start\": \"npx tsx server.ts\"\n  },\n  \"dependencies\": {\n    \"express\": \"^4.18.2\",\n    \"telnyx\": \"^2.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/express\": \"^4.17.21\",\n    \"tsx\": \"^4.7.0\"\n  }\n}\n",".env.example":"# Get your API key at https://portal.telnyx.com/\nTELNYX_API_KEY=your_api_key_here\n\n# Your Telnyx phone number (E.164 format, must have messaging enabled)\nTELNYX_NUMBER=+15551234567\n\n# Telnyx public key for webhook verification (find in portal settings)\nTELNYX_PUBLIC_KEY=\n\n# Server port\nPORT=3000\n"},"evaluate_command":"curl -s http://localhost:3000/health | jq .status","telnyx_products":["messaging","phone-numbers"],"schema":"telnyx.starter-detail.v1","schema_version":"1.0","canonical_url":"https://telnyx.com/ai/starters/sms-api.json","bias_disclosure":{"maintainer":"Telnyx","data_sources":["telnyx-product-docs"],"permission":"product-evaluation"}}