A quick lookup of everything hanging off context (Node) / Context (C#). For the conceptual tour, see context object. For method-level detail, trust TypeScript IntelliSense or dotnet's IntelliSense — this page is the "what can I type" reference.
| Capability | Node.js | C# |
|---|---|---|
| Key-value cache | context.cache | Context.Cache |
| Blob / file storage | context.storage | Context.Storage |
| SQLite database | context.database (path) | Context.Database.ConnectionString |
| Structured logger | console / context.log | Context.Logger |
| Project + function info | context.config | Context.Configuration |
| Env vars | context.env / process.env | Context.Configuration |
| Request ID | context.requestId | HttpContext.TraceIdentifier |
| Timestamp | context.timestamp | (see request headers) |
| Voice SDK client | context.voice | Context.Voice |
| Conversation SDK | context.conversation | Context.Conversation |
| SMS SDK | context.sms | Context.Sms |
| Numbers SDK | context.numbers | Context.Numbers |
| Verification SDK (C#) | — | Context.Verification |
| Private assets | context.assets(filename) | (package helpers) |
SDK client properties are null / undefined if the required environment variables are missing — always null-check. See SDK env vars for which variables unlock each client.
// Node
await context.cache.set('session:abc', { userId: 'u1' }, 1800); // TTL seconds
const session = await context.cache.get<{ userId: string }>('session:abc');
const exists = await context.cache.has('session:abc');
await context.cache.extend('session:abc', 600);
await context.cache.delete('session:abc');
const keys = await context.cache.keys('session:*');
const many = await context.cache.getMany<Session>(keys);// C#
await Context.Cache.Set("session:abc", new { UserId = "u1" }, 1800);
var session = await Context.Cache.Get<MySession>("session:abc");
if (await Context.Cache.Exists("session:abc"))
await Context.Cache.Delete("session:abc");
var keys = await Context.Cache.GetKeys();Default TTL is 3600 seconds. Values are JSON-serialized.
// Node
await context.storage.write('reports/daily.json', JSON.stringify(data));
const buf = await context.storage.read('reports/daily.json');
const files = await context.storage.list('reports/');
const exists = await context.storage.exists('reports/old.json');
await context.storage.delete('reports/old.json');// C#
await Context.Storage.WriteAsync("reports/daily.json", JsonSerializer.Serialize(data));
var text = await Context.Storage.ReadTextAsync("reports/daily.json");
var bytes = await Context.Storage.ReadAsync("reports/daily.json");
var files = await Context.Storage.ListAsync("reports/");
// Streams for large files
using var stream = await Context.Storage.ReadStreamAsync("large-file.bin");Keys can include / — treat them as folder paths.
// Node — context.database is a file path
import initSqlJs from 'sql.js';
import { readFileSync, writeFileSync, existsSync } from 'fs';
const SQL = await initSqlJs();
const buf = existsSync(context.database) ? readFileSync(context.database) : undefined;
const db = new SQL.Database(buf);
db.run('CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT)');
writeFileSync(context.database, Buffer.from(db.export()));// C# — Context.Database.ConnectionString is an ADO.NET connection string
using var conn = new SqliteConnection(Context.Database.ConnectionString);
conn.Open();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT)";
cmd.ExecuteNonQuery();In production, the platform automatically replicates and backs up the database. No code changes required.
// Node
console.log('Request received', { callId: data.callid });
context.log?.info('Request received', { callId: data.callid });// C#
Logger.LogInformation("Request received from {Cli}", data.Cli);Output is captured and streamed via sinch functions logs.
// Node — raw
context.config.projectId
context.config.functionName
context.config.environment // 'development' | 'production'
context.config.variables // object of sinch.json variables
// Node — UniversalConfig helper
import { createConfig } from '@sinch/functions-runtime';
const config = createConfig(context);
config.requireSecret('STRIPE_SECRET_KEY'); // throws if missing
config.getVariable('COMPANY_NAME', 'Acme Corp');
config.getApplicationCredentials(); // voice app key/secret
config.isDevelopment();
config.isProduction();// C#
var companyName = Configuration["COMPANY_NAME"] ?? "Acme Corp";
var apiKey = Configuration["STRIPE_SECRET_KEY"]
?? throw new InvalidOperationException("STRIPE_SECRET_KEY is required");See configuration & secrets for how the layers fit together.
Initialized by the runtime when the required env vars are present. See SDK env vars for the full list.
// Node — always null-check
if (context.voice) {
await context.voice.callouts.custom({ ... });
}
if (context.conversation) {
await context.conversation.messages.send({ ... });
}
if (context.sms) {
await context.sms.batches.send({ ... });
}// C# — same pattern
if (Context.Voice is not null)
await Context.Voice.Callouts.CustomAsync(...);
if (Context.Conversation is not null)
await Context.Conversation.Messages.Send(...);Private files you ship alongside your code. Do not use fs.readFileSync('./assets/...') — the deployed artifact structure differs from dev.
// Node
const greeting = await context.assets('greetings/en.txt');
const audioBuffer = await context.assets('prompts/welcome.wav');// C# — context.Assets helper
var greeting = await Context.Assets("greetings/en.txt");For public static files (images, CSS), use the public/ directory — the runtime serves it at /.
// Node
context.requestId // tracing ID unique to this invocation
context.timestamp // ISO 8601 when the request entered the runtimeInclude context.requestId in logs and upstream calls for cross-service debugging.
For non-voice custom endpoints, the second argument is a FunctionRequest:
interface FunctionRequest {
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
path: string;
query?: Record<string, string | string[]>;
headers?: Record<string, string>;
body?: unknown;
params?: Record<string, string>;
}And you return a plain { statusCode, body, headers? } object:
return { statusCode: 200, body: { status: 'healthy' } };
return { statusCode: 400, body: { error: 'Missing field: name' } };
return { statusCode: 404, body: { error: 'Not found' } };C# custom endpoints use regular ASP.NET MVC action results (Ok, NotFound, BadRequest, StatusCode, etc.).
- Context object concept — narrative tour of the same APIs
- SDK env vars — which variables unlock which client
- Configuration & secrets — how config flows into the runtime
- Node.js runtime / C# runtime — runtime guides