Trigger runs and hand work to agents

Start work from your own systems and apps: an automation from a webhook or a schedule, an agent from a message, with no one in Studio. Every start comes back as a run you can follow, with your system named as the actor.

For
Developers
Ways in
Webhook · Schedule · Agent run · MCP
Credential
Machine token or member session
Comes back
A run id

01Choose the way in

You want toCallWithComes back as
Start an automation from your systemPOST …/workflows/{workflow_id}?env=, the secure webhookMachine tokenaccepted with a correlation_id, and a run
Run an automation now from your appPOST …/workflows/{workflow_id}/run?env=Member session202
Run on a timetableA schedule in the automation, turned on with POST …/schedules/{schedule_id}/enableMember sessionA run each time it fires
Hand an agent a taskPOST …/agents-config/{agent_config_id}/testMember sessionrun_id
Let an outside assistant call your agentsThe MCP server, in previewMCP keyA run per call

Runs · Ways inYour systems, your apps, and your assistants reaching an instance, and the credential each one carries.

02From your system: the secure webhook

Your system holds no member session, so it starts automations through a secure webhook and a machine token. The token belongs to one environment and is attached to the webhook in Studio, so a leaked token can only start the automations it is attached to, in one environment.

  1. In Studio, open the automation, mark its webhook secure, and attach the environment's machine token. GET …/workflows-config/{workflow_config_id}/webhook-url?env= gives you the URL for that environment.
  2. From your system, POST a JSON body. The run starts with it as its input.
  3. Keep the correlation_id from the answer. It joins your request to the run on the record.
POST/workflow/api/v1/organizations/{org_id}/workflows/{workflow_id}?env={env_id}

curl -s -X POST "$NSTACK_BASE/workflow/api/v1/organizations/$NSTACK_ORG/workflows/$WORKFLOW_ID?env=$ENV_ID" \
  -H "Authorization: Bearer $NSTACK_MACHINE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"claim_id":"CLM-20419","event":"fnol.received"}'

const res = await fetch(`${base}/workflow/api/v1/organizations/${org}/workflows/${workflowId}?env=${envId}`, {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.NSTACK_MACHINE_TOKEN}`, "Content-Type": "application/json" },
  body: JSON.stringify({ claim_id: "CLM-20419", event: "fnol.received" }),
});
if (!res.ok) throw new Error(`webhook refused: ${res.status}`);
const { correlation_id } = await res.json();

res = requests.post(f"{base}/workflow/api/v1/organizations/{org}/workflows/{workflow_id}",
                    params={"env": env_id}, json={"claim_id": "CLM-20419", "event": "fnol.received"},
                    headers={"Authorization": f"Bearer {os.environ['NSTACK_MACHINE_TOKEN']}"})
res.raise_for_status()
correlation_id = res.json()["correlation_id"]
Response200 · sample values

{ "status": "accepted", "correlation_id": "5d6e7f80-91a2-4b3c-8d4e-5f6a7b8c9d0e", "message": "Workflow execution triggered" }
  • Exactly once. Call with test=true to set the run up without starting it, keep the instance_id it answers, then start it by instance_id. A second start with the same id answers 409 conflict and starts nothing.
  • On the record. The run names your token as its actor, machine:{token_id}, so the record shows which system started it.
  • Errors. 403 machine_token_not_attached means the token is not attached to this webhook for this environment. 503 machine_token_unverified means retry with backoff. The authentication page lists them all.

03Hand an agent a task, headlessly

Send a message to an agent in one environment and you get a run_id back at once. The agent works after the response: it reads the datasets assigned to its version through query_<dataset>, searches knowledge through search_knowledge, and writes its answer to the run. Your code reads the run when it ends.

POST/agents/api/v1/organizations/{org_id}/agents-config/{agent_config_id}/test

curl -s -X POST "$NSTACK_BASE/agents/api/v1/organizations/$NSTACK_ORG/agents-config/$AGENT_CONFIG_ID/test" \
  -H "Authorization: Bearer $NSTACK_SESSION" -H "Content-Type: application/json" \
  -d '{"message":"Prepare the brief for household 1042 ahead of Thursday's review.","env_id":"'"$ENV_ID"'","thread_id":"household-1042-2026-10"}'

const res = await fetch(`${base}/agents/api/v1/organizations/${org}/agents-config/${agentConfigId}/test`, {
  method: "POST",
  headers: { ...auth, "Content-Type": "application/json" },
  body: JSON.stringify({
    message: "Prepare the brief for household 1042 ahead of Thursday's review.",
    env_id: envId,
    thread_id: "household-1042-2026-10",
  }),
});
const { run_id } = await res.json();
const run = await followRun(run_id);   // see Follow runs
showDraftToAdvisor(run);

res = requests.post(f"{base}/agents/api/v1/organizations/{org}/agents-config/{agent_config_id}/test",
                    json={"message": "Prepare the brief for household 1042 ahead of Thursday's review.",
                          "env_id": env_id, "thread_id": "household-1042-2026-10"}, headers=auth)
run_id = res.json()["run_id"]
run = follow_run(run_id)   # see Follow runs
show_draft_to_advisor(run)
FieldTypeMeaning
messagestringThe task.
env_idUUID, optionalThe environment to run in. The run uses the version pinned there, else the latest version, else the saved draft.
thread_idstring, optionalContinues an earlier conversation. Use one id per case, customer, or ticket.
execution_reportbooleanTrue by default. Adds an execution summary to the finished run. GET …/runtime/{correlation_id}/download gives a short-lived link to the full report.
Mode

Agents run in Reads and drafts mode today. The answer is a draft; a person acts on it. Build your integration so the draft lands in front of the right person, with the run id next to it.

Requires

A member session with read agents, which the Member role holds. Your service holds a session the same way a browser app does: sign in with your sign-in application and refresh the token before each call.

04On a timetable

An automation carries its schedules in its graph. A schedule starts off. Test the automation in the environment first, then turn the schedule on; a change to its timing turns it off again until someone turns it back on.

POST/workflow/api/v1/organizations/{org_id}/workflows/{workflow_id}/test?env={env_id}

Test the automation in an environment with an optional JSON body. Answers the run's instance id.

NeedsTest and run automations

POST/workflow/api/v1/organizations/{org_id}/workflows-config/{workflow_config_id}/schedules/{schedule_id}/enable

Turn a schedule on.

NeedsUpdate automations, plus manage production for a PROD environment

GET/workflow/api/v1/organizations/{org_id}/workflows/{workflow_id}/instances?status=

The automation's runs, filtered by status.

NeedsRead automations

05From your app, as a member

An app your people sign in to can start work with their session. POST …/workflows/{workflow_id}/run?env= runs an automation now and answers 202. The agent route above takes the same session. Builders do this outside PROD; a PROD run needs the permission to manage production, and your app should ask for a confirmation the way Studio does.

06Which id to keep

You holdRead it withWhat you get
run_id from an agent runGET /core/api/v1/organizations/{org_id}/runs/{run_id}Header, steps, and child runs; …/events for the full sequence
correlation_id from a webhookGET …/runs filtered by subject_id and time, or the automation's …/instancesThe run the webhook started
instance_id from a test or a dry runGET /workflow/api/v1/organizations/{org_id}/workflows/instances/{instance_id}One run of an automation and its tasks; …/stream-ticket follows it live

The Follow runs guide shows the polling loop and the state model.

07In design

  • In design Machine access to REST, so your system hands agents tasks with a machine credential instead of a member session.
  • In design A production run route for agents, POST …/agents-config/{agent_config_id}/runs, with an Idempotency-Key header.
  • In design Signals, so your system answers a waiting run, such as an approval, through the API.

08Next