Skip to content
Go To Dashboard

Using Capabilities in Steps

Every workflow step’s run(input, ctx) receives a pre-authenticated, tenant-scoped capability client at ctx.sapiom. The shape of that client is defined by @sapiom/tools — use your editor’s autocomplete and npm run typecheck rather than guessing. A wrong method name or a namespace that doesn’t exist fails the type check before it ever touches the cloud.

ctx.sapiom.sandboxes — create or reattach an isolated ephemeral compute instance. A sandbox can run shell commands, read/write files, and serve as the execution environment for a coding-agent run.

const sandbox = await ctx.sapiom.sandboxes.create({ name: "my-box", tier: "s" });
const { stdout } = await sandbox.exec("node --version");
ctx.logger.info("node version", { stdout });
await sandbox.destroy();

attach lets a later step reconnect to a sandbox kept alive by an earlier one (pass the name you used at create):

const sandbox = ctx.sapiom.sandboxes.attach("my-box");

Methods on the returned Sandbox handle: exec, execStream, readFile, writeFile, uploadFile, destroy, and lower-level multipart-upload helpers.

ctx.sapiom.repositories — create and manage in-network git repositories, then use repo.pushFromSandbox(sandbox) to commit and push a coding agent’s work.

const repo = await ctx.sapiom.repositories.create("my-app");
// … run the coding agent against the repo …
const { sha } = await repo.pushFromSandbox(sandbox, { message: "build: generated" });

Namespace methods: create(slug), get(slug), list(), delete(slug), attach(slug, cloneUrl). attach adopts a known repository without a network round-trip — useful when one step creates a repo and a later step needs to operate on it using the slug and clone URL stored in ctx.shared.

ctx.sapiom.agent.coding — run an LLM-powered coding agent against a natural-language task. The agent edits a repository checkout in a sandbox; when the task is done, use repo.pushFromSandbox to publish the result.

run — inline, awaits completion:

const result = await ctx.sapiom.agent.coding.run({
task: "Add a /health endpoint that returns { ok: true }.",
gitRepository: repo, // cloned into the sandbox at /workspace/<slug>
});
ctx.logger.info("coding run", { status: result.status, summary: result.summary });

launch — non-blocking, for long-running tasks:

Long-running coding runs should use launch + pauseUntilSignal so the step doesn’t time out waiting for the agent:

import {
defineStep,
pauseUntilSignal,
} from "@sapiom/orchestration";
import { CODING_RESULT_SIGNAL } from "@sapiom/tools";
const launchCoding = defineStep({
name: "launchCoding",
next: ["collect"],
pause: { signal: CODING_RESULT_SIGNAL, resumeStep: "collect" },
async run(input: { task: string }, ctx) {
const repo = await ctx.sapiom.repositories.create("output");
const handle = await ctx.sapiom.agent.coding.launch({
task: input.task,
gitRepository: repo,
});
ctx.shared.set("repoSlug", repo.slug);
ctx.shared.set("repoCloneUrl", repo.cloneUrl);
return pauseUntilSignal(handle, { resumeStep: "collect" });
},
});

The step pauses until the coding run signals agent.coding.result; the resumed step (collect) receives the result as its input. Import CodingResultPayload from @sapiom/tools to type the resumed step’s input accurately.

See the Authoring guide for the full pause/resume pattern, including re-attaching a sandbox from executionEnvironment in the resumed step.

ctx.sapiom.fileStorage — tenant-scoped object storage with presigned URLs. Upload initiates the transfer and returns a URL you PUT bytes to yourself; getDownloadUrl returns a URL to GET them back.

// Initiate an upload
const { fileId, uploadUrl, requiredHeaders } = await ctx.sapiom.fileStorage.upload({
contentType: "application/json",
fileName: "report.json",
visibility: "private",
});
// PUT the bytes yourself — the presigned URL is external; fetch is fine here
await fetch(uploadUrl, {
method: "PUT",
headers: requiredHeaders,
body: JSON.stringify({ result: "done" }),
});
// Later: get a download URL
const { downloadUrl } = await ctx.sapiom.fileStorage.getDownloadUrl(fileId);

Namespace methods: upload(input), getDownloadUrl(fileId), list(opts?), delete(fileId), setVisibility(fileId, visibility).

Image generation (contentGeneration.images)

Section titled “Image generation (contentGeneration.images)”

ctx.sapiom.contentGeneration.images — generate images from a text prompt. Pass storage to persist each output into file storage; the returned images then carry fileId.

const out = await ctx.sapiom.contentGeneration.images.create({
prompt: "A minimalist line-art icon of a workflow graph, white on dark background.",
numImages: 1,
storage: { visibility: "private" }, // optional — persists outputs to file storage
});
const url = out.images?.[0]?.url;
const fileId = out.images?.[0]?.fileId; // present when storage was passed
Section titled “Web search, scraping & email lookup (search)”

ctx.sapiom.search — search the web, read a page as markdown, and look up professional emails.

const answer = await ctx.sapiom.search.webSearch({ query: "best vector databases 2026" });
const page = await ctx.sapiom.search.scrape({ url: "https://example.com/post" });
const found = await ctx.sapiom.search.emailSearch.findEmail({ /* see the typed FindEmailInput */ });

Namespace methods: scrape, webSearch, and emailSearch (findEmail, verifyEmail, domainSearch). The Hunter enrichment operations (company / people / combined) are not on the typed client — reach them via the SDK.

Video generation (contentGeneration.video)

Section titled “Video generation (contentGeneration.video)”

ctx.sapiom.contentGeneration.video — generate a video from a prompt (async). create submits and waits for the result; launch returns a dispatchable handle you can pauseUntilSignal on. Pass storage to persist the output to file storage.

const out = await ctx.sapiom.contentGeneration.video.create({
prompt: "A 5-second loop of a workflow graph drawing itself, white on dark.",
storage: { visibility: "private" },
});

ctx.sapiom.orchestrations — run another deployed orchestration by its slug (the definition field) and await its terminal result, or launch it and pause until it signals (the same dispatch pattern as the coding agent).

const result = await ctx.sapiom.orchestrations.run({ definition: "enrich-lead", input: { domain: "acme.com" } });

Namespace methods: run(spec), launch(spec). Use launch + pauseUntilSignal for long-running children so the parent step doesn’t time out.

ctx.sapiom.database — provision an on-demand Postgres database, returned with direct connection credentials. duration is required.

const db = await ctx.sapiom.database.create({ duration: "1h" });
ctx.logger.info("postgres ready", { id: db.id });

Namespace methods: create(input), get(idOrHandle), delete(idOrHandle). Redis, Vector, and Search are not on the typed client — reach them via the SDK.

ctx.sapiom.withAttribution(attribution) derives a new client that records all its capability calls under a different agent or trace id. Step-authoring code almost never needs this — the workflow engine already constructs ctx.sapiom with attribution set to the current execution. The escape hatch is for the router case: one step that dispatches on behalf of multiple agents.

const attributed = ctx.sapiom.withAttribution({
agentName: "sub-agent-a",
traceId: ctx.executionId,
});
const sandbox = await attributed.sandboxes.create({ name: "sub-a-box" });

All ctx.sapiom.* calls are stubbed when you run sapiom_dev_orchestrations_run_local. No capability requests leave your machine, and your tenant is not charged. The stubs return sensible defaults so a fresh workflow runs end to end with no setup.

Add overrides in .sapiom-dev/stubs.json only when a step branches on a specific result — for example, to test both the "completed" and "failed" paths through a coding-agent step:

{
"version": 1,
"steps": {
"launchCoding": { "agent.coding.launch": { "status": "completed", "summary": "Done." } }
}
}

Stub paths use the plural namespace for namespace calls (repositories.list, fileStorage.upload) and the singular handle for handle methods (repository.pushFromSandbox, sandbox.exec).

The ctx.sapiom client exposes eight namespaces today: sandboxes, repositories, agent (with agent.coding), orchestrations, fileStorage, contentGeneration (images and video), search (scrape, webSearch, emailSearch), and database, plus the withAttribution method. The rest of the Sapiom gateway catalog — AI model access, audio, browser automation, messaging, Domains/DNS, GitHub Export, the Hunter company/people enrichment operations, and the Redis/Vector/Search data stores — is available via the SDK but does not yet have a typed workflow-step surface. npm run typecheck is the definitive check: if ctx.sapiom.<anything> compiles, it exists and is callable.