{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-docs/functions/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["partial"]},"type":"markdown"},"seo":{"title":"Developer Documentation","siteUrl":"https://developers.sinch.com","llmstxt":{"title":"Sinch Developer Documentation","description":"LLMs.txt containing a map of all the documentation files for Sinch.","sections":[{"title":"SMS API","description":"The SMS API allows you to send and receive SMS messages with a few easy steps. You can also send bulk SMS messages to multiple customers using the Sinch SMS service.","includeFiles":["docs/sms/**/*.md","docs/sms/**/*.yaml"],"excludeFiles":["docs/sms/index.md"]},{"title":"Numbers API","description":"The Numbers API enables you to search for, view, and activate numbers. It's considered a precursor to other APIs in the Sinch product family. The numbers API can be used in tandem with any of our APIs that perform messaging or calling.","includeFiles":["docs/numbers/**/*.md","docs/numbers/**/*.yaml"],"excludeFiles":["docs/numbers/index.md"]},{"title":"Conversation API","description":"Send and receive messages globally on many popular channels with ease and confidence when using Sinch's Conversation API. Conversation API is the preferred API for sending mobile messages on SMS and other social channels with Sinch. It is a simple API with unified error messages, consistent request payloads, and common webhook payloads that are channel-agnostic.","includeFiles":["docs/conversation/**/*.md","docs/conversation/**/*.yaml"],"excludeFiles":["docs/conversation/index.md"]},{"title":"Voice API","description":"The Voice API works as a big telephony switch. The Voice API handles incoming phone calls (also known as incoming call “legs”), sets up outgoing phone calls (or outgoing call “legs”), and bridges the two. The incoming call leg may come in over a data connection (from a smartphone or web application using the Sinch SDKs) or through a local phone number (from the PSTN network). Similarly, the outgoing call leg can be over data (to another smartphone or web application using the Sinch SDKs) or the PSTN network.","includeFiles":["docs/voice/**/*.md","docs/voice/**/*.yaml"],"excludeFiles":["docs/voice/index.md"]},{"title":"Verification API","description":"The Verification API is a platform for phone number verification. It consists of the API and different software development kits (the Sinch SDKs) that you integrate with your smartphone or web application and cloud based back-end services. Together they enable SMS, Flashcall, Phone Call and Data verification in your application.","includeFiles":["docs/verification/**/*.md","docs/verification/**/*.yaml"],"excludeFiles":["docs/verification/index.md"]},{"title":"Provisioning API","description":"Provisioning API allows you to programmatically set up your senders, accounts and templates on your favorite messaging platforms on the Conversation API. For now, you can create your first WhatsApp channel through Meta's Embedded sign up, you can configure your first SMS App and configure your webhooks. As development continues, we will be adding the most commonly used channels.","includeFiles":["docs/provisioning-api/**/*.md","docs/provisioning-api/**/*.json"],"excludeFiles":["docs/provisioning-api/index.md"]},{"title":"Elastic SIP Trunking API","description":"With Elastic SIP Trunking you can create and manage your SIP trunks and phone numbers programmatically.","includeFiles":["docs/est/**/*.md","docs/est/**/*.yaml"],"excludeFiles":["docs/est/index.md"]},{"title":"Fax API","description":"Send and receive HIPAA compliant faxes on our modern fax platform using our developer-friendly API.","includeFiles":["docs/fax/**/*.md","docs/fax/**/*.yaml"],"excludeFiles":["docs/fax/index.md"]},{"title":"In-app Voice and Video SDK","description":"The In-app Voice and Video SDK enables you to add voice and video calling capabilities directly into your mobile or web application using the Sinch SDKs.","includeFiles":["docs/in-app-calling/**/*.md"],"excludeFiles":["docs/in-app-calling/index.md"]}],"hide":false,"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"nodejs-runtime","__idx":0},"children":["Node.js runtime"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["It's Express with conventions."]}," You export handlers, the runtime routes URLs to them, you receive a request and return a response. If you already know Express, skim this page and you are basically done."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Package: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@sinch/functions-runtime"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"project-structure","__idx":1},"children":["Project structure"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A typical Node function looks like this:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"my-function/\n├── function.ts        ← entry point — all exports live here\n├── package.json\n├── tsconfig.json      ← uses the shared config, do not change module settings\n├── sinch.json         ← project manifest (name, runtime, variables, secrets)\n├── .env               ← local dev secrets (gitignored)\n├── assets/            ← private files, read with context.assets()\n└── public/            ← static files, served at /\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All source files live at the project root — the runtime expects them flat. You can split logic into ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["harness.ts"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["db.ts"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["voice.ts"]},", etc., and import them from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["function.ts"]},". Relative imports use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".js"]}," extensions because the shared tsconfig uses NodeNext:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { handleIce } from './voice.js';\nimport { handleMessage } from './harness.js';\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"hello-world","__idx":2},"children":["Hello world"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"// function.ts\nimport type { VoiceFunction } from '@sinch/functions-runtime';\nimport { IceSvamlBuilder } from '@sinch/functions-runtime';\n\nexport default {\n  async ice(context, data) {\n    return new IceSvamlBuilder()\n      .say('Hello from Sinch Functions!')\n      .hangup()\n      .build();\n  },\n} satisfies VoiceFunction;\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["That's a complete, deployable voice function. Save, run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch functions dev"]},", point a Sinch number at the tunnel URL, and call it."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"handlers-the-express-part","__idx":3},"children":["Handlers: the Express part"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every named property on the default export becomes an HTTP endpoint. The runtime matches request paths to export names:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Request path"},"children":["Request path"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Export called"},"children":["Export called"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Treated as"},"children":["Treated as"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /ice"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ice"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Voice callback"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /ace"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ace"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Voice callback"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /pie"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pie"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Voice callback"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /dice"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dice"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Voice callback"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /webhook/conversation"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["conversationWebhook"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Conversation"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /api/health"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["health"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Custom HTTP"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /api/v2/users"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["users"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Custom HTTP"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["default"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["home"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Custom HTTP root"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["handlers"]}," for the full rules. Short version: the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["last path segment"]}," is matched to an export name, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ice"]},"/",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ace"]},"/",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pie"]},"/",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dice"]},"/",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["notify"]}," are always voice callbacks, and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/webhook/<service>"]}," maps to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<service>Webhook"]}," camelCased."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"the-context-argument","__idx":4},"children":["The context argument"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every handler receives a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FunctionContext"]}," as its first argument. It gives you cache, storage, database, a logger, and pre-wired SDK clients:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"async ice(context, data) {\n  await context.cache.set(`call:${data.callid}`, { cli: data.cli }, 3600);\n  const greeting = await context.assets('greetings/en.txt');\n\n  return new IceSvamlBuilder()\n    .say(greeting)\n    .runMenu(MenuTemplates.business(context.config.variables.COMPANY_NAME))\n    .build();\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["context object"]}," for the full tour, or ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/docs/functions/reference/function-context"},"children":["function context reference"]}," for the Node + C# side-by-side cheat sheet."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"custom-http-endpoints","__idx":5},"children":["Custom HTTP endpoints"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Any named export that is not a voice callback becomes a custom HTTP endpoint. Signature is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["(context, request)"]}," and you return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{ statusCode, body, headers? }"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import type { FunctionContext, FunctionRequest } from '@sinch/functions-runtime';\n\nexport async function status(context: FunctionContext, request: FunctionRequest) {\n  return { statusCode: 200, body: { status: 'healthy', uptime: process.uptime() } };\n}\n\nexport async function createUser(context: FunctionContext, request: FunctionRequest) {\n  const body = request.body as { name?: string };\n  if (!body.name) {\n    return { statusCode: 400, body: { error: 'Missing name' } };\n  }\n  return { statusCode: 201, body: { id: 'u_123', name: body.name } };\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FunctionRequest"]}," has ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["method"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["path"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["query"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["headers"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["body"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["params"]},". Return any ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{ statusCode, body, headers? }"]}," object."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["add a custom endpoint"]}," for a fuller example."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"responding-to-voice-callbacks","__idx":6},"children":["Responding to voice callbacks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Voice callbacks return SVAML via one of the three builders. Each callback type has its own builder with the right subset of actions:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { IceSvamlBuilder, AceSvamlBuilder, PieSvamlBuilder } from '@sinch/functions-runtime';\n\n// ICE — full action surface\nreturn new IceSvamlBuilder()\n  .say('Welcome!')\n  .connectPstn('+15551234567', { cli: data.cli })\n  .build();\n\n// ACE — continue or hangup only\nreturn new AceSvamlBuilder().continue().build();\n\n// PIE — respond to a menu result\nreturn new PieSvamlBuilder()\n  .say('Connecting you to sales.')\n  .connectPstn('+15551111111')\n  .build();\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/docs/functions/reference/svaml-cheatsheet"},"children":["SVAML cheat sheet"]}," for the most-used actions and ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["voice callbacks"]}," for the lifecycle."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"conversation-webhooks","__idx":7},"children":["Conversation webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Two approaches:"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Simple:"]}," export a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["conversationWebhook"]}," function and parse the body yourself."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Structured:"]}," extend ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ConversationController"]}," and override the event methods you care about."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import type { FunctionContext } from '@sinch/functions-runtime';\nimport { ConversationController, getText, getChannel, getContactId } from '@sinch/functions-runtime';\n\nclass MyBot extends ConversationController {\n  async handleMessageInbound(event) {\n    const text = getText(event);\n    const channel = getChannel(event);\n\n    await this.conversation!.messages.send({\n      sendMessageRequestBody: this.reply(event, `You said: ${text}`),\n    });\n  }\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Helper functions for navigating inbound events without nested property access:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import {\n  getText, getMedia, getPostbackData, getContactId, getConversationId,\n  getChannel, getIdentity, getTo, getLocation,\n  isTextMessage, isMediaMessage, isPostback,\n} from '@sinch/functions-runtime';\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["build an SMS responder"]}," for a worked example."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"startup-hook-and-websockets","__idx":8},"children":["Startup hook and WebSockets"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Export a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["setup()"]}," function to run initialization before the server accepts requests, or to register WebSocket endpoints (e.g., for ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["connectStream"]}," audio bridging)."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import type { SinchRuntime, FunctionContext } from '@sinch/functions-runtime';\n\nexport function setup(runtime: SinchRuntime) {\n  runtime.onStartup(async (context: FunctionContext) => {\n    // Create tables, warm caches, open connections\n  });\n\n  runtime.onWebSocket('/stream', (ws, req) => {\n    ws.on('message', (data) => { /* audio frames */ });\n  });\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SinchRuntime"]}," is intentionally narrow. You cannot reach the Express app or add middleware — the runtime guarantees platform middleware runs regardless of what your function does."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"universalconfig","__idx":9},"children":["UniversalConfig"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Convenience wrapper around ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["context.config"]}," and secrets:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { createConfig } from '@sinch/functions-runtime';\n\nconst config = createConfig(context);\n\nconst apiKey = config.requireSecret('STRIPE_SECRET_KEY');   // throws if missing\nconst company = config.getVariable('COMPANY_NAME', 'Acme Corp');\nif (config.isProduction()) { /* prod only */ }\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"voice-utilities","__idx":10},"children":["Voice utilities"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import {\n  toLocalPhoneNumber,    // '+15551234567' → '(555) 123-4567'\n  formatDuration,        // 150 → '2m 30s'\n  extractCallerNumber,   // strip non-digits\n  VoiceErrorHelper,      // .serviceUnavailable(), .invalidInput(), .goodbye()\n} from '@sinch/functions-runtime';\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"do-and-dont","__idx":11},"children":["Do and don't"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Do"]}," use the builders for SVAML — never return raw JSON from voice callbacks."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Do"]}," use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["context.assets(...)"]}," for private files, not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fs.readFileSync"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Do"]}," use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".js"]}," extension in relative imports — the tsconfig is NodeNext."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Don't"]}," add instructions after calling ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".build()"]}," — the builder is frozen at that point."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Don't"]}," use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AceSvamlBuilder"]}," to connect calls — ACE only supports ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["continue"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["hangup"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Don't"]}," override ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["module"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["moduleResolution"]}," in your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tsconfig.json"]}," — the shared base handles it."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"related","__idx":12},"children":["Related"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["Handlers concept"]}," — URL-to-handler mapping in detail"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["Context object concept"]}," — cache/storage/database/SDK clients"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/docs/functions/reference/function-context"},"children":["Function context reference"]}," — side-by-side cheat sheet"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/docs/functions/reference/svaml-cheatsheet"},"children":["SVAML cheat sheet"]}," — most-used actions"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["C# runtime"]}," — the same thing for .NET devs"]}]}]},"headings":[{"value":"Node.js runtime","id":"nodejs-runtime","depth":1},{"value":"Project structure","id":"project-structure","depth":2},{"value":"Hello world","id":"hello-world","depth":2},{"value":"Handlers: the Express part","id":"handlers-the-express-part","depth":2},{"value":"The context argument","id":"the-context-argument","depth":2},{"value":"Custom HTTP endpoints","id":"custom-http-endpoints","depth":2},{"value":"Responding to voice callbacks","id":"responding-to-voice-callbacks","depth":2},{"value":"Conversation webhooks","id":"conversation-webhooks","depth":2},{"value":"Startup hook and WebSockets","id":"startup-hook-and-websockets","depth":2},{"value":"UniversalConfig","id":"universalconfig","depth":2},{"value":"Voice utilities","id":"voice-utilities","depth":2},{"value":"Do and don't","id":"do-and-dont","depth":2},{"value":"Related","id":"related","depth":2}],"frontmatter":{"seo":{"title":""}},"lastModified":"2026-04-15T14:23:23.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/docs/functions/runtimes/nodejs","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}