{"intent":"fax-api","title":"Send and Receive Faxes Programmatically","description":"Use the Telnyx Fax API to send faxes from any URL and receive inbound faxes via webhook. Automate fax workflows for healthcare, legal, and enterprise use cases.","category":"messaging","difficulty":"beginner","time_to_run":"5 minutes","stack":["node.js","typescript","telnyx-sdk"],"commands":[{"step":"Clone and install","command":"mkdir fax-api && cd fax-api && cp -r ../starter/* . && npm install","expected_output":"added 10 packages in 2s"},{"step":"Configure environment","command":"cp .env.example .env && edit .env with your API key and phone number","expected_output":".env file created"},{"step":"Send a test fax","command":"npx tsx send-fax.ts","expected_output":"Fax sent! ID: fax_..."},{"step":"Start receiving faxes","command":"npx tsx receive-fax.ts","expected_output":"Fax receiver listening on http://localhost:3000"}],"prerequisites":["Telnyx account with fax enabled","Node.js 24+","A Telnyx phone number with fax capability","A publicly accessible document URL for sending"],"files":{"send-fax.ts":"import Telnyx from \"telnyx\";\n\n// Load environment variables\nconst TELNYX_API_KEY = process.env.TELNYX_API_KEY;\nconst TELNYX_NUMBER = process.env.TELNYX_NUMBER;\nconst FAX_DESTINATION = process.env.FAX_DESTINATION || \"+15559876543\";\nconst FAX_DOCUMENT_URL = process.env.FAX_DOCUMENT_URL;\n\nif (!TELNYX_API_KEY || !TELNYX_NUMBER) {\n  console.error(\"Missing required environment variables. Check .env file.\");\n  process.exit(1);\n}\n\nif (!FAX_DOCUMENT_URL) {\n  console.error(\"FAX_DOCUMENT_URL is required. Provide a publicly accessible URL to a PDF.\");\n  process.exit(1);\n}\n\nconst telnyx = new Telnyx(TELNYX_API_KEY);\n\nasync function sendFax() {\n  try {\n    console.log(`Sending fax from ${TELNYX_NUMBER} to ${FAX_DESTINATION}...`);\n    console.log(`Document: ${FAX_DOCUMENT_URL}`);\n\n    const fax = await telnyx.faxes.create({\n      from: TELNYX_NUMBER,\n      to: FAX_DESTINATION,\n      media_url: FAX_DOCUMENT_URL,\n    });\n\n    console.log(`✓ Fax sent successfully!`);\n    console.log(`  Fax ID: ${fax.id}`);\n    console.log(`  Status: ${fax.status}`);\n    console.log(`  From: ${fax.from}`);\n    console.log(`  To: ${fax.to}`);\n    console.log(`  Pages: ${fax.page_count || \"pending\"}`);\n    console.log(\"\\nTrack status via webhook or poll the Fax API.\");\n  } catch (error: any) {\n    console.error(\"Failed to send fax:\", error.message || error);\n    if (error.raw?.message) {\n      console.error(\"Details:\", error.raw.message);\n    }\n    process.exit(1);\n  }\n}\n\nsendFax();\n","receive-fax.ts":"import express from \"express\";\nimport Telnyx from \"telnyx\";\nimport { createHmac } from \"crypto\";\nimport { writeFile, mkdir } from \"fs/promises\";\n{/*\n * Load environment variables\n */}\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\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 hmac = createHmac(\"sha256\", TELNYX_PUBLIC_KEY);\n    hmac.update(payload);\n    const digest = hmac.digest(\"hex\");\n    return signature === digest;\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// Receive fax status via webhook\napp.post(\"/webhook\", async (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    switch (event_type) {\n      case \"fax.received\": {\n        const { from, to, media_url, page_count } = data.payload;\n        console.log(`📠 Fax received from ${from} to ${to}`);\n        console.log(`  Pages: ${page_count}`);\n        console.log(`  Media URL: ${media_url}`);\n\n        // Download the fax document\n        if (media_url) {\n          try {\n            const response = await fetch(media_url, {\n              headers: { Authorization: `Bearer ${TELNYX_API_KEY}` },\n            });\n            if (response.ok) {\n              const buffer = Buffer.from(await response.arrayBuffer());\n              await mkdir(\"./received-faxes\", { recursive: true });\n              const filename = `./received-faxes/fax-${Date.now()}.pdf`;\n              await writeFile(filename, buffer);\n              console.log(`  Saved to: ${filename}`);\n            }\n          } catch (downloadErr) {\n            console.error(\"  Failed to download fax:\", downloadErr);\n          }\n        }\n        break;\n      }\n      case \"fax.send.failed\": {\n        console.error(`❌ Fax send failed: ${JSON.stringify(data.payload)}`);\n        break;\n      }\n      case \"fax.send.completed\": {\n        console.log(`✅ Fax send completed: ${data.payload.fax_id}`);\n        break;\n      }\n      default:\n        console.log(`Fax event: ${event_type}`);\n    }\n\n    // Always respond 200 to Telnyx webhooks\n    res.status(200).json({ received: true });\n  } catch (error) {\n    console.error(\"Webhook processing error:\", error);\n    res.status(200).json({ received: true });\n  }\n});\n\napp.listen(PORT, () => {\n  console.log(`Fax receiver 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 for inbound faxes.\");\n});\n","package.json":"{\n  \"name\": \"fax-api\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"engines\": {\n    \"node\": \">=24\"\n  },\n  \"scripts\": {\n    \"send\": \"npx tsx send-fax.ts\",\n    \"receive\": \"npx tsx receive-fax.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 fax capability)\nTELNYX_NUMBER=+15551234567\n\n# Destination fax number\nFAX_DESTINATION=+15559876543\n\n# URL to a publicly accessible PDF document to fax\nFAX_DOCUMENT_URL=https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf\n\n# Telnyx public key for webhook verification (find in portal settings)\nTELNYX_PUBLIC_KEY=\n\n# Server port (for receive-fax)\nPORT=3000\n"},"evaluate_command":"curl -s http://localhost:3000/health | jq .status","telnyx_products":["fax","phone-numbers"],"schema":"telnyx.starter-detail.v1","schema_version":"1.0","canonical_url":"https://telnyx.com/ai/starters/fax-api.json","bias_disclosure":{"maintainer":"Telnyx","data_sources":["telnyx-product-docs"],"permission":"product-evaluation"}}