ctx.callFunctionAsync(path, input?) queues a call to another route in the same project, which runs as a new, independent execution. The call resolves once accepted, not once the target finishes.
Signature
callFunctionAsync(path: string, input?: unknown): Promise<void>
| Parameter | Type | Description |
|---|---|---|
path | string | The target route’s path, for example 'leads/enrich'. Not validated against your project’s real routes at call time. |
input | unknown (optional) | Passed to the target as its ctx.input. |
Returns a Promise<void> that resolves once the call is accepted. There is no return value, status, or execution ID from the target.
ctx.parentExecutionId
Available on the ctx a target route receives:
| Field | Type | Description |
|---|---|---|
parentExecutionId | string | null | The execution ID of the caller that started this execution with callFunctionAsync, or null when this execution was not started by an async call (a direct request or a schedule). |
Constraints
| Constraint | Behavior |
|---|---|
| Dispatch method | Always POST, regardless of the caller’s own method. The target must accept POST. |
| Chaining depth | A chain is at most two hops deep: the execution that starts it may call callFunctionAsync, and the execution that call starts may call it again, but the execution after that may not. |
| Delivery | At-least-once, with no idempotency key. A transient failure can cause the target to run again for the same call. |
| Unmatched path | Not validated at call time. With no matching route and no catch-all, the call is dropped silently. A catch-all route absorbs a mistyped path instead of surfacing an error. |
| Result | None. The call is fire-and-forget: no return value, status, or execution ID is available to the caller. |
Example
// functions/leads/webhook.post.ts, POST /leads/webhook
import { defineFunction } from '@trayai/helix-sdk';
export default defineFunction(async (ctx) => {
await ctx.callFunctionAsync('leads/enrich', { leadId: ctx.input.leadId });
return { received: true };
});
// functions/leads/enrich.post.ts, POST /leads/enrich
import { defineFunction } from '@trayai/helix-sdk';
export default defineFunction(async (ctx) => {
ctx.log.info('Enriching lead', { leadId: ctx.input.leadId, calledBy: ctx.parentExecutionId });
});
Local development
helix dev fires the call directly at the target route on the same dev server and returns immediately: there’s no separate execution locally, so ctx.parentExecutionId is always null. This is a permanent property of local dev, not a gap that’s expected to close, so don’t gate local testing of a target on that field being set.
Idempotency concerns, the chaining limit, and the mistyped-path gotcha are covered in the async function calls guide.