Developer section: these pages cover the S6S workflow engine for self-hosted installs and developers. On s6s.ai, API keys and MCP access are issued by S6S on request (contact us); on a self-hosted install, an admin creates them on the API Keys page. For the AI Visibility product, start at the AI Visibility docs.
TypeScript SDK
Deploy, run, and manage workflows programmatically with a fluent builder API.
Installation
Not on npm yet
@s6s/sdk is not published to npm yet; publishing is pending. Until then, use it from the S6S repository, where it lives in packages/sdk.
The repository is private during the private beta; ask S6S for access (contact us). With access, clone it and build the packages:
git clone https://github.com/Sawan6/S6S-ai.git cd S6S-ai pnpm install pnpm build:pkg
The build writes the SDK to packages/sdk/dist. The SDK depends on the workspace package @s6s/ir, so use it from a package inside the repository's pnpm workspace (apps/* or packages/*) by adding "@s6s/sdk": "workspace:*" to its dependencies. It needs Node.js 18 or later.
Quick Start
Create a client, build a workflow with WorkflowBuilder, and deploy it in a single call. Builder nodes get random IDs, so reference an earlier step's output with ref(nodeId, path), which returns {{steps.<nodeId>.output.<path>}}.
import { S6SClient, WorkflowBuilder, ref } from "@s6s/sdk";
const client = new S6SClient({
baseUrl: "https://s6s.ai", // required: your S6S URL
apiKey: process.env.S6S_API_KEY!,
});
// Build a workflow with the fluent builder
const builder = new WorkflowBuilder("Data Pipeline")
.schedule("*/15 * * * *")
.httpGet("Fetch Data", "https://api.example.com/data");
const fetchId = builder.lastNodeId;
builder.transform("Shape Output", {
items: ref(fetchId, "body.items"),
});
// Deploy. Schedule workflows run on their schedule, not on deploy.
const result = await client.deployWorkflow(builder.build());
console.log(result.workflowId, result.runId); // runId is null here
// Pre-flight check: credentials, config, schedule
const check = await client.validateDeployedWorkflow(result.workflowId);
console.log(check.status); // "ready" | "warnings" | "blocked"S6SClient API
Create a client with new S6SClient({ baseUrl, apiKey, timeoutMs }). baseUrl is required (there is no default). apiKey is sent as a Bearer token; the SDK does not read environment variables. timeoutMs defaults to 30000. Methods return promises, except validateWorkflow (synchronous) and streamRun (an async generator). Failed requests throw an S6SError with the HTTP status and response body.
| Method | Description |
|---|---|
| deployWorkflow(json, opts?) | Deploy a Recipe or IR workflow (POST /api/v1/workflows/deploy). Returns workflowId, versionId, runId (null unless a run started), monitorUrl, webhook details and credential status. Options: slug, autoRun, waitForCompletion, callbackUrl. |
| validateDeployedWorkflow(id) | Server-side pre-flight check (credentials, config, schedule, structure). Returns ready, warnings or blocked. |
| runWorkflow(id, input?) | Start a run for a deployed workflow. The input becomes the trigger output (body for webhook triggers, payload for manual triggers). |
| getRunStatus(runId) | Get status, timing, errorCode, failureReason, and per-step results. |
| waitForRun(runId, opts?) | Poll until the run succeeds, fails, or is cancelled. Polls every 1 s and times out after 5 minutes by default. |
| streamRun(runId) | Async generator of status, step, done and error events (Server-Sent Events). |
| listWorkflows() | List deployed workflows (up to 50) with id, slug, name and enabled. |
| listRuns(opts?) | List runs. Filter by status; limit up to 200 (default 50). |
| cancelRun(runId) | Cancel an active run. |
| retryFailed(runId) | Retry a failed run. The same run restarts; other statuses are rejected. |
| getCatalog() | Get the platform catalog: triggers, node types with config schemas, expression and flow syntax. |
| dryRun(json) | Check an IR workflow on the server without running it: validation, policy, capabilities and a step projection. |
| validateWorkflow(json) | Client-side IR schema validation (no network call). |
Options for the run a deploy starts
Deploy starts a run only when the trigger is not a schedule or a webhook (webhooks need autoRun: true). These options apply to that run: input (a JSON object) becomes its trigger input and is checked against the workflow's input schema (422 when it fails); idempotencyKey makes a repeat deploy of the same version return that run instead of starting another; waitForCompletion waits up to 30 s; and callbackUrl is called when it finishes. When no run starts, the response has a DEPLOY_RUN_OPTIONS_UNUSED warning; start one with runWorkflow(id, input).
WorkflowBuilder
The builder provides a fluent interface with auto-chaining — each node automatically connects to the previous one. Start with one trigger: the builder has .webhook() and .schedule(). Call .build() at the end to produce IR JSON ready for deployWorkflow().
Steps that need a credential take { id }, where id is "default" (any ready connection for that provider), an account label, or a credential ID. See Credentials.
| Method | Node Type |
|---|---|
| .webhook(path, method?) | trigger.webhook (method GET or POST, default POST) |
| .schedule(cron, timezone?) | trigger.schedule (timezone default UTC) |
| .http(name, url, method?, opts?) | action.http |
| .httpGet(name, url, opts?) | action.http (GET) |
| .httpPost(name, url, opts?) | action.http (POST) |
| .transform(name, template) | action.transform |
| .code(name, expression) | action.code (JavaScript) |
| .slack(name, channel, textTemplate, { id }) | action.slack |
| .email(name, to, subject, bodyTemplate, { id }) | action.email (SMTP credential) |
| .database(name, query, { params?, operation?, credential: { id } }) | action.database |
| .filter(name, arrayExpression, condition, mode?) | action.filter |
| .condition(name, expression, operator, value) | logic.condition |
| .wait(name, seconds) | logic.wait |
| .addDetached(name, type, effect, config, opts?) | Any node, not chained. Returns its node ID |
| .edge(sourceId, targetId, condition?) | Custom edge (condition: "on_error") |
| .onError(targetId) | on_error edge from the last added node to targetId |
| .lastNodeId | ID of the last added node (getter) |
| .build() / .toJSON() | The IR object / the same as a JSON string |
| ref(nodeId, path) | Expression string {{steps.<nodeId>.output.<path>}} |
Examples
Deploy, run, and wait for the result
import { S6SClient, WorkflowBuilder, ref } from "@s6s/sdk";
const client = new S6SClient({
baseUrl: "https://s6s.ai",
apiKey: process.env.S6S_API_KEY!,
});
const builder = new WorkflowBuilder("Health Check")
.schedule("0 * * * *")
.httpGet("Ping", "https://example.com/health");
const pingId = builder.lastNodeId;
builder.transform("Format", {
status: ref(pingId, "status"),
checkedAt: "{{run.startedAt}}",
});
const { workflowId } = await client.deployWorkflow(builder.build());
console.log("Deployed:", workflowId);
// Don't wait for the schedule: start a run now
const { runId } = await client.runWorkflow(workflowId);
const result = await client.waitForRun(runId);
console.log("Status:", result.status);
console.log(result.steps.map((s) => `${s.nodeType}: ${s.status}`));Webhook trigger with a Slack notification
const builder = new WorkflowBuilder("Order Processor")
.webhook("/orders/new", "POST")
.transform("Extract", {
orderId: "{{trigger.output.body.order_id}}",
email: "{{trigger.output.body.customer_email}}",
});
const extractId = builder.lastNodeId;
builder
.slack("Notify Team", "#orders",
`New order #${ref(extractId, "orderId")} from ${ref(extractId, "email")}`,
{ id: "default" },
);
const result = await client.deployWorkflow(builder.build());
if (result.missing_credentials?.length) {
// Connect Slack, then deploy again to get the webhook details
console.log(result.missing_credentials.map((c) => c.connect_url));
} else {
console.log("Webhook URL:", result.webhookTrigger?.invokeUrl);
console.log("Secret:", result.webhookTrigger?.secret); // first deploy only
}List and manage workflows
// List all workflows
const workflows = await client.listWorkflows();
for (const wf of workflows) {
console.log(wf.id, wf.name, wf.enabled);
}
// List recent failed runs
const failedRuns = await client.listRuns({
status: "failed",
limit: 5,
});
// Retry the most recent failed run
const failed = failedRuns[0];
if (failed) {
const retried = await client.retryFailed(failed.runId);
console.log("Retried:", retried.runId, retried.status);
}
// Cancel an active run
const activeRuns = await client.listRuns({ status: "running" });
const active = activeRuns[0];
if (active) {
await client.cancelRun(active.runId);
console.log("Cancelled:", active.runId);
}Start building with the SDK
Get an API key, build the SDK from the repository, and deploy your first workflow.