{"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":"local-vs-production","__idx":0},"children":["Local vs production"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Sinch runtime is designed so that the code you write runs the same in both environments. No \"if (dev) use this library, else use that one\" branches scattered through your function. The differences are swapped in at build time, not in your code."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"two-packages-per-runtime-one-import-path","__idx":1},"children":["Two packages per runtime, one import path"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each runtime publishes two flavors of its package:"]},{"$$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":""},"children":[]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Dev package"},"children":["Dev package"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Prod package"},"children":["Prod package"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["What you install"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@sinch/functions-runtime"]}," (Node) / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Sinch.Functions.Runtime"]}," (NuGet)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Same name"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["What you import"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@sinch/functions-runtime"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Same name"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Cache"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["In-memory, lost on restart"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Persistent, shared across invocations"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Storage"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Local filesystem (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["./storage/"]},")"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Durable cloud storage with local read cache"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Database"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Plain SQLite file in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["./data/"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["SQLite with automatic durable replication"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Secrets"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["OS keychain + ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]}," file"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Platform secret store, injected as env vars"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Tunnel"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Local tunnel via ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch functions dev"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Not present — the function has a real URL"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Logging"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Console output"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Captured, indexed, streamed via ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch functions logs"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You never reference the prod package directly. The build pipeline swaps it in during ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch functions deploy"]},", so your imports stay the same and your code is identical."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"what-this-means-for-your-code","__idx":2},"children":["What this means for your code"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can write:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"await context.cache.set('session:abc', data, 1800);\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["And it works locally (in-memory) and in production (distributed cache) with zero changes. Same for storage, database, and SDK clients."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The few places where you ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["do"]}," need to care about the environment:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Log verbosity."]}," You may want more detail in dev — ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["createConfig(context).isDevelopment()"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["NODE_ENV === 'development'"]}," gives you the switch."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["External integrations with dev vs prod accounts."]}," If you use Stripe test vs live keys, or two different Voice Apps, your own configuration decides which to use — set different secrets per environment via ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch secrets add"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Behavior that should never run locally."]}," For example, \"send a real SMS to the customer\" might be skipped when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["isDevelopment()"]},"."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"what-local-dev-gives-you-that-production-doesnt","__idx":3},"children":["What local dev gives you that production doesn't"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"hot-reload","__idx":4},"children":["Hot reload"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Node:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tsup"]}," watches your source and rebuilds on save."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["C#:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dotnet watch"]}," rebuilds and restarts."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your function reloads in under a second — no deploy loop to iterate."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"public-tunnel","__idx":5},"children":["Public tunnel"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch functions dev"]}," can open a public tunnel that points at your local process. This is how Sinch reaches your laptop. When you accept the tunnel prompt, the Sinch CLI optionally updates your Voice App's callback URL to the tunnel URL so real calls hit your laptop directly."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When the dev server exits, the tunnel closes and the CLI reverts any callback URLs it touched."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"interactive-logs-tui","__idx":6},"children":["Interactive logs TUI"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch functions logs --follow"]}," opens a terminal UI that shows each incoming request with full payload, headers, and response. Arrow keys to navigate, Enter to expand, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["q"]}," to quit. Much faster than grepping a log file."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"what-production-gives-you-that-local-dev-doesnt","__idx":7},"children":["What production gives you that local dev doesn't"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A stable URL."]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://fn-<id>.functions.sinch.com"]}," — doesn't change between deploys."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Auto-scaling."]}," The platform spins more instances up under load."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Persistent cache and storage."]}," Nothing disappears when the process restarts."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Platform middleware."]}," Billing, logging, webhook signature validation, and error reporting all run automatically."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Durable database replication."]}," Your SQLite database is continuously backed up, so it survives restarts and scale events."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"the-feature-parity-contract","__idx":8},"children":["The \"feature parity\" contract"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every API on the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FunctionContext"]}," — cache, storage, database, SDK clients, logger — behaves the same in both environments. If it works locally, it works deployed. If it breaks in one but not the other, that is a runtime bug, not something you should work around in your code."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The one practical caveat: dev storage is on your laptop's disk, so you will not hit production-scale limits locally. Test with realistic payload sizes before shipping anything that writes a lot of data."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"related","__idx":9},"children":["Related"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["Configuration & secrets"]}," — how variables and secrets flow into the runtime"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["Deployment"]}," — what actually happens when you run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch functions deploy"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"#"},"children":["Context object"]}," — the APIs that are consistent across dev and prod"]}]}]},"headings":[{"value":"Local vs production","id":"local-vs-production","depth":1},{"value":"Two packages per runtime, one import path","id":"two-packages-per-runtime-one-import-path","depth":2},{"value":"What this means for your code","id":"what-this-means-for-your-code","depth":2},{"value":"What local dev gives you that production doesn't","id":"what-local-dev-gives-you-that-production-doesnt","depth":2},{"value":"Hot reload","id":"hot-reload","depth":3},{"value":"Public tunnel","id":"public-tunnel","depth":3},{"value":"Interactive logs TUI","id":"interactive-logs-tui","depth":3},{"value":"What production gives you that local dev doesn't","id":"what-production-gives-you-that-local-dev-doesnt","depth":2},{"value":"The \"feature parity\" contract","id":"the-feature-parity-contract","depth":2},{"value":"Related","id":"related","depth":2}],"frontmatter":{"seo":{"title":""}},"lastModified":"2026-04-15T14:23:23.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/docs/functions/concepts/local-vs-prod","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}