# 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 — the CLI loads the real values from the OS keychain at dev time, and substitutes them at deploy 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 — running `sinch secrets add` from your function directory also adds the empty `KEY=` line to `.env` for you, so you don't have to keep `.env` in sync by hand:

```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
```

> If `.env` already declares the key with a non-empty value, that value wins over the keychain at deploy. Clear the value (`KEY=`) to use the keychain value instead.


### Recovering a missing .env

`sinch functions deploy` refuses to deploy a Node.js function when `.env` is missing — without it the deployed function has no configuration and will not run. Run `sinch env init` to scaffold a new one from your `sinch.json` variables and stored credentials:

```bash
sinch env init
```

Two things it cannot bring back: secret **values** (they stay in the keychain and resolve at deploy, so they are written as empty `KEY=` placeholders) and any non-secret values you had typed in by hand — re-enter those. Pass `--force` to overwrite an existing `.env`.

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](/docs/functions/functions/concepts/context-object) — how to access variables from your handler code
- [`sinch secrets` commands](/docs/functions/cli/commands/secrets) — full CLI reference
- [`sinch env` commands](/docs/functions/cli/commands/env) — recreate a missing `.env`
- [Local vs prod](/docs/functions/functions/concepts/local-vs-prod) — what else changes between environments