How the pieces fit
The building blocks a user may put in a workflow are exactly what is attached
to the builder agent: its tools, its toolkits (Slack, Gmail, …) and any
organization workflows attached to it. Nothing else exists from the user’s
point of view, so an agent with only a CRM tool and Slack can only build
CRM-to-Slack automations.
Setting up a builder agent
1
Attach the Workflow Builder tools
The builder tools are static tools under the
workflow-builder provider.
List them with GET /v1/tools and add them to the agent’s agentTools
like any other tool. All thirteen belong together — attach the whole
group.2
Attach the building blocks
Attach the tools, toolkits and organization workflows the user should be
able to automate with. For toolkit actions the user’s own account must be
connected; the agent gets
list_connections and connect_toolkit for
that, and schedule_user_workflow refuses with missing_connections
until it is.3
Publish the agent
Publish and activate as usual. The platform adds its own guidance block to
the agent’s system prompt: the authoring contract, the list of blocks it
may use, and the working agreement below. You do not need to explain any
of this in your own prompt.
The agent follows a fixed working agreement: discover what the user needs,
save a draft, fix any issues the save reports, run a test and check the
result together, confirm the timezone and preview the next run times, then
schedule. Runs from a test really post to Slack and really send email, which
is why the spec carries a
test_input with safe targets (the user’s own
address, a test channel) that the test uses by default.What a user workflow looks like
The agent authors a small linear spec, deliberately simpler than the full workflow definition your Studio uses: an ordered list of up to 15 steps, each with an id, reading values from the workflow input ($.input.<field>) or from
an earlier step ($.steps.<id>.<path>).
- Recipients and destinations are inputs. Anything that addresses the
outside world — channels, email recipients, webhook URLs, folder ids — must
be a workflow input, never a literal in a step. The test run fills those
from
test_input; the schedule supplies the real values as itsinput. output_textis the message the user reads. It is a template rendered against the run and posted to the result thread. Without it the user gets a plain completion notice with the raw output attached.
GET /v1/workflows/{id} returns both: the compiled definition the
runtime executes and the authoring spec the agent re-reads and patches. The
compiler allow-lists every tool and workflow name against what the builder
agent has attached.
Schedules
A schedule is structured, never a free-form cron. The agent and the user agree on arecurrence in the user’s IANA timezone:
Every schedule also has:
timezone— an IANA name such asEurope/Paris. All times are local to it.starts_at/ends_at— optional window. A schedule whose window has passed, or aoncethat has fired, becomescompleted.input— the workflow input for every run (the real recipients).notify—thread(default) files each result as a conversation;noneonly logs the run.thread_title— the title of every result thread, e.g. “Daily signups digest”.copilot_id— which widget the result thread is listed under.
GET /v1/workflow-schedules/preview (or the agent’s preview_schedule)
answers the next run times without creating anything. The recurring minimum is
hourly by construction, and the same minimum is enforced on the raw cron
that backend callers may send instead of a recurrence.
Lifecycle
How runs happen
The API polls for due schedules every 30 seconds and claims each one atomically, so two API instances never double-fire. A few rules keep unattended runs sane:- Missed slots collapse to one. If the server was down through several fires, the schedule catches up once, from now.
- Stale slots are skipped. A slot more than 10 minutes late is logged as
skippedwithskip_reason: "misfire"rather than firing a stale 08:00 digest at 14:00. - No overlap. If the previous run is still
running, the new slot is skipped withskip_reason: "overlap". - One retry for transient failures. Anything but an auth, usage-limit, validation or not-found error is re-attempted once after 10 minutes, in the same result thread. The “failed” message is only posted when the final attempt fails.
- Disabled after 5 consecutive failures. Only final outcomes count. A successful run resets the counter.
status (running, succeeded,
failed, skipped), the text that was posted, any error, the thread_id
of its result thread, and a request_id that joins it to its trace.
Result threads
Whennotify is thread, each run creates a real conversation for the user
before it starts — so a long run is visible immediately — and posts the result
when it finishes. The thread carries the workflow invocation, the rendered
output_text and any rich-UI components (charts, tables, KPI cards) the
workflow produced, in the same shape the chat widget already renders.
These are ordinary threads with a little extra metadata:
metadata.sourceis"workflow". Filter withGET /v1/threads?source=workflow.metadata.unreadistrueuntil the user opens it. Filter with&unread=truefor a badge;POST /v1/threads/{id}/readclears it.metadata.runStatustracksrunning→retrying→succeeded/failed.
POST /v1/chat routes to the agent
that built the workflow, with the run in its history, so “why is Tuesday
missing?” just works.
Credentials
Runs execute unattended, so they cannot use the credentials on a live request. They read the user’s stored tool credentials — the same store a token mint orPATCH /v1/users/{userId} writes (see
Tool credentials). When the
agent schedules a workflow it captures the credentials the current request
carries for the providers the workflow uses, so a schedule created mid-chat
is ready to run.
There is no fallback: a run whose user has no stored credentials for a
provider the workflow needs fails as an auth error, and repeated auth failures
disable the schedule with a reason that says so. Fix it by pushing fresh
credentials from your backend (PATCH /v1/users/{userId}) and resuming the
schedule, or by asking the assistant to refresh and resume.
Toolkit actions (Slack, Gmail, …) use the user’s connected account, not a
credential you push. If the user disconnects it, runs fail the same way and
reconnecting through the assistant fixes them.
Building your own UI on it
Your frontend can do everything the assistant does, with the session token it already holds. Every call is scoped to the session’s user: they see only their own workflows, schedules and runs, and anything else answers404.
Creating a schedule from the browser has three rules, all enforced server-side
so the body cannot be used to act as someone else:
- The schedule is created as the session’s user.
user_idmay be omitted, or must match — naming anyone else is a400. - The billing group comes from the session, never from the body.
- The workflow must be one of the user’s own or a published organization
workflow. Another user’s workflow is a
404.
description (“every weekday
at 08:00 Europe/Paris”), next_fire_at, next_fire_local, and the next three
run times.
Running from a button is a single call:
From your backend
With the organization API key the same endpoints work org-wide, plus a few things a browser cannot do:GET /v1/workflows?owner=chatterlists every user-built workflow in the organization;&userId=narrows to one user;?owner=allmixes them with your Studio-authored ones.POST /v1/workflow-schedulestakes a requireduser_id(the user the runs execute as: billing group, integrations, result-thread owner), an optionalgroup, and accepts a rawcroninstead of a recurrence. Use it to schedule any published workflow — yours or the user’s — for a user without a conversation.GET /v1/workflow-schedulesandGET /v1/workflow-runsreturn the whole organization, filterable byworkflowId,userId,status,owner(org|chatter) and, for runs,trigger.
owner_kind: "org"), even when they run as a user. They are managed with the
API key and do not appear in that user’s session-scoped listing; schedules the
assistant or the user’s own session create are owned by the user and do.
Every endpoint here is also an MCP tool, so an operator’s
assistant can inspect and manage schedules the same way.