Document the Conductor API and the dbosctl CLI - #605
Merged
Conversation
Conductor's v2 API is now deployed in DBOS Cloud and describes itself with a generated OpenAPI 3.1 spec, and dbosctl is the command-line client built on it. Neither had documentation. Adds two pages under "Deploy To Production": - Conductor API: where to get the spec (cloud, self-hosted, or the conductor binary's `openapi` subcommand), authentication, resource scoping, the RFC 9457 error model, listing and filtering conventions, a map of the endpoint surface by area, the operations a no-auth self-hosted deployment does not register, and how to generate a client. - dbosctl CLI Reference: installation, profiles, authentication, configuration precedence, output formats, exit codes, and a command-by-command reference, following the shape of the existing Cloud CLI reference page. Also links both from the Conductor overview page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Conductor un-gated listPermissions; the grantable-permission catalog is a static allow-list, fetchable in every mode. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two corrections against the shipped API: The applicationName filter is gone (conductor #187), so the tip describing it was documenting an input that no longer exists. What survives is the response side: workflows, queues, and schedules each report the application that owns them, which is how you tell whose objects you are looking at when several applications share a system database. Listing is always scoped to the application in the path, and there is deliberately no way to read a co-tenant's objects through it -- address that application directly. The four autoscaling operations require a DBOS Teams plan and return 403 otherwise, which the page did not say. Also notes that both GETs 404 with no policy installed, and mentions KEDA alongside the other scalers since that is what conductor's own design targets. Names the three domain-claim operations in the no-auth list rather than referring to them collectively, so all sixteen OAuth-gated operations are greppable by operation ID. Verified against the spec: the listWorkflows query parameters and the search body fields both still match, no operations were added or removed, and the CLI command surface is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There is one public API. Calling out that "the API is versioned" and that this page documents v2 invites a reader to wonder which version they want and whether a v1 is available to them -- neither of which is a question they have. The predecessor is internal. The v2 in URLs stays, since that is what you actually type, but nothing in the prose treats it as a choice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The CLI now shows which application owns a workflow, queue, or schedule. The API page explains the concept, but a reader in the CLI reference would never cross over, and this is the only way to tell whose objects you are looking at when applications share a system database. Put in Output rather than in the three command entries: this page documents commands and flags, not output fields, and singling out one field in three places would read as though the rest were exhaustive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"Everything you can do from the console you can also do over this API" was wrong in both directions that matter. The console also drives DBOS Cloud -- deploying applications, provisioning databases, secrets, billing -- none of which this API touches. A reader taking the claim literally would go looking for deployment endpoints that do not exist. Reframes it around what the API actually is: the Conductor control plane, which is what the console shows for a Conductor-connected application whether it runs on your own infrastructure or on DBOS Cloud. Points the cloud-specific operations at the pages that own them. The same overclaim was in the Conductor overview bullet, which said everything the dashboard does is available over the API. Now scoped to workflow, queue, and schedule management. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
We ship the Docker image, not a standalone binary, so `conductor openapi` was an instruction nobody could follow. The image needs `--entrypoint ./dbos-conductor` to reach the subcommand: its entrypoint script waits for Postgres, runs migrations, and execs the server without forwarding arguments, so `docker run <image> openapi` would hang waiting for a database it does not need. Verified against dbosdev/conductor:0.17.0 -- both the 3.1 and the 3.0 downgrade produce the expected spec, and its output is byte-identical to what conductor main generates today. Also suggests pinning a version tag over latest when the spec feeds code generation, so a regenerated client only moves when you decide it does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Requiring a Go toolchain is a strange prerequisite for a CLI that talks to a server, and it was the only option this page offered. Releases now ship archives for Linux, macOS, and Windows on amd64 and arm64. Drops `make build` from this page: it needs a clone of the repository, which end-user documentation should not assume. It stays in the repository README where contributors will find it. The example filename matches goreleaser's archive naming, so it is the name a reader will actually see on the releases page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The CLI repository is now dbos-inc/dbos-ctl, so the releases link and the `go install` path both pointed at a name that only resolved through a GitHub redirect. Leads with the install script rather than a manual download: it picks the right archive for the platform, and it checks the download against the release checksums, which a reader unpacking a tarball by hand would not. Direct download and `go install` stay for people who want them. Verified that the documented `VERSION=... BIN_DIR=... sh` form actually passes both through the pipe, under dash as well as bash. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kraftp
reviewed
Aug 13, 2026
| `dbosctl` is a command-line client for the [Conductor API](./conductor-api.md). It manages workflows, queues, schedules, applications, and API keys against DBOS-managed Conductor or a [self-hosted Conductor](./hosting-conductor.md), with the target selected by a named **profile**. | ||
|
|
||
| :::info | ||
| The binary is named `dbosctl`, not `dbos`. The DBOS language SDKs ship their own `dbos` entrypoints (the Python SDK, for example, installs a `dbos` console script), so the `ctl` suffix keeps this CLI unambiguous alongside any of them. `dbosctl` is also distinct from [`dbos-cloud`](./dbos-cloud/cloud-cli.md), which manages applications and databases hosted on DBOS Cloud. |
Member
There was a problem hiding this comment.
Not really needed, clear enough from the name of the page
kraftp
reviewed
Aug 13, 2026
| Against a self-hosted Conductor running without authentication: | ||
|
|
||
| ```shell | ||
| dbosctl config set local --url http://localhost:8090 |
Member
There was a problem hiding this comment.
What about self-hosted with auth?
kraftp
reviewed
Aug 13, 2026
| The mutating workflow operations — cancel, resume, restart, fork, and delete — are carried out by your application, not by Conductor's database. Conductor dispatches them over the websocket to a healthy connected executor. If an application has no healthy executor connected (resume and restart additionally require one running the application's latest version), the call fails rather than being queued for later. | ||
| ::: | ||
|
|
||
| ### Queues and autoscaling |
Member
There was a problem hiding this comment.
Let's not document this until it's done (I know it's in the OpenAPI spec, but we can keep it out of our public docs)
kraftp
reviewed
Aug 13, 2026
| Workflow inputs and outputs can be large, so they are omitted unless you ask for them with `loadInput` and `loadOutput`. | ||
|
|
||
| :::tip | ||
| Several applications can share one system database. When they do, each workflow, queue, and schedule reports the application that owns it in an `applicationName` field, so you can tell whose objects you are looking at. The field is null for objects recorded before DBOS Transact tracked application names, and for in-memory queues. |
Member
There was a problem hiding this comment.
Probably remove this, API detail that doesn't deserve so much prominence (and misleading, applicationName is reported whether the system database is shared or not).
kraftp
approved these changes
Aug 13, 2026
kraftp
reviewed
Aug 13, 2026
| curl -O https://cloud.dbos.dev/conductor/v2/openapi-3.0.json | ||
| ``` | ||
|
|
||
| The spec served here is Conductor's own, with only its `servers` entry repointed at `/conductor` so that generated clients resolve paths correctly through DBOS Cloud. Nothing else — no path, schema, or security scheme — is rewritten or filtered. |
Member
There was a problem hiding this comment.
Suggested change
| The spec served here is Conductor's own, with only its `servers` entry repointed at `/conductor` so that generated clients resolve paths correctly through DBOS Cloud. Nothing else — no path, schema, or security scheme — is rewritten or filtered. | |
| The spec served here is Conductor's own, with only its `servers` entry repointed at `/conductor` so that generated clients resolve paths correctly through DBOS Cloud. |
kraftp
reviewed
Aug 13, 2026
| The semantics of cancelling, resuming, and forking are described in [Workflow Management](./workflow-management.md). **Restart** starts a new execution of a workflow, with a new ID and the same inputs, from its first step; the original workflow is left untouched. The bulk variants take an array of workflow IDs and apply the same operation to each, which is far cheaper than issuing the calls one at a time. **Export** and **import** move a workflow and its steps between deployments as a JSON document — useful for reproducing a production failure in a development environment. | ||
|
|
||
| :::info | ||
| The mutating workflow operations — cancel, resume, restart, fork, and delete — are carried out by your application, not by Conductor's database. Conductor dispatches them over the websocket to a healthy connected executor. If an application has no healthy executor connected (resume and restart additionally require one running the application's latest version), the call fails rather than being queued for later. |
Member
There was a problem hiding this comment.
Suggested change
| The mutating workflow operations — cancel, resume, restart, fork, and delete — are carried out by your application, not by Conductor's database. Conductor dispatches them over the websocket to a healthy connected executor. If an application has no healthy executor connected (resume and restart additionally require one running the application's latest version), the call fails rather than being queued for later. | |
| Workflow operations are carried out by your application, not by Conductor's database. Conductor dispatches them over the websocket to a healthy connected executor. If an application has no healthy executor connected, the call fails. |
kraftp
reviewed
Aug 13, 2026
| https://cloud.dbos.dev/conductor/v2/orgs/my_org/apps/my-app/workflows | ||
| ``` | ||
|
|
||
| The paths themselves are identical in both deployments — only the base differs. This is deliberate: a single generated client works against DBOS-managed and self-hosted Conductor with nothing but the base URL changed. |
Member
There was a problem hiding this comment.
Suggested change
| The paths themselves are identical in both deployments — only the base differs. This is deliberate: a single generated client works against DBOS-managed and self-hosted Conductor with nothing but the base URL changed. | |
| The paths themselves are identical in both deployments; only the base differs. |
- Apply the three inline suggestions: tighten the base-URL and served-spec notes, and simplify the workflow-dispatch admonition. - Drop the applicationName tip. It gave an API detail too much prominence and was misleading: applicationName is reported whether or not the system database is shared. Trims the matching aside in the dbosctl reference, which linked into it. - Drop the autoscaling endpoints, their Teams-plan note, and the policy explanation. They are in the OpenAPI spec but not finished, so keep them out of the public docs for now; the section is just "Queues" again. - Drop the dbosctl naming admonition — the page title carries it. - Add a self-hosted-with-OIDC quick start, between the managed and no-auth ones; the profile table already covered the shape but nothing showed it. - Remove the restart endpoint. Conductor deleted it in dbos-inc/conductor#190 and dbos-ctl#10 dropped the matching command; fork covers the same ground. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The note sat under Workflows and named cancel/resume/fork/delete, implying dispatch was a property of mutating a workflow. It is not: in services/conductor, queues, schedules, and the workflow reads (list, get, steps, events, notifications, streams, export, aggregates) all dispatch to a healthy executor too, as do listing application versions and setting the latest. ListWorkflows returns ErrNoHealthyExecutors exactly like CancelWorkflow does. Replace it with a "How Operations Are Served" section that splits the API by what answers the request — Conductor's database (identity, roles, API keys, application registration and settings, alerting, audit, metrics) versus your application over the websocket — and notes the 503/502 that only the second group returns, now also listed among the common statuses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The profile table listed the managed shape as OIDC-issued user JWT only, implying dbos_ API keys were a self-hosted concession. They are not: APIKeyAuthenticationMiddleware keys off the dbos_ prefix on the bearer token and is wired in wherever OAuth is on, so both authenticated shapes accept either credential. Managed and self-hosted-with-OIDC differ in where the OIDC settings come from, not in what they authenticate. Also correct the identity column, which claimed "your real user" for both: an API key caller has no user identity, which is why whoami needs a login and why an API-key profile needs an explicit --org. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Dispatch is not a property of mutating a workflow: every workflow command, reads included, goes to a healthy executor, which the Conductor API page now covers once. Point there instead of restating it per command. What is command-specific is the version constraint, and it is narrower than the note claimed. Only ResumeWorkflow and BulkResumeWorkflow filter executors to the latest application version; cancel, delete, fork, fork-from-failure, import/export, and every read take any healthy executor. So resume is the one worth calling out, and the caller-visible consequence is that a connected application mid-rollout can resume nothing while everything else works. Also drop the `workflow restart` reference section, missed when the command came out in dbos-ctl#10. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Scoping the note to workflow commands still implied they were special. They are not: queue and schedule commands dispatch identically, as do app versions and app set-version (ListApplicationVersions, SetLatestApplicationVersion), while the rest of app plus api-key, permission, and the auth and config commands are answered by Conductor itself. Since the split cuts across the reference sections, hoist it above them as its own table rather than repeating a variant per section, and leave the workflow section with only what is genuinely local to it, the resume version constraint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Conductor's HTTP API and the
dbosctlCLI had no documentation. This adds two pages under Deploy To Production and links them from the Conductor overview.Both are new files, so the diff is additive — nothing existing is rewritten beyond one bullet on
conductor.md.The pages
Conductor API — base URLs, the three ways to get the OpenAPI spec, authentication, resource scoping, the RFC 9457 error model, listing and filtering conventions, a map of all 64 operations by area, the operations a no-auth self-hosted deployment does not register, and how to generate a client.
dbosctl CLI Reference — installation, profiles, authentication, configuration precedence, output formats, exit codes, and a command-by-command reference. Follows the shape of the existing Cloud CLI reference page.
The sidebar is autogenerated from the directory, so no
sidebars.jschange is needed.Where the content came from
Not from the READMEs. The endpoint reference, parameter lists, and error shapes were read out of the OpenAPI spec, and the CLI reference was generated from a recursive
--helpdump of a real build, then diffed back against it mechanically — 38 leaf commands, no undocumented commands, no invented flags.Details that came from reading the implementation rather than the spec text, because a reader would otherwise hit them as surprises:
--entrypoint. The image's entrypoint waits for Postgres and runs migrations without forwarding arguments, so the obviousdocker run <image> openapihangs waiting for a database the subcommand does not need.Two scoping calls worth a look
The API is not "everything the console does." An earlier draft said so; it is wrong, because the console also drives DBOS Cloud — deploying applications, provisioning databases, billing — none of which this API touches. Both pages now scope it to the Conductor control plane, which is what the console shows for a Conductor-connected application whether it runs on your own infrastructure or on DBOS Cloud, and point the cloud-specific operations at the pages that own them.
Nothing is framed as versioned. There is one public API; the predecessor is internal.
v2appears only in URLs and paths, where you actually type it, and never in prose as though it were a choice.Verified against the shipped API
The pages track conductor
mainas of theapplicationNamerevert (dbos-inc/dbos-conductor#187) and the CLI as of dbos-inc/dbos-ctl#8 and #9:listWorkflowsquery parameters and the full workflow-search body field list match the spec exactly;applicationNameis documented as a response field only — the request filters were removed by Better Python Styling #187;dbos-cli→dbos-ctlrepository rename and lead with the new install script.The site builds. The only broken-anchor warnings are pre-existing ones on the Go reference pages, untouched by this branch.
One sequencing note
The
dbosctlpage links to the CLI's releases page and itsraw.githubusercontent.cominstall script, both of which are only reachable once that repository is public and a release is tagged. The page carries an early-access admonition, so it is not misleading in the meantime, but this probably wants to land alongside that flip.🤖 Generated with Claude Code