w6w docs

Call any API

Applies to: SDK · CLI · HTTP. (From an MCP client, the same call is the w6w_invoke tool — see Connect an MCP client.)

One request runs anything in your account. You give it a URN — a string that says what to run — and w6w works out whether that is an action on one of your connected apps, a Function, an Endpoint or a Workflow, runs it with your credentials, and tells you which kind it hit.

You need the same two things as every other w6w call:

export W6W_BASE_URL=https://<your-host>
export W6W_TOKEN=<your-token>   # Studio → Tokens → New token

What a URN can be

You passIt runsNeeds action?
conn_01H…An action on one of your connected appsYes — the action’s key
fn_01H… or fn:<key>A FunctionNo
ep_01H… or ep:<key>An EndpointNo
wf_01H… or wf:<key>A WorkflowNo
<alias-name>Whatever that alias points atNo

An ID you copy from Studio and a key you chose yourself both work. A Function, Endpoint or Workflow ID that carries no prefix does not: write fn:send-welcome (its key), not the bare ID.

Run an app action

The connection id comes from Studio → Connections, or from client.connections.list().

import { W6WClient } from "@w6w/sdk";

const client = new W6WClient(); // reads W6W_BASE_URL and W6W_TOKEN

const result = await client.run({
  urn: "conn_01H…",
  action: "<action-key>",
  payload: { to: "ada@example.com", subject: "Hello" },
});

if (result.kind === "action") console.log(result.value);
curl -sS "$W6W_BASE_URL/run" \
  -H "Authorization: Bearer $W6W_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "urn": "conn_01H…",
    "action": "<action-key>",
    "payload": { "to": "ada@example.com", "subject": "Hello" }
  }'
w6w run conn_01H… --action <action-key> --payload '{"to":"ada@example.com","subject":"Hello"}'

The answer is 200 with the app’s result:

{
  "kind": "action",
  "value": { "…": "whatever the app returned" },
  "output": { "…": "the same payload as value" },
  "invocationId": "inv_01H…",
  "status": "succeeded",
  "startedAt": "2026-10-06T09:30:00.000Z",
  "finishedAt": "2026-10-06T09:30:00.412Z",
  "durationMs": 412
}

value and output hold the same payload. status is w6w’s own verdict on the call — it is not the status code the app’s vendor sent back inside its payload. The invocationId is your receipt; see Invocations.

Run a Function

A Function takes payload and returns output.

const result = await client.run({
  urn: "fn:send-welcome",
  payload: { email: "ada@example.com" },
});

if (result.kind === "function") console.log(result.output);
curl -sS "$W6W_BASE_URL/run" \
  -H "Authorization: Bearer $W6W_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "urn": "fn:send-welcome", "payload": { "email": "ada@example.com" } }'

If you already know it is a Function, the dedicated route does the same thing: POST /functions/<id-or-key>/invoke with a body of { "inputs": { … } }. The SDK form is client.functions.run("send-welcome", { … }). An Endpoint has the equivalent at POST /endpoints/<id-or-key>/invoke.

Start a Workflow

A Workflow runs in the background, so the call answers straight away with a handle, and the HTTP status is 202. That is success — the run is queued.

curl -sS "$W6W_BASE_URL/run" \
  -H "Authorization: Bearer $W6W_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "urn": "wf:nightly-report", "payload": { "day": "2026-10-05" } }'
{
  "kind": "workflow",
  "runId": "<run-id>",
  "invocationId": "inv_01H…",
  "status": "queued",
  "startedAt": "2026-10-06T09:30:00.000Z"
}

status: "queued" means started, not finished. Follow the run with its runId:

const { body } = await client.request<{ run: { status: string } }>({
  method: "GET",
  path: `/runs/${result.runId}`,
});
console.log(body.run.status);
curl -sS "$W6W_BASE_URL/runs/<run-id>" -H "Authorization: Bearer $W6W_TOKEN"

To start a Workflow and wait for it in one call, use the dedicated route with ?wait=true: POST /workflows/<id-or-key>/run?wait=true (SDK: client.workflows.run(id, { wait: true })). The wait is bounded; if the run is still going when it ends you get the 202 handle instead, and can carry on polling.

Tell the kinds apart

Every success carries a kind. Check it rather than guessing from which field is present:

kindResult fieldHTTP status
actionvalue (and output)200
functionoutput200
workflowrunId + status: "queued"202

An Endpoint or an alias answers with the kind of whatever it is wired to. Treat an unfamiliar kind as data to pass along, not as an error — new kinds can appear before your client learns them.

When it goes wrong

Errors come back as { "error": { "code": "…", "message": "…" } }. The ones you will meet:

StatuscodeWhat it means
400invalid_bodyThe body is not JSON, urn is missing, urn is not a recognised form, action is missing for a conn_ URN, or you sent action alongside an alias.
400unroutable_idYou passed the ID of a Function, Endpoint or Workflow that has no fn_/ep_/wf_ prefix. Use fn:<key>, ep:<key>, wf:<key>, or an alias.
401unauthorizedThe token is missing, expired or wrong.
403app_not_entitledYour account is not allowed to use that app.
404unknown_connection, unknown_function, unknown_endpoint, unknown_workflowNothing by that name in your account.
409ambiguous_keyThe same key exists in two of your projects — use the ID.
422function_incomplete, function_disabled, function_not_publishedThe Function exists but cannot run yet.
424(the app’s own code), fetch_failedThe app or its vendor refused the call — a wrong scope, a bad parameter; the message is the vendor’s reason. fetch_failed means the app’s source could not be fetched.
429concurrency_exceeded and othersYou are over a limit. Honour the Retry-After header.

A 424 is deliberately a 4xx: it is almost always something to fix in the call or the connection, not a w6w outage. The exception is fetch_failed: the host serving the app’s source (GitHub, for example) did not answer, so retry later. Failures carry the same invocationId a success does, so you can look the attempt up afterwards.

Next