# Configuration & secrets 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 vs secrets | | 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 `.gitignore`d 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 | ## Declaring variables — `sinch.json` ```json { "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. ## Declaring secrets — `.env` and `sinch secrets` 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. ```bash # .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: ```bash 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_KEY ``` Secrets are scoped per function — the function name in `sinch.json` is the keychain namespace. Two functions with different names have isolated secret stores. ### Where the keychain actually is | 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. ## Reading configuration from your handler ### Node.js — `createConfig` The richest API is `createConfig(context)` (aka `createUniversalConfig`): ```typescript 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: ```typescript const companyName = process.env.COMPANY_NAME ?? 'Acme Corp'; ``` ### C# — `IConfiguration` C# gets `IConfiguration` injected into controllers by ASP.NET: ```csharp 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. ## The base Sinch credentials 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. ## Environment variables that unlock SDK clients 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. ## Environment detection 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. ## How it all flows together ``` 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. ## Related - [Context object](#) — how to access variables from your handler code - [`sinch secrets` commands](/docs/functions/cli/commands/secrets) — full CLI reference - [Local vs prod](#) — what else changes between environments