Every non-trivial function needs configuration: a company name for the welcome message, API keys for upstream services, phone numbers for routing. Sinch Functions draws a hard line between two things:
- Variables — non-sensitive settings that can live in source control.
- Secrets — sensitive values that must never touch source control.
They flow through different mechanisms, and the distinction is worth understanding before you start storing things.
| Variables | Secrets | |
|---|---|---|
| Examples | Company name, support number, feature flags | API keys, passwords, client secrets |
| Stored in | sinch.json → variables | OS keychain (local) + platform secret store (production) |
| Committed? | Yes — checked into your repo | No — .env is .gitignored by default |
| Updated with | Edit sinch.json + redeploy | sinch secrets add KEY value |
| Visible in logs | Plain text | Redacted |
| Available as | process.env.FOO / IConfiguration["FOO"] | Same — runtime injects both into env vars |
{
"name": "my-function",
"runtime": "nodejs20",
"variables": {
"COMPANY_NAME": "Acme Corp",
"SUPPORT_NUMBER": "+15551234567",
"ENABLE_RECORDING": "true"
}
}Variables are bundled into the deploy and loaded into the runtime's environment on startup. To update one, edit sinch.json and run sinch functions deploy again.
Your project's .env file lists the secret keys your function needs. The values are left empty because the CLI loads them from the OS keychain at dev time.
# .env — keys only, values come from the keychain
PROJECT_ID=
PROJECT_ID_API_KEY=
PROJECT_ID_API_SECRET=
STRIPE_SECRET_KEY=
ELEVENLABS_API_KEY=Store the values once with the CLI:
sinch secrets add STRIPE_SECRET_KEY sk_live_abc123
sinch secrets add ELEVENLABS_API_KEY ...
sinch secrets list
sinch secrets get STRIPE_SECRET_KEY
sinch secrets delete STRIPE_SECRET_KEYSecrets are scoped per function — the function name in sinch.json is the keychain namespace. Two functions with different names have isolated secret stores.
| Platform | Storage backend |
|---|---|
| Windows | Windows Credential Manager |
| macOS | Keychain Access |
| Linux | Secret Service API (libsecret / GNOME Keyring) |
On Linux without a graphical session you may need to install libsecret and configure a keychain daemon.
The richest API is createConfig(context) (aka createUniversalConfig):
import { createConfig } from '@sinch/functions-runtime';
const config = createConfig(context);
const apiKey = config.requireSecret('STRIPE_SECRET_KEY'); // throws if missing
const companyName = config.getVariable('COMPANY_NAME', 'Acme Corp');
const creds = config.getApplicationCredentials(); // voice app key/secret
if (config.isDevelopment()) { /* local dev only */ }
if (config.isProduction()) { /* deployed only */ }process.env still works if you want the raw access:
const companyName = process.env.COMPANY_NAME ?? 'Acme Corp';C# gets IConfiguration injected into controllers by ASP.NET:
var companyName = Configuration["COMPANY_NAME"] ?? "Acme Corp";
var apiKey = Configuration["STRIPE_SECRET_KEY"]
?? throw new InvalidOperationException("STRIPE_SECRET_KEY is required");IConfiguration reads from environment variables (what the runtime populates from variables + secrets) and from appsettings.json if you have one.
Every function needs three environment variables for the Sinch SDK clients to initialize. They come from sinch auth login and are stored in your OS keychain:
| Variable | Source |
|---|---|
PROJECT_ID | sinch auth login |
PROJECT_ID_API_KEY | sinch auth login |
PROJECT_ID_API_SECRET | sinch auth login |
These are automatically injected during sinch functions dev and shipped with each deploy — you do not need to manage them in .env yourself.
The runtime initializes a Sinch SDK client only if its required environment variables are present. Set these either via sinch.json variables (if they are non-secret identifiers) or via sinch secrets add (if they are credentials).
| Client | Required variables |
|---|---|
| Voice | VOICE_APPLICATION_KEY, VOICE_APPLICATION_SECRET |
| Conversation | CONVERSATION_APP_ID (+ optional CONVERSATION_REGION: US/EU/BR) |
| SMS | SMS_SERVICE_PLAN_ID |
| Numbers | ENABLE_NUMBERS_API=true |
| Verification (C#) | VERIFICATION_APPLICATION_ID, VERIFICATION_APPLICATION_SECRET |
If a variable is missing, the corresponding context.voice / context.conversation / etc. property is null and you should check before using it.
The runtime sets these automatically:
| Environment | Node.js | C# |
|---|---|---|
| Local dev | NODE_ENV=development | ASPNETCORE_ENVIRONMENT=Development |
| Production | NODE_ENV=production | ASPNETCORE_ENVIRONMENT=Production |
Use them for dev-only logging, feature toggles, or bypassing integrations that do not exist locally.
sinch auth login ──▶ OS keychain ──┐
│
sinch secrets add KEY val ─▶ keychain┤
├──▶ sinch functions dev ──▶ process.env in your local runtime
sinch.json (variables) ────────┤
│
sinch.json (variables) ────────┤
├──▶ sinch functions deploy ─▶ Sinch platform secret store ─▶ process.env in production
sinch secrets add KEY val ─▶ keychain┘The CLI reconciles the three sources (keychain, sinch.json, .env) at deploy time, uploads everything, and the platform injects the final set as environment variables when your container starts. Secrets are encrypted at rest on the platform and redacted from logs.
- Context object — how to access variables from your handler code
sinch secretscommands — full CLI reference- Local vs prod — what else changes between environments