Every Helix function execution receives a ctx object as its handler argument. It carries the project-scoped key-value store, outbound HTTP including authenticated calls, AI model calls, logging, the validated request input, response helpers, execution metadata, and caller identity. Shared code outside the handler can reach the same object with getContext().
Surface
| Field | Type | Reference |
|---|---|---|
log | ContextLogger | ctx.log |
http | KyInstance | ctx.http |
auth(alias) | Promise<Record<string, string>> | ctx.auth() |
kv() | KeyValueStore | ctx.kv() |
ai(alias) | HelixAIProvider | ctx.ai() |
callFunctionAsync(path, input?) | Promise<void> | ctx.callFunctionAsync() |
input | Validated merged input | Request and response |
method, headers, path | string, Headers, string | Request and response |
request | Raw request accessor | Request and response |
status(code) | void | Request and response |
error(status, message) | HttpError (throw it) | Request and response |
trigger | Trigger data by function type | Request and response |
identity | IdentityContext | null | ctx.identity |
state | Record<string, any> | Request and response |
executionId | string | This page, below |
parentExecutionId | string | null | ctx.callFunctionAsync() |
userId | string | null | This page, below |
projectId | string | null | This page, below |
workspaceId | string | null | This page, below |
organizationId | string | null | This page, below |
kv() takes no argument and is always project-scoped. identity ships, but only user.id populates reliably right now, so treat email, name, groups, roles, and org as incomplete. HTTP requests and cron schedules (defineSchedule) are the triggers a function can have. The shipped data service is the project-scoped key-value store.
Execution metadata
Five fields identify where an execution runs and who invoked it. Each is null when unknown, for example in an unprovisioned local project.
| Field | Meaning |
|---|---|
executionId | UUID per invocation, used for correlation and tracing |
userId | The invoking user, mirrors identity.user.id |
projectId | The project this deployment belongs to |
workspaceId | The workspace this deployment belongs to |
organizationId | The organization this deployment belongs to |
| Field | Production | Local (helix dev) |
|---|---|---|
executionId | Set per invocation | Set per request |
userId | The signed-in caller, when known | From the local session, or null |
projectId | The deployed project | projectId in helix.config.ts (null if unprovisioned) |
workspaceId | The deployment’s workspace | workspaceId in helix.config.ts (null if unset) |
organizationId | The deployment’s organization | From local identity when available, otherwise null |
projectId, workspaceId, and organizationId identify the deployment; userId identifies the caller. See ctx.identity.
getContext()
Handlers receive ctx as a parameter. Shared utilities deeper in the call chain can access the same context without threading it through every call:
import { getContext } from '@trayai/helix-sdk';
function getContext(): FunctionContext
The context is stored in Node.js AsyncLocalStorage, scoped per execution, so concurrent requests never bleed into each other. Calling getContext() outside a function execution throws: “getContext() called outside of a Helix function execution.”
// functions/_shared/salesforce.ts
import { getContext } from '@trayai/helix-sdk';
const AUTH = 'salesforce_prod';
const API = 'https://acme.my.salesforce.com/services/data/v59.0';
export async function query<T = any>(soql: string): Promise<T[]> {
const { http } = getContext(); // no ctx parameter needed
const result = await http
.authed(AUTH)
.get(`${API}/query`, { searchParams: { q: soql } })
.json<{ records: T[] }>();
return result.records;
}
| Situation | Use |
|---|---|
| Function handler (top level) | The ctx parameter |
Shared utilities in _shared/ | getContext() |
| Unit testing shared code | runWithContext() from the testing module |
| Code outside a function execution | Neither works; the error message says so |
runWithContext() for tests
To unit test shared code that calls getContext(), wrap the call in a mock context:
// tests/shared/salesforce.test.ts
import { runWithContext } from '@trayai/helix-sdk/testing';
import { query } from '../functions/_shared/salesforce';
test('query returns contacts', async () => {
const result = await runWithContext(
{ http: mockKyInstance, kv: mockKvStore },
() => query('SELECT Id FROM Contact'),
);
expect(result).toEqual([{ Id: '003xx...' }]);
});
Availability by function type
Not every field exists on every function type. Accessing an unavailable field throws a clear runtime error naming the function types that support it.
| Context field | defineFunction | defineSchedule |
|---|---|---|
log | ✓ | ✓ |
http | ✓ | ✓ |
auth() | ✓ | ✓ |
kv() | ✓ | ✓ |
ai(alias) | ✓ | ✓ |
callFunctionAsync() | ✓ | ✓ |
parentExecutionId | ✓ | ✗ |
input | ✓ | ✗ |
method | ✓ | ✗ |
headers | ✓ | ✗ |
request | ✓ | ✗ |
status() | ✓ | ✗ |
error() | ✓ | ✓ |
trigger.scheduledAt | ✗ | ✓ |
identity | ✓ | ✗ |
state | ✓ | ✗ |
For how each function type is declared and routed, see the functions and routing guide.