Workstreams
A Workstream is a record of multi-step business work in motion — a campaign launch, a lead engine, a weekly report, a communication loop. One per business outcome. Each has a status, an ordered set of phases, attached resources (datasets, pages, jobs, experiments), and a chronological event log — a git-log-style record of what happened and what’s next. In your workspace, Workstreams live under Activity. As an agent takes on a multi-step job it opens a Workstream and keeps it current — advancing phases, logging what it did, and flipping status toawaiting_user when it needs you. You watch progress, read the event log, and pick up
or hand work back from there. A Workstream can host Experiments that decide whether
a change worked.
A Workstream opened inside a conversation is also linked back to the thread that started it: the
thread’s Work panel shows its phases and assignments moving while the run is going, so you can
watch a build progress without leaving the chat. The link is made automatically when the agent
creates the Workstream.
A root workstream can also operate as a Strategy: attach campaign workstreams as
direct child resources and read their one-level portfolio from the root ledger.
Concepts
- Status —
active,awaiting_user(paused for input/approval),blocked(external blocker),completed. - Phases — ordered steps (
brand-brief,landing-pages, …), each with its own status. - Event log — append short factual lines (
"Brand brief drafted, sent for review","Blocked: Google Ads admin access missing","Next: build the landing pages"). This is how you and future agents read history and what remains. - Assignments — bounded responsibilities that can run in parallel and survive the coordinator that created them. An assignment stays the same piece of work when a worker is retried; each try is an attempt.
- Submission and adoption — a worker submits a candidate result, pinned to the exact Page, Knowledge, or Dataset revision it produced. Technical completion moves the assignment to review; it does not make the result authoritative. The Workstream accepts, rejects, or supersedes it explicitly.
- Decisions — a workstream remembers a great deal of context: what was discussed, what was tried, what somebody suggested. A decision is the narrower thing the business actually committed to — a course, the reason it was chosen, and who authorized it — and it stays the standing course until a later decision explicitly replaces it. That distinction is what lets an agent picking the work up weeks later continue what you agreed rather than re-argue it, and it makes a change of course visible: reversing a decision records a new one naming the decision it supersedes. A decision does not need an external change behind it — approving an action records one, and so does an agent committing to a strategy that changes nothing outside Erdo, such as which audience a campaign targets. The full record — every decision, what executed, and what the evidence said afterwards — is searchable on its own surface.
Projects
Every Workstream lives in a project — the work context that groups related work and can grant project members access to attached resources. The organization remains the tenant and security boundary; selecting a project focuses project-owned lists rather than creating another tenant. Creating a workstream requires a project slug, so if you’re driving Erdo headlessly and don’t have one yet, create it first. Projects are referenced by slug, and project membership is managed in the app (there’s no programmatic member surface). Listing and creating projects is available on all three surfaces:erdo_list_projects and erdo_create_project; the REST mirrors are
GET /v1/projects and POST /v1/projects (body: name, slug, optional description).
Arming the loop, the ledger, and resources
A workstream can be driven entirely by hand, or handed to Erdo’s reconciliation loop to run on its own cadence. Arming attaches that loop to one workstream with a budget envelope — a token spend cap and a daily escalation cap — so autonomous work can never spend or interrupt past a limit you set. Nothing runs autonomously until you arm it; how much the loop may change live, customer-facing state on its own is a separate control, the autonomy mode. Set the budget envelope on the workstream state (token_budget_millicents, escalation_budget_per_day)
before or while arming, or pass token_budget_millicents when you create the workstream.
The spend cap is in millicents: 100,000 per dollar, so 10000000 is 250. Size it for the whole outcome rather than one sitting — a lead engine that builds pages,
runs a campaign and keeps a loop reading for weeks spends in the low hundreds of dollars, and a
workstream that reaches its ceiling can fund nothing: every allocator recommendation becomes a
hold, dispatch and fan-out refuse, and the loop pauses and files an attention item asking you to
raise the ceiling or ship what it has. Raising it resumes the loop on its next scheduled pass and
resolves that item.
The ledger is the loop’s single source of truth, read in one call: the budget envelope, the
workstream’s experiments and their recent observations, the judge calibration record and
trust dial, the decisions currently in force, your steering notes, open attention items,
and the allocator’s current recommendation. Current decisions arrive with the ledger rather than
being something the loop has to go looking for, because a course you approved is only worth
recording if the next pass reads it before it reasons — so the loop continues what is in force, and
when it does want to change course it proposes a new decision naming the one it would replace.
The ledger also carries monitor findings: what your organization’s monitors reported about the
campaigns and ad groups this workstream declared as its scope, and about the pages attached to it,
open, or raised (or raised again) in the last week. A monitor reports to the whole organization, so this is how the loop that manages a campaign
reads a finding about it and acts on it in its next pass. A workstream with no declared scope and no
attached pages receives none.
Attaching a resource records that a page, dataset, experiment, job, thread, or another
workstream belongs to this workstream in a named role (its output, an input, evidence, something it
monitors), so the ledger and event log show the full shape of the work.
Programmatic access
You can also create, drive, and observe Workstreams over MCP, the REST API, or the CLI — the same surface an agent uses internally. Everything is org-scoped and RBAC’d (read needs project view, writes need contribute), and Workstreams are referenced by slug, never UUID.CLI
create accepts --token-budget <millicents> so a workstream starts with its envelope. arm
accepts --interval <min>, --model <id>, and --timezone <tz>; on both arm and set-state
the --token-budget (millicents, 100,000 per dollar) and --escalation-budget (per day) flags
set the workstream’s envelope — on arm that state update runs first, then the loop is armed. attach --type is one of page, dataset, heartbeat, experiment_run,
thread, job, knowledge_object, approval, or workstream, and --rel is the relationship
(output, input, evidence, monitoring, subject, parent, child, running), with optional
--title and --phase. detach takes the same --type and --id; --rel is optional there and
narrows the removal to one role, so omitting it removes every link the workstream holds to that
resource. It prints removed_count rather than a success line — a detach that matched nothing
answers 0, which is what you want to see when the id was wrong rather than the link stale.
One link will not detach while it is doing work. A workstream’s monitoring heartbeat is its
armed reconciliation loop, and that link is what charges the loop’s own spend to the workstream
and tells the next arm that a loop already exists — so unlinking it while the automation still
runs would leave the loop running against nobody’s budget and let a second one be armed beside it.
Detaching a live one is refused, naming the automation and how to stand it down
(erdo automations disable <id>). Wait for any pending or running executions to finish, then
detach. Detaching permanently retires the stopped automation while preserving its execution
history; it cannot be edited, enabled, run, or attached as a monitoring loop again, and its
triggers cannot be deleted. Each reconciliation heartbeat belongs to one workstream, so another workstream
cannot attach the same heartbeat as its monitoring loop. To resume reconciliation later,
arm a new loop on the workstream. A link to an already-deleted automation can be removed
immediately.
The CLI authenticates with your Erdo token and the active organization (erdo login,
erdo org).
MCP tools
Identity and RBAC ride the request context, so a caller can only reach their own org’s work.
REST
Base URLhttps://api.erdo.ai. Authenticate with Authorization: Bearer <token> and select the
org with X-Organization-ID.
POST /v1/workstreams takes project_slug, slug, title, and optional description, phases
and token_budget_millicents (the spend ceiling, 100,000 millicents per dollar).
POST /v1/workstreams/:slug/arm takes optional interval_minutes, model, and timezone (set or
raise the budget envelope with PATCH /v1/workstreams/:slug/state).
GET /v1/workstreams/:slug/ledger takes optional observations_per_experiment.
POST /v1/workstreams/:slug/resources takes the resource type, id, relationship rel, and
optional title and phase.
DELETE /v1/workstreams/:slug/resources unlinks one, addressed the same way — resource_type and
resource_id as query parameters, plus an optional relationship_type that narrows the removal to
one role. It answers removed_count and the links it removed, so a call that matched nothing
reports 0 instead of a bare success. It refuses with failed_precondition when the link named is
a reconciliation loop that is still running — disable the automation first, wait for its executions to finish, then detach and retire it.
