API reference

The operations Blowhorn defines, with their request fields and responses.

Blowhorn is used through its command-line interface and its MCP server. These are the operations behind them, listed with the fields each one takes and returns.

Not an HTTP API

Blowhorn ships a command-line interface and an MCP server today. It does not run an HTTP REST listener, so there is no server URL to send these requests to.

104 operations in 17 resources

Actions

POST /cli/accept Accept pending LinkedIn connection invitations other people have sent you, one at a time with a randomized pause between each. An invitation LinkedIn flags with its "Take care when connecting" warning is dismissed and skipped, never accepted, and the run carries on; accepts and skips are reported separately, by name. Ignoring or declining an invitation is out of scope - this command only accepts.

Accept pending LinkedIn connection invitations other people have sent you, one at a time with a randomized pause between each. An invitation LinkedIn flags with its "Take care when connecting" warning is dismissed and skipped, never accepted, and the run carries on; accepts and skips are reported separately, by name. Ignoring or declining an invitation is out of scope - this command only accepts.

Request body required

The flags and arguments of `blowhorn accept`, one property per parameter.

Request body of Accept pending LinkedIn connection invitations other people have sent you, one at a time with a randomized pause between each. An invitation LinkedIn flags with its "Take care when connecting" warning is dismissed and skipped, never accepted, and the run carries on; accepts and skips are reported separately, by name. Ignoring or declining an invitation is out of scope - this command only accepts. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

ignore_risk boolean

Run this once even if your Risk Profile would hold it (a daily cap, quiet hours, outreach turned off or a pause after a sign-in challenge). Only this run; your Risk Profile stays as it is. A limit the platform itself publishes still applies

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

limit integer

Maximum acceptances confirmed in one run. A click is not an acceptance: only an invitation confirmed gone from the received list counts, and an invitation skipped because LinkedIn warned about it never consumes the budget. 0 means no limit (the run stops at the examination ceiling).

Default: 20

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Pace of the random delays between 'blowhorn accept' actions (page loads, typing, clicks, API calls): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn accept'. One of: linkedin, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What `blowhorn accept --json` prints on exit 0: the received invitations the run examined as profile URLs, how many it accepted, and how many it skipped. A row never carries a display name: identity is the profile URL only, so no person's name lands in a committed artifact. A flagged invitation ("Take care when connecting") is skipped, never confirmed: the warning's own confirm control is never clicked. On a dry run `sent` is 0 and `candidates` are the rows a real run would accept; the preview cannot know which invitation would raise the warning, because LinkedIn raises it only after Accept is clicked, so a dry run lists every candidate and says so rather than promising none would be skipped.

Response 200 of Accept pending LinkedIn connection invitations other people have sent you, one at a time with a randomized pause between each. An invitation LinkedIn flags with its "Take care when connecting" warning is dismissed and skipped, never accepted, and the run carries on; accepts and skips are reported separately, by name. Ignoring or declining an invitation is out of scope - this command only accepts. fields
NameTypeDescription
accepted integer

Invitations confirmed accepted, the same count as `sent`.

candidates required array of object

The received invitations the run examined, as profile URLs, in list order.

candidates[].profile_url required string (uri)

The inviter's LinkedIn profile URL. Never a display name.

dry_run required boolean

True when nothing was clicked and nothing was written.

examined integer

Rows examined, including rows left unanswered.

failed integer

Rows whose acceptance failed after the control was clicked.

held required array of object

Rows held as clicked with the outcome not read. Acceptance records none of its own; the list is empty unless a held row was carried in.

held[].profile_url required string (uri)
sent required integer

Invitations confirmed accepted. Always 0 on a dry run.

skipped required integer

Rows examined and left unanswered (flagged, stuck, unactionable, unidentifiable, or unreadable).

unconfirmed integer

Rows clicked whose acceptance could not be confirmed from the list.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AcceptResponse

Variant fields
NameTypeDescription
accepted integer

Invitations confirmed accepted, the same count as `sent`.

candidates required array of object

The received invitations the run examined, as profile URLs, in list order.

candidates[].profile_url required string (uri)

The inviter's LinkedIn profile URL. Never a display name.

dry_run required boolean

True when nothing was clicked and nothing was written.

examined integer

Rows examined, including rows left unanswered.

failed integer

Rows whose acceptance failed after the control was clicked.

held required array of object

Rows held as clicked with the outcome not read. Acceptance records none of its own; the list is empty unless a held row was carried in.

held[].profile_url required string (uri)
sent required integer

Invitations confirmed accepted. Always 0 on a dry run.

skipped required integer

Rows examined and left unanswered (flagged, stuck, unactionable, unidentifiable, or unreadable).

unconfirmed integer

Rows clicked whose acceptance could not be confirmed from the list.

POST /cli/comment Post LinkedIn, Reddit, or Bluesky comments from scheduled activities or ad-hoc target/message flags.

Post LinkedIn, Reddit, or Bluesky comments from scheduled activities or ad-hoc target/message flags.

Request body required

The flags and arguments of `blowhorn comment`, one property per parameter.

Request body of Post LinkedIn, Reddit, or Bluesky comments from scheduled activities or ad-hoc target/message flags. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

ignore_risk boolean

Run this once even if your Risk Profile would hold it (a daily cap, quiet hours, outreach turned off or a pause after a sign-in challenge). Only this run; your Risk Profile stays as it is. A limit the platform itself publishes still applies

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

message string

Comment text for ad-hoc mode

pace string

Pace of the random delays between 'blowhorn comment' actions (page loads, typing, clicks, API calls): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn comment'. One of: bluesky, linkedin, reddit, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

target string

LinkedIn, Reddit, or Bluesky post URL for ad-hoc mode (for Slack thread replies, use 'blowhorn post --target ...' instead)

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

The command prints human-readable text; it has no --json contract.

Human-readable stdout from a command that has no --json flag. Its wording is not a contract; parse nothing out of it.

Schema: CliTextOutput

POST /cli/find Search Bluesky and GitHub for posts, issues, and pull requests matching a query, with optional date, author, and relevancy filters. Read-only: results are printed, never posted, and no browser is opened.

Search Bluesky and GitHub for posts, issues, and pull requests matching a query, with optional date, author, and relevancy filters. Read-only: results are printed, never posted, and no browser is opened.

Request body required

The flags and arguments of `blowhorn find`, one property per parameter.

Request body of Search Bluesky and GitHub for posts, issues, and pull requests matching a query, with optional date, author, and relevancy filters. Read-only: results are printed, never posted, and no browser is opened. fields
NameTypeDescription
author string

Filter by author handle

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

domain string

Filter by linked domain

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

lang string

Filter by language code

limit integer

Max results (1-100, default 25)

Default: 25

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

mentions string

Filter by mentioned account

pace string

Pace of the random delays between 'blowhorn find' actions (page loads, typing, clicks, API calls): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn find'. One of: bluesky, github, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

query required string

Search query string (Lucene syntax supported)

repo array of string

GitHub only: search only in owner/name repositories (repeatable, or comma-delimited)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

since string

Filter posts after date (YYYY-MM-DD)

sort string

Ranking order: 'top' or 'latest' (default: latest)

state string

GitHub only: open, closed, or all (default: open)

Default: "open"

tag string

Filter by hashtag(s), comma-delimited

type_ string

GitHub only: issue, pr, or all (default: all)

Default: "all"

until string

Filter posts before date (YYYY-MM-DD)

url string

Filter by linked URL

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

The command prints human-readable text; it has no --json contract.

Human-readable stdout from a command that has no --json flag. Its wording is not a contract; parse nothing out of it.

Schema: CliTextOutput

POST /cli/follow Follow accounts on X, Bluesky, or GitHub. --limit 0 is target-only mode: the --target handle itself is followed and nothing else. With a positive --limit, --target is only the seed - the run follows up to --limit accounts drawn from that account's follower graph, and never follows the target. GitHub runs over the REST API with no browser, so its --dry-run performs the real follower walk and names exactly who would be followed.

Follow accounts on X, Bluesky, or GitHub. --limit 0 is target-only mode: the --target handle itself is followed and nothing else. With a positive --limit, --target is only the seed - the run follows up to --limit accounts drawn from that account's follower graph, and never follows the target. GitHub runs over the REST API with no browser, so its --dry-run performs the real follower walk and names exactly who would be followed.

Request body required

The flags and arguments of `blowhorn follow`, one property per parameter.

Request body of Follow accounts on X, Bluesky, or GitHub. --limit 0 is target-only mode: the --target handle itself is followed and nothing else. With a positive --limit, --target is only the seed - the run follows up to --limit accounts drawn from that account's follower graph, and never follows the target. GitHub runs over the REST API with no browser, so its --dry-run performs the real follower walk and names exactly who would be followed. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

ignore_risk boolean

Run this once even if your Risk Profile would hold it (a daily cap, quiet hours, outreach turned off or a pause after a sign-in challenge). Only this run; your Risk Profile stays as it is. A limit the platform itself publishes still applies

Default: false

limit integer

Max follows; 0 means target-only mode

Default: 20

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Pace of the random delays between 'blowhorn follow' actions (page loads, typing, clicks, API calls): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn follow'. One of: bluesky, github, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

target string

Target account handle (comma-delimited when --limit 0)

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

The command prints human-readable text; it has no --json contract.

Human-readable stdout from a command that has no --json flag. Its wording is not a contract; parse nothing out of it.

Schema: CliTextOutput

POST /cli/post Publish pending posts or amplify existing content across supported platforms. GitHub is the one platform 'blowhorn post' cannot publish to: it is amplify-only, so '--platform github' needs an issue or pull-request URL (from --amplify or a github activity's 'Amplify' column) and refuses without one. That amplification adds all five uplifting reactions -- +1, laugh, hooray, heart, rocket -- to the target from every selected profile under its own GH_TOKEN, over the REST API with no browser, and re-running it adds none of them twice. With --payload-stdin it is a dry fire instead: one order that is not saved, read on stdin, is walked through the same dry run as far as the platform's final click, and nothing is posted or written to your Blowhorn account.

Publish pending posts or amplify existing content across supported platforms. GitHub is the one platform 'blowhorn post' cannot publish to: it is amplify-only, so '--platform github' needs an issue or pull-request URL (from --amplify or a github activity's 'Amplify' column) and refuses without one. That amplification adds all five uplifting reactions -- +1, laugh, hooray, heart, rocket -- to the target from every selected profile under its own GH_TOKEN, over the REST API with no browser, and re-running it adds none of them twice. With --payload-stdin it is a dry fire instead: one order that is not saved, read on stdin, is walked through the same dry run as far as the platform's final click, and nothing is posted or written to your Blowhorn account.

Request body required

The flags and arguments of `blowhorn post`, one property per parameter.

Request body of Publish pending posts or amplify existing content across supported platforms. GitHub is the one platform 'blowhorn post' cannot publish to: it is amplify-only, so '--platform github' needs an issue or pull-request URL (from --amplify or a github activity's 'Amplify' column) and refuses without one. That amplification adds all five uplifting reactions -- +1, laugh, hooray, heart, rocket -- to the target from every selected profile under its own GH_TOKEN, over the REST API with no browser, and re-running it adds none of them twice. With --payload-stdin it is a dry fire instead: one order that is not saved, read on stdin, is walked through the same dry run as far as the platform's final click, and nothing is posted or written to your Blowhorn account. fields
NameTypeDescription
amplify string

URL of existing content to amplify instead of publishing something new. Overrides the activity's 'Amplify' column for this run. LinkedIn repost, X retweet/quote, Reddit upvote, Bluesky repost, GitHub reactions. On GitHub it names an issue or pull request and blowhorn adds all five uplifting reactions to it (+1, laugh, hooray, heart, rocket) from every selected profile under that profile's own GH_TOKEN; re-running adds none of them twice. --platform github REQUIRES a target -- this flag, or a github activity's 'Amplify' column -- because it amplifies only and cannot publish a new GitHub post; it refuses the run rather than doing nothing. Ignored for Slack -- use --target instead; Slack's 'Amplify' column selects the channel/DM/thread target, not an amplified source. Ignored for blog platforms (layer5/meshery).

check_queue boolean

Show pending posts from the content queue without publishing anything. Honors --platform / --profile / --exclude for filtering. Read-only - does not open a browser, hit a platform API, or invoke 'claude' for blog activities.

Default: false

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

ignore_risk boolean

Run this once even if your Risk Profile would hold it (a daily cap, quiet hours, outreach turned off or a pause after a sign-in challenge). Only this run; your Risk Profile stays as it is. A limit the platform itself publishes still applies

Default: false

json_output boolean

With --payload-stdin, end with the dry fire's result as one JSON object

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

message string

Send this text as one ad-hoc Slack message instead of posting from the content queue: no activity is read or marked done. Needs --platform slack, one profile named with --profile (not all, not a list) and --target ('#channel', '@user', a Slack channel link, or a Slack message link for a thread reply -- anything else is refused, never sent to #general); a profile from .blowhorn.yaml or BLOWHORN_PROFILE does not count as named. A link also picks the workspace's credentials. Refused with --check-queue or --amplify. --dry-run says what would be sent and sends nothing.

pace string

Pace of the random delays between 'blowhorn post' actions (page loads, typing, clicks, API calls): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

payload_stdin boolean

Dry fire one order that is not saved: read one content item's values as JSON on stdin (the fields 'content upsert' takes, naming one Profile and one Platform) and walk it through the dry run as far as the platform's final click, printing each stage as it is reached. Needs --dry-run; --profile and --platform are not read. Writes nothing - no content item, schedule, ledger entry or run record. Hacker News is refused, because its dry run signs in.

Default: false

platform string

Platform scope for 'blowhorn post'. One of: bluesky, github, hn, layer5, linkedin, meshery, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

slack_auth string

Which Slack credential may send, for one run: 'session' (your captured signed-in web session; the message is yours, with no app attached), 'user-token' (an xoxp- app token for you) or 'bot-token' (an xoxb- bot token), or a comma-delimited list of them tried in order (e.g. 'session,user-token,bot-token'). One value is directive: that credential or a refusal naming why. A token may show the Slack app's attribution. This flag, then BLOWHORN_SLACK_AUTH, then defaults.slack.auth_mode in .blowhorn.yaml, then the built-in, selects it; any other value is refused before anything runs. Dry runs and results name the credential that ran and whether it was a fallback

target string

Ad-hoc target that overrides the activity's values for this run. Slack: '#channel', '@user', a Slack channel link, or a Slack message link (posts as a thread reply); a link also picks that workspace's credentials. Other platforms: generally leave unset and use the activity's 'Comment on' column for replies. Ignored for blog platforms (layer5/meshery).

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What a dry fire did: `post --dry-run --payload-stdin --json` read one order that is not saved (one content item's values, naming one profile and one platform) and walked it through the dry run as far as the platform's final click. `stages` lists each point the walk reached, in order, as it printed them. Nothing was posted and nothing was written to the store - no content item, schedule, ledger entry or run record. `post --json` reports nothing else: a queue run prints text. A refusal (Hacker News, a profile without the credential, a sign-in that failed) is the failure response, with the stages it reached.

Response 200 of Publish pending posts or amplify existing content across supported platforms. GitHub is the one platform 'blowhorn post' cannot publish to: it is amplify-only, so '--platform github' needs an issue or pull-request URL (from --amplify or a github activity's 'Amplify' column) and refuses without one. That amplification adds all five uplifting reactions -- +1, laugh, hooray, heart, rocket -- to the target from every selected profile under its own GH_TOKEN, over the REST API with no browser, and re-running it adds none of them twice. With --payload-stdin it is a dry fire instead: one order that is not saved, read on stdin, is walked through the same dry run as far as the platform's final click, and nothing is posted or written to your Blowhorn account. fields
NameTypeDescription
dry_run required any
ok required any
platform required string

The platform walked. Hacker News is refused, because its dry run signs in.

Values: linkedin, x, reddit, slack, bluesky, github

posted required any

Always false. A dry fire never posts.

profile required string

The profile the order was rehearsed as, lowercased.

rehearsal required any
stages required array of object
stages[].detail required string

The stage in words, as the line printed for it.

stages[].stage required string

`checked` the profile may post there; `browser` its browser profile opened; `signin` the session check passed; `api` the platform posts over its API, with no browser; `stopped` the platform's own dry run returned, short of composing; `refused` the walk ended here, and `detail` says why.

Values: checked, browser, signin, api, stopped, refused

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

PostResponse

Variant fields
NameTypeDescription
dry_run required any
ok required any
platform required string

The platform walked. Hacker News is refused, because its dry run signs in.

Values: linkedin, x, reddit, slack, bluesky, github

posted required any

Always false. A dry fire never posts.

profile required string

The profile the order was rehearsed as, lowercased.

rehearsal required any
stages required array of object
stages[].detail required string

The stage in words, as the line printed for it.

stages[].stage required string

`checked` the profile may post there; `browser` its browser profile opened; `signin` the session check passed; `api` the platform posts over its API, with no browser; `stopped` the platform's own dry run returned, short of composing; `refused` the walk ended here, and `detail` says why.

Values: checked, browser, signin, api, stopped, refused

POST /cli/source Collect people into the store from GitHub or LFX. GitHub lists one audience of one or more repositories (stargazers unless you name another) and stores one row per login. LFX reads one mentorship project's mentee list and upserts the mentees roster. One run names one platform: --repo and --audience belong to GitHub, --project and --start-page belong to LFX. A GitHub run lists the audience with the profile's stored GH_TOKEN. --dry-run lists that audience and writes nothing. Addresses are not printed. An LFX dry run names the list and writes nothing; a real LFX run reads each profile through Chrome. It does not follow, connect, or send mail.

Collect people into the store from GitHub or LFX. GitHub lists one audience of one or more repositories (stargazers unless you name another) and stores one row per login. LFX reads one mentorship project's mentee list and upserts the mentees roster. One run names one platform: --repo and --audience belong to GitHub, --project and --start-page belong to LFX. A GitHub run lists the audience with the profile's stored GH_TOKEN. --dry-run lists that audience and writes nothing. Addresses are not printed. An LFX dry run names the list and writes nothing; a real LFX run reads each profile through Chrome. It does not follow, connect, or send mail.

Request body required

The flags and arguments of `blowhorn source`, one property per parameter.

Request body of Collect people into the store from GitHub or LFX. GitHub lists one audience of one or more repositories (stargazers unless you name another) and stores one row per login. LFX reads one mentorship project's mentee list and upserts the mentees roster. One run names one platform: --repo and --audience belong to GitHub, --project and --start-page belong to LFX. A GitHub run lists the audience with the profile's stored GH_TOKEN. --dry-run lists that audience and writes nothing. Addresses are not printed. An LFX dry run names the list and writes nothing; a real LFX run reads each profile through Chrome. It does not follow, connect, or send mail. fields
NameTypeDescription
audience string

GitHub only: which people to list (default stargazers). One of owner, contributors, forks, stargazers, watchers, subscribers, issues; all is their union and applies only when you type it

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

limit integer

Maximum people examined in the run (default: no limit)

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Pace of the random delays between 'blowhorn source' actions (page loads, typing, clicks, API calls): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn source'. One of: github, lfx (any other platform, including 'all', is rejected)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

project string

LFX only, required: a project UUID or a mentees URL on mentorship.lfx.linuxfoundation.org

repo array of string

GitHub only, repeatable: a repository as owner/name. At least one is required

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

start_page integer

LFX only: zero-based page to resume from (default 0)

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

The command prints human-readable text; it has no --json contract.

Human-readable stdout from a command that has no --json flag. Its wording is not a contract; parse nothing out of it.

Schema: CliTextOutput

POST /cli/star Star GitHub repositories. From every selected profile, over the REST API with no browser: --platform github --repo owner/repo, repeatable. Already-starred repositories are counted apart, never re-credited. --dry-run reads each repository's star state for real and names exactly which would be starred, issuing no PUT. A live run exits 0 only when at least one repository was confirmed and every attempted repository was confirmed by at least one profile. Star cannot be scheduled: it stars only the repositories named on the command line.

Star GitHub repositories. From every selected profile, over the REST API with no browser: --platform github --repo owner/repo, repeatable. Already-starred repositories are counted apart, never re-credited. --dry-run reads each repository's star state for real and names exactly which would be starred, issuing no PUT. A live run exits 0 only when at least one repository was confirmed and every attempted repository was confirmed by at least one profile. Star cannot be scheduled: it stars only the repositories named on the command line.

Request body required

The flags and arguments of `blowhorn star`, one property per parameter.

Request body of Star GitHub repositories. From every selected profile, over the REST API with no browser: --platform github --repo owner/repo, repeatable. Already-starred repositories are counted apart, never re-credited. --dry-run reads each repository's star state for real and names exactly which would be starred, issuing no PUT. A live run exits 0 only when at least one repository was confirmed and every attempted repository was confirmed by at least one profile. Star cannot be scheduled: it stars only the repositories named on the command line. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

ignore_risk boolean

Run this once even if your Risk Profile would hold it (a daily cap, quiet hours, outreach turned off or a pause after a sign-in challenge). Only this run; your Risk Profile stays as it is. A limit the platform itself publishes still applies

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Pace of the random delays between 'blowhorn star' actions (page loads, typing, clicks, API calls): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn star'. One of: github (any other platform, including 'all', is rejected)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

repo array of string

GitHub only, repeatable: a repository as owner/name, or a github.com URL for one. At least one is required

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

The command prints human-readable text; it has no --json contract.

Human-readable stdout from a command that has no --json flag. Its wording is not a contract; parse nothing out of it.

Schema: CliTextOutput

POST /cli/unfollow Unfollow one Bluesky account (--platform bluesky --target <handle>) through the Bluesky API. X unfollow does not run: it is refused before a browser opens until a follow-back badge and a confirmed landing are both read, so no X account is unfollowed or logged as unfollowed. Unfollow cannot be scheduled.

Unfollow one Bluesky account (--platform bluesky --target <handle>) through the Bluesky API. X unfollow does not run: it is refused before a browser opens until a follow-back badge and a confirmed landing are both read, so no X account is unfollowed or logged as unfollowed. Unfollow cannot be scheduled.

Request body required

The flags and arguments of `blowhorn unfollow`, one property per parameter.

Request body of Unfollow one Bluesky account (--platform bluesky --target <handle>) through the Bluesky API. X unfollow does not run: it is refused before a browser opens until a follow-back badge and a confirmed landing are both read, so no X account is unfollowed or logged as unfollowed. Unfollow cannot be scheduled. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

limit integer

Accepted and not read: a run unfollows the one --target from each profile

Default: 20

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Pace of the random delays between 'blowhorn unfollow' actions (page loads, typing, clicks, API calls): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn unfollow'. One of: bluesky (any other platform, including 'all', is rejected)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

target string

Bluesky handle to unfollow

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

The command prints human-readable text; it has no --json contract.

Human-readable stdout from a command that has no --json flag. Its wording is not a contract; parse nothing out of it.

Schema: CliTextOutput

POST /cli/withdraw Withdraw LinkedIn invitations that have been pending longer than --older-than days, freeing headroom under LinkedIn's cap on unaccepted invitations. People only, never Pages. Previews by default; clicks only with --apply.

Withdraw LinkedIn invitations that have been pending longer than --older-than days, freeing headroom under LinkedIn's cap on unaccepted invitations. People only, never Pages. Previews by default; clicks only with --apply.

Request body required

The flags and arguments of `blowhorn withdraw`, one property per parameter.

Request body of Withdraw LinkedIn invitations that have been pending longer than --older-than days, freeing headroom under LinkedIn's cap on unaccepted invitations. People only, never Pages. Previews by default; clicks only with --apply. fields
NameTypeDescription
apply boolean

Actually withdraw. Without it the run clicks nothing and only lists what it would withdraw. LinkedIn blocks resending to a withdrawn person for up to 3 weeks, so this is not reversible; --dry-run overrides this flag.

Default: false

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

limit integer

Maximum withdrawals confirmed in one run. A click is not a withdrawal: only a card confirmed gone from the sent list counts. 0 means withdraw nothing.

Default: 20

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

older_than integer

Minimum days an invitation must have been pending to be eligible. LinkedIn renders only relative ages, and one worded in a way Blowhorn cannot read is always kept and reported under Unreadable.

Default: 21

pace string

Pace of the random delays between 'blowhorn withdraw' actions (page loads, typing, clicks, API calls): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn withdraw'. One of: linkedin, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What `blowhorn withdraw --json` prints on exit 0: the sent invitations the run examined as profile URLs, how many withdrawals it confirmed, and how many cards it left alone. A row never carries a display name: identity is the profile URL only, so no person's name lands in a committed artifact. `--limit` is spent only on confirmed withdrawals: a click that reads no confirmation neither consumes the budget nor counts as withdrawn, and a combined unconfirmed/failed streak halts the run. A card whose age cannot be parsed is always kept and counted under `unreadable`, never withdrawn. On a dry run `sent` is 0 and `candidates` are the eligible rows `--apply` would withdraw.

Response 200 of Withdraw LinkedIn invitations that have been pending longer than --older-than days, freeing headroom under LinkedIn's cap on unaccepted invitations. People only, never Pages. Previews by default; clicks only with --apply. fields
NameTypeDescription
candidates required array of object

The eligible sent invitations the run examined, as profile URLs, oldest first as the list renders them.

candidates[].profile_url required string (uri)

The invitee's LinkedIn profile URL. Never a display name.

dry_run required boolean

True when nothing was withdrawn and nothing was written.

examined integer

Cards examined, including cards left alone.

failed integer

Cards whose withdrawal failed after the control was clicked.

held required array of object

Rows held as clicked with the outcome not read. Withdrawal records none of its own; the list is empty unless a held row was carried in.

held[].profile_url required string (uri)
sent required integer

Withdrawals confirmed (the card provably left the sent list). Always 0 on a dry run.

skipped required integer

Cards examined and left alone (too new to withdraw, or an age that could not be parsed).

too_new integer

Cards newer than the `--older-than` cutoff, left alone.

unconfirmed integer

Cards clicked whose withdrawal could not be confirmed from the list.

unreadable integer

Cards whose pending age could not be parsed, kept and never withdrawn.

withdrawn integer

Withdrawals confirmed, the same count as `sent`.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

WithdrawResponse

Variant fields
NameTypeDescription
candidates required array of object

The eligible sent invitations the run examined, as profile URLs, oldest first as the list renders them.

candidates[].profile_url required string (uri)

The invitee's LinkedIn profile URL. Never a display name.

dry_run required boolean

True when nothing was withdrawn and nothing was written.

examined integer

Cards examined, including cards left alone.

failed integer

Cards whose withdrawal failed after the control was clicked.

held required array of object

Rows held as clicked with the outcome not read. Withdrawal records none of its own; the list is empty unless a held row was carried in.

held[].profile_url required string (uri)
sent required integer

Withdrawals confirmed (the card provably left the sent list). Always 0 on a dry run.

skipped required integer

Cards examined and left alone (too new to withdraw, or an age that could not be parsed).

too_new integer

Cards newer than the `--older-than` cutoff, left alone.

unconfirmed integer

Cards clicked whose withdrawal could not be confirmed from the list.

unreadable integer

Cards whose pending age could not be parsed, kept and never withdrawn.

withdrawn integer

Withdrawals confirmed, the same count as `sent`.

Profile

POST /cli/profile/accounts List, map or unmap a profile's social accounts. An account is a Blowhorn

List, map or unmap a profile's social accounts. An account is a Blowhorn connection on one platform; a profile acts through the accounts mapped to it, one per platform, whoever owns them, so a person may keep several profiles and a brand profile reaches the accounts mapped to it. --map replaces the profile's account on that platform; --unmap leaves the connection itself intact. --page records the account's own page on the account already mapped for that platform, which is where the profile's handle is read from: Chrome profile discovery finds a sign-in, never a LinkedIn /in/ name.

Request body required

The flags and arguments of `blowhorn profile accounts`, one property per parameter.

Request body of List, map or unmap a profile's social accounts. An account is a Blowhorn fields
NameTypeDescription
account_page string

Record this account page (x.com/<handle>, linkedin.com/in/<name>, github.com/<login>, reddit.com/user/<name> or bsky.app/profile/<handle>) on the account mapped for its platform

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

map_connection string

Map this connection id to the profile, replacing its account on that platform

name required string

Profile name

pace string

Pace of the random delays between 'blowhorn profile accounts' browser steps: a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn profile'. One of: bluesky, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

unmap_connection string

Unmap this connection id from the profile; the connection is left intact

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

A profile's social accounts: the connections in your Blowhorn account mapped to it (one per platform) and the organization's other platform connections it could map. `change` names what `--map`, `--unmap` or `--page` did, and is null for a plain listing. A `page` change carries the account page recorded (`url`) and whether anything was sent (`outcome`).

Response 200 of List, map or unmap a profile's social accounts. An account is a Blowhorn fields
NameTypeDescription
available required array of ProfileAccount
available[].id required string
available[].name required string | null
available[].owner required string | null
available[].platform required string
available[].status required string | null
available[].url required string | null
change required one of null, object
mapped required array of ProfileAccount
mapped[].id required string
mapped[].name required string | null
mapped[].owner required string | null
mapped[].platform required string
mapped[].status required string | null
mapped[].url required string | null
ok required any
profile required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ProfileAccountsResponse

Variant fields
NameTypeDescription
available required array of ProfileAccount
available[].id required string
available[].name required string | null
available[].owner required string | null
available[].platform required string
available[].status required string | null
available[].url required string | null
change required one of null, object
mapped required array of ProfileAccount
mapped[].id required string
mapped[].name required string | null
mapped[].owner required string | null
mapped[].platform required string
mapped[].status required string | null
mapped[].url required string | null
ok required any
profile required string
POST /cli/profile/auth Refresh authentication/session state for one or more platforms.

Refresh authentication/session state for one or more platforms.

Supported platforms and authentication mechanisms:

linkedin - Persistent browser session. Automated login via LI_USERNAME / LI_PASSWORD in the store, with fallback to manual browser entry.

x - Persistent browser session. Automated login via X_USERNAME / X_PASSWORD in the store, with fallback to manual browser entry.

reddit - Persistent browser session. Automated login via RDDT_USERNAME / RDDT_PASSWORD in the store, with fallback to manual browser entry.

slack - Hybrid: session credentials (xoxc token + d cookie) captured through the run's browser driver (the extension by default), saved to the profile's Slack credential in the store, and reused for direct API calls. No Slack app install required. Use --workspace <host> to capture a new workspace; omit it to refresh every workspace listed under 'slack_workspaces' in that credential. Use --mode paste for the legacy OAuth-token flow.

bluesky - No browser. Logs in to Bluesky with a handle and app password -- the same login every post makes -- and stores the pair only when Bluesky accepts it, so a refused credential is never saved. For one named profile in a terminal it asks for both (the stored handle is the default; a blank app password keeps the stored one); --handle and --app-password-stdin supply them without a prompt. Otherwise the stored pair is logged in with and nothing is written.

Use --profile all to refresh every profile in your organization after applying any --exclude filters. The profiles are read from the store; a store that cannot be reached is the named cause and exit 3.

With --headless the browser still opens, as a background window (Chrome is never run headless); only automated credential login is attempted and the manual browser fallback is skipped.

Request body required

The flags and arguments of `blowhorn profile auth`, one property per parameter.

Request body of Refresh authentication/session state for one or more platforms. fields
NameTypeDescription
all boolean

Refresh every platform 'blowhorn profile auth' can log in to (linkedin, x, reddit, slack). Supersedes --platform: when --all is set the platform is ignored, whether you typed it or inherited it from BLOWHORN_PLATFORM or defaults.platform

Default: false

app_password_stdin boolean

Bluesky only, one named profile: read the app password from the first line of stdin, so it never reaches argv, the daily log or a prompt. A blank line is refused and nothing is written

Default: false

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

handle string

Bluesky only, one named profile: the handle to log in as (alice.bsky.social; a leading @ is dropped). Stored with the app password only when Bluesky accepts the login; omit it to use the stored handle

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

mode string

Slack only: 'session' (default) captures xoxc/d via browser; 'paste' prompts for a User OAuth Token (xoxp-…) - requires app install.

pace string

Pace of the random delays between 'blowhorn profile auth' browser steps: a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn profile'. One of: bluesky, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

workspace string

Slack only: capture session credentials for this workspace domain (e.g. 'mesheryio.slack.com'). Opens a browser window; sign in to Slack, and the xoxc token + d cookie are extracted. If omitted, all workspaces listed under 'slack_workspaces' in the profile's Slack credential in the store are refreshed.

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints, per profile and platform, whether sign-in was refreshed (or would be, with `--dry-run`). `ok` is false when any row failed, and the same document is printed on that path.

Response 200 of Refresh authentication/session state for one or more platforms. fields
NameTypeDescription
dry_run required boolean
failed required array of ProfileAuthRow
failed[].detail string

What was read or stored, never a secret.

failed[].platform required string
failed[].profile required string
failed[].status required string

A short outcome such as ok, would refresh or a failure with its reason in parentheses.

ok required boolean
rows required array of ProfileAuthRow
rows[].detail string

What was read or stored, never a secret.

rows[].platform required string
rows[].profile required string
rows[].status required string

A short outcome such as ok, would refresh or a failure with its reason in parentheses.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ProfileAuthResponse

Variant fields
NameTypeDescription
dry_run required boolean
failed required array of ProfileAuthRow
failed[].detail string

What was read or stored, never a secret.

failed[].platform required string
failed[].profile required string
failed[].status required string

A short outcome such as ok, would refresh or a failure with its reason in parentheses.

ok required boolean
rows required array of ProfileAuthRow
rows[].detail string

What was read or stored, never a secret.

rows[].platform required string
rows[].profile required string
rows[].status required string

A short outcome such as ok, would refresh or a failure with its reason in parentheses.

POST /cli/profile/create Create a profile in your organization on Blowhorn.

Create a profile in your organization on Blowhorn. It reads a JSON document on stdin (--payload-stdin) holding the fields a person sets: name (required), description and userId. userId is the organization member the profile represents, as `blowhorn org members` lists them; leave it out for a brand. Blowhorn adds the organization, and you own the profile. The name must be handle-shaped (lowercase letters, digits, '-' or '_') and not one another profile uses. Any other key is refused, every refusal comes before anything is sent, and --dry-run makes the same checks and sends nothing. No folder is created and no email is looked up.

Request body required

The JSON document `profile create --payload-stdin` and `profile update --payload-stdin` read: the v1beta3 `ProfilePayload` fields a person sets. Blowhorn adds `organizationId`; `owner` and `metadata` are never taken from this document. Any other key is refused. On an update the document is the whole of these fields: a `description` left out is cleared and a `userId` left out makes the profile a brand.

Request body of Create a profile in your organization on Blowhorn. fields
NameTypeDescription
description string

What the profile is for. Blank is the same as left out.

name required string

The profile's name, handle-shaped: lowercase letters, digits, `-` or `_`, starting with a letter or digit, at most 64 characters, never `all`, and not a name another visible profile uses. Trimmed and lowercased before it is checked.

userId string (uuid) | null

The member of the organization the profile represents, as `org members` lists them. Null or left out for a brand.

Responses

200

What the command prints to stdout with --json when it exits 0.

What `profile create --payload-stdin` did: the person-set fields it sent (or, with --dry-run, would send) to `createProfile`, and the profile Blowhorn answered with. `profile` is null on a dry run, which sends nothing.

Response 200 of Create a profile in your organization on Blowhorn. fields
NameTypeDescription
dry_run required boolean
ok required any
payload required object

The document after it was trimmed and normalized; a blank description or userId is left out.

payload.description string
payload.name required string
payload.userId string
profile required one of null, ProfileListItem

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ProfileCreateResponse

Variant fields
NameTypeDescription
dry_run required boolean
ok required any
payload required object

The document after it was trimmed and normalized; a blank description or userId is left out.

payload.description string
payload.name required string
payload.userId string
profile required one of null, ProfileListItem
POST /cli/profile/current Show the active profile after resolving CLI, env, and config defaults.

Show the active profile after resolving CLI, env, and config defaults.

Request body required

The flags and arguments of `blowhorn profile current`, one property per parameter.

Request body of Show the active profile after resolving CLI, env, and config defaults. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn profile current' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn profile'. One of: bluesky, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn profile current' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

The command prints human-readable text; it has no --json contract.

Human-readable stdout from a command that has no --json flag. Its wording is not a contract; parse nothing out of it.

Schema: CliTextOutput

POST /cli/profile/delete Delete a profile in your organization, named by its name or its id: Blowhorn asks

Delete a profile in your organization, named by its name or its id: Blowhorn asks your Blowhorn account to delete it, its accounts are unlinked, and the accounts themselves are kept. Nothing on this computer is moved or removed. A profile your organization does not list is refused. Needs the store: unreachable is exit 3 and nothing is deleted. Requires --yes; --dry-run asks nothing and deletes nothing.

Request body required

The flags and arguments of `blowhorn profile delete`, one property per parameter.

Request body of Delete a profile in your organization, named by its name or its id: Blowhorn asks fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

name required string

The profile's name, or its id as profile list prints it

pace string

Accepted by 'blowhorn profile delete' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn profile'. One of: bluesky, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn profile delete' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

yes boolean

Confirm the delete; without it the command refuses and exits 1

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints whether the profile was deleted in your organization (`deleteProfile`): its accounts are unlinked and the accounts themselves kept. `name` is the deleted profile's name, also when it was named by its id. Nothing on this computer is moved or removed. With `--dry-run` nothing is asked and nothing is deleted.

Response 200 of Delete a profile in your organization, named by its name or its id: Blowhorn asks fields
NameTypeDescription
deleted required boolean
dry_run required boolean
name required string
ok required any

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ProfileDeleteResponse

Variant fields
NameTypeDescription
deleted required boolean
dry_run required boolean
name required string
ok required any
POST /cli/profile/get Print one profile as your Blowhorn account holds it: its id, description and the

Print one profile as your Blowhorn account holds it: its id, description and the person it represents, the accounts mapped to it, and which fields each platform's credential holds, every one shown as {"set": true}. No credential value is ever printed, in JSON or in the table. A store that cannot be reached is the named cause and exit 3.

Request body required

The flags and arguments of `blowhorn profile get`, one property per parameter.

Request body of Print one profile as your Blowhorn account holds it: its id, description and the fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

name required string

Profile name

pace string

Accepted by 'blowhorn profile get' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn profile'. One of: bluesky, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn profile get' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

One profile as Blowhorn holds it: the profile, the accounts mapped to it, and which fields each platform's credential holds. A credential field is only ever `{"set": true}`; its value is never printed.

Response 200 of Print one profile as your Blowhorn account holds it: its id, description and the fields
NameTypeDescription
accounts required one of null, array of ProfileAccount

The accounts mapped to the profile; null when they could not be read.

accounts_error required string | null

Why the accounts could not be read; null when they were.

credentials required one of null, object

Platform to the credential fields it holds, each `{"set": true}`; null when the credentials could not be read.

credentials_read required AvailabilityStatus

Whether a source a command reads (the store's credentials, the roster) could be read, and the one sentence why not.

credentials_read.available required boolean
credentials_read.error required string | null
ok required any
profile required ProfileListItem

One v1beta3 profile as Blowhorn prints it: its identity, the fields a person sets, and the person it represents, read from the organization's members. `person` is null for a brand (no `userId`) and for a represented user who is not a member Blowhorn can read.

profile.description required string
profile.id required string

The Cloud profile id, Blowhorn's identity for the profile.

profile.name required string

The profile's name, what `--profile` takes.

profile.owner required string | null
profile.person required one of null, object
profile.userId required string | null

The member the profile represents; null for a brand.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ProfileGetResponse

Variant fields
NameTypeDescription
accounts required one of null, array of ProfileAccount

The accounts mapped to the profile; null when they could not be read.

accounts_error required string | null

Why the accounts could not be read; null when they were.

credentials required one of null, object

Platform to the credential fields it holds, each `{"set": true}`; null when the credentials could not be read.

credentials_read required AvailabilityStatus

Whether a source a command reads (the store's credentials, the roster) could be read, and the one sentence why not.

credentials_read.available required boolean
credentials_read.error required string | null
ok required any
profile required ProfileListItem

One v1beta3 profile as Blowhorn prints it: its identity, the fields a person sets, and the person it represents, read from the organization's members. `person` is null for a brand (no `userId`) and for a represented user who is not a member Blowhorn can read.

profile.description required string
profile.id required string

The Cloud profile id, Blowhorn's identity for the profile.

profile.name required string

The profile's name, what `--profile` takes.

profile.owner required string | null
profile.person required one of null, object
profile.userId required string | null

The member the profile represents; null for a brand.

POST /cli/profile/list List the profiles your organization holds in your Blowhorn account: each profile's name, description, and the person it represents (none for a brand). This is the list every command selects profiles from; a folder on this computer does not add a profile to it. One read of the profiles and one of your organization's members; a store that cannot be reached is the named cause and exit 3.

List the profiles your organization holds in your Blowhorn account: each profile's name, description, and the person it represents (none for a brand). This is the list every command selects profiles from; a folder on this computer does not add a profile to it. One read of the profiles and one of your organization's members; a store that cannot be reached is the named cause and exit 3.

Request body required

The flags and arguments of `blowhorn profile list`, one property per parameter.

Request body of List the profiles your organization holds in your Blowhorn account: each profile's name, description, and the person it represents (none for a brand). This is the list every command selects profiles from; a folder on this computer does not add a profile to it. One read of the profiles and one of your organization's members; a store that cannot be reached is the named cause and exit 3. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn profile list' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn profile'. One of: bluesky, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn profile list' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The organization's profiles as Blowhorn lists them (`getProfiles`), the list every command selects profiles from. Each item is the v1beta3 profile's identity and the person it represents, read from the organization's members in one pass.

Response 200 of List the profiles your organization holds in your Blowhorn account: each profile's name, description, and the person it represents (none for a brand). This is the list every command selects profiles from; a folder on this computer does not add a profile to it. One read of the profiles and one of your organization's members; a store that cannot be reached is the named cause and exit 3. fields
NameTypeDescription
count required integer
ok required any
profiles required array of ProfileListItem
profiles[].description required string
profiles[].id required string

The Cloud profile id, Blowhorn's identity for the profile.

profiles[].name required string

The profile's name, what `--profile` takes.

profiles[].owner required string | null
profiles[].person required one of null, object
profiles[].userId required string | null

The member the profile represents; null for a brand.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ProfileListResponse

Variant fields
NameTypeDescription
count required integer
ok required any
profiles required array of ProfileListItem
profiles[].description required string
profiles[].id required string

The Cloud profile id, Blowhorn's identity for the profile.

profiles[].name required string

The profile's name, what `--profile` takes.

profiles[].owner required string | null
profiles[].person required one of null, object
profiles[].userId required string | null

The member the profile represents; null for a brand.

POST /cli/profile/set Store KEY=VALUE pairs on a profile: its platform credentials and its run settings.

Store KEY=VALUE pairs on a profile: its platform credentials and its run settings. A platform key (LI_*, X_*, RDDT_*, HN_*, SLACK_*, BLUESKY_*, GH_*, YT_*, IG_*, FB_*, TIKTOK_*, slack_workspaces and the <WORKSPACE>_SLACK_* session keys) is stored in that platform's credential in the store, where runs read it. A run setting (Risk Profile, Risk Profile By Platform, Daily Caps, Quiet Hours, Quiet Hours Time Zone) is stored in the profile's settings in your Blowhorn account, and a value a run could not read is refused. A blank value clears a key. Any other key is refused, and every refusal comes before anything is sent. Nothing is written to a file. The output names the keys that changed and never echoes a value. Replacing a credential an API check judges (GH_TOKEN, the Bluesky handle or app password, a Slack token or captured session) sets that platform back to not checked in profile status until the next check, because each such check records which credential it judged. A browser platform's check is about its saved session and stays.

A profile is created with profile create, and who it represents changes with profile update.

Request body required

The flags and arguments of `blowhorn profile set`, one property per parameter.

Request body of Store KEY=VALUE pairs on a profile: its platform credentials and its run settings. fields
NameTypeDescription
assignments array of string

KEY=VALUE pairs to write (slack_workspaces takes a comma-delimited list)

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

name required string

Profile name

pace string

Accepted by 'blowhorn profile set' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn profile'. One of: bluesky, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn profile set' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints which keys `profile set` stored (names only, never values): platform credential keys in the store, and run settings in the profile's `metadata`. With `--dry-run` it prints only the plan.

ProfileSetDryRun

Variant fields
NameTypeDescription
dry_run required any
keys required array of string
name required string
ok required any

ProfileSetWritten

Variant fields
NameTypeDescription
changed_keys required array of string

The keys whose stored value changed.

name required string
ok required any

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ProfileSetResponse

POST /cli/profile/status Report what each profile in your organization is set up for: the platforms it holds

Report what each profile in your organization is set up for: the platforms it holds an account for (credential key names only -- values are never printed), the browser session directory and its last-modified time, whether another blowhorn process holds its lock, the last recorded session check, and whether the profile is excluded by default. The profiles are your organization's list in your Blowhorn account, read once; a store that cannot be reached is the named cause and exit 3. No browser and no platform network by default: accounts are read from the store's credentials, once for every profile, with the person each profile represents. A credential read the store fails leaves every account unknown and is named on a Credentials: line, not fatal, and a check made then skips every platform and keeps its last results. With --json each platform also carries account_status: the status of the profile's account in your Blowhorn account (connected, disconnected, ...), read once for every profile, or null when none is mapped.

--check-sessions runs a read-only probe per configured platform (LinkedIn, X and Reddit open the saved session in a background window and look for the signed-in page; GitHub, Bluesky and Slack call their APIs) and records the result in the profile's session_status.json under data/cache/profiles/, and records a LinkedIn, X or Reddit sign-in it finds (or one that ended) on the profile's account in your Blowhorn account. It never logs in, never types a credential, and reports 'busy' instead of waiting when a session is in use. --check-platform runs that same probe for one platform only and leaves every other platform's recorded result alone; --check-platform github checks the profile's GitHub token in the store against GitHub (the login it signs in as, its scopes and what they lack) with no browser at all. --roster additionally reads the profiles roster from the store to show which platforms each profile has a handle for; a store that cannot be reached is the named cause and exit 3, and a roster that reads empty or cannot be read is reported on a Roster: line, not fatal. --chrome additionally reads this machine's Chrome profile mappings from the store, once, and Chrome's own profile list, and adds a Chrome column: the mapped Chrome profile directory and whether the name and account recorded when it was mapped still match (drift, missing, unknown). A mapping is identity, never permission. A Chrome profile list that cannot be read is reported on a Chrome: line, not fatal.

Request body required

The flags and arguments of `blowhorn profile status`, one property per parameter.

Request body of Report what each profile in your organization is set up for: the platforms it holds fields
NameTypeDescription
check_platform string

Run the --check-sessions probe for this one platform only (implies --check-sessions) and leave the other platforms' recorded results as they are. One of: linkedin, x, reddit, hn, slack, bluesky, github; github, bluesky and slack are API calls and open no browser. Unlike --platform it is never read from BLOWHORN_PLATFORM or defaults.platform, so a default cannot narrow a check nobody asked to narrow

check_sessions boolean

Probe each configured platform's saved session read-only (in a background browser window unless --headed) and record the result in the profile's session_status.json under data/cache/profiles/; never logs in, and reports 'busy' instead of waiting for a session another blowhorn process is using

Default: false

chrome boolean

Read this machine's Chrome profile mappings from the store and Chrome's profile list, and add a Chrome column: the mapped directory and whether its recorded name and account still match. Opens no browser; a store that cannot be reached is the named cause and exit 3

Default: false

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Pace of the random delays between 'blowhorn profile status' browser steps: a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn profile'. One of: bluesky, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

roster boolean

Also read the profiles roster from the store and report which platforms each profile has a handle for. Needs the store: a store that cannot be reached is the named cause and exit 3; a roster that reads empty or cannot be read is reported on a Roster: line, not fatal

Default: false

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the status board: for each profile its identity, roster membership, per-platform credential, browser-session, last-check and Cloud account facts. A check records each LinkedIn, X and Reddit verdict on the profile's Cloud account (connected, or disconnected when the sign-in ended). With `--check-sessions` or `--check-platform` it probes first.

Response 200 of Report what each profile in your organization is set up for: the platforms it holds fields
NameTypeDescription
account_errors required array of string

One sentence per checked sign-in the store refused to record; empty without a check.

accounts required AvailabilityStatus

Whether each profile's accounts in your Blowhorn account were read; false with no error for a store that keeps no accounts.

accounts.available required boolean
accounts.error required string | null
checked_platforms required array of string

The platforms this run probed; empty for a read without a check.

checked_sessions required boolean
chrome required object
chrome.available required boolean
chrome.error required string | null
chrome.host required string | null
count required integer
credentials required AvailabilityStatus

Whether a source a command reads (the store's credentials, the roster) could be read, and the one sentence why not.

credentials.available required boolean
credentials.error required string | null
excluded_by_default required array of string
generated_at required string
ok required any
people required AvailabilityStatus

Whether the people the profiles represent were read, one read for every profile.

people.available required boolean
people.error required string | null
profiles required array of ProfileStatusEntry
profiles[].chrome required one of ProfileChromeEntry, null

The Chrome mapping, only with `--chrome`; null for an unmapped profile or when nothing was read.

profiles[].display_name required string

The full name of the member the profile represents; empty for a brand, or when the people could not be read.

profiles[].email required string

The email of the member the profile represents; empty for a brand, or when the people could not be read.

profiles[].excluded_by_default required boolean

True when the default exclude list leaves the profile out of `--profile all`.

profiles[].in_roster required boolean | null

Null when the roster could not be read.

profiles[].last_check_at required string | null
profiles[].last_check_error required string | null
profiles[].name required string
profiles[].platforms required object

Platform name (bluesky, github, linkedin, reddit, slack, x) to its status.

profiles[].roster_full_name required string
roster required AvailabilityStatus

Whether a source a command reads (the store's credentials, the roster) could be read, and the one sentence why not.

roster.available required boolean
roster.error required string | null

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ProfileStatusResponse

Variant fields
NameTypeDescription
account_errors required array of string

One sentence per checked sign-in the store refused to record; empty without a check.

accounts required AvailabilityStatus

Whether each profile's accounts in your Blowhorn account were read; false with no error for a store that keeps no accounts.

accounts.available required boolean
accounts.error required string | null
checked_platforms required array of string

The platforms this run probed; empty for a read without a check.

checked_sessions required boolean
chrome required object
chrome.available required boolean
chrome.error required string | null
chrome.host required string | null
count required integer
credentials required AvailabilityStatus

Whether a source a command reads (the store's credentials, the roster) could be read, and the one sentence why not.

credentials.available required boolean
credentials.error required string | null
excluded_by_default required array of string
generated_at required string
ok required any
people required AvailabilityStatus

Whether the people the profiles represent were read, one read for every profile.

people.available required boolean
people.error required string | null
profiles required array of ProfileStatusEntry
profiles[].chrome required one of ProfileChromeEntry, null

The Chrome mapping, only with `--chrome`; null for an unmapped profile or when nothing was read.

profiles[].display_name required string

The full name of the member the profile represents; empty for a brand, or when the people could not be read.

profiles[].email required string

The email of the member the profile represents; empty for a brand, or when the people could not be read.

profiles[].excluded_by_default required boolean

True when the default exclude list leaves the profile out of `--profile all`.

profiles[].in_roster required boolean | null

Null when the roster could not be read.

profiles[].last_check_at required string | null
profiles[].last_check_error required string | null
profiles[].name required string
profiles[].platforms required object

Platform name (bluesky, github, linkedin, reddit, slack, x) to its status.

profiles[].roster_full_name required string
roster required AvailabilityStatus

Whether a source a command reads (the store's credentials, the roster) could be read, and the one sentence why not.

roster.available required boolean
roster.error required string | null
POST /cli/profile/switch Set a profile as the default profile in global config. The profile must be in your organization's list, read from the store; a store that cannot be reached is the named cause and exit 3.

Set a profile as the default profile in global config. The profile must be in your organization's list, read from the store; a store that cannot be reached is the named cause and exit 3.

Request body required

The flags and arguments of `blowhorn profile switch`, one property per parameter.

Request body of Set a profile as the default profile in global config. The profile must be in your organization's list, read from the store; a store that cannot be reached is the named cause and exit 3. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

name required string

Profile name to set as default

pace string

Accepted by 'blowhorn profile switch' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn profile'. One of: bluesky, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn profile switch' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

The command prints human-readable text; it has no --json contract.

Human-readable stdout from a command that has no --json flag. Its wording is not a contract; parse nothing out of it.

Schema: CliTextOutput

POST /cli/profile/update Replace the fields a person sets on one profile.

Replace the fields a person sets on one profile. The profile is named by its name or its id, and the fields come from the JSON document on stdin (--payload-stdin): name (required), description and userId. The document is the whole of them: a description left out is cleared, and a userId left out makes the profile a brand that represents nobody. The profile's run settings (Risk Profile, caps, quiet hours) are read just before the write and sent back as they are, and its owner is unchanged. The same refusals as `profile create` apply, a rename onto another profile's name included; --dry-run sends nothing.

Request body required

The JSON document `profile create --payload-stdin` and `profile update --payload-stdin` read: the v1beta3 `ProfilePayload` fields a person sets. Blowhorn adds `organizationId`; `owner` and `metadata` are never taken from this document. Any other key is refused. On an update the document is the whole of these fields: a `description` left out is cleared and a `userId` left out makes the profile a brand.

Request body of Replace the fields a person sets on one profile. fields
NameTypeDescription
description string

What the profile is for. Blank is the same as left out.

name required string

The profile's name, handle-shaped: lowercase letters, digits, `-` or `_`, starting with a letter or digit, at most 64 characters, never `all`, and not a name another visible profile uses. Trimmed and lowercased before it is checked.

userId string (uuid) | null

The member of the organization the profile represents, as `org members` lists them. Null or left out for a brand.

Responses

200

What the command prints to stdout with --json when it exits 0.

What `profile update --payload-stdin` did: the person-set fields it sent (or, with --dry-run, would send) to `updateProfile`, and the profile Blowhorn answered with. `profile` is null on a dry run, which sends nothing.

Response 200 of Replace the fields a person sets on one profile. fields
NameTypeDescription
dry_run required boolean
ok required any
payload required object

The document after it was trimmed and normalized; a blank description or userId is left out.

payload.description string
payload.name required string
payload.userId string
profile required one of null, ProfileListItem

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ProfileUpdateResponse

Variant fields
NameTypeDescription
dry_run required boolean
ok required any
payload required object

The document after it was trimmed and normalized; a blank description or userId is left out.

payload.description string
payload.name required string
payload.userId string
profile required one of null, ProfileListItem

Org

POST /cli/org/members List the members of your organization on Blowhorn.

List the members of your organization on Blowhorn. Each one's user id, name and email, read once from the organization's member list. These are the people a profile can represent: the id is the userId `profile create` and `profile update` take.

Request body required

The flags and arguments of `blowhorn org members`, one property per parameter.

Request body of List the members of your organization on Blowhorn. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn org members' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn org': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn org members' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The members of the selected organization in your Blowhorn account (`getUsersForOrg`), the people a profile can represent, sorted by name and then email. `name` is the member's first and last name, empty when their account has neither.

Response 200 of List the members of your organization on Blowhorn. fields
NameTypeDescription
count required integer
members required array of object
members[].email required string
members[].id required string

The member's user id, the `userId` a profile document takes.

members[].name required string
ok required any

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

OrgMembersResponse

Variant fields
NameTypeDescription
count required integer
members required array of object
members[].email required string
members[].id required string

The member's user id, the `userId` a profile document takes.

members[].name required string
ok required any

Content

POST /cli/content/approve Approve one activity by number, so a post run may publish it, and record who approved it and when in the activity's 'Approved by' column. This is the person's door under approval mode (defaults.approval.mode): activities an agent queued wait here. Approving is a person's act, so a caller marked as an agent (BLOWHORN_ACTOR=agent) or without a terminal is refused in every mode. An activity that is already approved is refused. --dry-run reports the activity and approves nothing.

Approve one activity by number, so a post run may publish it, and record who approved it and when in the activity's 'Approved by' column. This is the person's door under approval mode (defaults.approval.mode): activities an agent queued wait here. Approving is a person's act, so a caller marked as an agent (BLOWHORN_ACTOR=agent) or without a terminal is refused in every mode. An activity that is already approved is refused. --dry-run reports the activity and approves nothing.

Request body required

The flags and arguments of `blowhorn content approve`, one property per parameter.

Request body of Approve one activity by number, so a post run may publish it, and record who approved it and when in the activity's 'Approved by' column. This is the person's door under approval mode (defaults.approval.mode): activities an agent queued wait here. Approving is a person's act, so a caller marked as an agent (BLOWHORN_ACTOR=agent) or without a terminal is refused in every mode. An activity that is already approved is refused. --dry-run reports the activity and approves nothing. fields
NameTypeDescription
activity required integer

Activity number, as 'content list' prints it (stable for the life of the activity)

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output (the shape the desktop app's Content screen reads)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn content approve' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn content': 'content list' keeps the activities whose Platform column names this platform, and no other content subcommand reads it. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every activity is listed)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn content approve' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the content row that was approved, with its resulting state, fingerprint and the 'Approved by' record the approval wrote. `--dry-run` (the demo runs every command dry) prints the plan and approves nothing.

ContentApproveDryRun

Variant fields
NameTypeDescription
activity required integer
dry_run required any
ok required any

ContentApproveDone

Variant fields
NameTypeDescription
activity required integer
fingerprint required string
ok required any
platform required string
state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

values.Amplify required string
values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

values.Approved? required string
values.Comment on required string
values.Date Promoted required string
values.Destination required string
values.Encoded Post required string
values.GDrive Link required string
values.Image URL required string
values.Message Text required string
values.Platform required string
values.Profile required string
values.Promote On required string
values.Promotion Link required string
values.Title required string
values.Video URL required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ContentApproveResponse

POST /cli/content/delete Delete one activity by number. Requires --yes; --dry-run reports the activity and touches nothing; --expected-fingerprint refuses the delete if the activity changed since it was read. The activity is removed from the store and every other activity keeps its number.

Delete one activity by number. Requires --yes; --dry-run reports the activity and touches nothing; --expected-fingerprint refuses the delete if the activity changed since it was read. The activity is removed from the store and every other activity keeps its number.

Request body required

The flags and arguments of `blowhorn content delete`, one property per parameter.

Request body of Delete one activity by number. Requires --yes; --dry-run reports the activity and touches nothing; --expected-fingerprint refuses the delete if the activity changed since it was read. The activity is removed from the store and every other activity keeps its number. fields
NameTypeDescription
activity required integer

Activity number, as 'content list' prints it (stable for the life of the activity)

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

expected_fingerprint string

Refuse the write if the post's fingerprint (from 'content get' or 'content list') no longer matches

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output (the shape the desktop app's Content screen reads)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn content delete' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn content': 'content list' keeps the activities whose Platform column names this platform, and no other content subcommand reads it. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every activity is listed)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn content delete' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

yes boolean

Confirm the delete; without it the command refuses and exits 1 (use --dry-run to preview)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the content activity that was deleted, with the values it held.

Response 200 of Delete one activity by number. Requires --yes; --dry-run reports the activity and touches nothing; --expected-fingerprint refuses the delete if the activity changed since it was read. The activity is removed from the store and every other activity keeps its number. fields
NameTypeDescription
activity required integer
deleted required any
ok required any
values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

values.Amplify required string
values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

values.Approved? required string
values.Comment on required string
values.Date Promoted required string
values.Destination required string
values.Encoded Post required string
values.GDrive Link required string
values.Image URL required string
values.Message Text required string
values.Platform required string
values.Profile required string
values.Promote On required string
values.Promotion Link required string
values.Title required string
values.Video URL required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ContentDeleteResponse

Variant fields
NameTypeDescription
activity required integer
deleted required any
ok required any
values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

values.Amplify required string
values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

values.Approved? required string
values.Comment on required string
values.Date Promoted required string
values.Destination required string
values.Encoded Post required string
values.GDrive Link required string
values.Image URL required string
values.Message Text required string
values.Platform required string
values.Profile required string
values.Promote On required string
values.Promotion Link required string
values.Title required string
values.Video URL required string
POST /cli/content/get Print one post by number: every column, its state, its validation problems, and the fingerprint that 'content upsert' and 'content delete' take as --expected-fingerprint so a later write can refuse an activity edited in the store meanwhile.

Print one post by number: every column, its state, its validation problems, and the fingerprint that 'content upsert' and 'content delete' take as --expected-fingerprint so a later write can refuse an activity edited in the store meanwhile.

Request body required

The flags and arguments of `blowhorn content get`, one property per parameter.

Request body of Print one post by number: every column, its state, its validation problems, and the fingerprint that 'content upsert' and 'content delete' take as --expected-fingerprint so a later write can refuse an activity edited in the store meanwhile. fields
NameTypeDescription
activity required integer

Activity number, as 'content list' prints it (stable for the life of the activity)

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output (the shape the desktop app's Content screen reads)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn content get' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn content': 'content list' keeps the activities whose Platform column names this platform, and no other content subcommand reads it. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every activity is listed)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn content get' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints one content row with its canonical column values, state, fingerprint and any validation errors. It carries no `ok` key.

Response 200 of Print one post by number: every column, its state, its validation problems, and the fingerprint that 'content upsert' and 'content delete' take as --expected-fingerprint so a later write can refuse an activity edited in the store meanwhile. fields
NameTypeDescription
activity required integer

The stable row number, never a position.

fingerprint required string

A hash of the values, to pass back as the expected fingerprint of an update.

platform required string

The normalized platform name; empty when the row names none.

preview required string
state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

validation_errors required array of string
values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

values.Amplify required string
values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

values.Approved? required string
values.Comment on required string
values.Date Promoted required string
values.Destination required string
values.Encoded Post required string
values.GDrive Link required string
values.Image URL required string
values.Message Text required string
values.Platform required string
values.Profile required string
values.Promote On required string
values.Promotion Link required string
values.Title required string
values.Video URL required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ContentGetResponse

Variant fields
NameTypeDescription
activity required integer

The stable row number, never a position.

fingerprint required string

A hash of the values, to pass back as the expected fingerprint of an update.

platform required string

The normalized platform name; empty when the row names none.

preview required string
state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

validation_errors required array of string
values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

values.Amplify required string
values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

values.Approved? required string
values.Comment on required string
values.Date Promoted required string
values.Destination required string
values.Encoded Post required string
values.GDrive Link required string
values.Image URL required string
values.Message Text required string
values.Platform required string
values.Profile required string
values.Promote On required string
values.Promotion Link required string
values.Title required string
values.Video URL required string
POST /cli/content/list List the content queue's activities with their state and, for each, the profiles a post run would select it for right now ('Pending for' -- the same selection 'blowhorn post --check-queue' prints, read from the profiles roster). One read of the store; nothing is written and no browser opens.

List the content queue's activities with their state and, for each, the profiles a post run would select it for right now ('Pending for' -- the same selection 'blowhorn post --check-queue' prints, read from the profiles roster). One read of the store; nothing is written and no browser opens.

--profile keeps activities addressed to those profiles or to 'all'; --platform keeps one platform; --pending keeps only activities some profile would pick up now. --json is the shape the desktop app's Content screen reads, fingerprints included.

Request body required

The flags and arguments of `blowhorn content list`, one property per parameter.

Request body of List the content queue's activities with their state and, for each, the profiles a post run would select it for right now ('Pending for' -- the same selection 'blowhorn post --check-queue' prints, read from the profiles roster). One read of the store; nothing is written and no browser opens. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output (the shape the desktop app's Content screen reads)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn content list' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

pending boolean

Only posts some profile would pick up right now: the posts 'blowhorn post --check-queue' lists

Default: false

platform string

Platform filter for 'blowhorn content': 'content list' keeps the activities whose Platform column names this platform, and no other content subcommand reads it. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every activity is listed)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn content list' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the content queue, optionally filtered, with each activity's state and the profiles it is still waiting on.

Response 200 of List the content queue's activities with their state and, for each, the profiles a post run would select it for right now ('Pending for' -- the same selection 'blowhorn post --check-queue' prints, read from the profiles roster). One read of the store; nothing is written and no browser opens. fields
NameTypeDescription
activities required array of object
activities[].activity required integer
activities[].fingerprint required string
activities[].pending_for required array of string

Profiles that have not posted this activity yet.

activities[].platform required string
activities[].preview required string
activities[].state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

activities[].values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

activities[].values.Amplify required string
activities[].values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

activities[].values.Approved? required string
activities[].values.Comment on required string
activities[].values.Date Promoted required string
activities[].values.Destination required string
activities[].values.Encoded Post required string
activities[].values.GDrive Link required string
activities[].values.Image URL required string
activities[].values.Message Text required string
activities[].values.Platform required string
activities[].values.Profile required string
activities[].values.Promote On required string
activities[].values.Promotion Link required string
activities[].values.Title required string
activities[].values.Video URL required string
count required integer
extra_columns required array of string
generated_at required string

Local time, formatted as YYYY-MM-DD HH:MM:SS.

missing_columns required array of string
missing_required_columns required array of string
ok required any
roster required AvailabilityStatus

Whether a source a command reads (the store's credentials, the roster) could be read, and the one sentence why not.

roster.available required boolean
roster.error required string | null

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ContentListResponse

Variant fields
NameTypeDescription
activities required array of object
activities[].activity required integer
activities[].fingerprint required string
activities[].pending_for required array of string

Profiles that have not posted this activity yet.

activities[].platform required string
activities[].preview required string
activities[].state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

activities[].values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

activities[].values.Amplify required string
activities[].values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

activities[].values.Approved? required string
activities[].values.Comment on required string
activities[].values.Date Promoted required string
activities[].values.Destination required string
activities[].values.Encoded Post required string
activities[].values.GDrive Link required string
activities[].values.Image URL required string
activities[].values.Message Text required string
activities[].values.Platform required string
activities[].values.Profile required string
activities[].values.Promote On required string
activities[].values.Promotion Link required string
activities[].values.Title required string
activities[].values.Video URL required string
count required integer
extra_columns required array of string
generated_at required string

Local time, formatted as YYYY-MM-DD HH:MM:SS.

missing_columns required array of string
missing_required_columns required array of string
ok required any
roster required AvailabilityStatus

Whether a source a command reads (the store's credentials, the roster) could be read, and the one sentence why not.

roster.available required boolean
roster.error required string | null
POST /cli/content/posts List the posts as one list: every post in the content queue with its own schedule inline (its 'Promote On', or not scheduled), then the schedule's jobs as rows of their own, labelled by their command. A job does not point at a post: a 'post' job drains the queue at run time, selecting by profile, platform, approval and 'Promote On', so the jobs are shown unlinked rather than joined to posts they do not name.

List the posts as one list: every post in the content queue with its own schedule inline (its 'Promote On', or not scheduled), then the schedule's jobs as rows of their own, labelled by their command. A job does not point at a post: a 'post' job drains the queue at run time, selecting by profile, platform, approval and 'Promote On', so the jobs are shown unlinked rather than joined to posts they do not name.

--profile and --platform filter both halves; --pending keeps only the posts some profile would pick up now (the jobs are not filtered by it). --json is the shape the desktop app's posts list reads, each post carrying its schedule and the jobs unlinked. One read of each half of the store; nothing is written and no browser opens.

Request body required

The flags and arguments of `blowhorn content posts`, one property per parameter.

Request body of List the posts as one list: every post in the content queue with its own schedule inline (its 'Promote On', or not scheduled), then the schedule's jobs as rows of their own, labelled by their command. A job does not point at a post: a 'post' job drains the queue at run time, selecting by profile, platform, approval and 'Promote On', so the jobs are shown unlinked rather than joined to posts they do not name. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output (the shape the desktop app's Content screen reads)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn content posts' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

pending boolean

Only posts some profile would pick up right now: the posts 'blowhorn post --check-queue' lists

Default: false

platform string

Platform filter for 'blowhorn content': 'content list' keeps the activities whose Platform column names this platform, and no other content subcommand reads it. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every activity is listed)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn content posts' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the one posts list: every post with its schedule inline (its own `Promote On`), then the schedule's jobs as unlinked rows in their `schedule list` shape. `ok` is false when either read reports missing columns.

Response 200 of List the posts as one list: every post in the content queue with its own schedule inline (its 'Promote On', or not scheduled), then the schedule's jobs as rows of their own, labelled by their command. A job does not point at a post: a 'post' job drains the queue at run time, selecting by profile, platform, approval and 'Promote On', so the jobs are shown unlinked rather than joined to posts they do not name. fields
NameTypeDescription
count required integer
extra_columns required array of string
generated_at required string

Local time, formatted as YYYY-MM-DD HH:MM:SS.

job_count required integer
jobs required array of ScheduleActivity

The schedule's jobs, unlinked: no job names a post, because a `post` job selects its content at run time; each job is labelled by its own command.

jobs[].activity required integer
jobs[].command required string
jobs[].connection object | null

The activity's Google Chrome connection, from the tick's gate (#1600): null when a run may start. `held` is a run that waits, not one that fails, with the plain reason and the one in-app action that clears it; `heal` is a connection Blowhorn reconnects on its own before the run; `open` is a mapped Google Chrome profile that is not open, which Blowhorn opens in the background before the run. Absent from older engines.

jobs[].connection.action required string | null

The one thing the person can do: `turn_on_extension` (turn the Blowhorn extension on in that Google Chrome profile), `reconnect` (reconnect it now), `choose_chrome_profile` (choose or confirm the Google Chrome profile) or `restart_chrome` (restart Google Chrome, the one action left when a native host process from before an upgrade is still answering and ending it did not clear the hold); null when Blowhorn heals it alone.

jobs[].connection.chrome_directory required string | null

The Google Chrome profile directory the activity's profile is mapped to; null when none is.

jobs[].connection.profile required string
jobs[].connection.reason required string | null

Why it waits or reconnects, in plain words that name no command.

jobs[].connection.state required string

Values: held, heal, open

jobs[].driver_mode required string
jobs[].due required string

`yes` when the activity's time has come: it is enabled and valid, no machine holds a live claim on it, and its `next_run` has passed. Always `no` when `next_run` is empty. A paused activity still reads `yes`, and `paused` says the tick is holding it.

Values: yes, no

jobs[].enabled required string

Values: yes, no

jobs[].errors required string

Validation errors joined by a semicolon; empty when the activity is valid.

jobs[].held_here required boolean

The claim is this install's own: this macOS user on this computer. Never decided by the hostname alone, which every macOS user on a Mac shares.

jobs[].last_error required string
jobs[].last_log_path required string
jobs[].last_run_at required string
jobs[].lock_stale required boolean
jobs[].locked required boolean
jobs[].locked_at required string
jobs[].locked_by required string
jobs[].max_retries required integer

The retry budget for each arming; see `Max Retries` in the activity's values.

jobs[].next_run required string

When the tick next takes the activity, in local time. Empty when it never will: a completed once-only activity, or a completed or failed activity with no Next Run At. Never the activity's old Run At.

jobs[].params required ScheduleParams

The parsed Params of a schedule activity, keyed by the command's own parameter names. A value is a string, a number or a boolean as the activity's text parses.

jobs[].pause_reason required string
jobs[].paused required string

Values: yes, no

jobs[].paused_at required string
jobs[].paused_by required string
jobs[].platform required string

The platform, or `all`.

jobs[].profile required string

The profile, or a hyphen when the activity names none.

jobs[].quiet_hours required string

`yes` when the activity is inside its quiet hours now (see `quiet_hours_window`): the tick holds it, unclaimed, and runs it when the window ends. Computed, never stored.

Values: yes, no

jobs[].quiet_hours_tz required string

The zone `quiet_hours_window` is read in: an IANA name, or this computer's zone; empty when no window is set.

jobs[].quiet_hours_until required string

When the current window ends, in local time; empty when the activity is not in quiet hours now.

jobs[].quiet_hours_window required string

The quiet-hours window in force for the activity's profile, `HH:MM-HH:MM` like `22:00-07:00`; empty when no window is set anywhere for it.

jobs[].recurrence required string
jobs[].retry_count required integer

Failed attempts since the activity was last armed; see `Retry Count` in the activity's values.

jobs[].retry_delay_minutes required integer
jobs[].risk_hold required string

Why a Run now would be held, including while the profile is warming up. Empty when the activity may run. A confirmation passes this hold except when `risk_hold_source` is `published`.

jobs[].risk_hold_source required string

Where the hold comes from. `published` is a limit the platform states, which a confirmation does not pass. Empty when `risk_hold` is empty.

jobs[].risk_profile required string

The preset the activity resolves to, from the tick's gate; empty for an activity without one profile and one platform, which names no single preset.

jobs[].risk_profile_effective required string

What the tick enforces: cautious while warming, whatever was chosen; empty with `risk_profile`.

jobs[].risk_profile_source required string

The layer that chose the preset (`schedule` when the activity's own value did, `builtin` when nothing chose one); empty with `risk_profile`.

jobs[].risk_profile_warming required string

`yes` while the profile or this computer is warming up; empty with `risk_profile` (an activity on every platform names no single preset).

Values: yes, no,

jobs[].run_at required string
jobs[].status required string

The activity's stored status, such as pending, scheduled, running, retrying or failed. Never a lock.

missing_columns required array of string

Schedule columns the store is missing, as `schedule list` reports them.

missing_required_columns required array of string
ok required boolean
pause required one of PauseState, null

The pause every machine honours, or null.

posts required array of PostWithSchedule
posts[].activity required integer
posts[].fingerprint required string
posts[].pending_for required array of string

Profiles that have not posted this activity yet.

posts[].platform required string
posts[].preview required string
posts[].schedule required PostSchedule

The schedule a post carries on itself: when it is set to go out, from its own `Promote On`, and whether that time is still ahead. A job never supplies it - no job points at a post - so an empty `promote_on` is the "not scheduled" case.

posts[].schedule.promote_on required string

The post's `Promote On`, formatted as YYYY-MM-DD HH:MM:SS; empty when the post carries none.

posts[].schedule.scheduled required boolean

`true` when the post is in the `scheduled` state: approved, not yet promoted, and its `Promote On` is still ahead.

posts[].state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

posts[].values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

posts[].values.Amplify required string
posts[].values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

posts[].values.Approved? required string
posts[].values.Comment on required string
posts[].values.Date Promoted required string
posts[].values.Destination required string
posts[].values.Encoded Post required string
posts[].values.GDrive Link required string
posts[].values.Image URL required string
posts[].values.Message Text required string
posts[].values.Platform required string
posts[].values.Profile required string
posts[].values.Promote On required string
posts[].values.Promotion Link required string
posts[].values.Title required string
posts[].values.Video URL required string
roster required AvailabilityStatus

Whether a source a command reads (the store's credentials, the roster) could be read, and the one sentence why not.

roster.available required boolean
roster.error required string | null

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ContentPostsResponse

Variant fields
NameTypeDescription
count required integer
extra_columns required array of string
generated_at required string

Local time, formatted as YYYY-MM-DD HH:MM:SS.

job_count required integer
jobs required array of ScheduleActivity

The schedule's jobs, unlinked: no job names a post, because a `post` job selects its content at run time; each job is labelled by its own command.

jobs[].activity required integer
jobs[].command required string
jobs[].connection object | null

The activity's Google Chrome connection, from the tick's gate (#1600): null when a run may start. `held` is a run that waits, not one that fails, with the plain reason and the one in-app action that clears it; `heal` is a connection Blowhorn reconnects on its own before the run; `open` is a mapped Google Chrome profile that is not open, which Blowhorn opens in the background before the run. Absent from older engines.

jobs[].connection.action required string | null

The one thing the person can do: `turn_on_extension` (turn the Blowhorn extension on in that Google Chrome profile), `reconnect` (reconnect it now), `choose_chrome_profile` (choose or confirm the Google Chrome profile) or `restart_chrome` (restart Google Chrome, the one action left when a native host process from before an upgrade is still answering and ending it did not clear the hold); null when Blowhorn heals it alone.

jobs[].connection.chrome_directory required string | null

The Google Chrome profile directory the activity's profile is mapped to; null when none is.

jobs[].connection.profile required string
jobs[].connection.reason required string | null

Why it waits or reconnects, in plain words that name no command.

jobs[].connection.state required string

Values: held, heal, open

jobs[].driver_mode required string
jobs[].due required string

`yes` when the activity's time has come: it is enabled and valid, no machine holds a live claim on it, and its `next_run` has passed. Always `no` when `next_run` is empty. A paused activity still reads `yes`, and `paused` says the tick is holding it.

Values: yes, no

jobs[].enabled required string

Values: yes, no

jobs[].errors required string

Validation errors joined by a semicolon; empty when the activity is valid.

jobs[].held_here required boolean

The claim is this install's own: this macOS user on this computer. Never decided by the hostname alone, which every macOS user on a Mac shares.

jobs[].last_error required string
jobs[].last_log_path required string
jobs[].last_run_at required string
jobs[].lock_stale required boolean
jobs[].locked required boolean
jobs[].locked_at required string
jobs[].locked_by required string
jobs[].max_retries required integer

The retry budget for each arming; see `Max Retries` in the activity's values.

jobs[].next_run required string

When the tick next takes the activity, in local time. Empty when it never will: a completed once-only activity, or a completed or failed activity with no Next Run At. Never the activity's old Run At.

jobs[].params required ScheduleParams

The parsed Params of a schedule activity, keyed by the command's own parameter names. A value is a string, a number or a boolean as the activity's text parses.

jobs[].pause_reason required string
jobs[].paused required string

Values: yes, no

jobs[].paused_at required string
jobs[].paused_by required string
jobs[].platform required string

The platform, or `all`.

jobs[].profile required string

The profile, or a hyphen when the activity names none.

jobs[].quiet_hours required string

`yes` when the activity is inside its quiet hours now (see `quiet_hours_window`): the tick holds it, unclaimed, and runs it when the window ends. Computed, never stored.

Values: yes, no

jobs[].quiet_hours_tz required string

The zone `quiet_hours_window` is read in: an IANA name, or this computer's zone; empty when no window is set.

jobs[].quiet_hours_until required string

When the current window ends, in local time; empty when the activity is not in quiet hours now.

jobs[].quiet_hours_window required string

The quiet-hours window in force for the activity's profile, `HH:MM-HH:MM` like `22:00-07:00`; empty when no window is set anywhere for it.

jobs[].recurrence required string
jobs[].retry_count required integer

Failed attempts since the activity was last armed; see `Retry Count` in the activity's values.

jobs[].retry_delay_minutes required integer
jobs[].risk_hold required string

Why a Run now would be held, including while the profile is warming up. Empty when the activity may run. A confirmation passes this hold except when `risk_hold_source` is `published`.

jobs[].risk_hold_source required string

Where the hold comes from. `published` is a limit the platform states, which a confirmation does not pass. Empty when `risk_hold` is empty.

jobs[].risk_profile required string

The preset the activity resolves to, from the tick's gate; empty for an activity without one profile and one platform, which names no single preset.

jobs[].risk_profile_effective required string

What the tick enforces: cautious while warming, whatever was chosen; empty with `risk_profile`.

jobs[].risk_profile_source required string

The layer that chose the preset (`schedule` when the activity's own value did, `builtin` when nothing chose one); empty with `risk_profile`.

jobs[].risk_profile_warming required string

`yes` while the profile or this computer is warming up; empty with `risk_profile` (an activity on every platform names no single preset).

Values: yes, no,

jobs[].run_at required string
jobs[].status required string

The activity's stored status, such as pending, scheduled, running, retrying or failed. Never a lock.

missing_columns required array of string

Schedule columns the store is missing, as `schedule list` reports them.

missing_required_columns required array of string
ok required boolean
pause required one of PauseState, null

The pause every machine honours, or null.

posts required array of PostWithSchedule
posts[].activity required integer
posts[].fingerprint required string
posts[].pending_for required array of string

Profiles that have not posted this activity yet.

posts[].platform required string
posts[].preview required string
posts[].schedule required PostSchedule

The schedule a post carries on itself: when it is set to go out, from its own `Promote On`, and whether that time is still ahead. A job never supplies it - no job points at a post - so an empty `promote_on` is the "not scheduled" case.

posts[].schedule.promote_on required string

The post's `Promote On`, formatted as YYYY-MM-DD HH:MM:SS; empty when the post carries none.

posts[].schedule.scheduled required boolean

`true` when the post is in the `scheduled` state: approved, not yet promoted, and its `Promote On` is still ahead.

posts[].state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

posts[].values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

posts[].values.Amplify required string
posts[].values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

posts[].values.Approved? required string
posts[].values.Comment on required string
posts[].values.Date Promoted required string
posts[].values.Destination required string
posts[].values.Encoded Post required string
posts[].values.GDrive Link required string
posts[].values.Image URL required string
posts[].values.Message Text required string
posts[].values.Platform required string
posts[].values.Profile required string
posts[].values.Promote On required string
posts[].values.Promotion Link required string
posts[].values.Title required string
posts[].values.Video URL required string
roster required AvailabilityStatus

Whether a source a command reads (the store's credentials, the roster) could be read, and the one sentence why not.

roster.available required boolean
roster.error required string | null
POST /cli/content/upsert Create a post, or update one in place, from a JSON object of column: value pairs in the queue's own column names. Without --activity a new activity is created and given the next activity number; with --activity the payload is merged over that activity, and every column it does not name is left as it was.

Create a post, or update one in place, from a JSON object of column: value pairs in the queue's own column names. Without --activity a new activity is created and given the next activity number; with --activity the payload is merged over that activity, and every column it does not name is left as it was.

Platform must be one 'blowhorn post' publishes to (linkedin, x, reddit, slack, bluesky, github, layer5, meshery); Approved? takes yes or no (JSON true/false are written as yes/no); Promote On must parse (YYYY-MM-DD HH:MM:SS); Profile must name a profile the store knows, or 'all'. Unknown fields are refused rather than written. --expected-fingerprint refuses the write if the activity changed since 'content get' or 'content list' read it. --dry-run prints the payload and touches nothing.

Request body required

The flags and arguments of `blowhorn content upsert`, one property per parameter.

Request body of Create a post, or update one in place, from a JSON object of column: value pairs in the queue's own column names. Without --activity a new activity is created and given the next activity number; with --activity the payload is merged over that activity, and every column it does not name is left as it was. fields
NameTypeDescription
activity integer

Activity number to update in place; omit to create a new activity

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

expected_fingerprint string

Refuse the write if the post's fingerprint (from 'content get' or 'content list') no longer matches

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output (the shape the desktop app's Content screen reads)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn content upsert' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

payload required string

JSON object of Content column: value pairs in the queue's own column names, for example '{"Platform":"x","Profile":"company","Message Text":"hello","Approved?":"yes"}'

platform string

Platform filter for 'blowhorn content': 'content list' keeps the activities whose Platform column names this platform, and no other content subcommand reads it. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every activity is listed)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn content upsert' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the content row that was created or updated, with its resulting state and fingerprint.

Response 200 of Create a post, or update one in place, from a JSON object of column: value pairs in the queue's own column names. Without --activity a new activity is created and given the next activity number; with --activity the payload is merged over that activity, and every column it does not name is left as it was. fields
NameTypeDescription
activity required integer
created required boolean
fingerprint required string
ok required any
platform required string
state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

values.Amplify required string
values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

values.Approved? required string
values.Comment on required string
values.Date Promoted required string
values.Destination required string
values.Encoded Post required string
values.GDrive Link required string
values.Image URL required string
values.Message Text required string
values.Platform required string
values.Profile required string
values.Promote On required string
values.Promotion Link required string
values.Title required string
values.Video URL required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ContentUpsertResponse

Variant fields
NameTypeDescription
activity required integer
created required boolean
fingerprint required string
ok required any
platform required string
state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

values required ContentValues

A content activity's columns, each a string (empty when unset): the post's platform, profile, text and media, whether it is approved, when it is promoted and where it landed.

values.Amplify required string
values.Approved by required string

Who approved the activity and when ("<who> at <when>"), recorded by 'blowhorn content approve'; empty until then. A record, never set from a payload.

values.Approved? required string
values.Comment on required string
values.Date Promoted required string
values.Destination required string
values.Encoded Post required string
values.GDrive Link required string
values.Image URL required string
values.Message Text required string
values.Platform required string
values.Profile required string
values.Promote On required string
values.Promotion Link required string
values.Title required string
values.Video URL required string

Schedule

POST /cli/schedule/bootstrap Preview or write a validated schedule starter set.

Preview or write a validated schedule starter set.

Request body required

The flags and arguments of `blowhorn schedule bootstrap`, one property per parameter.

Request body of Preview or write a validated schedule starter set. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule bootstrap' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

replace boolean

Replace the current scheduled actions before writing sample ones

Default: false

risk_profile string

Accepted by 'blowhorn schedule bootstrap' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What schedule bootstrap prints with --json on success: the sample rows it loaded into the schedule, or with --dry-run the rows it would load. Without --json a real run prints nothing.

Response 200 of Preview or write a validated schedule starter set. fields
NameTypeDescription
activities required array of object

The sample schedule rows, each keyed by schedule column name (Enabled, Command, Profile, Platform, Params, Run At, Recurrence, Status, ...).

count required integer
dry_run boolean

Present and true only with --dry-run.

ok required boolean
replace required boolean

Whether existing rows were (or would be) replaced.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleBootstrapResponse

Variant fields
NameTypeDescription
activities required array of object

The sample schedule rows, each keyed by schedule column name (Enabled, Command, Profile, Platform, Params, Run At, Recurrence, Status, ...).

count required integer
dry_run boolean

Present and true only with --dry-run.

ok required boolean
replace required boolean

Whether existing rows were (or would be) replaced.

POST /cli/schedule/delete Delete a scheduled action by number.

Delete a scheduled action by number.

Request body required

The flags and arguments of `blowhorn schedule delete`, one property per parameter.

Request body of Delete a scheduled action by number. fields
NameTypeDescription
activity required integer

Activity number to delete

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

expected_fingerprint string

Reject the delete if the current activity fingerprint no longer matches

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule delete' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn schedule delete' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What schedule delete prints with --json on success: the deleted activity, or with --dry-run the activity and fingerprint it would delete.

Deleted

Variant fields
NameTypeDescription
activity required integer | null
deleted required boolean
ok required boolean
values required object

The deleted activity's values keyed by schedule column name.

DryRun

Variant fields
NameTypeDescription
activity required integer | null
dry_run required boolean
expected_fingerprint required string | null
ok required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleDeleteResponse

POST /cli/schedule/destinations List the post destinations that belong to an organization rather than to a person -- the organization blogs today -- and whether this organization has each one: a destination is set up when at least one of its profiles is opted in to it (a platform opt-in in the store, the same fact 'blowhorn post' reads to decide who may publish there). A destination no profile is opted in to can never be posted to, so the desktop app offers no new activity for it; activities already saved for it still show and run. Reads the store once; writes nothing.

List the post destinations that belong to an organization rather than to a person -- the organization blogs today -- and whether this organization has each one: a destination is set up when at least one of its profiles is opted in to it (a platform opt-in in the store, the same fact 'blowhorn post' reads to decide who may publish there). A destination no profile is opted in to can never be posted to, so the desktop app offers no new activity for it; activities already saved for it still show and run. Reads the store once; writes nothing.

Request body required

The flags and arguments of `blowhorn schedule destinations`, one property per parameter.

Request body of List the post destinations that belong to an organization rather than to a person -- the organization blogs today -- and whether this organization has each one: a destination is set up when at least one of its profiles is opted in to it (a platform opt-in in the store, the same fact 'blowhorn post' reads to decide who may publish there). A destination no profile is opted in to can never be posted to, so the desktop app offers no new activity for it; activities already saved for it still show and run. Reads the store once; writes nothing. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule destinations' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn schedule destinations' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What schedule destinations prints with --json: each post destination that belongs to an organization rather than to a person, and whether a profile of this organization is opted in to it. The desktop app offers a new activity for a destination only when it is set up.

Response 200 of List the post destinations that belong to an organization rather than to a person -- the organization blogs today -- and whether this organization has each one: a destination is set up when at least one of its profiles is opted in to it (a platform opt-in in the store, the same fact 'blowhorn post' reads to decide who may publish there). A destination no profile is opted in to can never be posted to, so the desktop app offers no new activity for it; activities already saved for it still show and run. Reads the store once; writes nothing. fields
NameTypeDescription
destinations required array of object
destinations[].configured required boolean

At least one profile is opted in to this destination.

destinations[].platform required string

The platform name a content item or a scheduled activity uses (layer5, meshery).

destinations[].profiles required array of string

The profiles opted in, lowercased and sorted.

destinations[].site required string

The site the destination publishes to.

ok required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleDestinationsResponse

Variant fields
NameTypeDescription
destinations required array of object
destinations[].configured required boolean

At least one profile is opted in to this destination.

destinations[].platform required string

The platform name a content item or a scheduled activity uses (layer5, meshery).

destinations[].profiles required array of string

The profiles opted in, lowercased and sorted.

destinations[].site required string

The site the destination publishes to.

ok required boolean
POST /cli/schedule/fleet List every computer sharing your schedule whose background service has reported lately, and whether each is on duty, read from the last scheduler pass it recorded in the store: a pass that ran says on duty, a paused one says off duty, and one that failed says last pass failed. A computer with no pass in the reporting window (about half an hour) is left out, because nothing recent says what it is doing. Reads only -- it claims nothing and changes nothing.

List every computer sharing your schedule whose background service has reported lately, and whether each is on duty, read from the last scheduler pass it recorded in the store: a pass that ran says on duty, a paused one says off duty, and one that failed says last pass failed. A computer with no pass in the reporting window (about half an hour) is left out, because nothing recent says what it is doing. Reads only -- it claims nothing and changes nothing.

Request body required

The flags and arguments of `blowhorn schedule fleet`, one property per parameter.

Request body of List every computer sharing your schedule whose background service has reported lately, and whether each is on duty, read from the last scheduler pass it recorded in the store: a pass that ran says on duty, a paused one says off duty, and one that failed says last pass failed. A computer with no pass in the reporting window (about half an hour) is left out, because nothing recent says what it is doing. Reads only -- it claims nothing and changes nothing. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule fleet' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn schedule fleet' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What schedule fleet prints with --json: each computer whose background service recorded a scheduler pass in the store within the reporting window, and the duty that pass shows. A computer with no pass in the window is not listed, so a reader that knows of it from elsewhere counts it as not reporting, never as on duty. Read-only.

Response 200 of List every computer sharing your schedule whose background service has reported lately, and whether each is on duty, read from the last scheduler pass it recorded in the store: a pass that ran says on duty, a paused one says off duty, and one that failed says last pass failed. A computer with no pass in the reporting window (about half an hour) is left out, because nothing recent says what it is doing. Reads only -- it claims nothing and changes nothing. fields
NameTypeDescription
computers required array of object

One entry per computer, by host name.

computers[].duty required string

`on_duty` when the pass ran (ok or cancelled at a safe point), `off_duty` when it was paused, `not_on_duty` when it failed or could not reach the store.

Values: on_duty, off_duty, not_on_duty

computers[].host required string

The computer's name, as its run records carry it.

computers[].last_pass_at required string

When its latest scheduler pass was recorded, local `YYYY-MM-DD HH:MM:SS`.

computers[].status required string

That pass's ledger status: `ok`, `paused`, `cancelled`, `error`, `store_unavailable` or `missing_columns`.

ok required boolean
reporting_window_s required integer

How recent a computer's last scheduler pass must be for it to be listed, in seconds.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleFleetResponse

Variant fields
NameTypeDescription
computers required array of object

One entry per computer, by host name.

computers[].duty required string

`on_duty` when the pass ran (ok or cancelled at a safe point), `off_duty` when it was paused, `not_on_duty` when it failed or could not reach the store.

Values: on_duty, off_duty, not_on_duty

computers[].host required string

The computer's name, as its run records carry it.

computers[].last_pass_at required string

When its latest scheduler pass was recorded, local `YYYY-MM-DD HH:MM:SS`.

computers[].status required string

That pass's ledger status: `ok`, `paused`, `cancelled`, `error`, `store_unavailable` or `missing_columns`.

ok required boolean
reporting_window_s required integer

How recent a computer's last scheduler pass must be for it to be listed, in seconds.

POST /cli/schedule/get Return a single scheduled action with all fields.

Return a single scheduled action with all fields.

Request body required

The flags and arguments of `blowhorn schedule get`, one property per parameter.

Request body of Return a single scheduled action with all fields. fields
NameTypeDescription
activity required integer

Activity number to inspect

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule get' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn schedule get' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints one schedule activity with its column values, fingerprint, parsed params, next scheduled time, lock state, validation errors, the Risk Profile it resolves to, and the limits the platform publishes on its action. It carries no `ok` key.

Response 200 of Return a single scheduled action with all fields. fields
NameTypeDescription
activity required integer
fingerprint required string

A hash of the values, to pass back as the expected fingerprint of an update.

lock_stale required boolean
locked required boolean
params required ScheduleParams

The parsed Params of a schedule activity, keyed by the command's own parameter names. A value is a string, a number or a boolean as the activity's text parses.

published_limits required array of PublishedLimit

The limits the platform publishes on this activity's action, each naming the presets that would go past it. Empty without one profile and one platform, or where the platform publishes none.

published_limits[].above required array of string

The presets whose cap for this activity's profile, once warm, would go past this limit and is cut to it. Empty when every preset stays within it.

published_limits[].actions required array of string

The gate actions the one count covers; several when the platform counts them together.

published_limits[].as_of required string

The date the page was read.

published_limits[].count required integer
published_limits[].label required string

The provenance label of the reading (`UNVERIFIED - docs`).

published_limits[].per required string

Values: hour, day

published_limits[].platform required string
published_limits[].platform_label required string

The platform's display name (`X`, `Bluesky`).

published_limits[].source required string

The platform's page the limit was read from.

published_limits[].what required string

The platform's own unit in plain words (`follows`, `original posts`).

risk_profile required string

The preset the activity resolves to, from the tick's gate; empty without one profile and one platform.

risk_profile_effective required string

What the tick enforces: cautious while warming, whatever was chosen.

risk_profile_source required string

The layer that chose the preset (`schedule` when the activity's own value did).

risk_profile_warming required string

Values: yes, no

scheduled_for required string

The same instant `schedule list` prints as `next_run`: when the tick next takes the activity, in local time. Empty when it never will: a completed once-only activity, or a completed or failed activity with no Next Run At. Never the activity's old Run At.

validation_errors required array of string
values required ScheduleValues

A schedule row's columns, each a string (empty when unset): the command and its params as `key=value;key=value`, when it runs and how it repeats, its retry budget, its status and the lock a running machine holds.

values.Command required string
values.Driver Mode required string
values.Enabled required string
values.Last Error required string
values.Last Log Path required string
values.Last Run At required string
values.Locked At required string
values.Locked By required string
values.Max Retries required string

The retry budget for each arming: how many times a failed run is retried before the row is failed. Raising it alone does not schedule a failed row again.

values.Next Run At required string

When the row is next due, in local time. Empty on a completed or failed row means never due again. An update that gives a completed or failed row a Next Run At, or changes its Run At, schedules it again in the same write: Status scheduled and Retry Count 0, with Last Run At, Last Error and Last Log Path kept, unless the update sets Status itself.

values.Params required string
values.Platform required string
values.Profile required string
values.Recurrence required string
values.Retry Count required string

Failed attempts since the row was last armed. A success sets it to 0, and so does an update that schedules a completed or failed row again.

values.Retry Delay Minutes required string
values.Risk Profile required string

The activity's own Risk Profile (#1009): `cautious`, `standard` or `brisk`, or empty to inherit the preset its profile and platform resolve to.

values.Run At required string
values.Status required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleGetResponse

Variant fields
NameTypeDescription
activity required integer
fingerprint required string

A hash of the values, to pass back as the expected fingerprint of an update.

lock_stale required boolean
locked required boolean
params required ScheduleParams

The parsed Params of a schedule activity, keyed by the command's own parameter names. A value is a string, a number or a boolean as the activity's text parses.

published_limits required array of PublishedLimit

The limits the platform publishes on this activity's action, each naming the presets that would go past it. Empty without one profile and one platform, or where the platform publishes none.

published_limits[].above required array of string

The presets whose cap for this activity's profile, once warm, would go past this limit and is cut to it. Empty when every preset stays within it.

published_limits[].actions required array of string

The gate actions the one count covers; several when the platform counts them together.

published_limits[].as_of required string

The date the page was read.

published_limits[].count required integer
published_limits[].label required string

The provenance label of the reading (`UNVERIFIED - docs`).

published_limits[].per required string

Values: hour, day

published_limits[].platform required string
published_limits[].platform_label required string

The platform's display name (`X`, `Bluesky`).

published_limits[].source required string

The platform's page the limit was read from.

published_limits[].what required string

The platform's own unit in plain words (`follows`, `original posts`).

risk_profile required string

The preset the activity resolves to, from the tick's gate; empty without one profile and one platform.

risk_profile_effective required string

What the tick enforces: cautious while warming, whatever was chosen.

risk_profile_source required string

The layer that chose the preset (`schedule` when the activity's own value did).

risk_profile_warming required string

Values: yes, no

scheduled_for required string

The same instant `schedule list` prints as `next_run`: when the tick next takes the activity, in local time. Empty when it never will: a completed once-only activity, or a completed or failed activity with no Next Run At. Never the activity's old Run At.

validation_errors required array of string
values required ScheduleValues

A schedule row's columns, each a string (empty when unset): the command and its params as `key=value;key=value`, when it runs and how it repeats, its retry budget, its status and the lock a running machine holds.

values.Command required string
values.Driver Mode required string
values.Enabled required string
values.Last Error required string
values.Last Log Path required string
values.Last Run At required string
values.Locked At required string
values.Locked By required string
values.Max Retries required string

The retry budget for each arming: how many times a failed run is retried before the row is failed. Raising it alone does not schedule a failed row again.

values.Next Run At required string

When the row is next due, in local time. Empty on a completed or failed row means never due again. An update that gives a completed or failed row a Next Run At, or changes its Run At, schedules it again in the same write: Status scheduled and Retry Count 0, with Last Run At, Last Error and Last Log Path kept, unless the update sets Status itself.

values.Params required string
values.Platform required string
values.Profile required string
values.Recurrence required string
values.Retry Count required string

Failed attempts since the row was last armed. A success sets it to 0, and so does an update that schedules a completed or failed row again.

values.Retry Delay Minutes required string
values.Risk Profile required string

The activity's own Risk Profile (#1009): `cautious`, `standard` or `brisk`, or empty to inherit the preset its profile and platform resolve to.

values.Run At required string
values.Status required string
POST /cli/schedule/list List scheduled actions and whether they are currently due. With several machines sharing the store the Locked By column names the holder and marks this machine's own claims; a paused action reads paused: yes beside its due state, because due keeps meaning its time has passed.

List scheduled actions and whether they are currently due. With several machines sharing the store the Locked By column names the holder and marks this machine's own claims; a paused action reads paused: yes beside its due state, because due keeps meaning its time has passed.

Request body required

The flags and arguments of `blowhorn schedule list`, one property per parameter.

Request body of List scheduled actions and whether they are currently due. With several machines sharing the store the Locked By column names the holder and marks this machine's own claims; a paused action reads paused: yes beside its due state, because due keeps meaning its time has passed. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

due_only boolean

Show only activities that are currently due

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule list' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn schedule list' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the schedule rows with their due, retry, lock and pause state, the all-machines pause if one is set, and the organization-wide stop if one is in force. `ok` is false when required columns are missing.

Response 200 of List scheduled actions and whether they are currently due. With several machines sharing the store the Locked By column names the holder and marks this machine's own claims; a paused action reads paused: yes beside its due state, because due keeps meaning its time has passed. fields
NameTypeDescription
activities required array of ScheduleActivity
activities[].activity required integer
activities[].command required string
activities[].connection object | null

The activity's Google Chrome connection, from the tick's gate (#1600): null when a run may start. `held` is a run that waits, not one that fails, with the plain reason and the one in-app action that clears it; `heal` is a connection Blowhorn reconnects on its own before the run; `open` is a mapped Google Chrome profile that is not open, which Blowhorn opens in the background before the run. Absent from older engines.

activities[].connection.action required string | null

The one thing the person can do: `turn_on_extension` (turn the Blowhorn extension on in that Google Chrome profile), `reconnect` (reconnect it now), `choose_chrome_profile` (choose or confirm the Google Chrome profile) or `restart_chrome` (restart Google Chrome, the one action left when a native host process from before an upgrade is still answering and ending it did not clear the hold); null when Blowhorn heals it alone.

activities[].connection.chrome_directory required string | null

The Google Chrome profile directory the activity's profile is mapped to; null when none is.

activities[].connection.profile required string
activities[].connection.reason required string | null

Why it waits or reconnects, in plain words that name no command.

activities[].connection.state required string

Values: held, heal, open

activities[].driver_mode required string
activities[].due required string

`yes` when the activity's time has come: it is enabled and valid, no machine holds a live claim on it, and its `next_run` has passed. Always `no` when `next_run` is empty. A paused activity still reads `yes`, and `paused` says the tick is holding it.

Values: yes, no

activities[].enabled required string

Values: yes, no

activities[].errors required string

Validation errors joined by a semicolon; empty when the activity is valid.

activities[].held_here required boolean

The claim is this install's own: this macOS user on this computer. Never decided by the hostname alone, which every macOS user on a Mac shares.

activities[].last_error required string
activities[].last_log_path required string
activities[].last_run_at required string
activities[].lock_stale required boolean
activities[].locked required boolean
activities[].locked_at required string
activities[].locked_by required string
activities[].max_retries required integer

The retry budget for each arming; see `Max Retries` in the activity's values.

activities[].next_run required string

When the tick next takes the activity, in local time. Empty when it never will: a completed once-only activity, or a completed or failed activity with no Next Run At. Never the activity's old Run At.

activities[].params required ScheduleParams

The parsed Params of a schedule activity, keyed by the command's own parameter names. A value is a string, a number or a boolean as the activity's text parses.

activities[].pause_reason required string
activities[].paused required string

Values: yes, no

activities[].paused_at required string
activities[].paused_by required string
activities[].platform required string

The platform, or `all`.

activities[].profile required string

The profile, or a hyphen when the activity names none.

activities[].quiet_hours required string

`yes` when the activity is inside its quiet hours now (see `quiet_hours_window`): the tick holds it, unclaimed, and runs it when the window ends. Computed, never stored.

Values: yes, no

activities[].quiet_hours_tz required string

The zone `quiet_hours_window` is read in: an IANA name, or this computer's zone; empty when no window is set.

activities[].quiet_hours_until required string

When the current window ends, in local time; empty when the activity is not in quiet hours now.

activities[].quiet_hours_window required string

The quiet-hours window in force for the activity's profile, `HH:MM-HH:MM` like `22:00-07:00`; empty when no window is set anywhere for it.

activities[].recurrence required string
activities[].retry_count required integer

Failed attempts since the activity was last armed; see `Retry Count` in the activity's values.

activities[].retry_delay_minutes required integer
activities[].risk_hold required string

Why a Run now would be held, including while the profile is warming up. Empty when the activity may run. A confirmation passes this hold except when `risk_hold_source` is `published`.

activities[].risk_hold_source required string

Where the hold comes from. `published` is a limit the platform states, which a confirmation does not pass. Empty when `risk_hold` is empty.

activities[].risk_profile required string

The preset the activity resolves to, from the tick's gate; empty for an activity without one profile and one platform, which names no single preset.

activities[].risk_profile_effective required string

What the tick enforces: cautious while warming, whatever was chosen; empty with `risk_profile`.

activities[].risk_profile_source required string

The layer that chose the preset (`schedule` when the activity's own value did, `builtin` when nothing chose one); empty with `risk_profile`.

activities[].risk_profile_warming required string

`yes` while the profile or this computer is warming up; empty with `risk_profile` (an activity on every platform names no single preset).

Values: yes, no,

activities[].run_at required string
activities[].status required string

The activity's stored status, such as pending, scheduled, running, retrying or failed. Never a lock.

count required integer
missing_columns required array of string
ok required boolean
pause required one of PauseState, null

The pause every machine honours, or null.

stop required one of StopState, null

The organization-wide stop in force, or null.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleListResponse

Variant fields
NameTypeDescription
activities required array of ScheduleActivity
activities[].activity required integer
activities[].command required string
activities[].connection object | null

The activity's Google Chrome connection, from the tick's gate (#1600): null when a run may start. `held` is a run that waits, not one that fails, with the plain reason and the one in-app action that clears it; `heal` is a connection Blowhorn reconnects on its own before the run; `open` is a mapped Google Chrome profile that is not open, which Blowhorn opens in the background before the run. Absent from older engines.

activities[].connection.action required string | null

The one thing the person can do: `turn_on_extension` (turn the Blowhorn extension on in that Google Chrome profile), `reconnect` (reconnect it now), `choose_chrome_profile` (choose or confirm the Google Chrome profile) or `restart_chrome` (restart Google Chrome, the one action left when a native host process from before an upgrade is still answering and ending it did not clear the hold); null when Blowhorn heals it alone.

activities[].connection.chrome_directory required string | null

The Google Chrome profile directory the activity's profile is mapped to; null when none is.

activities[].connection.profile required string
activities[].connection.reason required string | null

Why it waits or reconnects, in plain words that name no command.

activities[].connection.state required string

Values: held, heal, open

activities[].driver_mode required string
activities[].due required string

`yes` when the activity's time has come: it is enabled and valid, no machine holds a live claim on it, and its `next_run` has passed. Always `no` when `next_run` is empty. A paused activity still reads `yes`, and `paused` says the tick is holding it.

Values: yes, no

activities[].enabled required string

Values: yes, no

activities[].errors required string

Validation errors joined by a semicolon; empty when the activity is valid.

activities[].held_here required boolean

The claim is this install's own: this macOS user on this computer. Never decided by the hostname alone, which every macOS user on a Mac shares.

activities[].last_error required string
activities[].last_log_path required string
activities[].last_run_at required string
activities[].lock_stale required boolean
activities[].locked required boolean
activities[].locked_at required string
activities[].locked_by required string
activities[].max_retries required integer

The retry budget for each arming; see `Max Retries` in the activity's values.

activities[].next_run required string

When the tick next takes the activity, in local time. Empty when it never will: a completed once-only activity, or a completed or failed activity with no Next Run At. Never the activity's old Run At.

activities[].params required ScheduleParams

The parsed Params of a schedule activity, keyed by the command's own parameter names. A value is a string, a number or a boolean as the activity's text parses.

activities[].pause_reason required string
activities[].paused required string

Values: yes, no

activities[].paused_at required string
activities[].paused_by required string
activities[].platform required string

The platform, or `all`.

activities[].profile required string

The profile, or a hyphen when the activity names none.

activities[].quiet_hours required string

`yes` when the activity is inside its quiet hours now (see `quiet_hours_window`): the tick holds it, unclaimed, and runs it when the window ends. Computed, never stored.

Values: yes, no

activities[].quiet_hours_tz required string

The zone `quiet_hours_window` is read in: an IANA name, or this computer's zone; empty when no window is set.

activities[].quiet_hours_until required string

When the current window ends, in local time; empty when the activity is not in quiet hours now.

activities[].quiet_hours_window required string

The quiet-hours window in force for the activity's profile, `HH:MM-HH:MM` like `22:00-07:00`; empty when no window is set anywhere for it.

activities[].recurrence required string
activities[].retry_count required integer

Failed attempts since the activity was last armed; see `Retry Count` in the activity's values.

activities[].retry_delay_minutes required integer
activities[].risk_hold required string

Why a Run now would be held, including while the profile is warming up. Empty when the activity may run. A confirmation passes this hold except when `risk_hold_source` is `published`.

activities[].risk_hold_source required string

Where the hold comes from. `published` is a limit the platform states, which a confirmation does not pass. Empty when `risk_hold` is empty.

activities[].risk_profile required string

The preset the activity resolves to, from the tick's gate; empty for an activity without one profile and one platform, which names no single preset.

activities[].risk_profile_effective required string

What the tick enforces: cautious while warming, whatever was chosen; empty with `risk_profile`.

activities[].risk_profile_source required string

The layer that chose the preset (`schedule` when the activity's own value did, `builtin` when nothing chose one); empty with `risk_profile`.

activities[].risk_profile_warming required string

`yes` while the profile or this computer is warming up; empty with `risk_profile` (an activity on every platform names no single preset).

Values: yes, no,

activities[].run_at required string
activities[].status required string

The activity's stored status, such as pending, scheduled, running, retrying or failed. Never a lock.

count required integer
missing_columns required array of string
ok required boolean
pause required one of PauseState, null

The pause every machine honours, or null.

stop required one of StopState, null

The organization-wide stop in force, or null.

POST /cli/schedule/pause Pause all scheduled processing: every tick keeps running but claims no activities until 'blowhorn schedule resume', and due times carry forward. A job already running finishes first. Pausing has three scopes and an activity is claimed only when none of them says paused: without a flag it is this machine only (a local file, never the store), --all is every machine sharing the store, and --activity N is one scheduled activity on every machine.

Pause all scheduled processing: every tick keeps running but claims no activities until 'blowhorn schedule resume', and due times carry forward. A job already running finishes first. Pausing has three scopes and an activity is claimed only when none of them says paused: without a flag it is this machine only (a local file, never the store), --all is every machine sharing the store, and --activity N is one scheduled activity on every machine.

Request body required

The flags and arguments of `blowhorn schedule pause`, one property per parameter.

Request body of Pause all scheduled processing: every tick keeps running but claims no activities until 'blowhorn schedule resume', and due times carry forward. A job already running finishes first. Pausing has three scopes and an activity is claimed only when none of them says paused: without a flag it is this machine only (a local file, never the store), --all is every machine sharing the store, and --activity N is one scheduled activity on every machine. fields
NameTypeDescription
activity integer

Pause one scheduled activity instead of a whole machine: that job is claimed by nobody until 'blowhorn schedule resume --activity N', and every other activity keeps running everywhere. Distinct from setting the activity's Enabled to no, which is an editorial state with no reason and no intent to resume

all_machines boolean

Pause every machine sharing this store, not just this one: no machine claims an activity until 'blowhorn schedule resume --all'. Recorded in the store with who paused it and why, so every machine reports the same reason. Without this flag the pause is this machine only

Default: false

by string

Tool name recorded as the pause owner, as <tool>@<host> (default: cli)

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule pause' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

reason string

Short note stored with the pause and shown by 'blowhorn schedule status'

risk_profile string

Accepted by 'blowhorn schedule pause' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the pause `schedule pause` set: on this machine (the pause file), on every machine sharing the store (`--all`) or on one activity (`--activity`). Pausing an existing pause changes nothing and says so in `was_paused`. `--dry-run` prints the plan.

SchedulePauseDryRun

Variant fields
NameTypeDescription
activity integer
by required string
dry_run required any
ok required any
path string

The pause file (machine scope).

reason required string
scope string

Values: all, activity

SchedulePauseSet

Variant fields
NameTypeDescription
activity integer
by required string | null
corrupt boolean
error string | null
ok required any
path string | null

The pause file (machine scope).

paused required any
reason required string
scope string

Values: all, activity

since required string | null
was_paused required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

SchedulePauseResponse

POST /cli/schedule/resume Resume scheduled processing after 'blowhorn schedule pause'; activities that fell due meanwhile run once, as soon as the service takes them up. The same three scopes: without a flag this machine's own pause is lifted, --all lifts the pause every machine sharing the store honours, and --activity N lifts one paused action. Lifting one scope leaves the other two as they were.

Resume scheduled processing after 'blowhorn schedule pause'; activities that fell due meanwhile run once, as soon as the service takes them up. The same three scopes: without a flag this machine's own pause is lifted, --all lifts the pause every machine sharing the store honours, and --activity N lifts one paused action. Lifting one scope leaves the other two as they were.

Request body required

The flags and arguments of `blowhorn schedule resume`, one property per parameter.

Request body of Resume scheduled processing after 'blowhorn schedule pause'; activities that fell due meanwhile run once, as soon as the service takes them up. The same three scopes: without a flag this machine's own pause is lifted, --all lifts the pause every machine sharing the store honours, and --activity N lifts one paused action. Lifting one scope leaves the other two as they were. fields
NameTypeDescription
activity integer

Let one held scheduled activity be claimed again, on every machine, the one 'blowhorn schedule pause --activity N' held. Every other scope stays as it is

all_machines boolean

Lift the pause every machine sharing this store honours, the one 'blowhorn schedule pause --all' set. This machine's own pause and any paused action stay as they are

Default: false

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule resume' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn schedule resume' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints what `schedule resume` lifted - this machine's pause file, the all-machines pause (`--all`) or one activity's hold (`--activity`) - and how long it lasted. Resuming something not paused changes nothing and says so in `was_paused`. `--dry-run` prints the plan.

ScheduleResumeDryRun

Variant fields
NameTypeDescription
activity integer
dry_run required any
ok required any
path string
scope string

Values: all, activity

ScheduleResumeLifted

Variant fields
NameTypeDescription
activity integer
ok required any
path string

The pause file (machine scope).

paused required any
paused_by string | null
paused_for_s integer | null
paused_since string | null
reason string

Why it had been paused (store scopes).

resumed_at string
scope string

Values: all, activity

was_corrupt boolean
was_paused required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleResumeResponse

POST /cli/schedule/status Show whether processing is paused, the last pass's heartbeat, whether passes are still running, today's run counts from the run ledger, and the background service's state.

Show whether processing is paused, the last pass's heartbeat, whether passes are still running, today's run counts from the run ledger, and the background service's state.

Request body required

The flags and arguments of `blowhorn schedule status`, one property per parameter.

Request body of Show whether processing is paused, the last pass's heartbeat, whether passes are still running, today's run counts from the run ledger, and the background service's state. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule status' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn schedule status' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the scheduler at a glance: whether this machine is paused, the last pass's heartbeat and whether passes are still running (`stalled` is true when none has run lately), today's run counts, the store as the last pass saw it (never a probe) and the background service's state. There is no countdown: the service runs when something is due, and re-scans at least every minute.

Response 200 of Show whether processing is paused, the last pass's heartbeat, whether passes are still running, today's run counts from the run ledger, and the background service's state. fields
NameTypeDescription
heartbeat_path required string | null
last_tick required one of ScheduleHeartbeat, null

The last pass's heartbeat; null when no pass has run.

log_dir required string | null
ok required any
pause required PauseState

A scheduler pause in the shape every scope shares. The machine scope (the pause file) carries `path`; the all-machines scope carries `scope: all` and a null `path`; a corrupt pause file adds `corrupt` and `error`.

pause.by required string | null
pause.corrupt boolean
pause.error string | null
pause.path string | null
pause.paused required boolean
pause.reason required string
pause.scope string

Values: machine, all, activity

pause.since required string | null

Local time, formatted as YYYY-MM-DD HH:MM:SS.

paused required boolean
runs_dir required string
service any of ServiceState, Problem

The background service, on macOS. When its state cannot be read (any platform but macOS, or a launchd read that failed) the command still exits 0 and this is a Problem naming why.

stalled required boolean

True when the last heartbeat is too old for a service that re-scans every minute, so no pass has run lately on this machine.

store required object
store.available required boolean | null

Null when no pass has said either way.

store.cause required string | null

The named cause beside `error` (schema_behind, tunnel_refused and so on); null when the last pass reached the store or no pass has said either way.

store.error required string | null
store.ticks_skipped required integer
store.unavailable_since required string | null
today required one of null, object

Today's pass counts from the run ledger; null without a log directory.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleStatusResponse

Variant fields
NameTypeDescription
heartbeat_path required string | null
last_tick required one of ScheduleHeartbeat, null

The last pass's heartbeat; null when no pass has run.

log_dir required string | null
ok required any
pause required PauseState

A scheduler pause in the shape every scope shares. The machine scope (the pause file) carries `path`; the all-machines scope carries `scope: all` and a null `path`; a corrupt pause file adds `corrupt` and `error`.

pause.by required string | null
pause.corrupt boolean
pause.error string | null
pause.path string | null
pause.paused required boolean
pause.reason required string
pause.scope string

Values: machine, all, activity

pause.since required string | null

Local time, formatted as YYYY-MM-DD HH:MM:SS.

paused required boolean
runs_dir required string
service any of ServiceState, Problem

The background service, on macOS. When its state cannot be read (any platform but macOS, or a launchd read that failed) the command still exits 0 and this is a Problem naming why.

stalled required boolean

True when the last heartbeat is too old for a service that re-scans every minute, so no pass has run lately on this machine.

store required object
store.available required boolean | null

Null when no pass has said either way.

store.cause required string | null

The named cause beside `error` (schema_behind, tunnel_refused and so on); null when the last pass reached the store or no pass has said either way.

store.error required string | null
store.ticks_skipped required integer
store.unavailable_since required string | null
today required one of null, object

Today's pass counts from the run ledger; null without a log directory.

POST /cli/schedule/tick Run one scheduler polling pass against due scheduled actions, executing each through the same command routing the CLI uses and writing a per-job log under <log-dir>/scheduler/. While 'blowhorn schedule pause' is in effect the pass claims nothing unless --ignore-pause is passed; a real pass also records its heartbeat in <log-dir>/scheduler/last-tick.json and appends one tick record to the run ledger, <log-dir>/runs/<YYYY-MM>.jsonl. Browser visibility comes from each job: its own headless=/headed= params, then the environment, then Settings.

Run one scheduler polling pass against due scheduled actions, executing each through the same command routing the CLI uses and writing a per-job log under <log-dir>/scheduler/. While 'blowhorn schedule pause' is in effect the pass claims nothing unless --ignore-pause is passed; a real pass also records its heartbeat in <log-dir>/scheduler/last-tick.json and appends one tick record to the run ledger, <log-dir>/runs/<YYYY-MM>.jsonl. Browser visibility comes from each job: its own headless=/headed= params, then the environment, then Settings.

Request body required

The flags and arguments of `blowhorn schedule tick`, one property per parameter.

Request body of Run one scheduler polling pass against due scheduled actions, executing each through the same command routing the CLI uses and writing a per-job log under <log-dir>/scheduler/. While 'blowhorn schedule pause' is in effect the pass claims nothing unless --ignore-pause is passed; a real pass also records its heartbeat in <log-dir>/scheduler/last-tick.json and appends one tick record to the run ledger, <log-dir>/runs/<YYYY-MM>.jsonl. Browser visibility comes from each job: its own headless=/headed= params, then the environment, then Settings. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

ignore_pause boolean

Run even while 'blowhorn schedule pause' is in effect; without it a paused scheduler claims nothing

Default: false

ignore_quiet_hours boolean

Run even inside the quiet hours (defaults.quiet_hours, or a profile's own Quiet Hours setting); without it a due activity inside the window is held until it ends

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

limit integer

Maximum number of due activities to process; 0 means no limit

Default: 0

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Pace handed to every action 'blowhorn schedule tick' runs in-process this pass -- the random delays between that action's browser or API steps: a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What schedule tick prints with --json: the pass's counts and one entry per activity it handled (or would handle with --dry-run).

Response 200 of Run one scheduler polling pass against due scheduled actions, executing each through the same command routing the CLI uses and writing a per-job log under <log-dir>/scheduler/. While 'blowhorn schedule pause' is in effect the pass claims nothing unless --ignore-pause is passed; a real pass also records its heartbeat in <log-dir>/scheduler/last-tick.json and appends one tick record to the run ledger, <log-dir>/runs/<YYYY-MM>.jsonl. Browser visibility comes from each job: its own headless=/headed= params, then the environment, then Settings. fields
NameTypeDescription
cancelled required boolean
candidates required integer
checked required integer
heartbeat_path required string | null
missing_columns required array of string
ok required boolean

False when the schedule is missing required columns, or when this install is already running a tick.

pause required object | null

The pause that held the pass, with its scope (machine, all or activity); null when not paused.

paused required boolean
processed required integer
record_path required string | null
refused required string | null

The sentence Blowhorn prints when this install is already running a tick. The command then exits 1. Null on a pass that ran.

results required array of object

One entry per activity the pass handled. Entries always carry activity, command, profile and status; a pass adds further keys for some statuses.

results[].activity required integer
results[].command required string
results[].profile required string
results[].reason string

Why a preview blocked the activity, in words. Set when status is blocked, and on a reclaim skip.

results[].status required string

For example would-run, blocked, held, invalid, done, failed or cancelled.

skipped_cancelled required array
skipped_paused required array

Due activities left waiting because of a pause.

stop required one of StopState, null

The organization-wide stop in force, or null.

stopped required boolean

True when the organization-wide stop ended the pass at a safe point; a stopped pass is also cancelled, with the stop named in `stop`.

store_error required string | null

Set on a degraded pass when the store was unreachable (the command then exits 3).

store_unavailable_since required string | null
store_unavailable_ticks required integer

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleTickResponse

Variant fields
NameTypeDescription
cancelled required boolean
candidates required integer
checked required integer
heartbeat_path required string | null
missing_columns required array of string
ok required boolean

False when the schedule is missing required columns, or when this install is already running a tick.

pause required object | null

The pause that held the pass, with its scope (machine, all or activity); null when not paused.

paused required boolean
processed required integer
record_path required string | null
refused required string | null

The sentence Blowhorn prints when this install is already running a tick. The command then exits 1. Null on a pass that ran.

results required array of object

One entry per activity the pass handled. Entries always carry activity, command, profile and status; a pass adds further keys for some statuses.

results[].activity required integer
results[].command required string
results[].profile required string
results[].reason string

Why a preview blocked the activity, in words. Set when status is blocked, and on a reclaim skip.

results[].status required string

For example would-run, blocked, held, invalid, done, failed or cancelled.

skipped_cancelled required array
skipped_paused required array

Due activities left waiting because of a pause.

stop required one of StopState, null

The organization-wide stop in force, or null.

stopped required boolean

True when the organization-wide stop ended the pass at a safe point; a stopped pass is also cancelled, with the stop named in `stop`.

store_error required string | null

Set on a degraded pass when the store was unreachable (the command then exits 3).

store_unavailable_since required string | null
store_unavailable_ticks required integer
POST /cli/schedule/trigger-now Force a specific scheduled action to run immediately. While 'blowhorn schedule pause' is in effect the activity is not run and the command exits 1, unless --ignore-pause is passed. A Risk Profile hold, including while a profile is warming up, also keeps it from running unless you confirm at the prompt or pass --ignore-risk. A limit the platform publishes still applies. Browser visibility comes from the job: its own headless=/headed= params, then the environment, then Settings.

Force a specific scheduled action to run immediately. While 'blowhorn schedule pause' is in effect the activity is not run and the command exits 1, unless --ignore-pause is passed. A Risk Profile hold, including while a profile is warming up, also keeps it from running unless you confirm at the prompt or pass --ignore-risk. A limit the platform publishes still applies. Browser visibility comes from the job: its own headless=/headed= params, then the environment, then Settings.

Request body required

The flags and arguments of `blowhorn schedule trigger-now`, one property per parameter.

Request body of Force a specific scheduled action to run immediately. While 'blowhorn schedule pause' is in effect the activity is not run and the command exits 1, unless --ignore-pause is passed. A Risk Profile hold, including while a profile is warming up, also keeps it from running unless you confirm at the prompt or pass --ignore-risk. A limit the platform publishes still applies. Browser visibility comes from the job: its own headless=/headed= params, then the environment, then Settings. fields
NameTypeDescription
activity required integer

Activity number to execute

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

ignore_pause boolean

Run even while 'blowhorn schedule pause' is in effect; without it a paused scheduler claims nothing

Default: false

ignore_quiet_hours boolean

Run even inside the quiet hours (defaults.quiet_hours, or a profile's own Quiet Hours setting); without it a due activity inside the window is held until it ends

Default: false

ignore_risk boolean

Run this once even if your Risk Profile would hold it (a daily cap, quiet hours, outreach turned off or a pause after a sign-in challenge). Only this run; your Risk Profile stays as it is. A limit the platform itself publishes still applies

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Pace handed to every action 'blowhorn schedule trigger-now' runs in-process this pass -- the random delays between that action's browser or API steps: a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What schedule trigger-now prints with --json: the pass's counts and one entry per activity it handled (or would handle with --dry-run).

Response 200 of Force a specific scheduled action to run immediately. While 'blowhorn schedule pause' is in effect the activity is not run and the command exits 1, unless --ignore-pause is passed. A Risk Profile hold, including while a profile is warming up, also keeps it from running unless you confirm at the prompt or pass --ignore-risk. A limit the platform publishes still applies. Browser visibility comes from the job: its own headless=/headed= params, then the environment, then Settings. fields
NameTypeDescription
cancelled required boolean
candidates required integer
checked required integer
heartbeat_path required string | null
missing_columns required array of string
ok required boolean

False when the schedule is missing required columns, or when this install is already running a tick.

pause required object | null

The pause that held the pass, with its scope (machine, all or activity); null when not paused.

paused required boolean
processed required integer
record_path required string | null
refused required string | null

The sentence Blowhorn prints when this install is already running a tick. The command then exits 1. Null on a pass that ran.

results required array of object

One entry per activity the pass handled. Entries always carry activity, command, profile and status; a pass adds further keys for some statuses.

results[].activity required integer
results[].command required string
results[].profile required string
results[].status required string

'For example would-run, held, invalid, done, failed, cancelled.'

skipped_cancelled required array
skipped_paused required array

Due activities left waiting because of a pause.

store_error required string | null

Set on a degraded pass when the store was unreachable (the command then exits 3).

store_unavailable_since required string | null
store_unavailable_ticks required integer

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleTriggerNowResponse

Variant fields
NameTypeDescription
cancelled required boolean
candidates required integer
checked required integer
heartbeat_path required string | null
missing_columns required array of string
ok required boolean

False when the schedule is missing required columns, or when this install is already running a tick.

pause required object | null

The pause that held the pass, with its scope (machine, all or activity); null when not paused.

paused required boolean
processed required integer
record_path required string | null
refused required string | null

The sentence Blowhorn prints when this install is already running a tick. The command then exits 1. Null on a pass that ran.

results required array of object

One entry per activity the pass handled. Entries always carry activity, command, profile and status; a pass adds further keys for some statuses.

results[].activity required integer
results[].command required string
results[].profile required string
results[].status required string

'For example would-run, held, invalid, done, failed, cancelled.'

skipped_cancelled required array
skipped_paused required array

Due activities left waiting because of a pause.

store_error required string | null

Set on a degraded pass when the store was unreachable (the command then exits 3).

store_unavailable_since required string | null
store_unavailable_ticks required integer
POST /cli/schedule/upsert Create or update a scheduled action from a JSON payload. Giving a completed or failed activity a new Next Run At, or a Run At that changes, schedules it again in place with a fresh retry budget (Status scheduled, Retry Count 0; its last run and last error are kept); raising Max Retries alone does not.

Create or update a scheduled action from a JSON payload. Giving a completed or failed activity a new Next Run At, or a Run At that changes, schedules it again in place with a fresh retry budget (Status scheduled, Retry Count 0; its last run and last error are kept); raising Max Retries alone does not.

Request body required

The flags and arguments of `blowhorn schedule upsert`, one property per parameter.

Request body of Create or update a scheduled action from a JSON payload. Giving a completed or failed activity a new Next Run At, or a Run At that changes, schedules it again in place with a fresh retry budget (Status scheduled, Retry Count 0; its last run and last error are kept); raising Max Retries alone does not. fields
NameTypeDescription
activity integer

Activity number to update; omit to create a new activity

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

expected_fingerprint string

Reject the write if the current activity fingerprint no longer matches

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule upsert' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

payload required string

JSON object representing scheduled action fields

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn schedule upsert' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the schedule activity that was created or updated, with the values and fingerprint it now has: the activity as written, including what the write decided on its own, such as a Next Run At that followed a changed Run At, or a completed or failed activity given a new time and scheduled again with Status scheduled and Retry Count 0, or an activity stored on all for a command that runs in one place (accept on all) stored as that place when the write names no platform.

Response 200 of Create or update a scheduled action from a JSON payload. Giving a completed or failed activity a new Next Run At, or a Run At that changes, schedules it again in place with a fresh retry budget (Status scheduled, Retry Count 0; its last run and last error are kept); raising Max Retries alone does not. fields
NameTypeDescription
activity required integer
created required boolean
fingerprint required string
ok required any
platform_rewritten object

Present only when the write named no platform and the activity's stored platform was replaced by the one place its command runs (accept stored on all becomes linkedin).

platform_rewritten.from required string
platform_rewritten.to required string
values required ScheduleValues

A schedule row's columns, each a string (empty when unset): the command and its params as `key=value;key=value`, when it runs and how it repeats, its retry budget, its status and the lock a running machine holds.

values.Command required string
values.Driver Mode required string
values.Enabled required string
values.Last Error required string
values.Last Log Path required string
values.Last Run At required string
values.Locked At required string
values.Locked By required string
values.Max Retries required string

The retry budget for each arming: how many times a failed run is retried before the row is failed. Raising it alone does not schedule a failed row again.

values.Next Run At required string

When the row is next due, in local time. Empty on a completed or failed row means never due again. An update that gives a completed or failed row a Next Run At, or changes its Run At, schedules it again in the same write: Status scheduled and Retry Count 0, with Last Run At, Last Error and Last Log Path kept, unless the update sets Status itself.

values.Params required string
values.Platform required string
values.Profile required string
values.Recurrence required string
values.Retry Count required string

Failed attempts since the row was last armed. A success sets it to 0, and so does an update that schedules a completed or failed row again.

values.Retry Delay Minutes required string
values.Risk Profile required string

The activity's own Risk Profile (#1009): `cautious`, `standard` or `brisk`, or empty to inherit the preset its profile and platform resolve to.

values.Run At required string
values.Status required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleUpsertResponse

Variant fields
NameTypeDescription
activity required integer
created required boolean
fingerprint required string
ok required any
platform_rewritten object

Present only when the write named no platform and the activity's stored platform was replaced by the one place its command runs (accept stored on all becomes linkedin).

platform_rewritten.from required string
platform_rewritten.to required string
values required ScheduleValues

A schedule row's columns, each a string (empty when unset): the command and its params as `key=value;key=value`, when it runs and how it repeats, its retry budget, its status and the lock a running machine holds.

values.Command required string
values.Driver Mode required string
values.Enabled required string
values.Last Error required string
values.Last Log Path required string
values.Last Run At required string
values.Locked At required string
values.Locked By required string
values.Max Retries required string

The retry budget for each arming: how many times a failed run is retried before the row is failed. Raising it alone does not schedule a failed row again.

values.Next Run At required string

When the row is next due, in local time. Empty on a completed or failed row means never due again. An update that gives a completed or failed row a Next Run At, or changes its Run At, schedules it again in the same write: Status scheduled and Retry Count 0, with Last Run At, Last Error and Last Log Path kept, unless the update sets Status itself.

values.Params required string
values.Platform required string
values.Profile required string
values.Recurrence required string
values.Retry Count required string

Failed attempts since the row was last armed. A success sets it to 0, and so does an update that schedules a completed or failed row again.

values.Retry Delay Minutes required string
values.Risk Profile required string

The activity's own Risk Profile (#1009): `cautious`, `standard` or `brisk`, or empty to inherit the preset its profile and platform resolve to.

values.Run At required string
values.Status required string
POST /cli/schedule/validate Validate the schedule's actions against the supported schema.

Validate the schedule's actions against the supported schema.

Request body required

The flags and arguments of `blowhorn schedule validate`, one property per parameter.

Request body of Validate the schedule's actions against the supported schema. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule validate' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn schedule validate' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the activities whose command, profile or params fail validation. `ok` is false when any activity is invalid or a required column is missing; the same document is printed then.

Response 200 of Validate the schedule's actions against the supported schema. fields
NameTypeDescription
invalid_activities required array of object
invalid_activities[].activity required integer
invalid_activities[].command required string
invalid_activities[].errors required array of string
invalid_activities[].profile required string
missing_columns required array of string
ok required boolean
total_activities required integer

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleValidateResponse

Variant fields
NameTypeDescription
invalid_activities required array of object
invalid_activities[].activity required integer
invalid_activities[].command required string
invalid_activities[].errors required array of string
invalid_activities[].profile required string
missing_columns required array of string
ok required boolean
total_activities required integer
POST /cli/schedule/why Explain why a scheduled activity has or has not run on this machine, in the order the scheduler decides it: whether the activity is enabled and valid, whether anything is pausing it (the activity itself, this machine, or every machine), whether it is due, whether another machine holds the claim and when that lease expires, whether this machine can reach the store and when it last ticked, and what the run history says happened the last time it ran. Reads only -- it claims nothing, runs nothing and changes nothing, so it is safe against an activity that is mid-flight.

Explain why a scheduled activity has or has not run on this machine, in the order the scheduler decides it: whether the activity is enabled and valid, whether anything is pausing it (the activity itself, this machine, or every machine), whether it is due, whether another machine holds the claim and when that lease expires, whether this machine can reach the store and when it last ticked, and what the run history says happened the last time it ran. Reads only -- it claims nothing, runs nothing and changes nothing, so it is safe against an activity that is mid-flight.

Request body required

The flags and arguments of `blowhorn schedule why`, one property per parameter.

Request body of Explain why a scheduled activity has or has not run on this machine, in the order the scheduler decides it: whether the activity is enabled and valid, whether anything is pausing it (the activity itself, this machine, or every machine), whether it is due, whether another machine holds the claim and when that lease expires, whether this machine can reach the store and when it last ticked, and what the run history says happened the last time it ran. Reads only -- it claims nothing, runs nothing and changes nothing, so it is safe against an activity that is mid-flight. fields
NameTypeDescription
activity required integer

The scheduled action's number to explain (the same number 'blowhorn schedule list' prints)

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn schedule why' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform filter for 'blowhorn schedule': it selects which scheduled activities this command reads or acts on, and is not a platform blowhorn posts to directly. One of: bluesky, github, hn, layer5, lfx, linkedin, meshery, reddit, slack, x, all ('all' applies no filter, so every scheduled activity is included)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn schedule why' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What schedule why prints with --json: the diagnosis ladder for one schedule activity (enabled, valid, paused, quiet hours, due, claimed, this machine, last run) and a one-sentence verdict. Read-only.

Response 200 of Explain why a scheduled activity has or has not run on this machine, in the order the scheduler decides it: whether the activity is enabled and valid, whether anything is pausing it (the activity itself, this machine, or every machine), whether it is due, whether another machine holds the claim and when that lease expires, whether this machine can reach the store and when it last ticked, and what the run history says happened the last time it ran. Reads only -- it claims nothing, runs nothing and changes nothing, so it is safe against an activity that is mid-flight. fields
NameTypeDescription
activity required integer
claim required object

The activity's lease - claimed, by, at, lease_expires_at, stale, this_machine.

command required string
due required boolean

Whether the activity's time has come, read from the same instant schedule list prints as next_run. The claim and the pauses are rungs of their own.

due_at required string | null

That instant in local time. Empty when the activity is never due again (a completed once-only activity, or a completed or failed activity with no Next Run At) or has no Run At.

due_for_s required integer | null
enabled required boolean
in_quiet_hours required boolean

Whether the activity is inside its quiet hours now.

last_run required object | null
not_due_reason required string | null
ok required boolean
pause required object | null

The pause holding the activity, with its scope; null when none.

pause_scopes required object or array | null

Each pause scope (machine, all, activity) and whether it holds the activity.

paused required boolean
platform required string
profile required string
quiet_hours required object | null

The quiet hours in force for the activity's profile; null when no window is set anywhere for it.

quiet_hours.held required boolean

Whether the activity is inside the window now.

quiet_hours.source required string

Whether the window came from the profile's own Quiet Hours setting or the defaults.

Values: profile, defaults

quiet_hours.tz required string

The zone the window is read in: an IANA name, or this computer's zone.

quiet_hours.until required string | null

When the window ends, as an instant in its zone; null when not held.

quiet_hours.until_clock required string | null

That ending as the zone's wall clock, `HH:MM`; null when not held.

quiet_hours.window required string

The window, `HH:MM-HH:MM` like `22:00-07:00`.

risk required object

The activity's Risk Profile (#1009): its own setting (empty when it inherits), the preset it effectively runs, and where that preset came from (`schedule` for its own setting).

risk.effective required string
risk.profile required string
risk.source required string
status required string
this_machine required object

This machine's host, last tick (the heartbeat's name for its last pass), store state, and whether passes are still running (passing).

valid required boolean
validation_errors required array of string
verdict required object
verdict.rung required string

The first rung that stopped the activity.

Values: enabled, valid, paused, quiet_hours, due, claimed, this_machine, last_run

verdict.sentence required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ScheduleWhyResponse

Variant fields
NameTypeDescription
activity required integer
claim required object

The activity's lease - claimed, by, at, lease_expires_at, stale, this_machine.

command required string
due required boolean

Whether the activity's time has come, read from the same instant schedule list prints as next_run. The claim and the pauses are rungs of their own.

due_at required string | null

That instant in local time. Empty when the activity is never due again (a completed once-only activity, or a completed or failed activity with no Next Run At) or has no Run At.

due_for_s required integer | null
enabled required boolean
in_quiet_hours required boolean

Whether the activity is inside its quiet hours now.

last_run required object | null
not_due_reason required string | null
ok required boolean
pause required object | null

The pause holding the activity, with its scope; null when none.

pause_scopes required object or array | null

Each pause scope (machine, all, activity) and whether it holds the activity.

paused required boolean
platform required string
profile required string
quiet_hours required object | null

The quiet hours in force for the activity's profile; null when no window is set anywhere for it.

quiet_hours.held required boolean

Whether the activity is inside the window now.

quiet_hours.source required string

Whether the window came from the profile's own Quiet Hours setting or the defaults.

Values: profile, defaults

quiet_hours.tz required string

The zone the window is read in: an IANA name, or this computer's zone.

quiet_hours.until required string | null

When the window ends, as an instant in its zone; null when not held.

quiet_hours.until_clock required string | null

That ending as the zone's wall clock, `HH:MM`; null when not held.

quiet_hours.window required string

The window, `HH:MM-HH:MM` like `22:00-07:00`.

risk required object

The activity's Risk Profile (#1009): its own setting (empty when it inherits), the preset it effectively runs, and where that preset came from (`schedule` for its own setting).

risk.effective required string
risk.profile required string
risk.source required string
status required string
this_machine required object

This machine's host, last tick (the heartbeat's name for its last pass), store state, and whether passes are still running (passing).

valid required boolean
validation_errors required array of string
verdict required object
verdict.rung required string

The first rung that stopped the activity.

Values: enabled, valid, paused, quiet_hours, due, claimed, this_machine, last_run

verdict.sentence required string
POST /cli/stop Stop everything, right now: on this machine, or on every machine sharing the store with --all. A stop does what a pause does -- nothing new is claimed anywhere it covers -- and one thing more: work already running stops at its next safe point, never mid-post, so a pass mid-flight on another machine, or a manual or agent run there, ends the way a cancelled one does and its work returns to the queue. 'blowhorn stop --resume' lifts a stop, and lifts only the pause the stop itself set: a pause that was already in place when the stop landed stays in place.

Stop everything, right now: on this machine, or on every machine sharing the store with --all. A stop does what a pause does -- nothing new is claimed anywhere it covers -- and one thing more: work already running stops at its next safe point, never mid-post, so a pass mid-flight on another machine, or a manual or agent run there, ends the way a cancelled one does and its work returns to the queue. 'blowhorn stop --resume' lifts a stop, and lifts only the pause the stop itself set: a pause that was already in place when the stop landed stays in place.

Request body required

The flags and arguments of `blowhorn stop`, one property per parameter.

Request body of Stop everything, right now: on this machine, or on every machine sharing the store with --all. A stop does what a pause does -- nothing new is claimed anywhere it covers -- and one thing more: work already running stops at its next safe point, never mid-post, so a pass mid-flight on another machine, or a manual or agent run there, ends the way a cancelled one does and its work returns to the queue. 'blowhorn stop --resume' lifts a stop, and lifts only the pause the stop itself set: a pause that was already in place when the stop landed stays in place. fields
NameTypeDescription
all_machines boolean

Stop every machine sharing this store, not just this one: no machine claims an activity, and work already running on any of them stops at its next safe point. Recorded in the store with who stopped it and why, so every machine reports the same reason. Without this flag the stop is this machine only

Default: false

by string

Tool name recorded as who stopped, as <tool>@<host> (default: cli)

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn stop' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn stop': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

reason string

Short note stored with the stop and shown wherever the stop is reported

resume boolean

Lift a stop instead of setting one: this machine's, or every machine's with --all. Lifting a stop also lifts the pause the stop itself set, and no other pause

Default: false

risk_profile string

Accepted by 'blowhorn stop' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What `blowhorn stop` prints with `--json`: a stop set on this machine or on every machine sharing the store (`--all`), or a stop lifted with `--resume`. Setting a stop that already stands changes nothing and says so in `was_stopped`. `flag_carried` is false when the backend cannot carry the stop record (your Blowhorn account, until the wire construct grows a member for it): the organization-wide pause the stop raises still lands, and `was_stopped` is then null, because no record exists to ask. `pass_cancelled` is what asking this machine's background service to stop its pass answered: true when a pass is stopping, false when none is in flight, null when no service answers here. `--dry-run` prints the plan.

StopSet

Variant fields
NameTypeDescription
by required string | null
flag_carried required boolean
ok required any
pass_cancelled required boolean | null
path string

The local stop record (machine scope).

raised_pause required boolean
reason required string
scope required string

Values: machine, all

since required string | null
stopped required any
was_stopped required boolean | null

StopResumed

Variant fields
NameTypeDescription
flag_carried boolean
ok required any
path string

The local stop record (machine scope).

pause_lifted required boolean
raised_pause boolean
reason string
resumed_at string
scope required string

Values: machine, all

stopped required any
stopped_by string | null
stopped_for_s integer | null
stopped_since string | null
was_stopped required boolean | null

StopDryRun

Variant fields
NameTypeDescription
by string
dry_run required any
ok required any
reason string
resume any
scope required string

Values: machine, all

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

StopResponse

Risk

POST /cli/risk/grid Show the Risk Profile of every profile on every platform at once: the choice for everyone, each platform's choice for every profile (and whether it is kept for every computer or on this computer only), each profile's own choice, and for each profile on each platform the preset in effect, the level that chose it, and whether it is still warming up and running cautious. Reads the store once.

Show the Risk Profile of every profile on every platform at once: the choice for everyone, each platform's choice for every profile (and whether it is kept for every computer or on this computer only), each profile's own choice, and for each profile on each platform the preset in effect, the level that chose it, and whether it is still warming up and running cautious. Reads the store once.

Request body required

The flags and arguments of `blowhorn risk grid`, one property per parameter.

Request body of Show the Risk Profile of every profile on every platform at once: the choice for everyone, each platform's choice for every profile (and whether it is kept for every computer or on this computer only), each profile's own choice, and for each profile on each platform the preset in effect, the level that chose it, and whether it is still warming up and running cautious. Reads the store once. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn risk grid' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn risk'. One of: bluesky, github, hn, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn risk grid' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The Risk Profile of every profile on every platform in one answer - the choice for everyone, each platform's choice for every profile, each profile's own choice, and each cell's preset with the level that chose it - read from the store once. What the desktop Safety page draws its grid from.

Response 200 of Show the Risk Profile of every profile on every platform at once: the choice for everyone, each platform's choice for every profile (and whether it is kept for every computer or on this computer only), each profile's own choice, and for each profile on each platform the preset in effect, the level that chose it, and whether it is still warming up and running cautious. Reads the store once. fields
NameTypeDescription
customized_dials required array of string

The dials this computer's file or environment set outside the preset for everyone; any makes the profile custom.

everyone required object

The choice for every profile on every platform, the slider's own answer.

everyone.risk_profile required string

Values: cautious, standard, brisk

everyone.source required string

The environment variable, your account's choice, this computer's .blowhorn.yaml, or the built-in standard.

Values: env, account, config, builtin

ok required boolean
platform_defaults required object

Whether the organization's choices for each platform could be read.

platform_defaults.error required string | null

Why they could not be read; null when they were. Until they can be, every cell resolves as the risk gate does, with each platform read as cautious.

platform_defaults.shared required boolean
platforms required array of object

One entry per platform a Risk Profile can be chosen for, in the order the grid draws them.

platforms[].platform required string

Values: linkedin, x, bluesky, reddit, hn, slack, github

platforms[].risk_profile required string | null

The platform's choice for every profile; null when it has none and its profiles inherit.

Values: cautious, standard, brisk,

platforms[].scope required string | null

shared when every computer reads it, this_computer when it is kept in this computer's .blowhorn.yaml only.

Values: shared, this_computer,

platforms[].set_at required string | null

When a shared choice was set, ISO 8601.

platforms[].set_by required string | null

Who set a shared choice, as the store recorded it.

platforms[].source required string | null

Values: store:platform, config:platform,

profiles required array of object
profiles[].cells required array of object
profiles[].cells[].effective required string

What a scheduled run uses now - cautious while warming up, else the chosen preset.

Values: cautious, standard, brisk

profiles[].cells[].level required string

Which level chose it: set for this cell, inherited from the platform's or the profile's choice (the more careful one when both choose), from everyone, or built in.

Values: cell, platform, profile, everyone, builtin

profiles[].cells[].platform required string

Values: linkedin, x, bluesky, reddit, hn, slack, github

profiles[].cells[].risk_profile required string

The preset chosen for this profile on this platform.

Values: cautious, standard, brisk

profiles[].cells[].set_here required boolean

True when the choice was made for this profile on this platform.

profiles[].cells[].source required string
profiles[].cells[].warming required boolean
profiles[].name required string

The profile's display name when it has one, else its name.

profiles[].profile required string

The profile's name, lowercase.

profiles[].risk_profile required string | null

The profile's own choice for every platform; null when it has none.

Values: cautious, standard, brisk,

profiles[].source required string | null

Values: store:profile, config:profile,

profiles[].warming required boolean

True while any of its platforms is still warming up.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

RiskGridResponse

Variant fields
NameTypeDescription
customized_dials required array of string

The dials this computer's file or environment set outside the preset for everyone; any makes the profile custom.

everyone required object

The choice for every profile on every platform, the slider's own answer.

everyone.risk_profile required string

Values: cautious, standard, brisk

everyone.source required string

The environment variable, your account's choice, this computer's .blowhorn.yaml, or the built-in standard.

Values: env, account, config, builtin

ok required boolean
platform_defaults required object

Whether the organization's choices for each platform could be read.

platform_defaults.error required string | null

Why they could not be read; null when they were. Until they can be, every cell resolves as the risk gate does, with each platform read as cautious.

platform_defaults.shared required boolean
platforms required array of object

One entry per platform a Risk Profile can be chosen for, in the order the grid draws them.

platforms[].platform required string

Values: linkedin, x, bluesky, reddit, hn, slack, github

platforms[].risk_profile required string | null

The platform's choice for every profile; null when it has none and its profiles inherit.

Values: cautious, standard, brisk,

platforms[].scope required string | null

shared when every computer reads it, this_computer when it is kept in this computer's .blowhorn.yaml only.

Values: shared, this_computer,

platforms[].set_at required string | null

When a shared choice was set, ISO 8601.

platforms[].set_by required string | null

Who set a shared choice, as the store recorded it.

platforms[].source required string | null

Values: store:platform, config:platform,

profiles required array of object
profiles[].cells required array of object
profiles[].cells[].effective required string

What a scheduled run uses now - cautious while warming up, else the chosen preset.

Values: cautious, standard, brisk

profiles[].cells[].level required string

Which level chose it: set for this cell, inherited from the platform's or the profile's choice (the more careful one when both choose), from everyone, or built in.

Values: cell, platform, profile, everyone, builtin

profiles[].cells[].platform required string

Values: linkedin, x, bluesky, reddit, hn, slack, github

profiles[].cells[].risk_profile required string

The preset chosen for this profile on this platform.

Values: cautious, standard, brisk

profiles[].cells[].set_here required boolean

True when the choice was made for this profile on this platform.

profiles[].cells[].source required string
profiles[].cells[].warming required boolean
profiles[].name required string

The profile's display name when it has one, else its name.

profiles[].profile required string

The profile's name, lowercase.

profiles[].risk_profile required string | null

The profile's own choice for every platform; null when it has none.

Values: cautious, standard, brisk,

profiles[].source required string | null

Values: store:profile, config:profile,

profiles[].warming required boolean

True while any of its platforms is still warming up.

POST /cli/risk/set Choose a Risk Profile: cautious, standard (the built-in default) or brisk. With no --profile and no --platform it is written to .blowhorn.yaml (defaults.risk_profile). With --platform alone it is the choice for every profile on that platform, saved for your whole organization so every computer reads it; when it cannot be saved there (your account does not keep it yet, you are not an organization admin, or the store cannot be reached) it is kept on this computer in .blowhorn.yaml (defaults.risk_profile_by_platform) and the command says so. With --profile it is stored on that profile in the store (Risk Profile, or Risk Profile By Platform with --platform). When a platform's choice and a profile's own choice differ, the more careful one applies. default clears the choice at that scope (for a platform, on this computer too). --dry-run says what would change and writes nothing.

Choose a Risk Profile: cautious, standard (the built-in default) or brisk. With no --profile and no --platform it is written to .blowhorn.yaml (defaults.risk_profile). With --platform alone it is the choice for every profile on that platform, saved for your whole organization so every computer reads it; when it cannot be saved there (your account does not keep it yet, you are not an organization admin, or the store cannot be reached) it is kept on this computer in .blowhorn.yaml (defaults.risk_profile_by_platform) and the command says so. With --profile it is stored on that profile in the store (Risk Profile, or Risk Profile By Platform with --platform). When a platform's choice and a profile's own choice differ, the more careful one applies. default clears the choice at that scope (for a platform, on this computer too). --dry-run says what would change and writes nothing.

Request body required

The flags and arguments of `blowhorn risk set`, one property per parameter.

Request body of Choose a Risk Profile: cautious, standard (the built-in default) or brisk. With no --profile and no --platform it is written to .blowhorn.yaml (defaults.risk_profile). With --platform alone it is the choice for every profile on that platform, saved for your whole organization so every computer reads it; when it cannot be saved there (your account does not keep it yet, you are not an organization admin, or the store cannot be reached) it is kept on this computer in .blowhorn.yaml (defaults.risk_profile_by_platform) and the command says so. With --profile it is stored on that profile in the store (Risk Profile, or Risk Profile By Platform with --platform). When a platform's choice and a profile's own choice differ, the more careful one applies. default clears the choice at that scope (for a platform, on this computer too). --dry-run says what would change and writes nothing. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn risk set' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn risk'. One of: bluesky, github, hn, linkedin, reddit, slack, x, all ('all' means every platform listed here)

preset required string

cautious, standard or brisk; default clears the choice at this scope

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn risk set' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Where the risk profile was written (or, with --dry-run, would be) and the value stored. Clearing a choice with "default" stores an empty value.

Response 200 of Choose a Risk Profile: cautious, standard (the built-in default) or brisk. With no --profile and no --platform it is written to .blowhorn.yaml (defaults.risk_profile). With --platform alone it is the choice for every profile on that platform, saved for your whole organization so every computer reads it; when it cannot be saved there (your account does not keep it yet, you are not an organization admin, or the store cannot be reached) it is kept on this computer in .blowhorn.yaml (defaults.risk_profile_by_platform) and the command says so. With --profile it is stored on that profile in the store (Risk Profile, or Risk Profile By Platform with --platform). When a platform's choice and a profile's own choice differ, the more careful one applies. default clears the choice at that scope (for a platform, on this computer too). --dry-run says what would change and writes nothing. fields
NameTypeDescription
cleared_this_computer required boolean

Whether clearing a platform's shared choice also cleared this computer's own choice for that platform in .blowhorn.yaml, so the platform inherits again everywhere.

dry_run required boolean
fallback_reason required string | null

Why a platform's choice for every profile could not be saved for the whole organization and was kept on this computer instead; null when it was saved where it was asked for.

key required string

The config key or store profile attribute written; organization: <platform> for a platform's choice saved for the whole organization.

ok required boolean
platform required string | null
profile required string | null
risk_profile required string | null

The preset set, null when the choice was cleared.

Values: cautious, standard, brisk,

scope required string

Where the choice lives: everyone (this computer's .blowhorn.yaml, every platform), shared (one platform for every profile, saved for the whole organization so every computer reads it), this_computer (one platform for every profile, kept in this computer's .blowhorn.yaml because the shared choice could not be saved), or profile (a profile's own choice in the store).

Values: everyone, shared, this_computer, profile

value required string | null

The value written. A per-platform store attribute holds a comma-joined platform:preset list; null or empty when cleared.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

RiskSetResponse

Variant fields
NameTypeDescription
cleared_this_computer required boolean

Whether clearing a platform's shared choice also cleared this computer's own choice for that platform in .blowhorn.yaml, so the platform inherits again everywhere.

dry_run required boolean
fallback_reason required string | null

Why a platform's choice for every profile could not be saved for the whole organization and was kept on this computer instead; null when it was saved where it was asked for.

key required string

The config key or store profile attribute written; organization: <platform> for a platform's choice saved for the whole organization.

ok required boolean
platform required string | null
profile required string | null
risk_profile required string | null

The preset set, null when the choice was cleared.

Values: cautious, standard, brisk,

scope required string

Where the choice lives: everyone (this computer's .blowhorn.yaml, every platform), shared (one platform for every profile, saved for the whole organization so every computer reads it), this_computer (one platform for every profile, kept in this computer's .blowhorn.yaml because the shared choice could not be saved), or profile (a profile's own choice in the store).

Values: everyone, shared, this_computer, profile

value required string | null

The value written. A per-platform store attribute holds a comma-joined platform:preset list; null or empty when cleared.

POST /cli/risk/show Show the Risk Profile in effect for each platform: the one chosen, the level and the source that chose it (--profile reads that profile's choices from the store; every run also reads the organization's choice for each platform, .blowhorn.yaml, BLOWHORN_RISK_PROFILE, your account's choice and the built-in standard) and what the preset sets. When a platform's choice and a profile's own choice differ, the more careful one applies. A new profile or computer still runs cautious until it is warm, whatever is chosen.

Show the Risk Profile in effect for each platform: the one chosen, the level and the source that chose it (--profile reads that profile's choices from the store; every run also reads the organization's choice for each platform, .blowhorn.yaml, BLOWHORN_RISK_PROFILE, your account's choice and the built-in standard) and what the preset sets. When a platform's choice and a profile's own choice differ, the more careful one applies. A new profile or computer still runs cautious until it is warm, whatever is chosen.

Request body required

The flags and arguments of `blowhorn risk show`, one property per parameter.

Request body of Show the Risk Profile in effect for each platform: the one chosen, the level and the source that chose it (--profile reads that profile's choices from the store; every run also reads the organization's choice for each platform, .blowhorn.yaml, BLOWHORN_RISK_PROFILE, your account's choice and the built-in standard) and what the preset sets. When a platform's choice and a profile's own choice differ, the more careful one applies. A new profile or computer still runs cautious until it is warm, whatever is chosen. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn risk show' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn risk'. One of: bluesky, github, hn, linkedin, reddit, slack, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn risk show' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The effective risk profile for each platform (or the one named), with the layer that chose it, the dials it sets, and the dials set outside it.

Response 200 of Show the Risk Profile in effect for each platform: the one chosen, the level and the source that chose it (--profile reads that profile's choices from the store; every run also reads the organization's choice for each platform, .blowhorn.yaml, BLOWHORN_RISK_PROFILE, your account's choice and the built-in standard) and what the preset sets. When a platform's choice and a profile's own choice differ, the more careful one applies. A new profile or computer still runs cautious until it is warm, whatever is chosen. fields
NameTypeDescription
customized_dials required array of string

The dials this machine's file or environment set outside the chosen preset, to a value the preset would not have set; a profile with any is a custom profile. Machine-wide, the same for every platform row.

ok required boolean
platform_defaults required object

Whether the organization's choices for each platform could be read for this answer.

platform_defaults.error required string | null

Why the organization's platform choices could not be read; null when they were. Until they can be read, scheduled runs treat each platform as cautious.

platform_defaults.shared required boolean

True when they were read (or no store is set up, so there are none); false when the read failed.

platforms required array of object
platforms[].concurrency required object

How much may run at once under this platform's preset, each value with the layer that supplied it (the preset itself, the file, or the environment).

platforms[].concurrency.max_runs required integer | null

Most runs at once across everything; null for no global limit.

platforms[].concurrency.max_runs_source required string

Which layer supplied max_runs (risk_profile, config or env).

platforms[].concurrency.per_platform required string

platform - one profile's run at a time on a platform; profile - runs for different profiles may share a platform.

Values: platform, profile

platforms[].concurrency.per_platform_source required string

Which layer supplied per_platform (risk_profile, config or env).

platforms[].level required string

The scope of the layer that chose it, most specific first: one run, one activity, one profile on one platform (cell), the platform's choice for every profile, the profile's own choice for every platform, everyone, or the built-in standard. When a platform and a profile both choose, the more careful one is the one named.

Values: run, activity, cell, platform, profile, everyone, builtin

platforms[].outreach required string

Whether outreach (connect, invite, follow) may run - off, established or on.

platforms[].pace required string

The preset's pace (slow, normal or fast).

platforms[].platform required string

Values: linkedin, x, bluesky, reddit, hn, slack, github

platforms[].platform_choice required string | null

What the platform's own choice for every profile says, whether or not it won; null when the platform has none.

Values: cautious, standard, brisk,

platforms[].platform_source required string | null

Where the platform's own choice comes from (store:platform for the organization's, config:platform for this computer's file); null when there is none.

platforms[].profile_choice required string | null

What the profile's own choice for every platform says, whether or not it won; null for no profile or no choice.

Values: cautious, standard, brisk,

platforms[].profile_source required string | null

Where the profile's own choice comes from (store:profile or config:profile); null when there is none.

platforms[].quiet_hours required array of integer | null

[start_hour, end_hour] local time during which nothing runs; null when there are none.

platforms[].risk_profile required string

Values: cautious, standard, brisk

platforms[].source required string

Which layer chose the profile (for example a command-line flag, the store, the organization's choice for the platform, the config file or the built-in default).

platforms[].warmup_days required integer
profile required string | null

The Blowhorn profile asked about, null for the machine-wide answer.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

RiskShowResponse

Variant fields
NameTypeDescription
customized_dials required array of string

The dials this machine's file or environment set outside the chosen preset, to a value the preset would not have set; a profile with any is a custom profile. Machine-wide, the same for every platform row.

ok required boolean
platform_defaults required object

Whether the organization's choices for each platform could be read for this answer.

platform_defaults.error required string | null

Why the organization's platform choices could not be read; null when they were. Until they can be read, scheduled runs treat each platform as cautious.

platform_defaults.shared required boolean

True when they were read (or no store is set up, so there are none); false when the read failed.

platforms required array of object
platforms[].concurrency required object

How much may run at once under this platform's preset, each value with the layer that supplied it (the preset itself, the file, or the environment).

platforms[].concurrency.max_runs required integer | null

Most runs at once across everything; null for no global limit.

platforms[].concurrency.max_runs_source required string

Which layer supplied max_runs (risk_profile, config or env).

platforms[].concurrency.per_platform required string

platform - one profile's run at a time on a platform; profile - runs for different profiles may share a platform.

Values: platform, profile

platforms[].concurrency.per_platform_source required string

Which layer supplied per_platform (risk_profile, config or env).

platforms[].level required string

The scope of the layer that chose it, most specific first: one run, one activity, one profile on one platform (cell), the platform's choice for every profile, the profile's own choice for every platform, everyone, or the built-in standard. When a platform and a profile both choose, the more careful one is the one named.

Values: run, activity, cell, platform, profile, everyone, builtin

platforms[].outreach required string

Whether outreach (connect, invite, follow) may run - off, established or on.

platforms[].pace required string

The preset's pace (slow, normal or fast).

platforms[].platform required string

Values: linkedin, x, bluesky, reddit, hn, slack, github

platforms[].platform_choice required string | null

What the platform's own choice for every profile says, whether or not it won; null when the platform has none.

Values: cautious, standard, brisk,

platforms[].platform_source required string | null

Where the platform's own choice comes from (store:platform for the organization's, config:platform for this computer's file); null when there is none.

platforms[].profile_choice required string | null

What the profile's own choice for every platform says, whether or not it won; null for no profile or no choice.

Values: cautious, standard, brisk,

platforms[].profile_source required string | null

Where the profile's own choice comes from (store:profile or config:profile); null when there is none.

platforms[].quiet_hours required array of integer | null

[start_hour, end_hour] local time during which nothing runs; null when there are none.

platforms[].risk_profile required string

Values: cautious, standard, brisk

platforms[].source required string

Which layer chose the profile (for example a command-line flag, the store, the organization's choice for the platform, the config file or the built-in default).

platforms[].warmup_days required integer
profile required string | null

The Blowhorn profile asked about, null for the machine-wide answer.

Config

POST /cli/config/get Print one option's effective value and which layer supplied it (a flag, the environment, the file or the built-in). With --json, the same row 'config show --json' carries for the option.

Print one option's effective value and which layer supplied it (a flag, the environment, the file or the built-in). With --json, the same row 'config show --json' carries for the option.

Request body required

The flags and arguments of `blowhorn config get`, one property per parameter.

Request body of Print one option's effective value and which layer supplied it (a flag, the environment, the file or the built-in). With --json, the same row 'config show --json' carries for the option. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the option report as machine-readable JSON

Default: false

key required string

Dotted option key, defaults.<name>, as 'blowhorn config show --json' lists them

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn config get' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn config': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn config get' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints one option as `config show` lists it, plus the path of the `.blowhorn.yaml` that holds it.

Response 200 of Print one option's effective value and which layer supplied it (a flag, the environment, the file or the built-in). With --json, the same row 'config show --json' carries for the option. fields
NameTypeDescription
accepts_number required boolean
builtin required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

by_platform array of object

The `pace` row only: the effective pace for every platform and the layer that supplied it (`pace_by_platform`, `defaults.pace`, `env`, `risk_profile` or `builtin`).

by_platform[].pace required string
by_platform[].platform required string
by_platform[].source required string
choices required array of string

The accepted values of an `enum` option; empty otherwise.

cli_flag required string

The flag that sets it for one run, or an empty string when it has none.

document string

Which document holds the option.

Values: config, private

document_path required string

The path of that document (`config get` only).

editable required boolean

Whether `config set` can store it.

effective required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

env required string

The environment variable that overrides it.

env_value required one of string, null, object

The raw override string when the variable is set, otherwise null.

hint required string
key required string

The option's short name, also its `env_overrides` key.

kind required string

Values: bool, enum, int, list, map, path, string

label required string
masked boolean

True for a secret option, whose values are {set: boolean}.

ok required any
path required string | null

The dotted path in the document (`defaults.headless`), or null for an option with no stored form.

source required string

The layer that supplied effective, such as config, env, builtin or risk_profile.

stored required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ConfigGetResponse

Variant fields
NameTypeDescription
accepts_number required boolean
builtin required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

by_platform array of object

The `pace` row only: the effective pace for every platform and the layer that supplied it (`pace_by_platform`, `defaults.pace`, `env`, `risk_profile` or `builtin`).

by_platform[].pace required string
by_platform[].platform required string
by_platform[].source required string
choices required array of string

The accepted values of an `enum` option; empty otherwise.

cli_flag required string

The flag that sets it for one run, or an empty string when it has none.

document string

Which document holds the option.

Values: config, private

document_path required string

The path of that document (`config get` only).

editable required boolean

Whether `config set` can store it.

effective required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

env required string

The environment variable that overrides it.

env_value required one of string, null, object

The raw override string when the variable is set, otherwise null.

hint required string
key required string

The option's short name, also its `env_overrides` key.

kind required string

Values: bool, enum, int, list, map, path, string

label required string
masked boolean

True for a secret option, whose values are {set: boolean}.

ok required any
path required string | null

The dotted path in the document (`defaults.headless`), or null for an option with no stored form.

source required string

The layer that supplied effective, such as config, env, builtin or risk_profile.

stored required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

POST /cli/config/path Print the effective global config file path.

Print the effective global config file path.

Request body required

The flags and arguments of `blowhorn config path`, one property per parameter.

Request body of Print the effective global config file path. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn config path' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn config': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn config path' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

The command prints human-readable text; it has no --json contract.

Human-readable stdout from a command that has no --json flag. Its wording is not a contract; parse nothing out of it.

Schema: CliTextOutput

POST /cli/config/set Set a dotted key in the global config file, keeping the file's comments, key order and unknown keys as written. A key the engine reads is stored in its own type (defaults.headless becomes a boolean, defaults.exclude a list even for one name, an empty value clears the key); for any other key, true/false become booleans and comma-delimited values become lists. A value that starts with a dash is read as an option unless you end the options first: write "blowhorn config set <key> -- <value>", which is also how a refused value gets the sentence saying what a valid one is.

Set a dotted key in the global config file, keeping the file's comments, key order and unknown keys as written. A key the engine reads is stored in its own type (defaults.headless becomes a boolean, defaults.exclude a list even for one name, an empty value clears the key); for any other key, true/false become booleans and comma-delimited values become lists. A value that starts with a dash is read as an option unless you end the options first: write "blowhorn config set <key> -- <value>", which is also how a refused value gets the sentence saying what a valid one is.

Request body required

The flags and arguments of `blowhorn config set`, one property per parameter.

Request body of Set a dotted key in the global config file, keeping the file's comments, key order and unknown keys as written. A key the engine reads is stored in its own type (defaults.headless becomes a boolean, defaults.exclude a list even for one name, an empty value clears the key); for any other key, true/false become booleans and comma-delimited values become lists. A value that starts with a dash is read as an option unless you end the options first: write "blowhorn config set <key> -- <value>", which is also how a refused value gets the sentence saying what a valid one is. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the option report as machine-readable JSON

Default: false

key required string

Dotted option key, defaults.<name>, as 'blowhorn config show --json' lists them

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn config set' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn config': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn config set' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

value string

Value to assign to the key; pass an explicit '' to clear a stored value

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the result of storing one option. A real write prints the whole document as `config show` does with the key written; a `--dry-run` prints only the key, the value that would be stored and the path.

ConfigSetDryRun

Variant fields
NameTypeDescription
dry_run required any
key required string
ok required any
path required string
value required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

ConfigSetWritten

Variant fields
NameTypeDescription
caps array of ConfigCapRow

The same daily-cap report `config show` prints.

caps[].action required string

The gate's action name; a user's `follow_per_day` is reported here as outreach.

Values: post, reaction, comment, share, outreach, accept

caps[].ceiling_per_day required integer

The built-in per-day ceiling for this platform and action.

caps[].effective_per_day required integer

The lower of the ceiling and the user's value (the ceiling when unset).

caps[].error string

Present only when `source` is `invalid`; names the unreadable `defaults.caps` entry.

caps[].platform required string

The platform's canonical name (x, linkedin, hn, ...).

caps[].source required string

`config` when the file sets the user's value, `none` when only the ceiling stands, `invalid` when `defaults.caps` cannot be read (the row then shows the ceiling and `error` says why; scheduled jobs are held until it is fixed).

Values: config, none, invalid

caps[].user_per_day required integer | null

The user's own per-day cap from `defaults.caps`, or null when unset.

changed_keys required array of string
created required boolean

Whether the write created the file.

dry_run required any
env_overrides object
exists required boolean
key required string
ok required any
options required array of ConfigOptionRow
options[].accepts_number required boolean
options[].builtin required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

options[].by_platform array of object

The `pace` row only: the effective pace for every platform and the layer that supplied it (`pace_by_platform`, `defaults.pace`, `env`, `risk_profile` or `builtin`).

options[].by_platform[].pace required string
options[].by_platform[].platform required string
options[].by_platform[].source required string
options[].choices required array of string

The accepted values of an `enum` option; empty otherwise.

options[].cli_flag required string

The flag that sets it for one run, or an empty string when it has none.

options[].document string

Which document holds the option.

Values: config, private

options[].document_path string

The path of that document (`config get` only).

options[].editable required boolean

Whether `config set` can store it.

options[].effective required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

options[].env required string

The environment variable that overrides it.

options[].env_value required one of string, null, object

The raw override string when the variable is set, otherwise null.

options[].hint required string
options[].key required string

The option's short name, also its `env_overrides` key.

options[].kind required string

Values: bool, enum, int, list, map, path, string

options[].label required string
options[].masked boolean

True for a secret option, whose values are {set: boolean}.

options[].ok any
options[].path required string | null

The dotted path in the document (`defaults.headless`), or null for an option with no stored form.

options[].source required string

The layer that supplied effective, such as config, env, builtin or risk_profile.

options[].stored required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

path required string
unknown_keys array of string
value required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

version integer | null

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ConfigSetResponse

POST /cli/config/show Display the current global config file contents. With --json, report every option .blowhorn.yaml honours instead: the value stored in the file, the environment override in force, the effective value and which of them supplied it, plus any keys under defaults the engine does not read.

Display the current global config file contents. With --json, report every option .blowhorn.yaml honours instead: the value stored in the file, the environment override in force, the effective value and which of them supplied it, plus any keys under defaults the engine does not read.

Request body required

The flags and arguments of `blowhorn config show`, one property per parameter.

Request body of Display the current global config file contents. With --json, report every option .blowhorn.yaml honours instead: the value stored in the file, the environment override in force, the effective value and which of them supplied it, plus any keys under defaults the engine does not read. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the option report as machine-readable JSON

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn config show' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn config': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn config show' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the global config document and every option in it: its stored, built-in and effective value, which layer supplied it, and the environment variable that overrides it.

Response 200 of Display the current global config file contents. With --json, report every option .blowhorn.yaml honours instead: the value stored in the file, the environment override in force, the effective value and which of them supplied it, plus any keys under defaults the engine does not read. fields
NameTypeDescription
caps required array of ConfigCapRow

Every platform action's daily cap: the built-in ceiling, the user's own value from `defaults.caps` when set, and the effective (lower) number.

caps[].action required string

The gate's action name; a user's `follow_per_day` is reported here as outreach.

Values: post, reaction, comment, share, outreach, accept

caps[].ceiling_per_day required integer

The built-in per-day ceiling for this platform and action.

caps[].effective_per_day required integer

The lower of the ceiling and the user's value (the ceiling when unset).

caps[].error string

Present only when `source` is `invalid`; names the unreadable `defaults.caps` entry.

caps[].platform required string

The platform's canonical name (x, linkedin, hn, ...).

caps[].source required string

`config` when the file sets the user's value, `none` when only the ceiling stands, `invalid` when `defaults.caps` cannot be read (the row then shows the ceiling and `error` says why; scheduled jobs are held until it is fixed).

Values: config, none, invalid

caps[].user_per_day required integer | null

The user's own per-day cap from `defaults.caps`, or null when unset.

env_overrides required object

Option key to the environment variable that overrides it.

exists required boolean
ok required any
options required array of ConfigOptionRow
options[].accepts_number required boolean
options[].builtin required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

options[].by_platform array of object

The `pace` row only: the effective pace for every platform and the layer that supplied it (`pace_by_platform`, `defaults.pace`, `env`, `risk_profile` or `builtin`).

options[].by_platform[].pace required string
options[].by_platform[].platform required string
options[].by_platform[].source required string
options[].choices required array of string

The accepted values of an `enum` option; empty otherwise.

options[].cli_flag required string

The flag that sets it for one run, or an empty string when it has none.

options[].document string

Which document holds the option.

Values: config, private

options[].document_path string

The path of that document (`config get` only).

options[].editable required boolean

Whether `config set` can store it.

options[].effective required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

options[].env required string

The environment variable that overrides it.

options[].env_value required one of string, null, object

The raw override string when the variable is set, otherwise null.

options[].hint required string
options[].key required string

The option's short name, also its `env_overrides` key.

options[].kind required string

Values: bool, enum, int, list, map, path, string

options[].label required string
options[].masked boolean

True for a secret option, whose values are {set: boolean}.

options[].ok any
options[].path required string | null

The dotted path in the document (`defaults.headless`), or null for an option with no stored form.

options[].source required string

The layer that supplied effective, such as config, env, builtin or risk_profile.

options[].stored required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

path required string

The config file read.

unknown_keys required array of string

Dotted `defaults.*` keys the document holds that no option describes.

version required integer | null

The document's `version`, or null when it has none (or does not exist).

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ConfigShowResponse

Variant fields
NameTypeDescription
caps required array of ConfigCapRow

Every platform action's daily cap: the built-in ceiling, the user's own value from `defaults.caps` when set, and the effective (lower) number.

caps[].action required string

The gate's action name; a user's `follow_per_day` is reported here as outreach.

Values: post, reaction, comment, share, outreach, accept

caps[].ceiling_per_day required integer

The built-in per-day ceiling for this platform and action.

caps[].effective_per_day required integer

The lower of the ceiling and the user's value (the ceiling when unset).

caps[].error string

Present only when `source` is `invalid`; names the unreadable `defaults.caps` entry.

caps[].platform required string

The platform's canonical name (x, linkedin, hn, ...).

caps[].source required string

`config` when the file sets the user's value, `none` when only the ceiling stands, `invalid` when `defaults.caps` cannot be read (the row then shows the ceiling and `error` says why; scheduled jobs are held until it is fixed).

Values: config, none, invalid

caps[].user_per_day required integer | null

The user's own per-day cap from `defaults.caps`, or null when unset.

env_overrides required object

Option key to the environment variable that overrides it.

exists required boolean
ok required any
options required array of ConfigOptionRow
options[].accepts_number required boolean
options[].builtin required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

options[].by_platform array of object

The `pace` row only: the effective pace for every platform and the layer that supplied it (`pace_by_platform`, `defaults.pace`, `env`, `risk_profile` or `builtin`).

options[].by_platform[].pace required string
options[].by_platform[].platform required string
options[].by_platform[].source required string
options[].choices required array of string

The accepted values of an `enum` option; empty otherwise.

options[].cli_flag required string

The flag that sets it for one run, or an empty string when it has none.

options[].document string

Which document holds the option.

Values: config, private

options[].document_path string

The path of that document (`config get` only).

options[].editable required boolean

Whether `config set` can store it.

options[].effective required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

options[].env required string

The environment variable that overrides it.

options[].env_value required one of string, null, object

The raw override string when the variable is set, otherwise null.

options[].hint required string
options[].key required string

The option's short name, also its `env_overrides` key.

options[].kind required string

Values: bool, enum, int, list, map, path, string

options[].label required string
options[].masked boolean

True for a secret option, whose values are {set: boolean}.

options[].ok any
options[].path required string | null

The dotted path in the document (`defaults.headless`), or null for an option with no stored form.

options[].source required string

The layer that supplied effective, such as config, env, builtin or risk_profile.

options[].stored required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

path required string

The config file read.

unknown_keys required array of string

Dotted `defaults.*` keys the document holds that no option describes.

version required integer | null

The document's `version`, or null when it has none (or does not exist).

Github

POST /cli/github/squatters Sweep a repository's assigned-but-stalled issues through GitHub's GraphQL API.

Sweep a repository's assigned-but-stalled issues through GitHub's GraphQL API.

For each assignee whose silence -- measured from the later of their assignment date and their last activity (a comment, a linked PR, a status change) -- reaches --nudge-after-days (default 14), the action posts a fixed-template nudge; --warn-after-days (default 7) later with still no reply it posts a final warning; --unassign-after-days (default 7) after that it removes the assignee and posts a closing comment. Issues with an open linked PR from the assignee, blocked or waiting-on-maintainer labels, security labels, or an assignee who is the issue's own author are never bumped. A dry run is the default: without --apply the sweep prints exactly what it would do and writes nothing. Immediately before any write the issue is re-read, so a run never unassigns someone who just became active. No CLI is required: the sweep talks to GitHub's GraphQL API over HTTPS with the store's GitHub token, and a token that is missing or unauthenticated is a hard error, never a silent fallback.

Request body required

The flags and arguments of `blowhorn github squatters`, one property per parameter.

Request body of Sweep a repository's assigned-but-stalled issues through GitHub's GraphQL API. fields
NameTypeDescription
apply boolean

Perform the writes: post the nudge, warning, or closing comment and remove the assignment. Without --apply the run is a dry-run preview and writes nothing

Default: false

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

limit integer

Maximum open issues examined in the run (default 100)

Default: 100

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

nudge_after_days integer

Days of assignee silence before a nudge is posted (default 14)

Default: 14

pace string

Accepted by 'blowhorn github squatters' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn github'. One of: github, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

repo string

Repository to sweep, as owner/name

risk_profile string

Accepted by 'blowhorn github squatters' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

unassign_after_days integer

Days after the final warning with still no reply before the assignment is released (default 7)

Default: 7

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

warn_after_days integer

Days after a nudge with still no reply before the final warning (default 7)

Default: 7

Responses

200

The command prints human-readable text; it has no --json contract.

Human-readable stdout from a command that has no --json flag. Its wording is not a contract; parse nothing out of it.

Schema: CliTextOutput

Chrome

POST /cli/chrome/active-tab Ask the Chrome extension for the front tab's address and title, through the bridge, and print it. The popover's New order asks here first: it retries briefly and never uses Apple Events, so macOS never prompts for Chrome. If the live read has no tab, a reachable extension may return its last remembered normal-window HTTP(S) tab with stale and age fields. This command navigates nowhere, probes no tab's page, and contacts no platform: it reads only the front tab's own address and title, from the Chrome profile whose bridge answers first. No reachable extension or no live or remembered tab answers with no tab, and --json carries connected and a detail saying why. That read writes nothing but the bridge lock, the same lock `chrome extension-status` writes. --json emits the answer as one object, for a script or the desktop app.

Ask the Chrome extension for the front tab's address and title, through the bridge, and print it. The popover's New order asks here first: it retries briefly and never uses Apple Events, so macOS never prompts for Chrome. If the live read has no tab, a reachable extension may return its last remembered normal-window HTTP(S) tab with stale and age fields. This command navigates nowhere, probes no tab's page, and contacts no platform: it reads only the front tab's own address and title, from the Chrome profile whose bridge answers first. No reachable extension or no live or remembered tab answers with no tab, and --json carries connected and a detail saying why. That read writes nothing but the bridge lock, the same lock `chrome extension-status` writes. --json emits the answer as one object, for a script or the desktop app.

Request body required

The flags and arguments of `blowhorn chrome active-tab`, one property per parameter.

Request body of Ask the Chrome extension for the front tab's address and title, through the bridge, and print it. The popover's New order asks here first: it retries briefly and never uses Apple Events, so macOS never prompts for Chrome. If the live read has no tab, a reachable extension may return its last remembered normal-window HTTP(S) tab with stale and age fields. This command navigates nowhere, probes no tab's page, and contacts no platform: it reads only the front tab's own address and title, from the Chrome profile whose bridge answers first. No reachable extension or no live or remembered tab answers with no tab, and --json carries connected and a detail saying why. That read writes nothing but the bridge lock, the same lock `chrome extension-status` writes. --json emits the answer as one object, for a script or the desktop app. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn chrome active-tab' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome active-tab' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The front Chrome tab's address and title, as the Blowhorn extension answers them through the bridge. `tab` is null when no Chrome with the extension answered a front tab; `connected` says whether any bridge socket answered at all, and `detail` names the failure, so a caller can tell "no Chrome with the extension" from "Chrome is up but no tab is front".

Response 200 of Ask the Chrome extension for the front tab's address and title, through the bridge, and print it. The popover's New order asks here first: it retries briefly and never uses Apple Events, so macOS never prompts for Chrome. If the live read has no tab, a reachable extension may return its last remembered normal-window HTTP(S) tab with stale and age fields. This command navigates nowhere, probes no tab's page, and contacts no platform: it reads only the front tab's own address and title, from the Chrome profile whose bridge answers first. No reachable extension or no live or remembered tab answers with no tab, and --json carries connected and a detail saying why. That read writes nothing but the bridge lock, the same lock `chrome extension-status` writes. --json emits the answer as one object, for a script or the desktop app. fields
NameTypeDescription
age_ms required integer | null

Age of the remembered tab in milliseconds, or null when the tab is live or unavailable.

connected required boolean
detail required string | null
ok required any
stale required boolean

True when the extension returned its last remembered normal-window HTTP(S) tab because the live read had no answer.

tab required one of null, object

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeActiveTabResponse

Variant fields
NameTypeDescription
age_ms required integer | null

Age of the remembered tab in milliseconds, or null when the tab is live or unavailable.

connected required boolean
detail required string | null
ok required any
stale required boolean

True when the extension returned its last remembered normal-window HTTP(S) tab because the live read had no answer.

tab required one of null, object
POST /cli/chrome/attach-chrome Start or report a dedicated Chrome that is open to remote debugging with no Allow dialog, so '--chrome-launch cdp' can attach to a real Chrome unattended. Chrome 136 and later ignore '--remote-debugging-port' on the default data directory, which is why this is a second Chrome on a tree of its own (~/.blowhorn/attach-chrome, on a port Chrome picks and records, bound to 127.0.0.1) and never your daily one. That port has no password and asks for no Allow, and loopback is shared by every macOS user on this Mac, so any other user signed in here can control that Chrome, its cookies and signed-in sessions included. That is why it is off by default: 'start' and 'install-agent' refuse without --allow-other-users. Sign it in only to the accounts Blowhorn posts from.

Start or report a dedicated Chrome that is open to remote debugging with no Allow dialog, so '--chrome-launch cdp' can attach to a real Chrome unattended. Chrome 136 and later ignore '--remote-debugging-port' on the default data directory, which is why this is a second Chrome on a tree of its own (~/.blowhorn/attach-chrome, on a port Chrome picks and records, bound to 127.0.0.1) and never your daily one. That port has no password and asks for no Allow, and loopback is shared by every macOS user on this Mac, so any other user signed in here can control that Chrome, its cookies and signed-in sessions included. That is why it is off by default: 'start' and 'install-agent' refuse without --allow-other-users. Sign it in only to the accounts Blowhorn posts from.

'status' (the default) reads that tree's 'Local State', 'DevToolsActivePort' and 'SingletonLock' and nothing else -- no browser opens, nothing connects -- and its last line says what to do next. 'signin' opens the tree with no remote debugging at all, for the one-time sign-in to each account Blowhorn posts from, because some sites (Google among them) refuse to sign in inside a browser started with debugging on; quit that Chrome when you are done and the sign-ins carry over. 'stop' quits the Chrome holding the tree the way Cmd-Q does (DevTools Browser.close, so fresh sign-ins are saved), waits, then sends SIGTERM; it signals only that Chrome and never SIGKILLs. 'install-agent' adds a per-user LaunchAgent (ai.blowhorn.attach-chrome) that runs 'start' once at each login, so scheduler ticks find it up, and 'uninstall-agent' removes it (the Chrome keeps running). 'start' launches Chrome on the tree, detached, with the port open, and prints the steps that finish the setup: point chrome.data_dir at the tree, map your profiles, then run with '--chrome-launch cdp'. It writes no configuration of its own. The tree is fixed and Chrome picks the port, so its one option of its own is the opt-in. A recorded port is evidence a Chrome is up on that tree, not proof a connect would succeed, and it counts only while a live Chrome holds the tree. Starting is refused when the tree is Google Chrome's own default data directory (never the one chrome.data_dir names, which step 1 above points at this tree), when a live Chrome already has a port recorded there, and while the tree is open without debugging (the signin window): quit that Chrome first. A port file a crashed Chrome left behind is cleared, not refused. --dry-run prints the command line and starts nothing.

Request body required

The flags and arguments of `blowhorn chrome attach-chrome`, one property per parameter.

Request body of Start or report a dedicated Chrome that is open to remote debugging with no Allow dialog, so '--chrome-launch cdp' can attach to a real Chrome unattended. Chrome 136 and later ignore '--remote-debugging-port' on the default data directory, which is why this is a second Chrome on a tree of its own (~/.blowhorn/attach-chrome, on a port Chrome picks and records, bound to 127.0.0.1) and never your daily one. That port has no password and asks for no Allow, and loopback is shared by every macOS user on this Mac, so any other user signed in here can control that Chrome, its cookies and signed-in sessions included. That is why it is off by default: 'start' and 'install-agent' refuse without --allow-other-users. Sign it in only to the accounts Blowhorn posts from. fields
NameTypeDescription
action string

'status' (the default) reports the tree without starting anything; 'signin' opens it with no remote debugging for the one-time sign-in; 'start' launches Chrome on it with the port open; 'stop' quits that Chrome cleanly; 'install-agent' / 'uninstall-agent' manage the LaunchAgent that starts it at login

Values: status, start, signin, stop, install-agent, uninstall-agent

Default: "status"

allow_other_users boolean

Required by 'start' and 'install-agent': accept that the debugging port they open has no password and that any other macOS user signed in on this Mac can control that Chrome through it

Default: false

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn chrome attach-chrome' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome attach-chrome' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the dedicated attach-chrome tree (a second Chrome with the debugging port open, never the daily one) or what an action did to it. `status`, `start`, `signin` and `stop --dry-run` report the tree; a real `stop` reports the Chrome it quit; `install-agent` and `uninstall-agent` report the login LaunchAgent.

ChromeAttachChromeTree

Variant fields
NameTypeDescription
app_path required string | null
argv array of string

The command line that starts (or would start) Chrome; present on `start` and `signin`.

data_dir required string
debugging any

Present on `signin`, which opens the tree with no debugging port.

devtools_port required ChromeFileCheck

What one of Chrome's files (`Local State`, `DevToolsActivePort`) said, or why it could not be read.

devtools_port.detail required string | null

Why the file is not `ok`; null when it is.

devtools_port.path required string
devtools_port.state required string

Values: ok, missing, unreadable, malformed

dry_run boolean

Present on `start`, `signin` and `stop`.

exists required boolean
held_without_debugging required boolean

True when a live Chrome holds the tree with no debugging port open.

holder_pid required integer | null

The live process holding the tree's SingletonLock, if any.

installed required boolean

Whether Google Chrome is installed.

last_used required string | null
listening required boolean
local_state required ChromeFileCheck

What one of Chrome's files (`Local State`, `DevToolsActivePort`) said, or why it could not be read.

local_state.detail required string | null

Why the file is not `ok`; null when it is.

local_state.path required string
local_state.state required string

Values: ok, missing, unreadable, malformed

minimum_version required integer

The oldest Chrome major Blowhorn supports.

next_step required string | null
next_steps array of string

What to do after a real `start`, or after `signin`.

ok required any
plan string

What a real `stop` would do; present on `stop --dry-run`.

profile_count required integer
recorded_port required integer | null
remote_debugging required ChromeRemoteDebugging

Whether Chrome's remote-debugging server is up, read from `DevToolsActivePort`. Every field is null when the file said nothing.

remote_debugging.browser_path required string | null
remote_debugging.enabled required boolean | null
remote_debugging.port required integer | null
stale_port required boolean

True when a port is recorded but no live Chrome holds the tree.

started boolean

Present on `status`, `start` and `signin`; true only after a real `start` or `signin`.

stopped any

Present on `stop --dry-run`, which signals nothing.

supported required boolean | null

Whether version is at least minimum_version; null when the version is unknown.

version required string | null

The Chrome app bundle's CFBundleShortVersionString; null when it could not be read.

ChromeAttachChromeStopped

Variant fields
NameTypeDescription
data_dir required string
detail required string
dry_run required any
method required enum

Values: browser_close, sigterm, none

ok required any
pid required integer | null

The Chrome that was asked to quit; null when nothing held the tree.

stopped required any

ChromeAttachChromeAgentInstalled

Variant fields
NameTypeDescription
dry_run required boolean
installed required boolean

True only after a real `install-agent`.

next_steps required array of string
ok required any
path required string
plist required object

The LaunchAgent written (or that would be written) to `path`.

plist.AbandonProcessGroup required boolean
plist.AssociatedBundleIdentifiers required array of string
plist.EnvironmentVariables required object
plist.Label required string
plist.ProgramArguments required array of string
plist.RunAtLoad required boolean
plist.StandardErrorPath required string
plist.StandardOutPath required string
plist.WorkingDirectory required string

ChromeAttachChromeAgentRemoved

Variant fields
NameTypeDescription
dry_run required boolean
ok required any
path required string
removed required boolean

True when a LaunchAgent file was removed.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeAttachChromeResponse

POST /cli/chrome/confirm Apply the confirm screen's confirmed choices: turn Chrome profiles into Blowhorn profiles, attach the discovered platform accounts, and map each Chrome profile -- the one write onboarding makes, and the person's confirm is the consent it records. The choices arrive as one JSON document on stdin (--payload-stdin), so a GitHub token or a Bluesky app password in it reaches neither argv nor the daily log:

Apply the confirm screen's confirmed choices: turn Chrome profiles into Blowhorn profiles, attach the discovered platform accounts, and map each Chrome profile -- the one write onboarding makes, and the person's confirm is the consent it records. The choices arrive as one JSON document on stdin (--payload-stdin), so a GitHub token or a Bluesky app password in it reaches neither argv nor the daily log:

{"assignments": [{"directory": "Profile 3", "profile": "ada-example", "create": true, "userId": "<the member's uuid, as 'org members' lists them; omit it for a brand>", "platforms": {"linkedin": {"attach": true, "session": true}, "x": {"attach": true}, "github": {"attach": true, "token": "..."}, "bluesky": {"attach": true, "app_password": "..."}, "slack": {"attach": true}}}]}

Per assignment, in order: the Blowhorn profile (an existing one the payload names by 'profileId' or by 'profile', the one the directory is already mapped to, or, with "create": true, a new one created in your Blowhorn account representing the member 'userId' names, or nobody for a brand; a name the payload omits is proposed from the Google name, and a profile the organization does not have is never created without "create"); then the attached platforms' credentials, stored in your Blowhorn account and nowhere on this computer; then the mapping, exactly as 'chrome map' records it and, when the extension is already on in that Chrome profile, paired the same way, opening the extension's pairing page there, since a run refuses a mapping the extension cannot prove; a profile without the extension on yet is paired once it is (the desktop's setup does this, or 'chrome map'); then each attached platform's username as the profile's account there: a connection registered and linked to the profile, named for the username and pointing at its account page when the username is a handle, which is where the profile's handles are read from. A platform row the payload marks with "session": true (LinkedIn, Hacker News) stores a confirmed-session credential naming this Chrome profile directory instead of a password: it satisfies eligibility the way a session-capture platform's identity key does, because the run checks that Chrome session before it reaches for a password. GitHub stores GH_USERNAME and still waits for GH_TOKEN; Bluesky stores the handle and still waits for the app password; both may ride in the same payload. Slack stores nothing: its session is captured afterwards by 'profile auth --platform slack', and the result says so.

A profile the document marks with "in_use": false is recorded not in use: this computer's mapping for that directory is removed, the Blowhorn profile is added to defaults.exclude when that name is not already there, and the profile and its credentials are kept. A platform row with "attach": false is recorded not in use for that profile in .blowhorn.chrome-not-in-use.json beside the config file; a platform left out of the assignment is left as it was recorded. The credential is kept, and a later run on this computer skips a platform still recorded not in use. Marking a platform in use clears that skip for the Blowhorn profile on every Chrome directory in the record, so a later choice in the same document wins. Marking the profile in use again removes it from defaults.exclude only when this screen added that name; a name already in the list is left as written, spelling and notes included. Omitting "in_use" applies only the attached rows, as before.

Each assignment answers for itself in the result -- profile, whether it was created, the keys written per platform, the mapping, the accounts recorded, or the one-sentence refusal -- so a partial confirm is a successful confirm: what landed stays landed, and what refused says why, by name. The exit code is 0 when at least one assignment landed and 1 when none did; a store that cannot be reached is the named cause and exit 3. With --dry-run every check runs and nothing is written, created or mapped.

Request body required

The JSON document `chrome confirm --payload-stdin` reads. One assignment per Chrome profile the person decided about. `in_use: false` records that Chrome profile as not in use. A platform object with `attach: false` records that platform as not in use on the Blowhorn profile. Neither choice deletes a profile or a credential. A GitHub token and a Bluesky app password travel in this request and are never echoed. Only `assignments` and, in each assignment, `directory` are required: the handler defaults every other field when it is omitted, and it ignores fields it does not name, at every level of the document. The one exception is an assignment's `email` or `subject`, which the handler refuses by name: they named a profile's person before `userId` did, and read as absent they would create a brand where the caller meant a person.

Request body of Apply the confirm screen's confirmed choices: turn Chrome profiles into Blowhorn profiles, attach the discovered platform accounts, and map each Chrome profile -- the one write onboarding makes, and the person's confirm is the consent it records. The choices arrive as one JSON document on stdin (--payload-stdin), so a GitHub token or a Bluesky app password in it reaches neither argv nor the daily log: fields
NameTypeDescription
assignments required array of object
assignments[].create boolean

Create the profile `profile` names (or the one proposed from the Chrome profile's name) in your Blowhorn account. A profile the organization does not have is never created without it.

assignments[].directory required string

The Chrome profile directory, as `chrome profiles` prints it.

assignments[].in_use boolean

False records this Chrome profile as not in use. Omitted means in use, so an older document that only lists chosen rows still applies those rows.

assignments[].platforms object

Platform id to the choice for that platform. A platform left out is not changed. Omitted means no platform is changed.

assignments[].profile string

An existing Blowhorn profile by name, or the name of the profile to create when `create` is set.

assignments[].profileId string (uuid)

An existing Blowhorn profile, by its id as `profile list` prints it. Never with `create`.

assignments[].userId string (uuid)

Create only. The member of the organization the new profile represents, as `org members` lists them. Omitted creates a brand.

Responses

200

What the command prints to stdout with --json when it exits 0.

One result per assignment `chrome confirm` was given on stdin, in the document's order: the Blowhorn profile each Chrome profile now posts as, whether confirm created it, the credential keys it wrote per platform (key names only, never a value), and how the Chrome profile mapping stands. `ok` is true when at least one assignment landed; a refused assignment carries its reason in its own row and does not stop the others. The assignments document itself, typed secrets included, arrives on stdin and is never echoed back.

Response 200 of Apply the confirm screen's confirmed choices: turn Chrome profiles into Blowhorn profiles, attach the discovered platform accounts, and map each Chrome profile -- the one write onboarding makes, and the person's confirm is the consent it records. The choices arrive as one JSON document on stdin (--payload-stdin), so a GitHub token or a Bluesky app password in it reaches neither argv nor the daily log: fields
NameTypeDescription
dry_run required boolean
host required string

The machine host the mappings are keyed by.

ok required boolean

True when at least one assignment landed.

results required array of object
results[].accounts required array of SessionAccount

The accounts this assignment recorded in your Blowhorn account - each attached platform's username, then the browser sign-ins it attached; empty for a dry run, a refusal, or a store that keeps no accounts.

results[].accounts[].account required one of ProfileAccount, null
results[].accounts[].outcome required enum

none - not signed in and no account is mapped, so nothing was written.

Values: created, updated, reconnected, disconnected, unchanged, none

results[].accounts[].platform required enum

Values: linkedin, x, reddit, hn, github, bluesky

results[].accounts[].profile required string
results[].created required boolean

Confirm created the profile in your Blowhorn account in this run.

results[].directory required string

The Chrome profile directory the assignment named.

results[].error required string | null

Why this assignment was refused, or null when it landed.

results[].mapped required one of enum, null

mapped - the mapping was written now; already - it already named this profile; would map - a dry run's answer; null when the assignment was refused before mapping, or when the Chrome profile was recorded not in use and no mapping was written.

results[].notes required array of string

Follow-ups the write could not finish itself, such as capturing a Slack session interactively.

results[].ok required boolean
results[].pairing required one of object, null

The pairing a mapping starts, or a mapping confirmed again re-starts, as 'chrome map' reports it, when the extension is already on in that Chrome profile; null when nothing was mapped (including a dry run) or the extension is not on there yet, which the desktop's setup pairs once it is (or 'chrome map'). When the page could not open, a note says how to pair.

results[].profile required string | null

The Blowhorn profile the Chrome profile posts as; null when the assignment was refused before a profile was settled.

results[].written required object

Credential key names written, per platform - names only, never a value.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeConfirmResponse

Variant fields
NameTypeDescription
dry_run required boolean
host required string

The machine host the mappings are keyed by.

ok required boolean

True when at least one assignment landed.

results required array of object
results[].accounts required array of SessionAccount

The accounts this assignment recorded in your Blowhorn account - each attached platform's username, then the browser sign-ins it attached; empty for a dry run, a refusal, or a store that keeps no accounts.

results[].accounts[].account required one of ProfileAccount, null
results[].accounts[].outcome required enum

none - not signed in and no account is mapped, so nothing was written.

Values: created, updated, reconnected, disconnected, unchanged, none

results[].accounts[].platform required enum

Values: linkedin, x, reddit, hn, github, bluesky

results[].accounts[].profile required string
results[].created required boolean

Confirm created the profile in your Blowhorn account in this run.

results[].directory required string

The Chrome profile directory the assignment named.

results[].error required string | null

Why this assignment was refused, or null when it landed.

results[].mapped required one of enum, null

mapped - the mapping was written now; already - it already named this profile; would map - a dry run's answer; null when the assignment was refused before mapping, or when the Chrome profile was recorded not in use and no mapping was written.

results[].notes required array of string

Follow-ups the write could not finish itself, such as capturing a Slack session interactively.

results[].ok required boolean
results[].pairing required one of object, null

The pairing a mapping starts, or a mapping confirmed again re-starts, as 'chrome map' reports it, when the extension is already on in that Chrome profile; null when nothing was mapped (including a dry run) or the extension is not on there yet, which the desktop's setup pairs once it is (or 'chrome map'). When the page could not open, a note says how to pair.

results[].profile required string | null

The Blowhorn profile the Chrome profile posts as; null when the assignment was refused before a profile was settled.

results[].written required object

Credential key names written, per platform - names only, never a value.

POST /cli/chrome/create-profile Give Blowhorn a Google Chrome profile of its own, for a Chrome that has only its main profile. Asks Chrome to open a new window in the profile directory 'Blowhorn'. Chrome creates that profile the first time and opens it again every time after, so running this twice makes no second one. Chrome gives the new profile a default name such as 'Person 1', which Blowhorn shows as Blowhorn's profile. Sign in there to LinkedIn, X and the other sites Blowhorn should use, then 'blowhorn chrome discover' finds the profile and its accounts. Reads Local State only to say whether the profile was already listed; writes nothing, maps nothing, and drives nothing in the window. If no Chrome is running, Chrome starts to open the window and is left running. Your own profiles, windows and tabs are left as they are.

Give Blowhorn a Google Chrome profile of its own, for a Chrome that has only its main profile. Asks Chrome to open a new window in the profile directory 'Blowhorn'. Chrome creates that profile the first time and opens it again every time after, so running this twice makes no second one. Chrome gives the new profile a default name such as 'Person 1', which Blowhorn shows as Blowhorn's profile. Sign in there to LinkedIn, X and the other sites Blowhorn should use, then 'blowhorn chrome discover' finds the profile and its accounts. Reads Local State only to say whether the profile was already listed; writes nothing, maps nothing, and drives nothing in the window. If no Chrome is running, Chrome starts to open the window and is left running. Your own profiles, windows and tabs are left as they are.

Request body required

The flags and arguments of `blowhorn chrome create-profile`, one property per parameter.

Request body of Give Blowhorn a Google Chrome profile of its own, for a Chrome that has only its main profile. Asks Chrome to open a new window in the profile directory 'Blowhorn'. Chrome creates that profile the first time and opens it again every time after, so running this twice makes no second one. Chrome gives the new profile a default name such as 'Person 1', which Blowhorn shows as Blowhorn's profile. Sign in there to LinkedIn, X and the other sites Blowhorn should use, then 'blowhorn chrome discover' finds the profile and its accounts. Reads Local State only to say whether the profile was already listed; writes nothing, maps nothing, and drives nothing in the window. If no Chrome is running, Chrome starts to open the window and is left running. Your own profiles, windows and tabs are left as they are. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn chrome create-profile' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome create-profile' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What chrome create-profile prints with --json: the Chrome profile directory Blowhorn asked Google Chrome to open a window in, whether Local State already listed it, and whether Chrome was asked to open it. Chrome creates a new profile for a directory it does not know and opens the existing one otherwise, so a repeat never makes a second profile; that was probed live on 2026-10-28 (Google Chrome 156 on macOS). Nothing after the open is read back, so whether Chrome created the profile on a given run is not claimed. A refusal (Google Chrome not installed) prints a Problem instead.

Response 200 of Give Blowhorn a Google Chrome profile of its own, for a Chrome that has only its main profile. Asks Chrome to open a new window in the profile directory 'Blowhorn'. Chrome creates that profile the first time and opens it again every time after, so running this twice makes no second one. Chrome gives the new profile a default name such as 'Person 1', which Blowhorn shows as Blowhorn's profile. Sign in there to LinkedIn, X and the other sites Blowhorn should use, then 'blowhorn chrome discover' finds the profile and its accounts. Reads Local State only to say whether the profile was already listed; writes nothing, maps nothing, and drives nothing in the window. If no Chrome is running, Chrome starts to open the window and is left running. Your own profiles, windows and tabs are left as they are. fields
NameTypeDescription
already_listed required boolean | null

Local State listed the directory before this call; null when Local State could not be read.

detail required string | null

Why the window did not open ('chrome could not open blowhorn's chrome profile: <reason>'), or the dry-run note; null when it opened.

directory required string

The Chrome profile directory Blowhorn works in, as chrome profiles prints it once Chrome has created it.

dry_run required boolean
ok required boolean

Chrome was asked to open the window (always true on a dry run); false with exit 1 when it could not be asked.

opened required boolean

Chrome was asked to open the window in this profile.

url required string

The page the window opened on.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeCreateProfileResponse

Variant fields
NameTypeDescription
already_listed required boolean | null

Local State listed the directory before this call; null when Local State could not be read.

detail required string | null

Why the window did not open ('chrome could not open blowhorn's chrome profile: <reason>'), or the dry-run note; null when it opened.

directory required string

The Chrome profile directory Blowhorn works in, as chrome profiles prints it once Chrome has created it.

dry_run required boolean
ok required boolean

Chrome was asked to open the window (always true on a dry run); false with exit 1 when it could not be asked.

opened required boolean

Chrome was asked to open the window in this profile.

url required string

The page the window opened on.

POST /cli/chrome/discover Report, for every Chrome profile, the platform accounts discovery can see there -- the read behind onboarding's confirm screen. For each profile: its directory, display name and signed-in Google account (from 'Local State', as 'chrome profiles' reads them), the Blowhorn profile it is mapped to, the platform rows discovery found, a proposed Blowhorn profile slug, whether that slug is free, and how the Google email resolves against the organization's Blowhorn users.

Report, for every Chrome profile, the platform accounts discovery can see there -- the read behind onboarding's confirm screen. For each profile: its directory, display name and signed-in Google account (from 'Local State', as 'chrome profiles' reads them), the Blowhorn profile it is mapped to, the platform rows discovery found, a proposed Blowhorn profile slug, whether that slug is free, and how the Google email resolves against the organization's Blowhorn users.

A platform row is a saved-login username and whether a session is live. The usernames come from the profile's 'Login Data' and 'Login Data For Account', for an allowlist of hosts only (linkedin.com, x.com and twitter.com, reddit.com, news.ycombinator.com, github.com, bsky.app and bsky.social, slack.com): only the realm and the username are ever selected -- the password column is a blob Chrome encrypts, and it is never selected, decrypted or copied. Whether a session is live comes from cookie names in 'Cookies' or 'Network/Cookies': presence only, never a value, except GitHub's 'dotcom_user', whose value is the login name itself and so is the GitHub username when a session is live. Hacker News's 'user' cookie is not read at all: its value bundles the username with a session secret. History and LevelDB are never opened -- a visit is not an identity. No browser opens, and a file Chrome holds locked reads as 'not found', never as an error. Nothing is written to Chrome. For each Chrome profile mapped here and in use, whose identity holds, a LinkedIn, X or Reddit sign-in it is picked for is recorded as that profile's account in your Blowhorn account, and a sign-in that ended marks the account disconnected; nothing is deleted, and --local and --dry-run record nothing. A not-in-use row for a directory Chrome no longer has is dropped from the record beside the config file.

The resolution answers the create rule without creating: 'one' (the email names exactly one Blowhorn user in the organization; that user's Cloud social links ride along, shown on the confirm screen but never treated as permission to post), 'several' (the member candidates, for a picker), 'none' (no member holds the email), or 'no-email' (nobody is signed in to that Chrome profile). --json emits the whole survey with a stable shape, for the desktop app. A store that cannot be reached is the named cause and exit 3; an unreadable profile list is one sentence and exit 1.

--local reads Chrome's files only and never opens the store, so it works before sign-in: every row's mapping is null, its resolution is 'unresolved' (or 'no-email') and its slug state 'unchecked'. The desktop app runs it when it first opens, then merges the full survey once you are signed in. Chrome's Guest and System profiles are never listed.

Request body required

The flags and arguments of `blowhorn chrome discover`, one property per parameter.

Request body of Report, for every Chrome profile, the platform accounts discovery can see there -- the read behind onboarding's confirm screen. For each profile: its directory, display name and signed-in Google account (from 'Local State', as 'chrome profiles' reads them), the Blowhorn profile it is mapped to, the platform rows discovery found, a proposed Blowhorn profile slug, whether that slug is free, and how the Google email resolves against the organization's Blowhorn users. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

local boolean

Read Chrome's files only, without the store: works before sign-in, resolves nobody

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn chrome discover' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome discover' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The `chrome profiles` rows, plus what setup's confirm step needs for each: the platform accounts discovery found in that Chrome profile (a saved-login username, or the live GitHub username; a signed-in flag from a session cookie's presence, never its value), how the profile's Google email resolves under the profile create rule, and the profile name a create would propose. Nothing is written to Chrome. For each Chrome profile mapped here, in use, whose identity holds, the LinkedIn, X and Reddit sign-ins it is picked for are recorded as the profile's accounts in your Blowhorn account (`accounts`); `--local` and `--dry-run` record nothing. A not-in-use row whose directory Chrome no longer has is dropped. Account names are real people's; never commit a real output.

Response 200 of Report, for every Chrome profile, the platform accounts discovery can see there -- the read behind onboarding's confirm screen. For each profile: its directory, display name and signed-in Google account (from 'Local State', as 'chrome profiles' reads them), the Blowhorn profile it is mapped to, the platform rows discovery found, a proposed Blowhorn profile slug, whether that slug is free, and how the Google email resolves against the organization's Blowhorn users. fields
NameTypeDescription
account_errors array of string

One sentence per account the store refused to record, naming the profile and platform.

accounts array of SessionAccount

What this survey recorded in your Blowhorn account, one entry per profile and browser platform it checked.

accounts[].account required one of ProfileAccount, null
accounts[].outcome required enum

none - not signed in and no account is mapped, so nothing was written.

Values: created, updated, reconnected, disconnected, unchanged, none

accounts[].platform required enum

Values: linkedin, x, reddit, hn, github, bluesky

accounts[].profile required string
data_dir required string
host required string

The machine host the mappings are keyed by.

last_used required string | null

Directory of the profile Chrome last used.

local boolean

True for `--local`, which reads Chrome's files only, never the store, so nobody is resolved or mapped.

local_state required object
local_state.detail required string | null
local_state.path required string
local_state.state required string
ok required any
profile_count required integer
profiles required array of object
profiles[].account required string

Signed-in Google account email, empty when nobody is signed in.

profiles[].directory required string
profiles[].display_name required string
profiles[].gaia_name required string
profiles[].identity required string | null

Whether the recorded identity of the mapping still holds (for example ok or drift); null when unmapped.

profiles[].in_use boolean

False when this Chrome profile was recorded not in use. True only when a record says it is in use. Absent when no choice was recorded.

profiles[].last_used required boolean
profiles[].mapped_to required string | null

The Blowhorn profile mapped to this Chrome profile on this host.

profiles[].platforms required object

One entry per platform discovery looks for; a username is a saved login's username or, for GitHub, the live account cookie's value when signed in.

profiles[].platforms.bluesky required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.github required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.hn required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.linkedin required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.reddit required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.slack required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.x required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].proposed_slug required string | null

The profile name a create would propose, from the Chrome profile's display name; null when nothing usable can be proposed.

profiles[].resolution required one of object, object, object, object, object

The create rule's answer for the profile's Google email, without creating anything.

profiles[].slug_state required enum

Whether the proposed name is free, already the resolved user's own profile, taken by another profile, or not a usable name; unchecked under `--local`.

Values: free, own, taken, invalid, unchecked

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeDiscoverResponse

Variant fields
NameTypeDescription
account_errors array of string

One sentence per account the store refused to record, naming the profile and platform.

accounts array of SessionAccount

What this survey recorded in your Blowhorn account, one entry per profile and browser platform it checked.

accounts[].account required one of ProfileAccount, null
accounts[].outcome required enum

none - not signed in and no account is mapped, so nothing was written.

Values: created, updated, reconnected, disconnected, unchanged, none

accounts[].platform required enum

Values: linkedin, x, reddit, hn, github, bluesky

accounts[].profile required string
data_dir required string
host required string

The machine host the mappings are keyed by.

last_used required string | null

Directory of the profile Chrome last used.

local boolean

True for `--local`, which reads Chrome's files only, never the store, so nobody is resolved or mapped.

local_state required object
local_state.detail required string | null
local_state.path required string
local_state.state required string
ok required any
profile_count required integer
profiles required array of object
profiles[].account required string

Signed-in Google account email, empty when nobody is signed in.

profiles[].directory required string
profiles[].display_name required string
profiles[].gaia_name required string
profiles[].identity required string | null

Whether the recorded identity of the mapping still holds (for example ok or drift); null when unmapped.

profiles[].in_use boolean

False when this Chrome profile was recorded not in use. True only when a record says it is in use. Absent when no choice was recorded.

profiles[].last_used required boolean
profiles[].mapped_to required string | null

The Blowhorn profile mapped to this Chrome profile on this host.

profiles[].platforms required object

One entry per platform discovery looks for; a username is a saved login's username or, for GitHub, the live account cookie's value when signed in.

profiles[].platforms.bluesky required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.github required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.hn required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.linkedin required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.reddit required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.slack required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].platforms.x required ChromeDiscoveredPlatform

What discovery found for one platform in one Chrome profile: the account name from a saved login (or, for GitHub, the live account cookie's value when signed in), and whether a live session is present - read from a session cookie's presence, never its value. No password and no cookie value is ever read.

profiles[].proposed_slug required string | null

The profile name a create would propose, from the Chrome profile's display name; null when nothing usable can be proposed.

profiles[].resolution required one of object, object, object, object, object

The create rule's answer for the profile's Google email, without creating anything.

profiles[].slug_state required enum

Whether the proposed name is free, already the resolved user's own profile, taken by another profile, or not a usable name; unchecked under `--local`.

Values: free, own, taken, invalid, unchecked

POST /cli/chrome/extension-enable Open the Blowhorn extension's Chrome Web Store page in each Chrome profile you name, so you can turn the extension on there with one click. When 'extension-install --all-profiles --external' has written Chrome's external-extensions file, Chrome adds the extension to each profile turned off, and the store page shows Enable now; a profile Chrome has not reopened since then shows Add to Chrome. Name a profile with --chrome-profile, the directory 'blowhorn chrome profiles' prints; repeat it for more than one. Reads Local State to check each directory; writes nothing. If no Chrome is running, Chrome starts to open the page and is left running; Blowhorn drives nothing in it. 'blowhorn chrome extension-status' then says whether each profile has it on (profile_states in --json).

Open the Blowhorn extension's Chrome Web Store page in each Chrome profile you name, so you can turn the extension on there with one click. When 'extension-install --all-profiles --external' has written Chrome's external-extensions file, Chrome adds the extension to each profile turned off, and the store page shows Enable now; a profile Chrome has not reopened since then shows Add to Chrome. Name a profile with --chrome-profile, the directory 'blowhorn chrome profiles' prints; repeat it for more than one. Reads Local State to check each directory; writes nothing. If no Chrome is running, Chrome starts to open the page and is left running; Blowhorn drives nothing in it. 'blowhorn chrome extension-status' then says whether each profile has it on (profile_states in --json).

Request body required

The flags and arguments of `blowhorn chrome extension-enable`, one property per parameter.

Request body of Open the Blowhorn extension's Chrome Web Store page in each Chrome profile you name, so you can turn the extension on there with one click. When 'extension-install --all-profiles --external' has written Chrome's external-extensions file, Chrome adds the extension to each profile turned off, and the store page shows Enable now; a profile Chrome has not reopened since then shows Add to Chrome. Name a profile with --chrome-profile, the directory 'blowhorn chrome profiles' prints; repeat it for more than one. Reads Local State to check each directory; writes nothing. If no Chrome is running, Chrome starts to open the page and is left running; Blowhorn drives nothing in it. 'blowhorn chrome extension-status' then says whether each profile has it on (profile_states in --json). fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

chrome_profiles array of string

A Chrome profile directory, exactly as 'blowhorn chrome profiles' prints it ('Profile 12'): open the store page there. Repeatable

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn chrome extension-enable' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome extension-enable' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What chrome extension-enable prints with --json: the store page it asked Chrome to open and, for each Chrome profile named, whether Chrome was asked. Whether you then turned the extension on is read by chrome extension-status (profile_states), never by this command.

Response 200 of Open the Blowhorn extension's Chrome Web Store page in each Chrome profile you name, so you can turn the extension on there with one click. When 'extension-install --all-profiles --external' has written Chrome's external-extensions file, Chrome adds the extension to each profile turned off, and the store page shows Enable now; a profile Chrome has not reopened since then shows Add to Chrome. Name a profile with --chrome-profile, the directory 'blowhorn chrome profiles' prints; repeat it for more than one. Reads Local State to check each directory; writes nothing. If no Chrome is running, Chrome starts to open the page and is left running; Blowhorn drives nothing in it. 'blowhorn chrome extension-status' then says whether each profile has it on (profile_states in --json). fields
NameTypeDescription
dry_run required boolean
ok required boolean

Chrome was asked to open the store page in at least one profile (always true on a dry run); false with exit 1 when it was asked in none.

profiles required array of object
profiles[].detail required string | null

Why the page did not open in this profile ('chrome could not open the Chrome Web Store page: <reason>'); null when it did.

profiles[].directory required string

The Chrome profile directory, as chrome profiles prints it.

profiles[].display_name required string
profiles[].opened required boolean

Chrome was asked to open the store page in this profile.

url required string

The extension's Chrome Web Store page.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeExtensionEnableResponse

Variant fields
NameTypeDescription
dry_run required boolean
ok required boolean

Chrome was asked to open the store page in at least one profile (always true on a dry run); false with exit 1 when it was asked in none.

profiles required array of object
profiles[].detail required string | null

Why the page did not open in this profile ('chrome could not open the Chrome Web Store page: <reason>'); null when it did.

profiles[].directory required string

The Chrome profile directory, as chrome profiles prints it.

profiles[].display_name required string
profiles[].opened required boolean

Chrome was asked to open the store page in this profile.

url required string

The extension's Chrome Web Store page.

POST /cli/chrome/extension-install Write the native-messaging host manifest Chrome launches for the Blowhorn Chrome extension, and print where the unpacked extension lives so you can load it. The manifest is com.blowhorn.chrome_extension.json under Chrome's NativeMessagingHosts directory (~/Library/Application Support/Google/Chrome/NativeMessagingHosts on macOS, ~/.config/google-chrome/NativeMessagingHosts on Linux), named com.blowhorn.chrome_extension, type stdio, path an executable launcher chrome_extension-host in Blowhorn's directory (~/Library/Application Support/Blowhorn on macOS, ~/.local/share/blowhorn on Linux) that runs the Python native-host program by default, or the Rust host with --native-host rust, and allowed_origins naming the extension id from the key in extension/manifest.json, the Chrome Web Store id and the older unpacked id. If you already installed the host, run this command again and reload the extension once. A missing key or an unreadable extension manifest is one sentence and nothing is written; --dry-run prints both paths and the manifest and writes nothing. This command does not launch Chrome and does not load the extension.

Write the native-messaging host manifest Chrome launches for the Blowhorn Chrome extension, and print where the unpacked extension lives so you can load it. The manifest is com.blowhorn.chrome_extension.json under Chrome's NativeMessagingHosts directory (~/Library/Application Support/Google/Chrome/NativeMessagingHosts on macOS, ~/.config/google-chrome/NativeMessagingHosts on Linux), named com.blowhorn.chrome_extension, type stdio, path an executable launcher chrome_extension-host in Blowhorn's directory (~/Library/Application Support/Blowhorn on macOS, ~/.local/share/blowhorn on Linux) that runs the Python native-host program by default, or the Rust host with --native-host rust, and allowed_origins naming the extension id from the key in extension/manifest.json, the Chrome Web Store id and the older unpacked id. If you already installed the host, run this command again and reload the extension once. A missing key or an unreadable extension manifest is one sentence and nothing is written; --dry-run prints both paths and the manifest and writes nothing. This command does not launch Chrome and does not load the extension.

--profile <name> names the Blowhorn profile whose mapped Chrome profile should get the extension (resolved through 'blowhorn chrome map' on this machine); --chrome-profile '<directory>' names the Chrome profile directory itself, as 'blowhorn chrome profiles' prints it, and can be given more than once. Either one writes the native host as above, checks the directory is in Chrome's Local State, and prints the steps to load the extension in that profile alone: Chrome has no file this command can write that installs an extension in one profile only, since the policy and the external-extensions file both reach every profile. A profile with no mapping, a directory Chrome does not have, and either flag with --all-profiles are one sentence and nothing is written; a store that cannot be reached is the named cause and exit 3. A --profile inherited from BLOWHORN_PROFILE or defaults.profile does not count.

--all-profiles also asks Chrome to install that extension in every Chrome profile on this machine. It is every Chrome profile, not --profile all. The default route writes ExtensionInstallForcelist: silent, every profile, Chrome shows Managed by your organization, and macOS may ask for an administrator password once. On macOS that policy is your own user's, never the computer's, so other macOS users' Chrome is left alone. On Linux the policy file (/etc/opt/chrome/policies/managed/blowhorn.json) and the external file (/usr/share/google-chrome/extensions/<id>.json) need root, Blowhorn never asks for it, and a write this user cannot make is refused with the file, its exact content and the sudo command that writes that one file. Never run this command under sudo: the host and launcher would land under root's home, which your Chrome never reads, so a run under sudo is refused by name. --external writes Chrome's external-extensions file instead, so Chrome offers the install in each profile, one click there, and does not show that banner. --external without --all-profiles is refused and writes nothing. Switching route removes the other route's file for this extension id. Both routes name the extension id from that same key and the Chrome Web Store update url; Chrome can fetch the extension only once that id is published. A policy file that exists and cannot be read is one sentence and nothing is written. Per-profile lines say what Chrome was asked to do, never that a profile's copy was confirmed. Those lines cover the configured Chrome data directory. The policy route applies the extension to every Chrome profile on this machine. --json appends one trailing object (manifest, launcher, extension_dir, removed), for a script or the desktop app. With --all-profiles that object also carries route, extension_id, profiles, profiles_error and profiles_scope; with --profile or --chrome-profile it carries targets. --ensure, with exactly one --profile or --chrome-profile, ensures the extension for that profile the way a run does on demand: it repairs the native host when missing or stale, registers the extension for that profile's data directory when nothing is there yet, and re-probes hello once. A profile already carrying a Chrome Web Store install is left alone, and a store install too old for this Blowhorn is reported with Chrome's own update step, never forced. --ensure refuses --all-profiles and --external, writes nothing with --dry-run, and never starts, restarts, or kills Chrome.

--native-host rust points the same launcher at the Rust native host instead of the Python one: the launcher execs engine/rust/target/release/blowhorn-host in this checkout (built with 'cargo build --release -p blowhorn-host' in engine/rust), or the binary --rust-host names. It is off by default. A Rust host that is missing or not executable is one sentence, nothing is written, and the installed launcher stays as it is. The choice is kept in the launcher itself: a later install or --ensure with no --native-host, the desktop app's automatic one included, keeps the Rust host while its binary is still executable, and puts the Python launcher back, saying so, once it is not. --native-host python switches back. --json carries native_host, python or rust, and native_host_fallback, the sentence when the Rust host was replaced.

Request body required

The flags and arguments of `blowhorn chrome extension-install`, one property per parameter.

Request body of Write the native-messaging host manifest Chrome launches for the Blowhorn Chrome extension, and print where the unpacked extension lives so you can load it. The manifest is com.blowhorn.chrome_extension.json under Chrome's NativeMessagingHosts directory (~/Library/Application Support/Google/Chrome/NativeMessagingHosts on macOS, ~/.config/google-chrome/NativeMessagingHosts on Linux), named com.blowhorn.chrome_extension, type stdio, path an executable launcher chrome_extension-host in Blowhorn's directory (~/Library/Application Support/Blowhorn on macOS, ~/.local/share/blowhorn on Linux) that runs the Python native-host program by default, or the Rust host with --native-host rust, and allowed_origins naming the extension id from the key in extension/manifest.json, the Chrome Web Store id and the older unpacked id. If you already installed the host, run this command again and reload the extension once. A missing key or an unreadable extension manifest is one sentence and nothing is written; --dry-run prints both paths and the manifest and writes nothing. This command does not launch Chrome and does not load the extension. fields
NameTypeDescription
all_profiles boolean

Also ask Chrome to install the Chrome extension in every Chrome profile on this machine. The default is the policy route (silent, every profile, Managed by your organization). This is every Chrome profile, not --profile all.

Default: false

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

chrome_profiles array of string

A Chrome profile directory, exactly as 'blowhorn chrome profiles' prints it ('Profile 12'): write the host and print the steps to load the extension in that profile alone. Repeatable. Refused with --all-profiles

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

ensure boolean

Ensure the extension for one named Chrome profile, the way a run does when it finds the extension missing, stale, or outdated: repair the native host, register the extension for that profile's data directory when nothing is there yet, and re-probe hello once. Needs exactly one --profile or --chrome-profile, and refuses --all-profiles and --external. A profile already carrying a Chrome Web Store install is left alone: Chrome's updater owns it, and this never forces a store update. Writes nothing with --dry-run. Never starts, restarts, or kills Chrome.

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

external boolean

With --all-profiles, write Chrome's external-extensions file instead of the policy, so Chrome offers the install in each profile and does not show Managed by your organization. Refused without --all-profiles; nothing is written.

Default: false

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

native_host string

Which native host the launcher runs: python (the default) or rust, the Rust host from engine/rust built with 'cargo build --release -p blowhorn-host'. Unset keeps what is installed. A Rust host that is missing or not executable is refused and nothing is written.

Values: python, rust

pace string

Accepted by 'blowhorn chrome extension-install' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome extension-install' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

rust_host string

With --native-host rust, the Rust host binary the launcher runs instead of this checkout's engine/rust/target/release/blowhorn-host. Refused without --native-host rust

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What chrome extension-install prints with --json. With --json, stdout is exactly this one JSON document; the human instruction lines are written to stderr instead, where they still reach a terminal and the desktop console's log. The object carries profile detail only for --profile or --all-profiles runs, and an ensure report only for --ensure runs.

Response 200 of Write the native-messaging host manifest Chrome launches for the Blowhorn Chrome extension, and print where the unpacked extension lives so you can load it. The manifest is com.blowhorn.chrome_extension.json under Chrome's NativeMessagingHosts directory (~/Library/Application Support/Google/Chrome/NativeMessagingHosts on macOS, ~/.config/google-chrome/NativeMessagingHosts on Linux), named com.blowhorn.chrome_extension, type stdio, path an executable launcher chrome_extension-host in Blowhorn's directory (~/Library/Application Support/Blowhorn on macOS, ~/.local/share/blowhorn on Linux) that runs the Python native-host program by default, or the Rust host with --native-host rust, and allowed_origins naming the extension id from the key in extension/manifest.json, the Chrome Web Store id and the older unpacked id. If you already installed the host, run this command again and reload the extension once. A missing key or an unreadable extension manifest is one sentence and nothing is written; --dry-run prints both paths and the manifest and writes nothing. This command does not launch Chrome and does not load the extension. fields
NameTypeDescription
dry_run required boolean
ensure object

Present for an --ensure run. The on-demand ensure report - what was repaired, what is registered, whether hello re-probed connected, and the one remaining person step when it did not. Never claims a store update - a store install too old for this Blowhorn is reported with Chrome's own update step in person_step.

ensure.bridge_held required boolean

Another run may hold the bridge; no ensure ran.

ensure.connected required boolean

Hello re-probed connected after the ensure.

ensure.connection_detail required string | null
ensure.data_dir required string
ensure.dry_run required boolean
ensure.external_path required string
ensure.external_present required boolean
ensure.external_present_before required boolean

The external-extensions file was already there before the ensure.

ensure.external_refusal required string | null

Why the external-extensions file could not be written, when it could not.

ensure.external_written required boolean
ensure.failure_sentence required string

Empty when connected; otherwise what was repaired plus the person step.

ensure.host_refusal required string | null

Why the native host could not be written, when it could not.

ensure.launcher_present_before required boolean
ensure.launcher_repaired required boolean
ensure.manifest_repaired required boolean
ensure.manifest_state required string

The native host manifest state before repair - current, stale, missing, or unreadable.

ensure.native_host required string

Which native host the launcher runs after the ensure.

Values: python, rust

ensure.native_host_fallback required string | null

Set when the installed Rust host was no longer executable and the launcher was (or, in a dry run, would be) put back to the Python host.

ensure.ok required boolean

Connected, or a dry run (which reports only).

ensure.person_step required string

The one remaining person step when the run did not connect.

ensure.policy_present required boolean
ensure.profile_directory required string
ensure.skew_detail required string | null

The re-probe's skew sentence, when the socket answered but the version or verbs are short of this Blowhorn.

ensure.store_outdated required boolean
ensure.store_version required string | null

The mapped profile's Chrome Web Store install version, when it has one.

ensure.unpacked required string | null

The mapped profile's loaded-unpacked install version, when it has one.

ensure.unpacked_manifest_present required boolean
ensure.unpacked_present required boolean
extension_dir required string

Path of the unpacked extension to load.

extension_id string
launcher required string

Path of the native host launcher.

manifest required string

Path of the native messaging host manifest.

native_host required string

Which native host the launcher runs (or, in a dry run, would run). python is the default; rust only after --native-host rust, kept while its binary is still executable.

Values: python, rust

native_host_fallback required string | null

The sentence saying the installed Rust host is missing or not executable and the launcher runs the Python host again; null otherwise.

ok required boolean
profiles array of object

Per Chrome profile outcome of an all-profiles install.

profiles[].account required string
profiles[].directory required string
profiles[].display_name required string
profiles[].result required string
profiles_error string | null
profiles_scope string
route string

Present for an all-profiles install.

Values: policy, external

targets array of object

Present for a one-profile install (--profile or --chrome-profile).

targets[].account required string
targets[].directory required string
targets[].display_name required string
targets[].profile required string | null
targets[].result required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeExtensionInstallResponse

Variant fields
NameTypeDescription
dry_run required boolean
ensure object

Present for an --ensure run. The on-demand ensure report - what was repaired, what is registered, whether hello re-probed connected, and the one remaining person step when it did not. Never claims a store update - a store install too old for this Blowhorn is reported with Chrome's own update step in person_step.

ensure.bridge_held required boolean

Another run may hold the bridge; no ensure ran.

ensure.connected required boolean

Hello re-probed connected after the ensure.

ensure.connection_detail required string | null
ensure.data_dir required string
ensure.dry_run required boolean
ensure.external_path required string
ensure.external_present required boolean
ensure.external_present_before required boolean

The external-extensions file was already there before the ensure.

ensure.external_refusal required string | null

Why the external-extensions file could not be written, when it could not.

ensure.external_written required boolean
ensure.failure_sentence required string

Empty when connected; otherwise what was repaired plus the person step.

ensure.host_refusal required string | null

Why the native host could not be written, when it could not.

ensure.launcher_present_before required boolean
ensure.launcher_repaired required boolean
ensure.manifest_repaired required boolean
ensure.manifest_state required string

The native host manifest state before repair - current, stale, missing, or unreadable.

ensure.native_host required string

Which native host the launcher runs after the ensure.

Values: python, rust

ensure.native_host_fallback required string | null

Set when the installed Rust host was no longer executable and the launcher was (or, in a dry run, would be) put back to the Python host.

ensure.ok required boolean

Connected, or a dry run (which reports only).

ensure.person_step required string

The one remaining person step when the run did not connect.

ensure.policy_present required boolean
ensure.profile_directory required string
ensure.skew_detail required string | null

The re-probe's skew sentence, when the socket answered but the version or verbs are short of this Blowhorn.

ensure.store_outdated required boolean
ensure.store_version required string | null

The mapped profile's Chrome Web Store install version, when it has one.

ensure.unpacked required string | null

The mapped profile's loaded-unpacked install version, when it has one.

ensure.unpacked_manifest_present required boolean
ensure.unpacked_present required boolean
extension_dir required string

Path of the unpacked extension to load.

extension_id string
launcher required string

Path of the native host launcher.

manifest required string

Path of the native messaging host manifest.

native_host required string

Which native host the launcher runs (or, in a dry run, would run). python is the default; rust only after --native-host rust, kept while its binary is still executable.

Values: python, rust

native_host_fallback required string | null

The sentence saying the installed Rust host is missing or not executable and the launcher runs the Python host again; null otherwise.

ok required boolean
profiles array of object

Per Chrome profile outcome of an all-profiles install.

profiles[].account required string
profiles[].directory required string
profiles[].display_name required string
profiles[].result required string
profiles_error string | null
profiles_scope string
route string

Present for an all-profiles install.

Values: policy, external

targets array of object

Present for a one-profile install (--profile or --chrome-profile).

targets[].account required string
targets[].directory required string
targets[].display_name required string
targets[].profile required string | null
targets[].result required string
POST /cli/chrome/extension-mode Show or set how runs drive the Blowhorn Chrome extension. background is the default. Both modes open Blowhorn's tab inactive beside the tab you are on (or in its own window with --window own-window), in a Blowhorn group that is collapsed by default, and take a screenshot through the debugger without selecting the tab. Nothing brings the tab forward. classic keeps focus emulation on only while an action holds the debugger and sends input through the page. background turns focus emulation on from the first page and sends trusted input. With no MODE it reports the mode a run started now would use and where it comes from: the BLOWHORN_EXTENSION_ROLLBACK override, your Blowhorn account setting, or the background default. A job whose driver mode is extension-classic or extension-background outranks the account setting for that job. Setting MODE writes extensionMode to your account's Blowhorn preferences in your Blowhorn account (sign in first with 'blowhorn auth login'); default clears it. --group collapsed or open writes chromeGroup the same way: how the Blowhorn tab group sits beside your tabs between steps. Collapsed is the default. --window side-tab or own-window writes chromeWindow the same way: where Blowhorn works. side-tab is an inactive tab beside your tabs, the default. own-window is one window of its own, mostly behind yours with an edge showing, opened without focus and reused for the browser session; on a Mac keep that edge showing, since a window fully covered, minimized, hidden or on another desktop reads like a background tab. --json emits the report as one object, for a script or the desktop app.

Show or set how runs drive the Blowhorn Chrome extension. background is the default. Both modes open Blowhorn's tab inactive beside the tab you are on (or in its own window with --window own-window), in a Blowhorn group that is collapsed by default, and take a screenshot through the debugger without selecting the tab. Nothing brings the tab forward. classic keeps focus emulation on only while an action holds the debugger and sends input through the page. background turns focus emulation on from the first page and sends trusted input. With no MODE it reports the mode a run started now would use and where it comes from: the BLOWHORN_EXTENSION_ROLLBACK override, your Blowhorn account setting, or the background default. A job whose driver mode is extension-classic or extension-background outranks the account setting for that job. Setting MODE writes extensionMode to your account's Blowhorn preferences in your Blowhorn account (sign in first with 'blowhorn auth login'); default clears it. --group collapsed or open writes chromeGroup the same way: how the Blowhorn tab group sits beside your tabs between steps. Collapsed is the default. --window side-tab or own-window writes chromeWindow the same way: where Blowhorn works. side-tab is an inactive tab beside your tabs, the default. own-window is one window of its own, mostly behind yours with an edge showing, opened without focus and reused for the browser session; on a Mac keep that edge showing, since a window fully covered, minimized, hidden or on another desktop reads like a background tab. --json emits the report as one object, for a script or the desktop app.

Request body required

The flags and arguments of `blowhorn chrome extension-mode`, one property per parameter.

Request body of Show or set how runs drive the Blowhorn Chrome extension. background is the default. Both modes open Blowhorn's tab inactive beside the tab you are on (or in its own window with --window own-window), in a Blowhorn group that is collapsed by default, and take a screenshot through the debugger without selecting the tab. Nothing brings the tab forward. classic keeps focus emulation on only while an action holds the debugger and sends input through the page. background turns focus emulation on from the first page and sends trusted input. With no MODE it reports the mode a run started now would use and where it comes from: the BLOWHORN_EXTENSION_ROLLBACK override, your Blowhorn account setting, or the background default. A job whose driver mode is extension-classic or extension-background outranks the account setting for that job. Setting MODE writes extensionMode to your account's Blowhorn preferences in your Blowhorn account (sign in first with 'blowhorn auth login'); default clears it. --group collapsed or open writes chromeGroup the same way: how the Blowhorn tab group sits beside your tabs between steps. Collapsed is the default. --window side-tab or own-window writes chromeWindow the same way: where Blowhorn works. side-tab is an inactive tab beside your tabs, the default. own-window is one window of its own, mostly behind yours with an edge showing, opened without focus and reused for the browser session; on a Mac keep that edge showing, since a window fully covered, minimized, hidden or on another desktop reads like a background tab. --json emits the report as one object, for a script or the desktop app. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

extension_mode string

classic, background, or default to clear the account setting; omit to show

group string

collapsed or open: how the Blowhorn tab group sits beside your tabs between steps. default clears the account setting (collapsed)

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn chrome extension-mode' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome extension-mode' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

window string

side-tab or own-window: where Blowhorn works, in an inactive tab beside your tabs or in a window of its own, mostly behind yours with an edge showing, that never takes focus (on a Mac, keep that edge showing). default clears the account setting (side-tab)

Responses

200

What the command prints to stdout with --json when it exits 0.

What chrome extension-mode prints with --json: the extension mode a run started now would use, where it comes from, the account setting, the rollback override, each feature on or off, the tab group setting and where Blowhorn works (the driving window). With MODE, --group or --window, written says the account setting was saved to your Blowhorn account.

Response 200 of Show or set how runs drive the Blowhorn Chrome extension. background is the default. Both modes open Blowhorn's tab inactive beside the tab you are on (or in its own window with --window own-window), in a Blowhorn group that is collapsed by default, and take a screenshot through the debugger without selecting the tab. Nothing brings the tab forward. classic keeps focus emulation on only while an action holds the debugger and sends input through the page. background turns focus emulation on from the first page and sends trusted input. With no MODE it reports the mode a run started now would use and where it comes from: the BLOWHORN_EXTENSION_ROLLBACK override, your Blowhorn account setting, or the background default. A job whose driver mode is extension-classic or extension-background outranks the account setting for that job. Setting MODE writes extensionMode to your account's Blowhorn preferences in your Blowhorn account (sign in first with 'blowhorn auth login'); default clears it. --group collapsed or open writes chromeGroup the same way: how the Blowhorn tab group sits beside your tabs between steps. Collapsed is the default. --window side-tab or own-window writes chromeWindow the same way: where Blowhorn works. side-tab is an inactive tab beside your tabs, the default. own-window is one window of its own, mostly behind yours with an edge showing, opened without focus and reused for the browser session; on a Mac keep that edge showing, since a window fully covered, minimized, hidden or on another desktop reads like a background tab. --json emits the report as one object, for a script or the desktop app. fields
NameTypeDescription
account required string | null

The extensionMode account preference, or null when it is not set.

Values: classic, background,

account_error required string | null

Why the account setting could not be read from your Blowhorn account; account is then the cached setting a run falls back to.

features required object
features.checkpoint_front required boolean
features.first_page required boolean
features.lease_at_goto required boolean
features.placement required boolean
features.quiet_screenshot required boolean
features.trusted_input required boolean
group required string

How the Blowhorn tab group sits beside your tabs between steps. Collapsed unless the account says open.

Values: collapsed, open

group_account required string | null

The chromeGroup account preference, or null when it is not set.

Values: collapsed, open,

mode required string

Values: classic, background

ok required boolean
rollback required string | null

The raw BLOWHORN_EXTENSION_ROLLBACK value, or null when it is unset.

rollback_error required string | null

Why that value is refused; a run would refuse to start with the same sentence.

source required string

Which layer decided. A job's driver mode is a fourth layer that only a running job has.

Values: rollback, account, default

window required string

Where Blowhorn works in Chrome. side_tab is an inactive tab beside your tabs. own_window is a window of its own, mostly behind yours with an edge showing, which never takes focus. side_tab unless the account says own_window.

Values: side_tab, own_window

window_account required string | null

The chromeWindow account preference, or null when it is not set.

Values: side_tab, own_window,

written required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeExtensionModeResponse

Variant fields
NameTypeDescription
account required string | null

The extensionMode account preference, or null when it is not set.

Values: classic, background,

account_error required string | null

Why the account setting could not be read from your Blowhorn account; account is then the cached setting a run falls back to.

features required object
features.checkpoint_front required boolean
features.first_page required boolean
features.lease_at_goto required boolean
features.placement required boolean
features.quiet_screenshot required boolean
features.trusted_input required boolean
group required string

How the Blowhorn tab group sits beside your tabs between steps. Collapsed unless the account says open.

Values: collapsed, open

group_account required string | null

The chromeGroup account preference, or null when it is not set.

Values: collapsed, open,

mode required string

Values: classic, background

ok required boolean
rollback required string | null

The raw BLOWHORN_EXTENSION_ROLLBACK value, or null when it is unset.

rollback_error required string | null

Why that value is refused; a run would refuse to start with the same sentence.

source required string

Which layer decided. A job's driver mode is a fourth layer that only a running job has.

Values: rollback, account, default

window required string

Where Blowhorn works in Chrome. side_tab is an inactive tab beside your tabs. own_window is a window of its own, mostly behind yours with an edge showing, which never takes focus. side_tab unless the account says own_window.

Values: side_tab, own_window

window_account required string | null

The chromeWindow account preference, or null when it is not set.

Values: side_tab, own_window,

written required boolean
POST /cli/chrome/extension-status Report whether the Blowhorn Chrome extension is installed and connected: the unpacked extension directory, the native-host manifest (missing, current, or stale against this checkout's extension/manifest.json), the launcher, and whether the Chrome extension answers hello on its socket. An answered hello proves the extension is loaded in the real Chrome and talking; this command navigates nowhere, probes no tab, and contacts no platform. A manifest whose launcher is gone is not installed. The report also says which install route is on disk: policy (ExtensionInstallForcelist, every Chrome profile, Managed by your organization), external (Chrome offers the install in each profile), or none. That read writes nothing. Which Chrome profile accepted that install is not something this command can see. It does name each Chrome profile whose Secure Preferences or Preferences record a Chrome Web Store install of the extension, with its version, and tells a store install too old for this Blowhorn to update in chrome://extensions, never to load it unpacked. The one thing written is a lock beside each Chrome profile's own socket, in Blowhorn's directory (~/Library/Application Support/Blowhorn on macOS, ~/.local/share/blowhorn on Linux), which every Blowhorn client holds while it talks to that profile's Chrome extension: while a run holds one profile's lock, that profile's hello is not written, is reported not checked, and --json carries bridge_busy: true with a detail saying a run may be holding the bridge; another profile's run never waits on it. --json emits the report as one object, for a script or the desktop app.

Report whether the Blowhorn Chrome extension is installed and connected: the unpacked extension directory, the native-host manifest (missing, current, or stale against this checkout's extension/manifest.json), the launcher, and whether the Chrome extension answers hello on its socket. An answered hello proves the extension is loaded in the real Chrome and talking; this command navigates nowhere, probes no tab, and contacts no platform. A manifest whose launcher is gone is not installed. The report also says which install route is on disk: policy (ExtensionInstallForcelist, every Chrome profile, Managed by your organization), external (Chrome offers the install in each profile), or none. That read writes nothing. Which Chrome profile accepted that install is not something this command can see. It does name each Chrome profile whose Secure Preferences or Preferences record a Chrome Web Store install of the extension, with its version, and tells a store install too old for this Blowhorn to update in chrome://extensions, never to load it unpacked. The one thing written is a lock beside each Chrome profile's own socket, in Blowhorn's directory (~/Library/Application Support/Blowhorn on macOS, ~/.local/share/blowhorn on Linux), which every Blowhorn client holds while it talks to that profile's Chrome extension: while a run holds one profile's lock, that profile's hello is not written, is reported not checked, and --json carries bridge_busy: true with a detail saying a run may be holding the bridge; another profile's run never waits on it. --json emits the report as one object, for a script or the desktop app.

Request body required

The flags and arguments of `blowhorn chrome extension-status`, one property per parameter.

Request body of Report whether the Blowhorn Chrome extension is installed and connected: the unpacked extension directory, the native-host manifest (missing, current, or stale against this checkout's extension/manifest.json), the launcher, and whether the Chrome extension answers hello on its socket. An answered hello proves the extension is loaded in the real Chrome and talking; this command navigates nowhere, probes no tab, and contacts no platform. A manifest whose launcher is gone is not installed. The report also says which install route is on disk: policy (ExtensionInstallForcelist, every Chrome profile, Managed by your organization), external (Chrome offers the install in each profile), or none. That read writes nothing. Which Chrome profile accepted that install is not something this command can see. It does name each Chrome profile whose Secure Preferences or Preferences record a Chrome Web Store install of the extension, with its version, and tells a store install too old for this Blowhorn to update in chrome://extensions, never to load it unpacked. The one thing written is a lock beside each Chrome profile's own socket, in Blowhorn's directory (~/Library/Application Support/Blowhorn on macOS, ~/.local/share/blowhorn on Linux), which every Blowhorn client holds while it talks to that profile's Chrome extension: while a run holds one profile's lock, that profile's hello is not written, is reported not checked, and --json carries bridge_busy: true with a detail saying a run may be holding the bridge; another profile's run never waits on it. --json emits the report as one object, for a script or the desktop app. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn chrome extension-status' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome extension-status' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints whether the Blowhorn Chrome extension is installed (native-messaging manifest, launcher, policy, unpacked directory) and whether it answers the bridge.

Response 200 of Report whether the Blowhorn Chrome extension is installed and connected: the unpacked extension directory, the native-host manifest (missing, current, or stale against this checkout's extension/manifest.json), the launcher, and whether the Chrome extension answers hello on its socket. An answered hello proves the extension is loaded in the real Chrome and talking; this command navigates nowhere, probes no tab, and contacts no platform. A manifest whose launcher is gone is not installed. The report also says which install route is on disk: policy (ExtensionInstallForcelist, every Chrome profile, Managed by your organization), external (Chrome offers the install in each profile), or none. That read writes nothing. Which Chrome profile accepted that install is not something this command can see. It does name each Chrome profile whose Secure Preferences or Preferences record a Chrome Web Store install of the extension, with its version, and tells a store install too old for this Blowhorn to update in chrome://extensions, never to load it unpacked. The one thing written is a lock beside each Chrome profile's own socket, in Blowhorn's directory (~/Library/Application Support/Blowhorn on macOS, ~/.local/share/blowhorn on Linux), which every Blowhorn client holds while it talks to that profile's Chrome extension: while a run holds one profile's lock, that profile's hello is not written, is reported not checked, and --json carries bridge_busy: true with a detail saying a run may be holding the bridge; another profile's run never waits on it. --json emits the report as one object, for a script or the desktop app. fields
NameTypeDescription
bridge_busy required boolean
connected required boolean
connection_detail required string | null
extension_id required string
external_installed required boolean
external_path required string
installed required boolean
launcher_path required string
launcher_present required boolean
manifest_detail required string | null
manifest_path required string
manifest_state required string

The manifest's state, such as current, stale, missing or invalid.

native_host_fallback required string | null

The sentence saying the installed Rust host is missing or not executable and the launcher runs the Python host again; null otherwise.

ok required any
policy_error required string | null
policy_installed required boolean
policy_path required string
profile_states required array of object

The extension's state in each Chrome profile (Default and Profile N directories with a Preferences file), read from that profile's Secure Preferences or Preferences. enabled is on; disabled is in the profile and turned off (how the external route adds it until you turn it on); removed is an external add you removed; absent is nothing recorded yet.

profile_states[].profile required string
profile_states[].state required string

Values: enabled, disabled, removed, absent

profile_states[].version required string | null
requirements required array of object

Engine-owned activity and platform prerequisites used by New order.

requirements[].activity required string
requirements[].platform required string
requirements[].reason required string
requirements[].requirement required string

Values: extension, chrome, none

route required string | null

How the extension reaches Chrome (policy, external or unpacked); null when none is installed.

unpacked_dir required string
unpacked_present required boolean
webstore_profiles required array of object

Chrome profiles that installed the extension from the web store.

webstore_profiles[].profile required string
webstore_profiles[].version required string | null

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeExtensionStatusResponse

Variant fields
NameTypeDescription
bridge_busy required boolean
connected required boolean
connection_detail required string | null
extension_id required string
external_installed required boolean
external_path required string
installed required boolean
launcher_path required string
launcher_present required boolean
manifest_detail required string | null
manifest_path required string
manifest_state required string

The manifest's state, such as current, stale, missing or invalid.

native_host_fallback required string | null

The sentence saying the installed Rust host is missing or not executable and the launcher runs the Python host again; null otherwise.

ok required any
policy_error required string | null
policy_installed required boolean
policy_path required string
profile_states required array of object

The extension's state in each Chrome profile (Default and Profile N directories with a Preferences file), read from that profile's Secure Preferences or Preferences. enabled is on; disabled is in the profile and turned off (how the external route adds it until you turn it on); removed is an external add you removed; absent is nothing recorded yet.

profile_states[].profile required string
profile_states[].state required string

Values: enabled, disabled, removed, absent

profile_states[].version required string | null
requirements required array of object

Engine-owned activity and platform prerequisites used by New order.

requirements[].activity required string
requirements[].platform required string
requirements[].reason required string
requirements[].requirement required string

Values: extension, chrome, none

route required string | null

How the extension reaches Chrome (policy, external or unpacked); null when none is installed.

unpacked_dir required string
unpacked_present required boolean
webstore_profiles required array of object

Chrome profiles that installed the extension from the web store.

webstore_profiles[].profile required string
webstore_profiles[].version required string | null
POST /cli/chrome/map Map a Blowhorn profile to one of this machine's Chrome profiles, by the Chrome profile's directory name as 'blowhorn chrome profiles' prints it ('Default', 'Profile 12'). The command checks that the Blowhorn profile is in the store, that Chrome has that directory (Chrome silently creates a new, empty profile for a directory it does not know, so a typo is refused rather than recorded), and that the directory is not already mapped to another Blowhorn profile on this machine; then it records the display name and signed-in account Chrome shows for the directory and prints what it recorded. A Blowhorn profile may have several Chrome profiles mapped on one machine (Work and Personal, say), so mapping another directory adds it; mapping a directory the profile already has refreshes the recorded identity, which is how a drifted name or account is confirmed. A Chrome profile belongs to one Blowhorn profile per machine. --replace removes the profile's other mappings on this machine in the same write.

Map a Blowhorn profile to one of this machine's Chrome profiles, by the Chrome profile's directory name as 'blowhorn chrome profiles' prints it ('Default', 'Profile 12'). The command checks that the Blowhorn profile is in the store, that Chrome has that directory (Chrome silently creates a new, empty profile for a directory it does not know, so a typo is refused rather than recorded), and that the directory is not already mapped to another Blowhorn profile on this machine; then it records the display name and signed-in account Chrome shows for the directory and prints what it recorded. A Blowhorn profile may have several Chrome profiles mapped on one machine (Work and Personal, say), so mapping another directory adds it; mapping a directory the profile already has refreshes the recorded identity, which is how a drifted name or account is confirmed. A Chrome profile belongs to one Blowhorn profile per machine. --replace removes the profile's other mappings on this machine in the same write.

Which Chrome profile a run uses, per platform: the one mapped --for that platform; else the profile's only mapping; else its one mapping with no --for (its default, which serves every platform no other mapping names). With several mappings and none of those, the run refuses by name. --for linkedin,x chooses this Chrome profile for those platforms and takes them off the profile's other mappings here; --default clears this mapping's platforms; neither keeps what the mapping had.

A mapping is identity, never permission: it does not make a profile eligible for anything; every browser run reads it to find the profile's Chrome and refuses a profile without one.

Mapping also pairs the profile: once the mapping is recorded the command asks Chrome to open a window on the Blowhorn extension's pairing page in that Chrome profile, carrying a token made from this machine's pairing key (chrome_pairing.key in Blowhorn's launcher directory, created on first use and readable only by you). The page keeps the token in the extension's storage, and a run through the extension refuses before any action unless the extension it reaches reports this profile's token. Mapping a profile to the directory it already has pairs it again. When Chrome cannot be asked to open the page the mapping still stands and the command says why and what to run. --json adds 'pairing': opened (whether Chrome was asked to open the page) and detail (why not, or null). With --dry-run the command checks everything, records nothing and opens no pairing page. Each refusal is one sentence and exit 1; a store that cannot be reached is the named cause and exit 3.

Request body required

The flags and arguments of `blowhorn chrome map`, one property per parameter.

Request body of Map a Blowhorn profile to one of this machine's Chrome profiles, by the Chrome profile's directory name as 'blowhorn chrome profiles' prints it ('Default', 'Profile 12'). The command checks that the Blowhorn profile is in the store, that Chrome has that directory (Chrome silently creates a new, empty profile for a directory it does not know, so a typo is refused rather than recorded), and that the directory is not already mapped to another Blowhorn profile on this machine; then it records the display name and signed-in account Chrome shows for the directory and prints what it recorded. A Blowhorn profile may have several Chrome profiles mapped on one machine (Work and Personal, say), so mapping another directory adds it; mapping a directory the profile already has refreshes the recorded identity, which is how a drifted name or account is confirmed. A Chrome profile belongs to one Blowhorn profile per machine. --replace removes the profile's other mappings on this machine in the same write. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

default boolean

Name no platform for this mapping: it serves every platform no other mapping of the profile names

Default: false

directory required string

The Chrome profile directory, exactly as 'blowhorn chrome profiles' prints it ('Default', 'Profile 12')

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

for_platforms array of string

The platforms this Chrome profile is the one for (comma-separated or repeated: linkedin,x); they move off the profile's other mappings on this machine

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

name required string

The Blowhorn profile, as the store's profiles name it (company)

pace string

Accepted by 'blowhorn chrome map' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

replace boolean

Remove the profile's other Chrome profile mappings on this machine in the same write

Default: false

risk_profile string

Accepted by 'blowhorn chrome map' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the mapping `chrome map` recorded (or, with `--dry-run`, would record) between a Blowhorn profile and a Chrome profile directory. `replaced` is the earlier mapping of the same directory, if any (mapping it again confirms a drifted name or account); `removed` lists the profile's other mappings on this machine that `--replace` dropped. A recorded mapping also carries `pairing`: whether the extension's pairing page was opened in that Chrome profile; a dry run opens nothing and has no `pairing`.

Response 200 of Map a Blowhorn profile to one of this machine's Chrome profiles, by the Chrome profile's directory name as 'blowhorn chrome profiles' prints it ('Default', 'Profile 12'). The command checks that the Blowhorn profile is in the store, that Chrome has that directory (Chrome silently creates a new, empty profile for a directory it does not know, so a typo is refused rather than recorded), and that the directory is not already mapped to another Blowhorn profile on this machine; then it records the display name and signed-in account Chrome shows for the directory and prints what it recorded. A Blowhorn profile may have several Chrome profiles mapped on one machine (Work and Personal, say), so mapping another directory adds it; mapping a directory the profile already has refreshes the recorded identity, which is how a drifted name or account is confirmed. A Chrome profile belongs to one Blowhorn profile per machine. --replace removes the profile's other mappings on this machine in the same write. fields
NameTypeDescription
binding required ChromeBinding

A Blowhorn profile mapped to a Chrome profile directory on one machine. A mapping is identity, not permission. A profile may map several Chrome profiles on one machine; a Chrome profile belongs to at most one Blowhorn profile there. A `--dry-run` preview carries only the first four fields and `platforms`.

binding.account required string | null
binding.created_at string | null
binding.directory required string

The Chrome profile directory name, such as Profile 5.

binding.display_name required string | null
binding.host string
binding.platforms array of string

The platforms a run uses this Chrome profile for. Empty makes it the profile's default: it serves every platform none of the profile's other Chrome profiles on this machine names.

binding.profile required string

The Blowhorn profile slug.

binding.subject_id string

The profile's subject uuid.

binding.updated_at string | null
dry_run required boolean
host required string
ok required any
pairing object
pairing.detail required string | null

Why the page was not opened, or null.

pairing.opened required boolean

Chrome was asked to open the pairing page in the mapped Chrome profile. Pairing is done once the page says so.

removed required array of ChromeBinding
removed[].account required string | null
removed[].created_at string | null
removed[].directory required string

The Chrome profile directory name, such as Profile 5.

removed[].display_name required string | null
removed[].host string
removed[].platforms array of string

The platforms a run uses this Chrome profile for. Empty makes it the profile's default: it serves every platform none of the profile's other Chrome profiles on this machine names.

removed[].profile required string

The Blowhorn profile slug.

removed[].subject_id string

The profile's subject uuid.

removed[].updated_at string | null
replaced required one of ChromeBinding, null

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeMapResponse

Variant fields
NameTypeDescription
binding required ChromeBinding

A Blowhorn profile mapped to a Chrome profile directory on one machine. A mapping is identity, not permission. A profile may map several Chrome profiles on one machine; a Chrome profile belongs to at most one Blowhorn profile there. A `--dry-run` preview carries only the first four fields and `platforms`.

binding.account required string | null
binding.created_at string | null
binding.directory required string

The Chrome profile directory name, such as Profile 5.

binding.display_name required string | null
binding.host string
binding.platforms array of string

The platforms a run uses this Chrome profile for. Empty makes it the profile's default: it serves every platform none of the profile's other Chrome profiles on this machine names.

binding.profile required string

The Blowhorn profile slug.

binding.subject_id string

The profile's subject uuid.

binding.updated_at string | null
dry_run required boolean
host required string
ok required any
pairing object
pairing.detail required string | null

Why the page was not opened, or null.

pairing.opened required boolean

Chrome was asked to open the pairing page in the mapped Chrome profile. Pairing is done once the page says so.

removed required array of ChromeBinding
removed[].account required string | null
removed[].created_at string | null
removed[].directory required string

The Chrome profile directory name, such as Profile 5.

removed[].display_name required string | null
removed[].host string
removed[].platforms array of string

The platforms a run uses this Chrome profile for. Empty makes it the profile's default: it serves every platform none of the profile's other Chrome profiles on this machine names.

removed[].profile required string

The Blowhorn profile slug.

removed[].subject_id string

The profile's subject uuid.

removed[].updated_at string | null
replaced required one of ChromeBinding, null
POST /cli/chrome/open-profile Open a window in the Chrome profile a Blowhorn profile is mapped to on this machine, so you can look at it or sign in to a platform there yourself. The mapping decides which Chrome profile opens, by the same rule a run uses: with --sign-in, the mapping chosen for that platform; without it, the profile's only mapping or its default one. A profile with no mapping here, a mapping Chrome no longer has, or a name or account that drifted since the mapping was made is refused by name and nothing opens -- a mapping is identity, never a guess at another profile. With --sign-in <platform> the window opens on that platform's sign-in page; without it, on a new tab. Blowhorn asks Chrome to open the window and steps back: it drives nothing in it, attaches nothing to it, and never restarts or closes Chrome -- a Chrome already running just gains the window. If no Chrome is running, Chrome starts to open the window and is left running. Writes nothing. After you sign in there, 'blowhorn chrome discover' sees the sign-in and records it as the profile's account.

Open a window in the Chrome profile a Blowhorn profile is mapped to on this machine, so you can look at it or sign in to a platform there yourself. The mapping decides which Chrome profile opens, by the same rule a run uses: with --sign-in, the mapping chosen for that platform; without it, the profile's only mapping or its default one. A profile with no mapping here, a mapping Chrome no longer has, or a name or account that drifted since the mapping was made is refused by name and nothing opens -- a mapping is identity, never a guess at another profile. With --sign-in <platform> the window opens on that platform's sign-in page; without it, on a new tab. Blowhorn asks Chrome to open the window and steps back: it drives nothing in it, attaches nothing to it, and never restarts or closes Chrome -- a Chrome already running just gains the window. If no Chrome is running, Chrome starts to open the window and is left running. Writes nothing. After you sign in there, 'blowhorn chrome discover' sees the sign-in and records it as the profile's account.

Request body required

The flags and arguments of `blowhorn chrome open-profile`, one property per parameter.

Request body of Open a window in the Chrome profile a Blowhorn profile is mapped to on this machine, so you can look at it or sign in to a platform there yourself. The mapping decides which Chrome profile opens, by the same rule a run uses: with --sign-in, the mapping chosen for that platform; without it, the profile's only mapping or its default one. A profile with no mapping here, a mapping Chrome no longer has, or a name or account that drifted since the mapping was made is refused by name and nothing opens -- a mapping is identity, never a guess at another profile. With --sign-in <platform> the window opens on that platform's sign-in page; without it, on a new tab. Blowhorn asks Chrome to open the window and steps back: it drives nothing in it, attaches nothing to it, and never restarts or closes Chrome -- a Chrome already running just gains the window. If no Chrome is running, Chrome starts to open the window and is left running. Writes nothing. After you sign in there, 'blowhorn chrome discover' sees the sign-in and records it as the profile's account. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

name required string

The Blowhorn profile, as the store's profiles name it (company)

pace string

Accepted by 'blowhorn chrome open-profile' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome open-profile' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

sign_in_platform string

Open on this platform's sign-in page instead of a new tab, and choose the mapping by that platform, as a run would. One of linkedin, x, reddit, hn, slack, bluesky, github

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What chrome open-profile prints with --json: the Chrome profile the Blowhorn profile's mapping chose on this machine, the page the window opened on (a platform's sign-in page with --sign-in, else a new tab), and whether Chrome was asked to open it. Whether you then signed in is read by a later session check, never by this command. A refusal (no mapping, a drifted or missing directory, a platform with no sign-in page) prints a Problem instead.

Response 200 of Open a window in the Chrome profile a Blowhorn profile is mapped to on this machine, so you can look at it or sign in to a platform there yourself. The mapping decides which Chrome profile opens, by the same rule a run uses: with --sign-in, the mapping chosen for that platform; without it, the profile's only mapping or its default one. A profile with no mapping here, a mapping Chrome no longer has, or a name or account that drifted since the mapping was made is refused by name and nothing opens -- a mapping is identity, never a guess at another profile. With --sign-in <platform> the window opens on that platform's sign-in page; without it, on a new tab. Blowhorn asks Chrome to open the window and steps back: it drives nothing in it, attaches nothing to it, and never restarts or closes Chrome -- a Chrome already running just gains the window. If no Chrome is running, Chrome starts to open the window and is left running. Writes nothing. After you sign in there, 'blowhorn chrome discover' sees the sign-in and records it as the profile's account. fields
NameTypeDescription
account required string

The signed-in account recorded when the mapping was made; empty when nobody was signed in.

detail required string | null

Why the window did not open ('chrome could not open that chrome profile: <reason>'), or the dry-run note; null when it opened.

directory required string

The Chrome profile directory the mapping chose, as chrome profiles prints it.

display_name required string

The display name recorded when the mapping was made.

dry_run required boolean
ok required boolean

Chrome was asked to open the window (always true on a dry run); false with exit 1 when it could not be asked.

opened required boolean

Chrome was asked to open the window in this profile.

platform required string | null

The --sign-in platform, or null when the window opened on a new tab.

profile required string

The Blowhorn profile whose mapping was opened.

url required string

The page the window opened on.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeOpenProfileResponse

Variant fields
NameTypeDescription
account required string

The signed-in account recorded when the mapping was made; empty when nobody was signed in.

detail required string | null

Why the window did not open ('chrome could not open that chrome profile: <reason>'), or the dry-run note; null when it opened.

directory required string

The Chrome profile directory the mapping chose, as chrome profiles prints it.

display_name required string

The display name recorded when the mapping was made.

dry_run required boolean
ok required boolean

Chrome was asked to open the window (always true on a dry run); false with exit 1 when it could not be asked.

opened required boolean

Chrome was asked to open the window in this profile.

platform required string | null

The --sign-in platform, or null when the window opened on a new tab.

profile required string

The Blowhorn profile whose mapping was opened.

url required string

The page the window opened on.

POST /cli/chrome/profiles List every profile Chrome knows: its directory name (the identity a mapping uses), its display name, the signed-in Google account (blank when nobody is signed in), a marker on the profile Chrome used last, and the Blowhorn profile it is mapped to on this machine with whether that mapping's recorded name and account still match ('ok' or 'drift'). The profile list is read from 'Local State' in the Chrome data directory, beside 'DevToolsActivePort', and from no other file; the mapping is read from the store once; no browser opens.

List every profile Chrome knows: its directory name (the identity a mapping uses), its display name, the signed-in Google account (blank when nobody is signed in), a marker on the profile Chrome used last, and the Blowhorn profile it is mapped to on this machine with whether that mapping's recorded name and account still match ('ok' or 'drift'). The profile list is read from 'Local State' in the Chrome data directory, beside 'DevToolsActivePort', and from no other file; the mapping is read from the store once; no browser opens.

--json emits the same list as JSON with a stable shape: one object per profile with directory, display_name, account, gaia_name, last_used, mapped_to (a Blowhorn profile name or null) and identity ('ok', 'drift' or null when unmapped), plus host, for a script or the desktop app to read. When 'Local State' is missing or cannot be read the command says so in one line and exits 1; a store that cannot be reached is the named cause and exit 3.

Request body required

The flags and arguments of `blowhorn chrome profiles`, one property per parameter.

Request body of List every profile Chrome knows: its directory name (the identity a mapping uses), its display name, the signed-in Google account (blank when nobody is signed in), a marker on the profile Chrome used last, and the Blowhorn profile it is mapped to on this machine with whether that mapping's recorded name and account still match ('ok' or 'drift'). The profile list is read from 'Local State' in the Chrome data directory, beside 'DevToolsActivePort', and from no other file; the mapping is read from the store once; no browser opens. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn chrome profiles' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome profiles' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The Chrome profiles found under the Chrome data directory, each with the Blowhorn profile mapped to it on this machine, if any. Account names are real people's; never commit a real output.

Response 200 of List every profile Chrome knows: its directory name (the identity a mapping uses), its display name, the signed-in Google account (blank when nobody is signed in), a marker on the profile Chrome used last, and the Blowhorn profile it is mapped to on this machine with whether that mapping's recorded name and account still match ('ok' or 'drift'). The profile list is read from 'Local State' in the Chrome data directory, beside 'DevToolsActivePort', and from no other file; the mapping is read from the store once; no browser opens. fields
NameTypeDescription
data_dir required string
host required string

The machine host the mappings are keyed by.

last_used required string | null

Directory of the profile Chrome last used.

local_state required object
local_state.detail required string | null
local_state.path required string
local_state.state required string
ok required boolean
profile_count required integer
profiles required array of object
profiles[].account required string

Signed-in Google account email, empty when nobody is signed in.

profiles[].directory required string
profiles[].display_name required string
profiles[].gaia_name required string
profiles[].identity required string | null

Whether the recorded identity of the mapping still holds (for example ok or drift); null when unmapped.

profiles[].last_used required boolean
profiles[].mapped_platforms required array of string

The platforms this Chrome profile is chosen for; empty when it is the profile's default or unmapped.

profiles[].mapped_to required string | null

The Blowhorn profile mapped to this Chrome profile on this host.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeProfilesResponse

Variant fields
NameTypeDescription
data_dir required string
host required string

The machine host the mappings are keyed by.

last_used required string | null

Directory of the profile Chrome last used.

local_state required object
local_state.detail required string | null
local_state.path required string
local_state.state required string
ok required boolean
profile_count required integer
profiles required array of object
profiles[].account required string

Signed-in Google account email, empty when nobody is signed in.

profiles[].directory required string
profiles[].display_name required string
profiles[].gaia_name required string
profiles[].identity required string | null

Whether the recorded identity of the mapping still holds (for example ok or drift); null when unmapped.

profiles[].last_used required boolean
profiles[].mapped_platforms required array of string

The platforms this Chrome profile is chosen for; empty when it is the profile's default or unmapped.

profiles[].mapped_to required string | null

The Blowhorn profile mapped to this Chrome profile on this host.

POST /cli/chrome/status Report whether Google Chrome is installed, whether it is running, whether its remote-debugging switch is on (chrome://inspect/#remote-debugging), the debugging port it recorded, how many profiles it has, and the launch mode a run would use right now: the configured setting (--chrome-launch, BLOWHORN_CHROME_LAUNCH, defaults.chrome.launch_mode, then the extension default), whether 'auto' would attach to this Chrome or launch Blowhorn's own, and why. When something is off, the last line says what to do next: where to turn the switch on, or that Chrome needs opening. Read-only: two files under the Chrome data directory and one process listing; no browser opens and nothing connects to Chrome.

Report whether Google Chrome is installed, whether it is running, whether its remote-debugging switch is on (chrome://inspect/#remote-debugging), the debugging port it recorded, how many profiles it has, and the launch mode a run would use right now: the configured setting (--chrome-launch, BLOWHORN_CHROME_LAUNCH, defaults.chrome.launch_mode, then the extension default), whether 'auto' would attach to this Chrome or launch Blowhorn's own, and why. When something is off, the last line says what to do next: where to turn the switch on, or that Chrome needs opening. Read-only: two files under the Chrome data directory and one process listing; no browser opens and nothing connects to Chrome.

--json emits the same report as JSON with a stable shape (installed, running, remote_debugging.enabled, remote_debugging.port, profile_count, last_used, ready, next_step, launch_mode.setting, launch_mode.mode, launch_mode.reason). The command exits 0 whenever it could report, whatever the report says.

Request body required

The flags and arguments of `blowhorn chrome status`, one property per parameter.

Request body of Report whether Google Chrome is installed, whether it is running, whether its remote-debugging switch is on (chrome://inspect/#remote-debugging), the debugging port it recorded, how many profiles it has, and the launch mode a run would use right now: the configured setting (--chrome-launch, BLOWHORN_CHROME_LAUNCH, defaults.chrome.launch_mode, then the extension default), whether 'auto' would attach to this Chrome or launch Blowhorn's own, and why. When something is off, the last line says what to do next: where to turn the switch on, or that Chrome needs opening. Read-only: two files under the Chrome data directory and one process listing; no browser opens and nothing connects to Chrome. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn chrome status' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome status' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints what Blowhorn can see of the operator's Chrome (the install and its version, whether it runs, the debugging port) and whether the configured launch mode is ready. Reads the app bundle's Info.plist and two files under Chrome's data directory, and nothing else.

Response 200 of Report whether Google Chrome is installed, whether it is running, whether its remote-debugging switch is on (chrome://inspect/#remote-debugging), the debugging port it recorded, how many profiles it has, and the launch mode a run would use right now: the configured setting (--chrome-launch, BLOWHORN_CHROME_LAUNCH, defaults.chrome.launch_mode, then the extension default), whether 'auto' would attach to this Chrome or launch Blowhorn's own, and why. When something is off, the last line says what to do next: where to turn the switch on, or that Chrome needs opening. Read-only: two files under the Chrome data directory and one process listing; no browser opens and nothing connects to Chrome. fields
NameTypeDescription
app_path required string | null
data_dir required string
devtools_port required ChromeFileCheck

What one of Chrome's files (`Local State`, `DevToolsActivePort`) said, or why it could not be read.

devtools_port.detail required string | null

Why the file is not `ok`; null when it is.

devtools_port.path required string
devtools_port.state required string

Values: ok, missing, unreadable, malformed

installed required boolean
last_used required string | null

The Chrome profile directory used last.

launch_mode required ChromeLaunchMode

How a run would reach Chrome right now, and whether that route is ready.

launch_mode.forced required boolean

True unless the setting is `auto`.

launch_mode.mode required string

The mode a run would use after `auto` is resolved.

launch_mode.ready required boolean
launch_mode.reason required string
launch_mode.setting required string

The configured launch mode (extension, persistent, cdp or auto).

launch_mode.source required string

Which layer chose the setting (flag, env, config or builtin).

launch_mode_note required string | null
local_state required ChromeFileCheck

What one of Chrome's files (`Local State`, `DevToolsActivePort`) said, or why it could not be read.

local_state.detail required string | null

Why the file is not `ok`; null when it is.

local_state.path required string
local_state.state required string

Values: ok, missing, unreadable, malformed

minimum_version required integer

The oldest Chrome major Blowhorn supports.

next_step required string | null
ok required any
profile_count required integer
ready required boolean
remote_debugging required ChromeRemoteDebugging

Whether Chrome's remote-debugging server is up, read from `DevToolsActivePort`. Every field is null when the file said nothing.

remote_debugging.browser_path required string | null
remote_debugging.enabled required boolean | null
remote_debugging.port required integer | null
running required boolean
supported required boolean | null

Whether version is at least minimum_version; null when the version is unknown.

version required string | null

The Chrome app bundle's CFBundleShortVersionString; null when it could not be read.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeStatusResponse

Variant fields
NameTypeDescription
app_path required string | null
data_dir required string
devtools_port required ChromeFileCheck

What one of Chrome's files (`Local State`, `DevToolsActivePort`) said, or why it could not be read.

devtools_port.detail required string | null

Why the file is not `ok`; null when it is.

devtools_port.path required string
devtools_port.state required string

Values: ok, missing, unreadable, malformed

installed required boolean
last_used required string | null

The Chrome profile directory used last.

launch_mode required ChromeLaunchMode

How a run would reach Chrome right now, and whether that route is ready.

launch_mode.forced required boolean

True unless the setting is `auto`.

launch_mode.mode required string

The mode a run would use after `auto` is resolved.

launch_mode.ready required boolean
launch_mode.reason required string
launch_mode.setting required string

The configured launch mode (extension, persistent, cdp or auto).

launch_mode.source required string

Which layer chose the setting (flag, env, config or builtin).

launch_mode_note required string | null
local_state required ChromeFileCheck

What one of Chrome's files (`Local State`, `DevToolsActivePort`) said, or why it could not be read.

local_state.detail required string | null

Why the file is not `ok`; null when it is.

local_state.path required string
local_state.state required string

Values: ok, missing, unreadable, malformed

minimum_version required integer

The oldest Chrome major Blowhorn supports.

next_step required string | null
ok required any
profile_count required integer
ready required boolean
remote_debugging required ChromeRemoteDebugging

Whether Chrome's remote-debugging server is up, read from `DevToolsActivePort`. Every field is null when the file said nothing.

remote_debugging.browser_path required string | null
remote_debugging.enabled required boolean | null
remote_debugging.port required integer | null
running required boolean
supported required boolean | null

Whether version is at least minimum_version; null when the version is unknown.

version required string | null

The Chrome app bundle's CFBundleShortVersionString; null when it could not be read.

POST /cli/chrome/suggest Print a proposed mapping from every Blowhorn profile in the store to a Chrome profile on this machine, matched by display name (a Chrome profile named 'Company' proposes for the Blowhorn profile company), and apply nothing. Each row says why: 'name match', 'none' when no Chrome profile matches, 'ambiguous' when two match, 'already mapped' for a profile that has a mapping, and the holder's name when the matching directory is mapped to someone else. Apply a row you agree with by running the 'blowhorn chrome map' line it prints.

Print a proposed mapping from every Blowhorn profile in the store to a Chrome profile on this machine, matched by display name (a Chrome profile named 'Company' proposes for the Blowhorn profile company), and apply nothing. Each row says why: 'name match', 'none' when no Chrome profile matches, 'ambiguous' when two match, 'already mapped' for a profile that has a mapping, and the holder's name when the matching directory is mapped to someone else. Apply a row you agree with by running the 'blowhorn chrome map' line it prints.

--json emits the same rows as JSON. A store that cannot be reached is the named cause and exit 3.

Request body required

The flags and arguments of `blowhorn chrome suggest`, one property per parameter.

Request body of Print a proposed mapping from every Blowhorn profile in the store to a Chrome profile on this machine, matched by display name (a Chrome profile named 'Company' proposes for the Blowhorn profile company), and apply nothing. Each row says why: 'name match', 'none' when no Chrome profile matches, 'ambiguous' when two match, 'already mapped' for a profile that has a mapping, and the holder's name when the matching directory is mapped to someone else. Apply a row you agree with by running the 'blowhorn chrome map' line it prints. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn chrome suggest' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome suggest' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints, for each Blowhorn profile, the Chrome profile directory whose signed-in account looks like it, and whether the profile is already mapped. Nothing is written.

Response 200 of Print a proposed mapping from every Blowhorn profile in the store to a Chrome profile on this machine, matched by display name (a Chrome profile named 'Company' proposes for the Blowhorn profile company), and apply nothing. Each row says why: 'name match', 'none' when no Chrome profile matches, 'ambiguous' when two match, 'already mapped' for a profile that has a mapping, and the holder's name when the matching directory is mapped to someone else. Apply a row you agree with by running the 'blowhorn chrome map' line it prints. fields
NameTypeDescription
data_dir required string
host required string
ok required any
suggestions required array of object
suggestions[].account required string | null
suggestions[].directory required string | null
suggestions[].display_name required string | null
suggestions[].identity required string | null

Whether the mapped directory still shows the expected account (ok, drift, missing or unknown); null when unmapped.

suggestions[].mapped required boolean
suggestions[].note required string | null
suggestions[].profile required string

The profile's name.

suggestions[].profile_id required string (uuid)

The profile's id, its identity; the name can change, the id never does.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeSuggestResponse

Variant fields
NameTypeDescription
data_dir required string
host required string
ok required any
suggestions required array of object
suggestions[].account required string | null
suggestions[].directory required string | null
suggestions[].display_name required string | null
suggestions[].identity required string | null

Whether the mapped directory still shows the expected account (ok, drift, missing or unknown); null when unmapped.

suggestions[].mapped required boolean
suggestions[].note required string | null
suggestions[].profile required string

The profile's name.

suggestions[].profile_id required string (uuid)

The profile's id, its identity; the name can change, the id never does.

POST /cli/chrome/unmap Remove one of a Blowhorn profile's Chrome profile mappings on this machine. Name the Chrome profile directory when the profile has more than one mapped here; with one, it may be left out. Chrome is not read and nothing under its directory is touched: only the store's row goes, so a mapping can be removed when 'Local State' is missing or unreadable. A profile with no mapping on this machine, or several and no directory named, is a one-sentence refusal and exit 1; with --dry-run the command says what would be removed and removes nothing.

Remove one of a Blowhorn profile's Chrome profile mappings on this machine. Name the Chrome profile directory when the profile has more than one mapped here; with one, it may be left out. Chrome is not read and nothing under its directory is touched: only the store's row goes, so a mapping can be removed when 'Local State' is missing or unreadable. A profile with no mapping on this machine, or several and no directory named, is a one-sentence refusal and exit 1; with --dry-run the command says what would be removed and removes nothing.

Request body required

The flags and arguments of `blowhorn chrome unmap`, one property per parameter.

Request body of Remove one of a Blowhorn profile's Chrome profile mappings on this machine. Name the Chrome profile directory when the profile has more than one mapped here; with one, it may be left out. Chrome is not read and nothing under its directory is touched: only the store's row goes, so a mapping can be removed when 'Local State' is missing or unreadable. A profile with no mapping on this machine, or several and no directory named, is a one-sentence refusal and exit 1; with --dry-run the command says what would be removed and removes nothing. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

directory string

The Chrome profile directory to unmap, as 'blowhorn chrome profiles' prints it; needed only when the profile has more than one mapped on this machine

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

name required string

The Blowhorn profile, as the store's profiles name it (company)

pace string

Accepted by 'blowhorn chrome unmap' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn chrome unmap' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The one mapping chrome unmap removed from this machine, or, with --dry-run, would remove. A profile with several mappings names the directory to unmap.

Response 200 of Remove one of a Blowhorn profile's Chrome profile mappings on this machine. Name the Chrome profile directory when the profile has more than one mapped here; with one, it may be left out. Chrome is not read and nothing under its directory is touched: only the store's row goes, so a mapping can be removed when 'Local State' is missing or unreadable. A profile with no mapping on this machine, or several and no directory named, is a one-sentence refusal and exit 1; with --dry-run the command says what would be removed and removes nothing. fields
NameTypeDescription
dry_run required boolean
host required string
ok required boolean
removed required object
removed.account required string
removed.created_at required string | null
removed.directory required string
removed.display_name required string
removed.host required string
removed.platforms required array of string
removed.profile required string
removed.subject_id required string
removed.updated_at required string | null

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ChromeUnmapResponse

Variant fields
NameTypeDescription
dry_run required boolean
host required string
ok required boolean
removed required object
removed.account required string
removed.created_at required string | null
removed.directory required string
removed.display_name required string
removed.host required string
removed.platforms required array of string
removed.profile required string
removed.subject_id required string
removed.updated_at required string | null

Auth

POST /cli/auth/anonymous-usage-stats Show or set whether your account shares anonymous usage data for product improvement.

Show or set whether your account shares anonymous usage data for product improvement.

Reads and writes anonymousUsageStats in your Blowhorn account's preferences (GET /api/identity/users/profile, PUT /api/identity/users/preferences with that one field, so no other preference changes). It belongs to the account, so every computer you use Blowhorn on follows it. Not set reads as on. Usage data is anonymous counts of what Blowhorn does; it never carries a post's text, a message, a credential or the contents of a page Blowhorn drives. Failure reports are a separate setting in the desktop app. With no flag it reads your account and always exits 0: signed out or unreachable, it reports the setting last read for this account (or on) and says why. --on and --off save the setting and exit 1 when your account cannot be written. --dry-run reads and sends nothing.

Request body required

The flags and arguments of `blowhorn auth anonymous-usage-stats`, one property per parameter.

Request body of Show or set whether your account shares anonymous usage data for product improvement. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn auth anonymous-usage-stats' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn auth': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn auth anonymous-usage-stats' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

usage_off boolean

Stop sharing anonymous usage data (saved to your account).

Default: false

usage_on boolean

Share anonymous usage data for product improvement (saved to your account).

Default: false

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What auth anonymous-usage-stats prints with --json: whether your account shares anonymous usage data for product improvement, the anonymousUsageStats preference as stored, and where the value came from. Not set reads as on. With --on or --off, written says the setting was saved to your Blowhorn account; a write that fails prints ok false and the reason. With --dry-run and --on or --off, nothing is sent: written is false, dry_run is true and would_set is the value that would have been written.

object

Variant fields
NameTypeDescription
account required boolean | null

The anonymousUsageStats account preference, or null when it is not set.

account_error required string | null

Why your account could not be read; account is then the value last read for this account, or null.

anonymousUsageStats required boolean

Whether usage data may be shared. True unless the account says false.

dry_run any

Present only with --dry-run and --on or --off; nothing was sent.

ok required any
source required string

account when the account holds a value, default when it is not set (or could not be read and nothing was cached).

Values: account, default

would_set boolean

Present only with --dry-run; the anonymousUsageStats value --on or --off would have written.

written required boolean

object

Variant fields
NameTypeDescription
error required string
ok required any

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AuthAnonymousUsageStatsResponse

POST /cli/auth/login Sign in to Blowhorn at https://cloud.blowhorn.ai in your browser.

Sign in to Blowhorn at https://cloud.blowhorn.ai in your browser.

Opens https://cloud.blowhorn.ai, where you sign in or create an account and allow the Blowhorn app, then returns here and keeps the token in the macOS Keychain (elsewhere a file only you can read beside cloud.yaml). It uses Blowhorn's native client sign-in: the browser answers on 127.0.0.1 with a one-time code this command redeems with a PKCE verifier; there is no client secret, and the token is never printed. After sign-in it reads your plan once; an account without a Blowhorn plan is sent straight to https://cloud.blowhorn.ai to start the beta. --no-browser prints the sign-in address instead of opening it; --json opens no browser either, its first line names the address to open (the desktop app runs --no-browser --json and opens it itself). --dry-run names where it would sign in and opens, waits for and saves nothing.

Request body required

The flags and arguments of `blowhorn auth login`, one property per parameter.

Request body of Sign in to Blowhorn at https://cloud.blowhorn.ai in your browser. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

no_browser boolean

Print the sign-in address and wait for the answer on 127.0.0.1 without opening a browser (--json implies it).

Default: false

pace string

Accepted by 'blowhorn auth login' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn auth': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn auth login' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

sign_up boolean

Open https://cloud.blowhorn.ai to create a new account instead of signing in; it comes back the same way.

Default: false

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What auth login prints on success. With --json and a real sign-in, stdout is a JSON Lines stream, not a single JSON document: a one-line {"event":"authorize"} object (not covered here) comes first, so the desktop app, which runs --no-browser, can open the browser when it appears, then this payload as the second line. A consumer must read stdout line by line, one JSON object per line. --dry-run prints the dry-run shape and opens or saves nothing.

object

Variant fields
NameTypeDescription
display_name required string | null
entitled required boolean | null

Whether the plan grants Blowhorn; null when the plan could not be read.

notice required string | null

Set when this organization has more computers registered than its plan allows.

ok required boolean
organization_id required string | null
previous_token_id_unknown boolean

True when a token was kept but Blowhorn never learned its id or issuing server, so it could not be revoked and may remain in your Blowhorn account's token list.

previous_token_revoked boolean | null

Whether the Blowhorn API token this sign-in replaced was revoked; null when none was kept. Also false when its id or issuing server is unknown (see previous_token_id_unknown).

signed_in required boolean
start_url required string | null

Blowhorn address to start the beta, set only when entitled is false.

token_store required string

Where the token was kept. The token itself is never printed.

Values: keychain, file

update_prompt required string | null

Set when this build has to be updated before it can register this computer.

object

Variant fields
NameTypeDescription
authorize_url required string

Blowhorn's native client consent page sign-in continues to.

dry_run required boolean
login_url required string

The page the browser opens first - Blowhorn's sign-in page, or its sign-up page with --sign-up.

ok required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AuthLoginResponse

POST /cli/auth/logout Sign out of Blowhorn and clear the local entitlement attestation cache.

Sign out of Blowhorn and clear the local entitlement attestation cache.

Removes the token 'blowhorn auth login' saved (Keychain item ai.blowhorn.cloud-token), the account it wrote to cloud.yaml (organization_id, display_name and the account facts), the process cache and the Keychain attestation item (ai.blowhorn.entitlement.v2). Unset BLOWHORN_CLOUD_TOKEN in the shell if it was exported.

Request body required

The flags and arguments of `blowhorn auth logout`, one property per parameter.

Request body of Sign out of Blowhorn and clear the local entitlement attestation cache. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn auth logout' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn auth': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn auth logout' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Confirms the saved Blowhorn token, account and entitlement cache were removed (or, with --dry-run, would be), and whether the token was revoked; or why sign-out failed.

object

Variant fields
NameTypeDescription
message required string
ok required any
revoked required boolean | null

True when Blowhorn revoked the saved Blowhorn API token. False when it was kept but not revoked: Blowhorn refused, or does not know its id or issuing server, so it may remain in your Blowhorn account's token list (the message says which). Null when no token was kept, and on --dry-run, which revokes nothing.

object

Variant fields
NameTypeDescription
error required string
ok required any

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AuthLogoutResponse

POST /cli/auth/status Show whether Blowhorn credentials and an entitlement attestation are present.

Show whether Blowhorn credentials and an entitlement attestation are present.

Reports token configured (never the token value), organization id, api_base, enforcement mode (BLOWHORN_ENTITLEMENT), and a summary of the last Feature read. Live, it first re-reads the signed-in account from Blowhorn (name, photo, organizations, plan) into cloud.yaml and follows the organization your Blowhorn session in the browser has selected; BLOWHORN_CLOUD_ORGANIZATION still wins when set, and the plan read and shown is that organization's. --offline answers from presence checks only: it reads no saved secret, so macOS shows no Keychain prompt, and it makes no network request (the desktop app runs it at launch). Exits 3 when this computer is not signed in: no token, a token Blowhorn refused, or Blowhorn could not be reached. --offline exits 3 only when no token is configured.

Request body required

The flags and arguments of `blowhorn auth status`, one property per parameter.

Request body of Show whether Blowhorn credentials and an entitlement attestation are present. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

offline boolean

Check only whether a token and a saved plan are present; read no secret and make no request.

Default: false

pace string

Accepted by 'blowhorn auth status' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn auth': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn auth status' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Blowhorn sign-in and entitlement state. The token itself is never printed, only whether one is configured. With --offline every field comes from presence checks: no saved secret is read (so macOS shows no Keychain prompt), no request is made, and attestation is null.

Response 200 of Show whether Blowhorn credentials and an entitlement attestation are present. fields
NameTypeDescription
account required object | null

Who is signed in, for the desktop app's account button and menu. Read from cloud.yaml, where sign-in caches it and a live auth status refreshes it from Blowhorn's own answers, each checked against the meshery/schemas construct Blowhorn consumes (schemas/meshery/consumed.yaml): User and its Preference from GET /api/identity/users/profile, AvailableOrganization from GET /api/identity/orgs, and the organization's Subscription and its Plan from /api/entitlement. So --offline answers it with no secret read and no request. Null when no token is configured. A field Blowhorn did not answer is null. The organization is the one organization_id names: the session's active organization (Preference.selectedOrganizationId when the account is still a member, else the first listed, the same choice the browser session makes), unless BLOWHORN_CLOUD_ORGANIZATION names another.

account.avatar_url required string | null

User.avatarUrl (meshery/schemas v1beta2 user), the profile photo in your Blowhorn account.

account.email required string | null

User.email (meshery/schemas v1beta2 user).

account.first_name required string | null

User.firstName (meshery/schemas v1beta2 user).

account.last_name required string | null

User.lastName (meshery/schemas v1beta2 user).

account.organization_count required integer | null

How many live organizations the account belongs to, as GET /api/identity/orgs last counted them; more than one offers Switch organization.

account.organization_name required string | null

AvailableOrganization.name (meshery/schemas v1beta2 organization) of the organization this status reports.

account.organizations required array of object

The account's live organizations (GET /api/identity/orgs, soft-deleted ones left out, up to Blowhorn's page of 100), in Blowhorn's order; Switch organization offers these.

account.organizations[].description required string | null

AvailableOrganization.description (meshery/schemas v1beta2 organization), null when Cloud gave none; shown on hover beside the id to tell two organizations of the same name apart.

account.organizations[].id required string

AvailableOrganization.id (meshery/schemas v1beta2 organization).

account.organizations[].name required string | null

AvailableOrganization.name (meshery/schemas v1beta2 organization).

account.plan_name required string | null

Plan.name (meshery/schemas v1beta3 plan, the PlanName enum) of the organization this status reports, exactly as Blowhorn names it, and Free when Blowhorn answered with no current subscription (every organization is on Free unless one names another plan). Null only when it has never been read for that organization; the app then shows the organization alone.

Values: Free, Team Designer, Team Operator, Enterprise,

api_base required string | null
attestation required object | null

The cached plan attestation, or null when none has been read.

attestation.age_seconds required integer
attestation.features required object
attestation.features.browser_automation required boolean
attestation.features.max_devices_per_seat required integer | null
attestation.features.max_profiles required integer | null

Null means unlimited.

attestation.features.platforms required array of string
attestation.features.scheduler required boolean
attestation.fetched_at required string

ISO 8601 UTC instant the plan was fetched.

attestation.fresh required boolean
attestation.organization_id required string | null
attestation_present required boolean

Whether a saved plan is held. With --offline, from the Keychain item's attributes only; live, whether attestation is non-null.

checked required string

presence for --offline (attributes only: no secret read, no request); live when the saved plan was read and, when due, refreshed from Blowhorn.

Values: presence, live

cloud_yaml required string | null

Path of the cloud.yaml in use, if any.

display_name required string | null
enforcement required string

The entitlement enforcement mode, or "invalid (<reason>)" when the setting does not parse.

fetch_error required string | null
ok required boolean

False when this computer is not signed in. True when a token is configured and, live, Blowhorn accepted it.

organization_id required string | null
permission_keys array of string

Live only: the ids of the permission keys the signed-in session holds in the Blowhorn parent organization (GET /api/identity/orgs/by-domain resolves the parent organization, then GET /api/identity/orgs/{id}/users/keys), which the desktop app's Diagnostics gate matches the Access Diagnostics key id against. Empty when no token is configured or when either read fails: the gate fails closed. Absent from the --offline payload, which makes no request.

previous_account required object | null

Who was signed in on this computer before the session ended, for the desktop app's returning sign-in screen, which greets that person by name. Read from cloud.yaml, where sign-out removes the account: a name still there with no token configured means the token was lost, removed or has timed out, never that the person signed out. Only the name and email, never the photo or the organizations. Null while a token is configured (account answers then), after a sign-out, and when cloud.yaml names nobody. Answered by --offline too: no secret read, no request.

previous_account.email required string | null

User.email (meshery/schemas v1beta2 user).

previous_account.first_name required string | null

User.firstName (meshery/schemas v1beta2 user).

previous_account.last_name required string | null

User.lastName (meshery/schemas v1beta2 user).

ready_to_fetch required boolean
sign_in_problem string

Why this computer is not signed in, present only when signed_in is false. no token means none is configured. token refused means Blowhorn answered 401 or 403. unreachable means Blowhorn could not be reached. --offline reports only no token.

Values: no token, token refused, unreachable

signed_in required boolean

Whether this computer is signed in to Blowhorn.

token_configured required boolean

Whether a Blowhorn token is configured. The token is never printed.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AuthStatusResponse

Variant fields
NameTypeDescription
account required object | null

Who is signed in, for the desktop app's account button and menu. Read from cloud.yaml, where sign-in caches it and a live auth status refreshes it from Blowhorn's own answers, each checked against the meshery/schemas construct Blowhorn consumes (schemas/meshery/consumed.yaml): User and its Preference from GET /api/identity/users/profile, AvailableOrganization from GET /api/identity/orgs, and the organization's Subscription and its Plan from /api/entitlement. So --offline answers it with no secret read and no request. Null when no token is configured. A field Blowhorn did not answer is null. The organization is the one organization_id names: the session's active organization (Preference.selectedOrganizationId when the account is still a member, else the first listed, the same choice the browser session makes), unless BLOWHORN_CLOUD_ORGANIZATION names another.

account.avatar_url required string | null

User.avatarUrl (meshery/schemas v1beta2 user), the profile photo in your Blowhorn account.

account.email required string | null

User.email (meshery/schemas v1beta2 user).

account.first_name required string | null

User.firstName (meshery/schemas v1beta2 user).

account.last_name required string | null

User.lastName (meshery/schemas v1beta2 user).

account.organization_count required integer | null

How many live organizations the account belongs to, as GET /api/identity/orgs last counted them; more than one offers Switch organization.

account.organization_name required string | null

AvailableOrganization.name (meshery/schemas v1beta2 organization) of the organization this status reports.

account.organizations required array of object

The account's live organizations (GET /api/identity/orgs, soft-deleted ones left out, up to Blowhorn's page of 100), in Blowhorn's order; Switch organization offers these.

account.organizations[].description required string | null

AvailableOrganization.description (meshery/schemas v1beta2 organization), null when Cloud gave none; shown on hover beside the id to tell two organizations of the same name apart.

account.organizations[].id required string

AvailableOrganization.id (meshery/schemas v1beta2 organization).

account.organizations[].name required string | null

AvailableOrganization.name (meshery/schemas v1beta2 organization).

account.plan_name required string | null

Plan.name (meshery/schemas v1beta3 plan, the PlanName enum) of the organization this status reports, exactly as Blowhorn names it, and Free when Blowhorn answered with no current subscription (every organization is on Free unless one names another plan). Null only when it has never been read for that organization; the app then shows the organization alone.

Values: Free, Team Designer, Team Operator, Enterprise,

api_base required string | null
attestation required object | null

The cached plan attestation, or null when none has been read.

attestation.age_seconds required integer
attestation.features required object
attestation.features.browser_automation required boolean
attestation.features.max_devices_per_seat required integer | null
attestation.features.max_profiles required integer | null

Null means unlimited.

attestation.features.platforms required array of string
attestation.features.scheduler required boolean
attestation.fetched_at required string

ISO 8601 UTC instant the plan was fetched.

attestation.fresh required boolean
attestation.organization_id required string | null
attestation_present required boolean

Whether a saved plan is held. With --offline, from the Keychain item's attributes only; live, whether attestation is non-null.

checked required string

presence for --offline (attributes only: no secret read, no request); live when the saved plan was read and, when due, refreshed from Blowhorn.

Values: presence, live

cloud_yaml required string | null

Path of the cloud.yaml in use, if any.

display_name required string | null
enforcement required string

The entitlement enforcement mode, or "invalid (<reason>)" when the setting does not parse.

fetch_error required string | null
ok required boolean

False when this computer is not signed in. True when a token is configured and, live, Blowhorn accepted it.

organization_id required string | null
permission_keys array of string

Live only: the ids of the permission keys the signed-in session holds in the Blowhorn parent organization (GET /api/identity/orgs/by-domain resolves the parent organization, then GET /api/identity/orgs/{id}/users/keys), which the desktop app's Diagnostics gate matches the Access Diagnostics key id against. Empty when no token is configured or when either read fails: the gate fails closed. Absent from the --offline payload, which makes no request.

previous_account required object | null

Who was signed in on this computer before the session ended, for the desktop app's returning sign-in screen, which greets that person by name. Read from cloud.yaml, where sign-out removes the account: a name still there with no token configured means the token was lost, removed or has timed out, never that the person signed out. Only the name and email, never the photo or the organizations. Null while a token is configured (account answers then), after a sign-out, and when cloud.yaml names nobody. Answered by --offline too: no secret read, no request.

previous_account.email required string | null

User.email (meshery/schemas v1beta2 user).

previous_account.first_name required string | null

User.firstName (meshery/schemas v1beta2 user).

previous_account.last_name required string | null

User.lastName (meshery/schemas v1beta2 user).

ready_to_fetch required boolean
sign_in_problem string

Why this computer is not signed in, present only when signed_in is false. no token means none is configured. token refused means Blowhorn answered 401 or 403. unreachable means Blowhorn could not be reached. --offline reports only no token.

Values: no token, token refused, unreachable

signed_in required boolean

Whether this computer is signed in to Blowhorn.

token_configured required boolean

Whether a Blowhorn token is configured. The token is never printed.

POST /cli/auth/switch-organization Switch the signed-in account to another of its organizations.

Switch the signed-in account to another of its organizations.

Makes ORGANIZATION_ID the organization the account's Blowhorn session has selected, with the same write Blowhorn's and Meshery's own organization switchers send (PUT /api/identity/users/preferences, selectedOrganizationId), so the browser and this computer follow it. ORGANIZATION_ID must be one of the account's organizations ('auth status --json' lists them as account.organizations). Then reports what 'auth status' reports, read live for the new organization. BLOWHORN_CLOUD_ORGANIZATION, when set, still names the organization this computer reports. --dry-run checks the id and sends nothing.

Request body required

The flags and arguments of `blowhorn auth switch-organization`, one property per parameter.

Request body of Switch the signed-in account to another of its organizations. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

organization_id required string

The id of one of the account's organizations (auth status --json: account.organizations).

pace string

Accepted by 'blowhorn auth switch-organization' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn auth': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn auth switch-organization' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What auth status reports, read live after the account's selected organization was switched in Blowhorn (or, with --dry-run, read offline with nothing sent). BLOWHORN_CLOUD_ORGANIZATION, when set, still names the organization this computer reports.

Response 200 of Switch the signed-in account to another of its organizations. fields
NameTypeDescription
account required object | null

Who is signed in, for the desktop app's account button and menu. Read from cloud.yaml, where sign-in caches it and a live auth status refreshes it from Blowhorn's own answers, each checked against the meshery/schemas construct Blowhorn consumes (schemas/meshery/consumed.yaml): User and its Preference from GET /api/identity/users/profile, AvailableOrganization from GET /api/identity/orgs, and the organization's Subscription and its Plan from /api/entitlement. So --offline answers it with no secret read and no request. Null when no token is configured. A field Blowhorn did not answer is null. The organization is the one organization_id names: the session's active organization (Preference.selectedOrganizationId when the account is still a member, else the first listed, the same choice the browser session makes), unless BLOWHORN_CLOUD_ORGANIZATION names another.

account.avatar_url required string | null

User.avatarUrl (meshery/schemas v1beta2 user), the profile photo in your Blowhorn account.

account.email required string | null

User.email (meshery/schemas v1beta2 user).

account.first_name required string | null

User.firstName (meshery/schemas v1beta2 user).

account.last_name required string | null

User.lastName (meshery/schemas v1beta2 user).

account.organization_count required integer | null

How many live organizations the account belongs to, as GET /api/identity/orgs last counted them; more than one offers Switch organization.

account.organization_name required string | null

AvailableOrganization.name (meshery/schemas v1beta2 organization) of the organization this status reports.

account.organizations required array of object

The account's live organizations (GET /api/identity/orgs, soft-deleted ones left out, up to Blowhorn's page of 100), in Blowhorn's order; Switch organization offers these.

account.organizations[].description required string | null

AvailableOrganization.description (meshery/schemas v1beta2 organization), null when Cloud gave none; shown on hover beside the id to tell two organizations of the same name apart.

account.organizations[].id required string

AvailableOrganization.id (meshery/schemas v1beta2 organization).

account.organizations[].name required string | null

AvailableOrganization.name (meshery/schemas v1beta2 organization).

account.plan_name required string | null

Plan.name (meshery/schemas v1beta3 plan, the PlanName enum) of the organization this status reports, exactly as Blowhorn names it, and Free when Blowhorn answered with no current subscription (every organization is on Free unless one names another plan). Null only when it has never been read for that organization; the app then shows the organization alone.

Values: Free, Team Designer, Team Operator, Enterprise,

api_base required string | null
attestation required object | null

The cached plan attestation, or null when none has been read.

attestation.age_seconds required integer
attestation.features required object
attestation.features.browser_automation required boolean
attestation.features.max_devices_per_seat required integer | null
attestation.features.max_profiles required integer | null

Null means unlimited.

attestation.features.platforms required array of string
attestation.features.scheduler required boolean
attestation.fetched_at required string

ISO 8601 UTC instant the plan was fetched.

attestation.fresh required boolean
attestation.organization_id required string | null
attestation_present required boolean

Whether a saved plan is held. With --offline, from the Keychain item's attributes only; live, whether attestation is non-null.

checked required string

presence for --offline (attributes only: no secret read, no request); live when the saved plan was read and, when due, refreshed from Blowhorn.

Values: presence, live

cloud_yaml required string | null

Path of the cloud.yaml in use, if any.

display_name required string | null
enforcement required string

The entitlement enforcement mode, or "invalid (<reason>)" when the setting does not parse.

fetch_error required string | null
ok required boolean

False when this computer is not signed in. True when a token is configured and, live, Blowhorn accepted it.

organization_id required string | null
permission_keys array of string

Live only: the ids of the permission keys the signed-in session holds in the Blowhorn parent organization (GET /api/identity/orgs/by-domain resolves the parent organization, then GET /api/identity/orgs/{id}/users/keys), which the desktop app's Diagnostics gate matches the Access Diagnostics key id against. Empty when no token is configured or when either read fails: the gate fails closed. Absent from the --offline payload, which makes no request.

previous_account required object | null

Who was signed in on this computer before the session ended, for the desktop app's returning sign-in screen, which greets that person by name. Read from cloud.yaml, where sign-out removes the account: a name still there with no token configured means the token was lost, removed or has timed out, never that the person signed out. Only the name and email, never the photo or the organizations. Null while a token is configured (account answers then), after a sign-out, and when cloud.yaml names nobody. Answered by --offline too: no secret read, no request.

previous_account.email required string | null

User.email (meshery/schemas v1beta2 user).

previous_account.first_name required string | null

User.firstName (meshery/schemas v1beta2 user).

previous_account.last_name required string | null

User.lastName (meshery/schemas v1beta2 user).

ready_to_fetch required boolean
sign_in_problem string

Why this computer is not signed in, present only when signed_in is false. no token means none is configured. token refused means Blowhorn answered 401 or 403. unreachable means Blowhorn could not be reached. --offline reports only no token.

Values: no token, token refused, unreachable

signed_in required boolean

Whether this computer is signed in to Blowhorn.

token_configured required boolean

Whether a Blowhorn token is configured. The token is never printed.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AuthSwitchOrganizationResponse

Variant fields
NameTypeDescription
account required object | null

Who is signed in, for the desktop app's account button and menu. Read from cloud.yaml, where sign-in caches it and a live auth status refreshes it from Blowhorn's own answers, each checked against the meshery/schemas construct Blowhorn consumes (schemas/meshery/consumed.yaml): User and its Preference from GET /api/identity/users/profile, AvailableOrganization from GET /api/identity/orgs, and the organization's Subscription and its Plan from /api/entitlement. So --offline answers it with no secret read and no request. Null when no token is configured. A field Blowhorn did not answer is null. The organization is the one organization_id names: the session's active organization (Preference.selectedOrganizationId when the account is still a member, else the first listed, the same choice the browser session makes), unless BLOWHORN_CLOUD_ORGANIZATION names another.

account.avatar_url required string | null

User.avatarUrl (meshery/schemas v1beta2 user), the profile photo in your Blowhorn account.

account.email required string | null

User.email (meshery/schemas v1beta2 user).

account.first_name required string | null

User.firstName (meshery/schemas v1beta2 user).

account.last_name required string | null

User.lastName (meshery/schemas v1beta2 user).

account.organization_count required integer | null

How many live organizations the account belongs to, as GET /api/identity/orgs last counted them; more than one offers Switch organization.

account.organization_name required string | null

AvailableOrganization.name (meshery/schemas v1beta2 organization) of the organization this status reports.

account.organizations required array of object

The account's live organizations (GET /api/identity/orgs, soft-deleted ones left out, up to Blowhorn's page of 100), in Blowhorn's order; Switch organization offers these.

account.organizations[].description required string | null

AvailableOrganization.description (meshery/schemas v1beta2 organization), null when Cloud gave none; shown on hover beside the id to tell two organizations of the same name apart.

account.organizations[].id required string

AvailableOrganization.id (meshery/schemas v1beta2 organization).

account.organizations[].name required string | null

AvailableOrganization.name (meshery/schemas v1beta2 organization).

account.plan_name required string | null

Plan.name (meshery/schemas v1beta3 plan, the PlanName enum) of the organization this status reports, exactly as Blowhorn names it, and Free when Blowhorn answered with no current subscription (every organization is on Free unless one names another plan). Null only when it has never been read for that organization; the app then shows the organization alone.

Values: Free, Team Designer, Team Operator, Enterprise,

api_base required string | null
attestation required object | null

The cached plan attestation, or null when none has been read.

attestation.age_seconds required integer
attestation.features required object
attestation.features.browser_automation required boolean
attestation.features.max_devices_per_seat required integer | null
attestation.features.max_profiles required integer | null

Null means unlimited.

attestation.features.platforms required array of string
attestation.features.scheduler required boolean
attestation.fetched_at required string

ISO 8601 UTC instant the plan was fetched.

attestation.fresh required boolean
attestation.organization_id required string | null
attestation_present required boolean

Whether a saved plan is held. With --offline, from the Keychain item's attributes only; live, whether attestation is non-null.

checked required string

presence for --offline (attributes only: no secret read, no request); live when the saved plan was read and, when due, refreshed from Blowhorn.

Values: presence, live

cloud_yaml required string | null

Path of the cloud.yaml in use, if any.

display_name required string | null
enforcement required string

The entitlement enforcement mode, or "invalid (<reason>)" when the setting does not parse.

fetch_error required string | null
ok required boolean

False when this computer is not signed in. True when a token is configured and, live, Blowhorn accepted it.

organization_id required string | null
permission_keys array of string

Live only: the ids of the permission keys the signed-in session holds in the Blowhorn parent organization (GET /api/identity/orgs/by-domain resolves the parent organization, then GET /api/identity/orgs/{id}/users/keys), which the desktop app's Diagnostics gate matches the Access Diagnostics key id against. Empty when no token is configured or when either read fails: the gate fails closed. Absent from the --offline payload, which makes no request.

previous_account required object | null

Who was signed in on this computer before the session ended, for the desktop app's returning sign-in screen, which greets that person by name. Read from cloud.yaml, where sign-out removes the account: a name still there with no token configured means the token was lost, removed or has timed out, never that the person signed out. Only the name and email, never the photo or the organizations. Null while a token is configured (account answers then), after a sign-out, and when cloud.yaml names nobody. Answered by --offline too: no secret read, no request.

previous_account.email required string | null

User.email (meshery/schemas v1beta2 user).

previous_account.first_name required string | null

User.firstName (meshery/schemas v1beta2 user).

previous_account.last_name required string | null

User.lastName (meshery/schemas v1beta2 user).

ready_to_fetch required boolean
sign_in_problem string

Why this computer is not signed in, present only when signed_in is false. no token means none is configured. token refused means Blowhorn answered 401 or 403. unreachable means Blowhorn could not be reached. --offline reports only no token.

Values: no token, token refused, unreachable

signed_in required boolean

Whether this computer is signed in to Blowhorn.

token_configured required boolean

Whether a Blowhorn token is configured. The token is never printed.

Slack

POST /cli/slack/delete Delete a Slack message this profile sent with 'blowhorn slack send' (chat.delete). Only your own messages, by the store's send ledger (not_sent_by_profile otherwise, before any Slack call), even when the session is a workspace admin's. A dry run unless --confirm. Exit codes as 'slack send'.

Delete a Slack message this profile sent with 'blowhorn slack send' (chat.delete). Only your own messages, by the store's send ledger (not_sent_by_profile otherwise, before any Slack call), even when the session is a workspace admin's. A dry run unless --confirm. Exit codes as 'slack send'.

Request body required

The flags and arguments of `blowhorn slack delete`, one property per parameter.

Request body of Delete a Slack message this profile sent with 'blowhorn slack send' (chat.delete). Only your own messages, by the store's send ledger (not_sent_by_profile otherwise, before any Slack call), even when the session is a workspace admin's. A dry run unless --confirm. Exit codes as 'slack send'. fields
NameTypeDescription
channel string

The channel id (C…, D… or G…) when --target is a bare ts

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

confirm boolean

Actually send. Without it the command is a dry run that sends nothing

Default: false

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Print the result as JSON (no secrets)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Pace of the one random wait 'blowhorn slack delete' takes: before each confirmed delete after the first when --profile names several profiles (a single profile, or a dry run, never waits): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn slack': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile array of string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

slack_auth string

Which Slack credential may send, for one run: 'session' (your captured signed-in web session; the message is yours, with no app attached), 'user-token' (an xoxp- app token for you) or 'bot-token' (an xoxb- bot token), or a comma-delimited list of them tried in order (e.g. 'session,user-token,bot-token'). One value is directive: that credential or a refusal naming why. A token may show the Slack app's attribution. This flag, then BLOWHORN_SLACK_AUTH, then defaults.slack.auth_mode in .blowhorn.yaml, then the built-in, selects it; any other value is refused before anything runs. Dry runs and results name the credential that ran and whether it was a fallback

target string

The message: a Slack message link, or its ts with --channel and --workspace

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

workspace string

The workspace host (e.g. mesheryio.slack.com) when --target does not name one; for whoami, which workspace to ask

Responses

200

What the command prints to stdout with --json when it exits 0.

What slack delete prints with --json on success: a dry run (the default), the deletion, or a multi-profile wrapper.

object

Variant fields
NameTypeDescription
action required string

Values: delete

channel required string
dry_run required boolean
exit_code required integer
fallback required boolean
idempotency_key required string
mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

message required string
ok required boolean
outcome required string

Values: dry_run

profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

thread_ts required string | null
ts required string
warnings required array of string
workspace required string | null

object

Variant fields
NameTypeDescription
action required string

Values: delete

changed required boolean
channel required string
dry_run required boolean
exit_code required integer
fallback required boolean
idempotency_key required string
mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

message string

Present only when nothing changed (Slack said already done).

ok required boolean
outcome required string

Values: deleted

profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

thread_ts required string | null
ts required string
warnings required array of string
workspace required string | null

object

Variant fields
NameTypeDescription
exit_code required integer
ok required boolean
outcome required string

Values: multi

results required array of object
results[].exit_code required integer
results[].ok required boolean
results[].outcome required string
summary required object
summary.failed required integer
summary.outcomes required object

Count of results by outcome.

summary.profiles required integer
summary.succeeded required integer

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

SlackDeleteResponse

POST /cli/slack/edit Replace the text of a Slack message this profile sent with 'blowhorn slack send' (chat.update). Only your own messages: the store's send ledger must show this profile sent that ts, or the result is not_sent_by_profile before any Slack call. A dry run unless --confirm. --target is the message link (its own ts, never the ?thread_ts= parent) or a bare ts with --channel and --workspace; the text comes from --message, --file or stdin ('-') and is sent untouched. Several profiles (--profile a,b) run one after another. Exit codes as 'slack send'.

Replace the text of a Slack message this profile sent with 'blowhorn slack send' (chat.update). Only your own messages: the store's send ledger must show this profile sent that ts, or the result is not_sent_by_profile before any Slack call. A dry run unless --confirm. --target is the message link (its own ts, never the ?thread_ts= parent) or a bare ts with --channel and --workspace; the text comes from --message, --file or stdin ('-') and is sent untouched. Several profiles (--profile a,b) run one after another. Exit codes as 'slack send'.

Request body required

The flags and arguments of `blowhorn slack edit`, one property per parameter.

Request body of Replace the text of a Slack message this profile sent with 'blowhorn slack send' (chat.update). Only your own messages: the store's send ledger must show this profile sent that ts, or the result is not_sent_by_profile before any Slack call. A dry run unless --confirm. --target is the message link (its own ts, never the ?thread_ts= parent) or a bare ts with --channel and --workspace; the text comes from --message, --file or stdin ('-') and is sent untouched. Several profiles (--profile a,b) run one after another. Exit codes as 'slack send'. fields
NameTypeDescription
channel string

The channel id (C…, D… or G…) when --target is a bare ts

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

confirm boolean

Actually send. Without it the command is a dry run that sends nothing

Default: false

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Print the result as JSON (no secrets)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

message string

The message text, sent untouched (Slack mrkdwn included); '-' reads it from stdin

message_file string

Read the message text from this file ('-' for stdin), sent untouched

pace string

Pace of the one random wait 'blowhorn slack edit' takes: before each confirmed edit after the first when --profile names several profiles (a single profile, or a dry run, never waits): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn slack': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile array of string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

slack_auth string

Which Slack credential may send, for one run: 'session' (your captured signed-in web session; the message is yours, with no app attached), 'user-token' (an xoxp- app token for you) or 'bot-token' (an xoxb- bot token), or a comma-delimited list of them tried in order (e.g. 'session,user-token,bot-token'). One value is directive: that credential or a refusal naming why. A token may show the Slack app's attribution. This flag, then BLOWHORN_SLACK_AUTH, then defaults.slack.auth_mode in .blowhorn.yaml, then the built-in, selects it; any other value is refused before anything runs. Dry runs and results name the credential that ran and whether it was a fallback

target string

The message: a Slack message link, or its ts with --channel and --workspace

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

workspace string

The workspace host (e.g. mesheryio.slack.com) when --target does not name one; for whoami, which workspace to ask

Responses

200

What the command prints to stdout with --json when it exits 0.

What slack edit prints with --json on success: a dry run (the default), the edit, or a multi-profile wrapper.

object

Variant fields
NameTypeDescription
action required string

Values: edit

channel required string
dry_run required boolean
exit_code required integer
fallback required boolean
idempotency_key required string
mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

message required string
ok required boolean
outcome required string

Values: dry_run

profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

thread_ts required string | null
ts required string
warnings required array of string
workspace required string | null

object

Variant fields
NameTypeDescription
action required string

Values: edit

attribution required object

Fields in Slack's answer showing the message is not plainly the person's (bot_id, app_id, bot_profile, subtype). Empty when none.

changed required boolean
channel required string
dry_run required boolean
exit_code required integer
fallback required boolean
idempotency_key required string
mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

message string

Present only when nothing changed (Slack said already done).

ok required boolean
outcome required string

Values: edited

permalink required string | null
profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

thread_ts required string | null
ts required string
warnings required array of string
workspace required string | null

object

Variant fields
NameTypeDescription
exit_code required integer
ok required boolean
outcome required string

Values: multi

results required array of object
results[].exit_code required integer
results[].ok required boolean
results[].outcome required string
summary required object
summary.failed required integer
summary.outcomes required object

Count of results by outcome.

summary.profiles required integer
summary.succeeded required integer

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

SlackEditResponse

POST /cli/slack/profiles List every profile's captured Slack workspaces (or only the --profile names given), the credentials each holds (session, user-token, bot-token) and which one --slack-auth would use. Reads the store and makes no Slack call. No secret is printed.

List every profile's captured Slack workspaces (or only the --profile names given), the credentials each holds (session, user-token, bot-token) and which one --slack-auth would use. Reads the store and makes no Slack call. No secret is printed.

Request body required

The flags and arguments of `blowhorn slack profiles`, one property per parameter.

Request body of List every profile's captured Slack workspaces (or only the --profile names given), the credentials each holds (session, user-token, bot-token) and which one --slack-auth would use. Reads the store and makes no Slack call. No secret is printed. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Print the result as JSON (no secrets)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn slack profiles' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn slack': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn slack profiles' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

slack_auth string

Which Slack credential may send, for one run: 'session' (your captured signed-in web session; the message is yours, with no app attached), 'user-token' (an xoxp- app token for you) or 'bot-token' (an xoxb- bot token), or a comma-delimited list of them tried in order (e.g. 'session,user-token,bot-token'). One value is directive: that credential or a refusal naming why. A token may show the Slack app's attribution. This flag, then BLOWHORN_SLACK_AUTH, then defaults.slack.auth_mode in .blowhorn.yaml, then the built-in, selects it; any other value is refused before anything runs. Dry runs and results name the credential that ran and whether it was a fallback

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Every profile's Slack workspaces and which credential kinds each holds, read from the store with no Slack call. Presence only, never a credential value.

Response 200 of List every profile's captured Slack workspaces (or only the --profile names given), the credentials each holds (session, user-token, bot-token) and which one --slack-auth would use. Reads the store and makes no Slack call. No secret is printed. fields
NameTypeDescription
exit_code required integer
ok required boolean
outcome required string

Values: listed

profiles required array of object
profiles[].known required boolean
profiles[].profile required string
profiles[].unprefixed required array of string

Credential kinds held without a workspace prefix (presence only).

profiles[].workspaces required array of object
profiles[].workspaces[].held required array of string

Which credential kinds the store holds (presence only).

profiles[].workspaces[].user required string | null
profiles[].workspaces[].user_id required string | null
profiles[].workspaces[].workspace required string
profiles[].workspaces[].would_use required string | null

The mechanism a send would use, or null when none may send.

slack_auth required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

SlackProfilesResponse

Variant fields
NameTypeDescription
exit_code required integer
ok required boolean
outcome required string

Values: listed

profiles required array of object
profiles[].known required boolean
profiles[].profile required string
profiles[].unprefixed required array of string

Credential kinds held without a workspace prefix (presence only).

profiles[].workspaces required array of object
profiles[].workspaces[].held required array of string

Which credential kinds the store holds (presence only).

profiles[].workspaces[].user required string | null
profiles[].workspaces[].user_id required string | null
profiles[].workspaces[].workspace required string
profiles[].workspaces[].would_use required string | null

The mechanism a send would use, or null when none may send.

slack_auth required string
POST /cli/slack/react Add an emoji reaction to any Slack message as the profile (reactions.add), or take yours back with --remove (reactions.remove). A dry run unless --confirm. A reaction already there (or already gone) is success with changed: false. --emoji takes white_check_mark or :white_check_mark:. Several profiles (--profile a,b) each react, one after another. Exit codes as 'slack send'.

Add an emoji reaction to any Slack message as the profile (reactions.add), or take yours back with --remove (reactions.remove). A dry run unless --confirm. A reaction already there (or already gone) is success with changed: false. --emoji takes white_check_mark or :white_check_mark:. Several profiles (--profile a,b) each react, one after another. Exit codes as 'slack send'.

Request body required

The flags and arguments of `blowhorn slack react`, one property per parameter.

Request body of Add an emoji reaction to any Slack message as the profile (reactions.add), or take yours back with --remove (reactions.remove). A dry run unless --confirm. A reaction already there (or already gone) is success with changed: false. --emoji takes white_check_mark or :white_check_mark:. Several profiles (--profile a,b) each react, one after another. Exit codes as 'slack send'. fields
NameTypeDescription
channel string

The channel id (C…, D… or G…) when --target is a bare ts

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

confirm boolean

Actually send. Without it the command is a dry run that sends nothing

Default: false

dry_run boolean

Simulate execution without making changes

Default: false

emoji string

The emoji name, with or without colons (white_check_mark, :+1:)

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Print the result as JSON (no secrets)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Pace of the one random wait 'blowhorn slack react' takes: before each confirmed reaction after the first when --profile names several profiles (a single profile, or a dry run, never waits): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn slack': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile array of string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

remove boolean

Take the reaction back (reactions.remove) instead of adding it

Default: false

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

slack_auth string

Which Slack credential may send, for one run: 'session' (your captured signed-in web session; the message is yours, with no app attached), 'user-token' (an xoxp- app token for you) or 'bot-token' (an xoxb- bot token), or a comma-delimited list of them tried in order (e.g. 'session,user-token,bot-token'). One value is directive: that credential or a refusal naming why. A token may show the Slack app's attribution. This flag, then BLOWHORN_SLACK_AUTH, then defaults.slack.auth_mode in .blowhorn.yaml, then the built-in, selects it; any other value is refused before anything runs. Dry runs and results name the credential that ran and whether it was a fallback

target string

The message: a Slack message link, or its ts with --channel and --workspace

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

workspace string

The workspace host (e.g. mesheryio.slack.com) when --target does not name one; for whoami, which workspace to ask

Responses

200

What the command prints to stdout with --json when it exits 0.

What slack react prints with --json on success: a dry run (the default), the reaction added or removed (changed false when Slack said it was already so), or a multi-profile wrapper.

object

Variant fields
NameTypeDescription
action required string

Values: react, unreact

channel required string
dry_run required boolean
emoji required string
exit_code required integer
fallback required boolean
mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

message required string
ok required boolean
outcome required string

Values: dry_run

profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

thread_ts required string | null
ts required string
warnings required array of string
workspace required string | null

object

Variant fields
NameTypeDescription
action required string

Values: react, unreact

changed required boolean
channel required string
dry_run required boolean
emoji required string
exit_code required integer
fallback required boolean
mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

message string

Present only when nothing changed (Slack said already done).

ok required boolean
outcome required string

Values: reacted, unreacted

profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

thread_ts required string | null
ts required string
warnings required array of string
workspace required string | null

object

Variant fields
NameTypeDescription
exit_code required integer
ok required boolean
outcome required string

Values: multi

results required array of object
results[].exit_code required integer
results[].ok required boolean
results[].outcome required string
summary required object
summary.failed required integer
summary.outcomes required object

Count of results by outcome.

summary.profiles required integer
summary.succeeded required integer

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

SlackReactResponse

POST /cli/slack/send Send one message to Slack as one profile, or as each of several (--profile a,b: one after another, each with its own idempotency key, ledger entry and result; one profile's failure never stops the next, and exit is 0 only when every profile succeeded), exactly once. A dry run unless --confirm: the dry run resolves the credential, the target and the idempotency key and says what would happen. --target takes a message link (a thread reply: the ?thread_ts= parent when the link is a reply's), a channel link (a top-level post), or #channel, @user or a channel id with --workspace. The text comes from --message, --file <path>, or stdin with --message - or --file -, and is sent untouched (Slack mrkdwn included). Every send carries a client_msg_id and is claimed in the store's send ledger under its idempotency key (--idempotency-key, else derived from profile, workspace, channel, thread and text) before it goes out: a key already sent replays its ts and link without sending again, and a send whose answer was never recorded is 'outcome unknown' and is never resent automatically. The result names the credential that ran (--slack-auth), whether that was a fallback, warns when a token may show a Slack app's attribution, and reports bot_id, app_id, bot_profile or subtype on Slack's answer. Exit codes: 0 sent, replayed or previewed; 1 refused (credential or Slack); 2 usage or key conflict; 3 store unavailable or a contended key; 4 outcome unknown; 5 rate-limited (Retry-After kept).

Send one message to Slack as one profile, or as each of several (--profile a,b: one after another, each with its own idempotency key, ledger entry and result; one profile's failure never stops the next, and exit is 0 only when every profile succeeded), exactly once. A dry run unless --confirm: the dry run resolves the credential, the target and the idempotency key and says what would happen. --target takes a message link (a thread reply: the ?thread_ts= parent when the link is a reply's), a channel link (a top-level post), or #channel, @user or a channel id with --workspace. The text comes from --message, --file <path>, or stdin with --message - or --file -, and is sent untouched (Slack mrkdwn included). Every send carries a client_msg_id and is claimed in the store's send ledger under its idempotency key (--idempotency-key, else derived from profile, workspace, channel, thread and text) before it goes out: a key already sent replays its ts and link without sending again, and a send whose answer was never recorded is 'outcome unknown' and is never resent automatically. The result names the credential that ran (--slack-auth), whether that was a fallback, warns when a token may show a Slack app's attribution, and reports bot_id, app_id, bot_profile or subtype on Slack's answer. Exit codes: 0 sent, replayed or previewed; 1 refused (credential or Slack); 2 usage or key conflict; 3 store unavailable or a contended key; 4 outcome unknown; 5 rate-limited (Retry-After kept).

Request body required

The flags and arguments of `blowhorn slack send`, one property per parameter.

Request body of Send one message to Slack as one profile, or as each of several (--profile a,b: one after another, each with its own idempotency key, ledger entry and result; one profile's failure never stops the next, and exit is 0 only when every profile succeeded), exactly once. A dry run unless --confirm: the dry run resolves the credential, the target and the idempotency key and says what would happen. --target takes a message link (a thread reply: the ?thread_ts= parent when the link is a reply's), a channel link (a top-level post), or #channel, @user or a channel id with --workspace. The text comes from --message, --file <path>, or stdin with --message - or --file -, and is sent untouched (Slack mrkdwn included). Every send carries a client_msg_id and is claimed in the store's send ledger under its idempotency key (--idempotency-key, else derived from profile, workspace, channel, thread and text) before it goes out: a key already sent replays its ts and link without sending again, and a send whose answer was never recorded is 'outcome unknown' and is never resent automatically. The result names the credential that ran (--slack-auth), whether that was a fallback, warns when a token may show a Slack app's attribution, and reports bot_id, app_id, bot_profile or subtype on Slack's answer. Exit codes: 0 sent, replayed or previewed; 1 refused (credential or Slack); 2 usage or key conflict; 3 store unavailable or a contended key; 4 outcome unknown; 5 rate-limited (Retry-After kept). fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

confirm boolean

Actually send. Without it the command is a dry run that sends nothing

Default: false

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

idempotency_key string

The key this send is recorded under; a key already sent is replayed, never sent twice (default: derived from profile, workspace, channel, thread and text)

json_output boolean

Print the result as JSON (no secrets)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

message string

The message text, sent untouched (Slack mrkdwn included); '-' reads it from stdin

message_file string

Read the message text from this file ('-' for stdin), sent untouched

pace string

Pace of the one random wait 'blowhorn slack send' takes: before each confirmed send after the first when --profile names several profiles (a single profile, or a dry run, never waits): a preset name (fast/normal/slow) or a numeric multiplier (e.g. 0.5, 2.0). Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn slack': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile array of string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Risk Profile this run uses: cautious, standard or brisk. For this one run it beats the schedule's value, the profile's choice and configuration. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

slack_auth string

Which Slack credential may send, for one run: 'session' (your captured signed-in web session; the message is yours, with no app attached), 'user-token' (an xoxp- app token for you) or 'bot-token' (an xoxb- bot token), or a comma-delimited list of them tried in order (e.g. 'session,user-token,bot-token'). One value is directive: that credential or a refusal naming why. A token may show the Slack app's attribution. This flag, then BLOWHORN_SLACK_AUTH, then defaults.slack.auth_mode in .blowhorn.yaml, then the built-in, selects it; any other value is refused before anything runs. Dry runs and results name the credential that ran and whether it was a fallback

target string

Where to send: a Slack message link (thread reply), a channel link (top-level post), or #channel, @user or a channel id with --workspace

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

workspace string

The workspace host (e.g. mesheryio.slack.com) when --target does not name one; for whoami, which workspace to ask

Responses

200

What the command prints to stdout with --json when it exits 0.

What slack send prints with --json on success: a dry run (the default), a real send, a replay of an earlier send under the same idempotency key, or, for several profiles, a multi-profile wrapper. No Slack credential is ever printed.

object

Variant fields
NameTypeDescription
channel required string
client_msg_id required string
dry_run required boolean
exit_code required integer
fallback required boolean
idempotency_key required string
kind required string

What the target resolved to (channel, thread, message link, ...).

mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

message required string
ok required boolean
outcome required string

Values: dry_run

profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

thread_ts required string | null
warnings required array of string
workspace required string | null

object

Variant fields
NameTypeDescription
channel required string
client_msg_id required string
dry_run required boolean
exit_code required integer
fallback required boolean
idempotency_key required string
kind required string

What the target resolved to (channel, thread, message link, ...).

mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

message required string
ok required boolean
outcome required string

Values: replayed

permalink required string | null
profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

thread_ts required string | null
ts required string | null
warnings required array of string
workspace required string | null

object

Variant fields
NameTypeDescription
attribution required object

Fields in Slack's answer showing the message is not plainly the person's (bot_id, app_id, bot_profile, subtype). Empty when none.

channel required string
client_msg_id required string
dry_run required boolean
exit_code required integer
fallback required boolean
idempotency_key required string
kind required string

What the target resolved to (channel, thread, message link, ...).

mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

ok required boolean
outcome required string

Values: sent

permalink required string | null
profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

thread_ts required string | null
ts required string
warnings required array of string
workspace required string | null

object

Variant fields
NameTypeDescription
exit_code required integer
ok required boolean
outcome required string

Values: multi

results required array of object
results[].exit_code required integer
results[].ok required boolean
results[].outcome required string
summary required object
summary.failed required integer
summary.outcomes required object

Count of results by outcome.

summary.profiles required integer
summary.succeeded required integer

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

SlackSendResponse

POST /cli/slack/whoami Ask Slack (auth.test) who a profile's Slack credential is in one workspace: user, user id and team, with the credential --slack-auth allowed and whether that was a fallback. Sends no message. --workspace may be left out when the profile has captured exactly one workspace.

Ask Slack (auth.test) who a profile's Slack credential is in one workspace: user, user id and team, with the credential --slack-auth allowed and whether that was a fallback. Sends no message. --workspace may be left out when the profile has captured exactly one workspace.

Request body required

The flags and arguments of `blowhorn slack whoami`, one property per parameter.

Request body of Ask Slack (auth.test) who a profile's Slack credential is in one workspace: user, user id and team, with the credential --slack-auth allowed and whether that was a fallback. Sends no message. --workspace may be left out when the profile has captured exactly one workspace. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Print the result as JSON (no secrets)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn slack whoami' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn slack': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn slack whoami' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

slack_auth string

Which Slack credential may send, for one run: 'session' (your captured signed-in web session; the message is yours, with no app attached), 'user-token' (an xoxp- app token for you) or 'bot-token' (an xoxb- bot token), or a comma-delimited list of them tried in order (e.g. 'session,user-token,bot-token'). One value is directive: that credential or a refusal naming why. A token may show the Slack app's attribution. This flag, then BLOWHORN_SLACK_AUTH, then defaults.slack.auth_mode in .blowhorn.yaml, then the built-in, selects it; any other value is refused before anything runs. Dry runs and results name the credential that ran and whether it was a fallback

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

workspace string

The workspace host (e.g. mesheryio.slack.com) when --target does not name one; for whoami, which workspace to ask

Responses

200

What the command prints to stdout with --json when it exits 0.

Who the profile's allowed Slack credential is in a workspace, from Slack's auth.test. Carries no credential.

Response 200 of Ask Slack (auth.test) who a profile's Slack credential is in one workspace: user, user id and team, with the credential --slack-auth allowed and whether that was a fallback. Sends no message. --workspace may be left out when the profile has captured exactly one workspace. fields
NameTypeDescription
exit_code required integer
fallback required boolean
identity required object

Slack auth.test answer fields that were present.

identity.bot_id string
identity.team string
identity.team_id string
identity.url string
identity.user string
identity.user_id string
mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

ok required boolean
outcome required string

Values: identified

profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

warnings required array of string
workspace required string | null

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

SlackWhoamiResponse

Variant fields
NameTypeDescription
exit_code required integer
fallback required boolean
identity required object

Slack auth.test answer fields that were present.

identity.bot_id string
identity.team string
identity.team_id string
identity.url string
identity.user string
identity.user_id string
mechanism required string | null

Credential kind used: session, user-token or bot-token. The credential itself is never printed.

ok required boolean
outcome required string

Values: identified

profile required string
skipped required array of string
slack_auth required string | null

The slack auth setting and what set it.

warnings required array of string
workspace required string | null

Service

POST /cli/service/adopt Retire what stands in one owner's way so one owner holds the label: with --by app, the file-installed service.

Retire what stands in one owner's way so one owner holds the label: with --by app, the file-installed service.

Request body required

The flags and arguments of `blowhorn service adopt`, one property per parameter.

Request body of Retire what stands in one owner's way so one owner holds the label: with --by app, the file-installed service. fields
NameTypeDescription
by string

Who takes the label: file (this CLI's LaunchAgent; nothing stands in its way) or app (the desktop app; retires the file agent).

Values: file, app

Default: "file"

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service adopt' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service adopt' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints whether `service adopt` retired the other owner's registration so the named owner can take the service. A refusal (a tick is running, the plist is unreadable) prints the same document with `ok: false` and the cause in `error`.

Response 200 of Retire what stands in one owner's way so one owner holds the label: with --by app, the file-installed service. fields
NameTypeDescription
adopted required boolean
by required string

The owner adopting the service (app or file).

dry_run boolean
error string

Why the adoption was refused (`ok` is false).

file_removed required boolean

Whether the file-installed plist was removed.

label required string
message string
ok required boolean
owner required string

The owner found before adopting (app, file, none or unknown).

reason string

Why nothing was done.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceAdoptResponse

Variant fields
NameTypeDescription
adopted required boolean
by required string

The owner adopting the service (app or file).

dry_run boolean
error string

Why the adoption was refused (`ok` is false).

file_removed required boolean

Whether the file-installed plist was removed.

label required string
message string
ok required boolean
owner required string

The owner found before adopting (app, file, none or unknown).

reason string

Why nothing was done.

POST /cli/service/cancel Ask the running background service to stop its pass at the next safe point; the activity in progress returns to the queue.

Ask the running background service to stop its pass at the next safe point; the activity in progress returns to the queue.

Request body required

The flags and arguments of `blowhorn service cancel`, one property per parameter.

Request body of Ask the running background service to stop its pass at the next safe point; the activity in progress returns to the queue. fields
NameTypeDescription
config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service cancel' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service cancel' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints what `service cancel` asked of the resident service: the pass in flight stops at its next safe point (never mid-post) and its row returns to the queue. With nothing in flight, or only a tick the service did not start, nothing is cancelled. `--dry-run` asks nothing.

ServiceCancelAnswer

Variant fields
NameTypeDescription
cancelling required boolean
foreign boolean

True when the running tick was not started by the service, so it is not the service's to stop.

idle required boolean

True when no pass was in flight.

label required string
log_path required string | null
message required string
ok required any
pid required integer | null

ServiceCancelDryRun

Variant fields
NameTypeDescription
control_socket required string
dry_run required any
label required string
message required string
ok required any
pass_pid required integer | null

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceCancelResponse

POST /cli/service/install Install the background service as a per-user LaunchAgent on a machine without the desktop app.

Install the background service as a per-user LaunchAgent on a machine without the desktop app.

Request body required

The flags and arguments of `blowhorn service install`, one property per parameter.

Request body of Install the background service as a per-user LaunchAgent on a machine without the desktop app. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service install' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service install' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What service install prints with --json on success: the installed service definition, or the reason nothing was done, or with --dry-run the plan.

Response 200 of Install the background service as a per-user LaunchAgent on a machine without the desktop app. fields
NameTypeDescription
bootstrapped required boolean
dry_run boolean

Present only with --dry-run.

installed required boolean
label required string

The launchd label of the background service.

message required string
ok required boolean
owner required string

Which owner registered the service (file, app or none).

plist_path required string
reason string

Present when nothing needed doing (for example not installed, already installed).

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceInstallResponse

Variant fields
NameTypeDescription
bootstrapped required boolean
dry_run boolean

Present only with --dry-run.

installed required boolean
label required string

The launchd label of the background service.

message required string
ok required boolean
owner required string

Which owner registered the service (file, app or none).

plist_path required string
reason string

Present when nothing needed doing (for example not installed, already installed).

POST /cli/service/restart Restart the background service between passes, so it picks up a changed checkout.

Restart the background service between passes, so it picks up a changed checkout.

Request body required

The flags and arguments of `blowhorn service restart`, one property per parameter.

Request body of Restart the background service between passes, so it picks up a changed checkout. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service restart' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service restart' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What service restart prints with --json on success: the service was asked to restart after its pass, or kickstarted; with --dry-run the plan.

Response 200 of Restart the background service between passes, so it picks up a changed checkout. fields
NameTypeDescription
action string

'What was done, for example bootstrapped, pass requested, busy, draining, restarting, kickstarted.'

dry_run boolean

Present only with --dry-run.

label required string

The launchd label of the background service.

message required string
ok required boolean
owner required string

Which owner registered the service (file, app or none).

reason string

Present when nothing needed doing (for example not installed, already installed).

reply object

The service control socket's answer when it was asked.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceRestartResponse

Variant fields
NameTypeDescription
action string

'What was done, for example bootstrapped, pass requested, busy, draining, restarting, kickstarted.'

dry_run boolean

Present only with --dry-run.

label required string

The launchd label of the background service.

message required string
ok required boolean
owner required string

Which owner registered the service (file, app or none).

reason string

Present when nothing needed doing (for example not installed, already installed).

reply object

The service control socket's answer when it was asked.

POST /cli/service/run Run the background service in this terminal: a pass whenever a routine falls due, re-scanning at least every minute, until stopped. This is what launchd runs.

Run the background service in this terminal: a pass whenever a routine falls due, re-scanning at least every minute, until stopped. This is what launchd runs.

Request body required

The flags and arguments of `blowhorn service run`, one property per parameter.

Request body of Run the background service in this terminal: a pass whenever a routine falls due, re-scanning at least every minute, until stopped. This is what launchd runs. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service run' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service run' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What service run prints with --json on success: with --dry-run the plan, otherwise a single line when the foreground supervisor exits.

DryRun

Variant fields
NameTypeDescription
checkout required string
control_socket required string
dry_run required boolean
log_dir required string
message required string
ok required boolean
pass_argv required array of string
python required string
rescan_s required number

The longest the service sleeps between passes; it wakes sooner for a due routine.

state_path required string
would_take_over required integer | null

Pid of a stale state the run would take over.

Exited

Variant fields
NameTypeDescription
exit_reason required string
ok required boolean

True when the supervisor exited with code 0.

state_path required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceRunResponse

POST /cli/service/start Start the installed background service, or ask a running one for a pass now.

Start the installed background service, or ask a running one for a pass now.

Request body required

The flags and arguments of `blowhorn service start`, one property per parameter.

Request body of Start the installed background service, or ask a running one for a pass now. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service start' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service start' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What service start prints with --json on success: the service was loaded, asked for a pass, or kickstarted; with --dry-run the plan.

Response 200 of Start the installed background service, or ask a running one for a pass now. fields
NameTypeDescription
action string

'What was done, for example bootstrapped, pass requested, busy, draining, restarting, kickstarted.'

dry_run boolean

Present only with --dry-run.

label required string

The launchd label of the background service.

message required string
ok required boolean
owner required string

Which owner registered the service (file, app or none).

reason string

Present when nothing needed doing (for example not installed, already installed).

reply object

The service control socket's answer when it was asked.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceStartResponse

Variant fields
NameTypeDescription
action string

'What was done, for example bootstrapped, pass requested, busy, draining, restarting, kickstarted.'

dry_run boolean

Present only with --dry-run.

label required string

The launchd label of the background service.

message required string
ok required boolean
owner required string

Which owner registered the service (file, app or none).

reason string

Present when nothing needed doing (for example not installed, already installed).

reply object

The service control socket's answer when it was asked.

POST /cli/service/status Show the background service: who registered it, whether it runs, its last pass.

Show the background service: who registered it, whether it runs, its last pass.

Request body required

The flags and arguments of `blowhorn service status`, one property per parameter.

Request body of Show the background service: who registered it, whether it runs, its last pass. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service status' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service status' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the background service's state - installed, loaded, owner, definition drift, tick in flight and the resident supervisor. Reads launchd and the service's state file.

Response 200 of Show the background service: who registered it, whether it runs, its last pass. fields
NameTypeDescription
chrome required null

Reserved; always null today.

control_socket required string
drift required array of object
drift[].expected required any
drift[].installed required any
drift[].key required string
drift_summary required string | null
installed required boolean
label required string

The launchd label.

legacy required object | null

Set when this checkout has a file-installed ai.blowhorn.service it wrote before the dev label (the app's label, which the app may still adopt). `blowhorn service update` moves it to this label.

legacy.label required string
legacy.loaded required boolean
legacy.message required string
legacy.plist_path required string
loaded required boolean
ok required any
owner required string

Values: app, file, none

parent_bundle_version required string | null

launchctl's `parent bundle version` for an app-registered job, the version of the copy that registered it. Null when not loaded, when launchctl names none, or for a file-owned job.

plist_path required string
program_path required string | null

The executable the loaded job runs. For an app-owned job, the running pid's executable (`ps -o comm=`), which names the copy of Blowhorn that registered it; for a file-owned job, ProgramArguments[0] of the installed plist. Null when unknown or not loaded.

project_dir required string
resident required one of ServiceResidentState, null
state_path required string
stderr required string
stdout required string
tick_running required object
tick_running.pid required integer | null
tick_running.running required boolean
tick_running.started_at required string | null
unreadable required string | null

Why an installed plist could not be compared.

up_to_date required boolean | null

Null when nothing is installed or the installed file could not be compared.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceStatusResponse

Variant fields
NameTypeDescription
chrome required null

Reserved; always null today.

control_socket required string
drift required array of object
drift[].expected required any
drift[].installed required any
drift[].key required string
drift_summary required string | null
installed required boolean
label required string

The launchd label.

legacy required object | null

Set when this checkout has a file-installed ai.blowhorn.service it wrote before the dev label (the app's label, which the app may still adopt). `blowhorn service update` moves it to this label.

legacy.label required string
legacy.loaded required boolean
legacy.message required string
legacy.plist_path required string
loaded required boolean
ok required any
owner required string

Values: app, file, none

parent_bundle_version required string | null

launchctl's `parent bundle version` for an app-registered job, the version of the copy that registered it. Null when not loaded, when launchctl names none, or for a file-owned job.

plist_path required string
program_path required string | null

The executable the loaded job runs. For an app-owned job, the running pid's executable (`ps -o comm=`), which names the copy of Blowhorn that registered it; for a file-owned job, ProgramArguments[0] of the installed plist. Null when unknown or not loaded.

project_dir required string
resident required one of ServiceResidentState, null
state_path required string
stderr required string
stdout required string
tick_running required object
tick_running.pid required integer | null
tick_running.running required boolean
tick_running.started_at required string | null
unreadable required string | null

Why an installed plist could not be compared.

up_to_date required boolean | null

Null when nothing is installed or the installed file could not be compared.

POST /cli/service/stop Stop the background service, waiting for the pass in flight to finish; the installed definition stays.

Stop the background service, waiting for the pass in flight to finish; the installed definition stays.

Request body required

The flags and arguments of `blowhorn service stop`, one property per parameter.

Request body of Stop the background service, waiting for the pass in flight to finish; the installed definition stays. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service stop' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service stop' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What service stop prints with --json on success: whether the service was stopped, the reason nothing was done, or with --dry-run the plan.

Response 200 of Stop the background service, waiting for the pass in flight to finish; the installed definition stays. fields
NameTypeDescription
dry_run boolean

Present only with --dry-run.

label required string

The launchd label of the background service.

message required string
ok required boolean
owner required string

Which owner registered the service (file, app or none).

reason string

Present when nothing needed doing (for example not installed, already installed).

stopped required boolean
waited_s number

Seconds waited for the service to leave launchd.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceStopResponse

Variant fields
NameTypeDescription
dry_run boolean

Present only with --dry-run.

label required string

The launchd label of the background service.

message required string
ok required boolean
owner required string

Which owner registered the service (file, app or none).

reason string

Present when nothing needed doing (for example not installed, already installed).

stopped required boolean
waited_s number

Seconds waited for the service to leave launchd.

POST /cli/service/test Validate the schedule and preview a pass, the way the service would run it.

Validate the schedule and preview a pass, the way the service would run it.

Request body required

The flags and arguments of `blowhorn service test`, one property per parameter.

Request body of Validate the schedule and preview a pass, the way the service would run it. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service test' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service test' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What service test prints with --json on success: the outputs of schedule validate and a dry-run schedule tick, or with --dry-run the two commands it would run.

Result

Variant fields
NameTypeDescription
label required string
loaded required boolean
message required string
ok required boolean
owner required string
preview required any

The parsed JSON of the dry-run schedule tick (see ScheduleTickResponse), or its raw text.

validate required any

The parsed JSON of schedule validate, or its raw text when it was not JSON.

DryRun

Variant fields
NameTypeDescription
commands required array of array of string
dry_run required boolean
message required string
ok required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceTestResponse

POST /cli/service/uninstall Stop and remove the per-user background service this CLI installed.

Stop and remove the per-user background service this CLI installed.

Request body required

The flags and arguments of `blowhorn service uninstall`, one property per parameter.

Request body of Stop and remove the per-user background service this CLI installed. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service uninstall' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service uninstall' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What service uninstall prints with --json on success: whether the service definition was removed, the reason nothing was done, or with --dry-run the plan.

Response 200 of Stop and remove the per-user background service this CLI installed. fields
NameTypeDescription
dry_run boolean

Present only with --dry-run.

label required string

The launchd label of the background service.

message required string
ok required boolean
owner required string

Which owner registered the service (file, app or none).

plist_path required string
reason string

Present when nothing needed doing (for example not installed, already installed).

removed required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceUninstallResponse

Variant fields
NameTypeDescription
dry_run boolean

Present only with --dry-run.

label required string

The launchd label of the background service.

message required string
ok required boolean
owner required string

Which owner registered the service (file, app or none).

plist_path required string
reason string

Present when nothing needed doing (for example not installed, already installed).

removed required boolean
POST /cli/service/update Bring the installed service definition up to date with this checkout's template.

Bring the installed service definition up to date with this checkout's template.

Request body required

The flags and arguments of `blowhorn service update`, one property per parameter.

Request body of Bring the installed service definition up to date with this checkout's template. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn service update' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn service': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn service update' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What service update prints with --json on success: whether the installed service definition was rewritten to match this checkout, or why nothing was done; with --dry-run the plan.

Response 200 of Bring the installed service definition up to date with this checkout's template. fields
NameTypeDescription
drift required array of string

The keys of the installed definition that differ from this checkout's.

drift_summary required string | null
dry_run boolean
label required string
loaded boolean
message string
ok required boolean
plist_path required string
reason string

Present when nothing needed doing (registered by Blowhorn.app, not installed, up to date).

reloaded boolean
tick_running required object
tick_running.pid required integer | null
tick_running.running required boolean
tick_running.started_at required string | null
up_to_date boolean
updated required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ServiceUpdateResponse

Variant fields
NameTypeDescription
drift required array of string

The keys of the installed definition that differ from this checkout's.

drift_summary required string | null
dry_run boolean
label required string
loaded boolean
message string
ok required boolean
plist_path required string
reason string

Present when nothing needed doing (registered by Blowhorn.app, not installed, up to date).

reloaded boolean
tick_running required object
tick_running.pid required integer | null
tick_running.running required boolean
tick_running.started_at required string | null
up_to_date boolean
updated required boolean

Desktop

POST /cli/desktop/build Package the Electron desktop app as an ad-hoc-signed universal macOS build.

Package the Electron desktop app as an ad-hoc-signed universal macOS build.

Request body required

The flags and arguments of `blowhorn desktop build`, one property per parameter.

Request body of Package the Electron desktop app as an ad-hoc-signed universal macOS build. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dir_only boolean

Build the unpacked app directory instead of a distributable

Default: false

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn desktop build' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn desktop': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn desktop build' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The desktop build command, planned (--dry-run) or run with its captured output.

object

Variant fields
NameTypeDescription
command required array of string
cwd required string
dry_run required boolean
ok required boolean

object

Variant fields
NameTypeDescription
command required array of string
ok required boolean
script required string

The package script that ran (for example dist:mac).

stderr required string
stdout required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

DesktopBuildResponse

POST /cli/desktop/install Install the Electron desktop app dependencies with pnpm (pnpm install --frozen-lockfile).

Install the Electron desktop app dependencies with pnpm (pnpm install --frozen-lockfile).

Request body required

The flags and arguments of `blowhorn desktop install`, one property per parameter.

Request body of Install the Electron desktop app dependencies with pnpm (pnpm install --frozen-lockfile). fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn desktop install' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn desktop': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn desktop install' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The dependency install command, planned (--dry-run) or run with its captured output.

object

Variant fields
NameTypeDescription
command required array of string
cwd required string
dry_run required boolean
ok required boolean

object

Variant fields
NameTypeDescription
command required array of string
ok required boolean
stderr required string
stdout required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

DesktopInstallResponse

POST /cli/desktop/start Launch the Electron desktop app through pnpm dev; refuse when one is already running.

Launch the Electron desktop app through pnpm dev; refuse when one is already running.

Request body required

The flags and arguments of `blowhorn desktop start`, one property per parameter.

Request body of Launch the Electron desktop app through pnpm dev; refuse when one is already running. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn desktop start' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn desktop': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn desktop start' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

wait boolean

Wait for the desktop app process to exit instead of detaching

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Starting the desktop app - planned (--dry-run), detached with a pid and log path, or (--wait) run to exit with its captured output.

object

Variant fields
NameTypeDescription
command required array of string
cwd required string
dry_run required boolean
ok required boolean
wait required boolean

object

Variant fields
NameTypeDescription
command required array of string
log_path required string
ok required boolean
pid required integer
wait required boolean

object

Variant fields
NameTypeDescription
command required array of string
ok required boolean
stderr required string
stdout required string
wait required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

DesktopStartResponse

POST /cli/desktop/status Show whether desktop dependencies and packaged output are present. Read-only: it names the package manager an install would start from (a matching pnpm, else corepack unprobed, else npm exec) and runs no corepack download; 'desktop install' is what checks corepack can run the pinned pnpm.

Show whether desktop dependencies and packaged output are present. Read-only: it names the package manager an install would start from (a matching pnpm, else corepack unprobed, else npm exec) and runs no corepack download; 'desktop install' is what checks corepack can run the pinned pnpm.

Request body required

The flags and arguments of `blowhorn desktop status`, one property per parameter.

Request body of Show whether desktop dependencies and packaged output are present. Read-only: it names the package manager an install would start from (a matching pnpm, else corepack unprobed, else npm exec) and runs no corepack download; 'desktop install' is what checks corepack can run the pinned pnpm. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn desktop status' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn desktop': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn desktop status' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Whether the desktop app's dependencies are installed and the app built, from the desktop directory on disk.

Response 200 of Show whether desktop dependencies and packaged output are present. Read-only: it names the package manager an install would start from (a matching pnpm, else corepack unprobed, else npm exec) and runs no corepack download; 'desktop install' is what checks corepack can run the pinned pnpm. fields
NameTypeDescription
built required boolean

The out directory exists.

desktop_dir required string
installed required boolean

node_modules exists.

ok required boolean
package_manager required string | null

Name of the package manager found, null when none.

pnpm_lock required boolean
release_dir required string
release_exists required boolean

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

DesktopStatusResponse

Variant fields
NameTypeDescription
built required boolean

The out directory exists.

desktop_dir required string
installed required boolean

node_modules exists.

ok required boolean
package_manager required string | null

Name of the package manager found, null when none.

pnpm_lock required boolean
release_dir required string
release_exists required boolean

Store

POST /cli/store Report whether this computer can use Blowhorn, and say why not when it cannot: signed in, a client the server still accepts, and a member of the organization. --replay-spool also drains the results already waiting in the local write-back spool; apart from it, nothing here writes anything.

Report whether this computer can use Blowhorn, and say why not when it cannot: signed in, a client the server still accepts, and a member of the organization. --replay-spool also drains the results already waiting in the local write-back spool; apart from it, nothing here writes anything.

Exits 0 when the store is usable and 3 when it is not -- the same code every other command uses for an unreachable store, so a wrapper can tell an outage from a failed job. --json emits the check as one object: ok, exitCode, cause, error, organization, reachable, spoolDepth, warnings, rungs, replay and cloud; configured is always true, and configPath, configMode, dsn, tunnel, serverVersion, schema and validation are always null.

Request body required

The flags and arguments of `blowhorn store`, one property per parameter.

Request body of Report whether this computer can use Blowhorn, and say why not when it cannot: signed in, a client the server still accepts, and a member of the organization. --replay-spool also drains the results already waiting in the local write-back spool; apart from it, nothing here writes anything. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the check as machine-readable JSON (the shape the desktop app's Setup row reads)

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn store' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn store': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

replay_spool boolean

Write out the results waiting in the local write-back spool now, rather than leaving them for the next run to drain, and report how many write-backs were pushed, kept and dropped. An entry whose activity no longer exists is named and dropped from the pending file (the month's ledger still holds it); nothing else is ever deleted. With --dry-run it writes nothing and reports what the run without it would have done

Default: false

risk_profile string

Accepted by 'blowhorn store' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the Blowhorn check: the capabilities handshake as one rung, a replay rung with `--replay-spool`, and any warnings. One shape covers a usable store (`ok: true`, exit 0) and a named cause (`ok: false`, exit 3), so the desktop's Setup row reads both alike. The keys the retired Postgres probe filled (`configPath`, `configMode`, `dsn`, `tunnel`, `serverVersion`, `schema`) are always null and `properties` is always empty.

Response 200 of Report whether this computer can use Blowhorn, and say why not when it cannot: signed in, a client the server still accepts, and a member of the organization. --replay-spool also drains the results already waiting in the local write-back spool; apart from it, nothing here writes anything. fields
NameTypeDescription
cause required string | null

The named cause (cloud_signed_out, cloud_refused, cloud_upgrade_required, cloud_rate_limited and so on); null when the store is usable.

cloud object

The capabilities handshake.

cloud.bound required boolean
cloud.cause required string | null
cloud.contract_version required string | null
cloud.message required string
cloud.notice required string | null

Set when this organization has more computers registered than its plan allows.

cloud.ok required boolean
cloud.organization_id required string | null
configMode required string | null

The private file's mode, such as 0600.

configPath required string | null
configured required boolean | null
dsn required string | null

The connection string with the password replaced by asterisks.

error required string | null
exitCode required integer

Values: 0, 3

offline required boolean
ok required boolean
organization required string | null
properties required array of StoreSettingRow
properties[].choices required array of string
properties[].document required string

Which file holds it - the tracked config or the private connection file.

Values: config, private

properties[].hint required string
properties[].key required string

The dotted setting name, such as store.host or defaults.store.organization.

properties[].kind required string

Values: bool, enum, int, list, path, string

properties[].label required string
properties[].masked required boolean
properties[].source required string

The layer that supplied the value, such as config, env or builtin.

properties[].value required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

reachable required boolean | null
replay required one of null, object
rungs required array of object
rungs[].detail required string
rungs[].name required string
rungs[].state required string

Values: ok, warn, fail, skipped

schema required one of null, object
serverVersion required string | null
spoolDepth required integer

Write-backs waiting in the local spool.

tunnel required one of null, object
validation required null

Reserved; always null today.

warnings required array of string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

StoreResponse

Variant fields
NameTypeDescription
cause required string | null

The named cause (cloud_signed_out, cloud_refused, cloud_upgrade_required, cloud_rate_limited and so on); null when the store is usable.

cloud object

The capabilities handshake.

cloud.bound required boolean
cloud.cause required string | null
cloud.contract_version required string | null
cloud.message required string
cloud.notice required string | null

Set when this organization has more computers registered than its plan allows.

cloud.ok required boolean
cloud.organization_id required string | null
configMode required string | null

The private file's mode, such as 0600.

configPath required string | null
configured required boolean | null
dsn required string | null

The connection string with the password replaced by asterisks.

error required string | null
exitCode required integer

Values: 0, 3

offline required boolean
ok required boolean
organization required string | null
properties required array of StoreSettingRow
properties[].choices required array of string
properties[].document required string

Which file holds it - the tracked config or the private connection file.

Values: config, private

properties[].hint required string
properties[].key required string

The dotted setting name, such as store.host or defaults.store.organization.

properties[].kind required string

Values: bool, enum, int, list, path, string

properties[].label required string
properties[].masked required boolean
properties[].source required string

The layer that supplied the value, such as config, env or builtin.

properties[].value required ConfigOptionValue

A value a config option can hold: a string, number, integer, boolean, a list of strings, null (unset), or, for a masked option such as `store.password`, the object `{set: boolean}` that says whether a value exists and never carries it.

reachable required boolean | null
replay required one of null, object
rungs required array of object
rungs[].detail required string
rungs[].name required string
rungs[].state required string

Values: ok, warn, fail, skipped

schema required one of null, object
serverVersion required string | null
spoolDepth required integer

Write-backs waiting in the local spool.

tunnel required one of null, object
validation required null

Reserved; always null today.

warnings required array of string

Analytics

POST /cli/analytics/activities Print several activities' results at once, from one set of store reads: the same result 'analytics activity' prints for each (delivery, reception where it is collected, and the derived follower change, per profile and platform), in the order given. An activity number the queue does not have is listed as not found rather than failing the rest. A computer with no store prints a short explanation and exits 3.

Print several activities' results at once, from one set of store reads: the same result 'analytics activity' prints for each (delivery, reception where it is collected, and the derived follower change, per profile and platform), in the order given. An activity number the queue does not have is listed as not found rather than failing the rest. A computer with no store prints a short explanation and exits 3.

Request body required

The flags and arguments of `blowhorn analytics activities`, one property per parameter.

Request body of Print several activities' results at once, from one set of store reads: the same result 'analytics activity' prints for each (delivery, reception where it is collected, and the derived follower change, per profile and platform), in the order given. An activity number the queue does not have is listed as not found rather than failing the rest. A computer with no store prints a short explanation and exits 3. fields
NameTypeDescription
activities required array of integer

Activity numbers, as 'content list' prints them

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn analytics activities' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn analytics'. One of: bluesky, linkedin, x, all ('all' means every platform listed here)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn analytics activities' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints several activities' results from one set of store reads: one `AnalyticsActivityResponse` per activity found, in the order asked, and `missing`, the activity numbers (as strings, the way `activityId` spells them) the content queue does not have, in the order asked. A computer with no store prints the Problem shape and exits 3.

Response 200 of Print several activities' results at once, from one set of store reads: the same result 'analytics activity' prints for each (delivery, reception where it is collected, and the derived follower change, per profile and platform), in the order given. An activity number the queue does not have is listed as not found rather than failing the rest. A computer with no store prints a short explanation and exits 3. fields
NameTypeDescription
activities required array of AnalyticsActivityResponse
activities[].activityId required string
activities[].entries required array of ActivityResultEntry
activities[].entries[].delivery required ActivityDelivery

Mirrors meshery/schemas v1beta3 `ActivityDelivery`. Whether the activity went out for one profile and platform, from the content item's recorded outcome and the run log. Measured, never derived.

activities[].entries[].followerChange required ActivityFollowerChange

Mirrors meshery/schemas v1beta3 `ActivityFollowerChange`. Derived: followers at the reading before the activity and the first reading at least `windowDays` after it. Correlation only.

activities[].entries[].platform required string
activities[].entries[].profile required string
activities[].entries[].reception required ActivityReception

Mirrors meshery/schemas v1beta3 `ActivityReception`. Measured reception with the date it was read, or collected false with a reason and one sentence. Never zeros for missing data. `comparison` is derived: the median of the same subject's other activities and this activity's ratio to it.

activities[].generatedAt required string (date-time)
activities[].kind required string

post, repost or comment.

activities[].ok required any
activities[].platform required string
activities[].state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

activities[].title required string
generatedAt required string (date-time)
missing required array of string
ok required any

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AnalyticsActivitiesResponse

Variant fields
NameTypeDescription
activities required array of AnalyticsActivityResponse
activities[].activityId required string
activities[].entries required array of ActivityResultEntry
activities[].entries[].delivery required ActivityDelivery

Mirrors meshery/schemas v1beta3 `ActivityDelivery`. Whether the activity went out for one profile and platform, from the content item's recorded outcome and the run log. Measured, never derived.

activities[].entries[].followerChange required ActivityFollowerChange

Mirrors meshery/schemas v1beta3 `ActivityFollowerChange`. Derived: followers at the reading before the activity and the first reading at least `windowDays` after it. Correlation only.

activities[].entries[].platform required string
activities[].entries[].profile required string
activities[].entries[].reception required ActivityReception

Mirrors meshery/schemas v1beta3 `ActivityReception`. Measured reception with the date it was read, or collected false with a reason and one sentence. Never zeros for missing data. `comparison` is derived: the median of the same subject's other activities and this activity's ratio to it.

activities[].generatedAt required string (date-time)
activities[].kind required string

post, repost or comment.

activities[].ok required any
activities[].platform required string
activities[].state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

activities[].title required string
generatedAt required string (date-time)
missing required array of string
ok required any
POST /cli/analytics/activity Print one activity's result, read from the store: for each profile and platform it is addressed to, whether it went out (when, the live link, which computer and browser profile ran it, and on a failure the error and the fix), how it was received where reception is collected (LinkedIn page activities, from the page content exports, with the date the figures were read and a comparison with the page's typical activity), and the follower change around it, labelled derived. Anything not collected says so, with a reason, never as a zero. A computer with no store prints a short explanation and exits 3.

Print one activity's result, read from the store: for each profile and platform it is addressed to, whether it went out (when, the live link, which computer and browser profile ran it, and on a failure the error and the fix), how it was received where reception is collected (LinkedIn page activities, from the page content exports, with the date the figures were read and a comparison with the page's typical activity), and the follower change around it, labelled derived. Anything not collected says so, with a reason, never as a zero. A computer with no store prints a short explanation and exits 3.

Request body required

The flags and arguments of `blowhorn analytics activity`, one property per parameter.

Request body of Print one activity's result, read from the store: for each profile and platform it is addressed to, whether it went out (when, the live link, which computer and browser profile ran it, and on a failure the error and the fix), how it was received where reception is collected (LinkedIn page activities, from the page content exports, with the date the figures were read and a comparison with the page's typical activity), and the follower change around it, labelled derived. Anything not collected says so, with a reason, never as a zero. A computer with no store prints a short explanation and exits 3. fields
NameTypeDescription
activity required integer

Activity number, as 'content list' prints it

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn analytics activity' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn analytics'. One of: bluesky, linkedin, x, all ('all' means every platform listed here)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn analytics activity' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints one activity's result: meshery/schemas v1beta3 `ActivityResult` (camelCase, as that contract spells it) plus `ok`. `activityId` is the content item's row number. One entry per profile and platform the activity is addressed to, each with delivery, reception and the derived follower change. A computer with no store prints the Problem shape and exits 3.

Response 200 of Print one activity's result, read from the store: for each profile and platform it is addressed to, whether it went out (when, the live link, which computer and browser profile ran it, and on a failure the error and the fix), how it was received where reception is collected (LinkedIn page activities, from the page content exports, with the date the figures were read and a comparison with the page's typical activity), and the follower change around it, labelled derived. Anything not collected says so, with a reason, never as a zero. A computer with no store prints a short explanation and exits 3. fields
NameTypeDescription
activityId required string
entries required array of ActivityResultEntry
entries[].delivery required ActivityDelivery

Mirrors meshery/schemas v1beta3 `ActivityDelivery`. Whether the activity went out for one profile and platform, from the content item's recorded outcome and the run log. Measured, never derived.

entries[].delivery.attemptedAt required string (date-time) | null
entries[].delivery.browserProfile required object | null
entries[].delivery.computer required string | null

Host name of the computer whose run delivered or attempted it.

entries[].delivery.deliveredAt required string (date-time) | null
entries[].delivery.durationSeconds required number | null
entries[].delivery.error required string | null
entries[].delivery.evidence required string

What the status rests on.

Values: recorded, run, none

entries[].delivery.fix required object | null
entries[].delivery.link required string | null

Link to the live item.

entries[].delivery.logPath required string | null

Log file of the matched run, on the computer that ran it.

entries[].delivery.runId required string | null
entries[].delivery.scheduledFor required string (date-time) | null
entries[].delivery.status required string

delivered, unconfirmed (clicked, outcome not read), failed (last attempt failed), scheduled, pending (due, not yet run), draft (not approved).

Values: delivered, unconfirmed, failed, scheduled, pending, draft

entries[].followerChange required ActivityFollowerChange

Mirrors meshery/schemas v1beta3 `ActivityFollowerChange`. Derived: followers at the reading before the activity and the first reading at least `windowDays` after it. Correlation only.

entries[].followerChange.after object | null
entries[].followerChange.before object | null
entries[].followerChange.change integer | null
entries[].followerChange.collected required boolean
entries[].followerChange.derived required any
entries[].followerChange.formula required string
entries[].followerChange.message string
entries[].followerChange.reason string

Values: platform, notdelivered, noreadings

entries[].followerChange.subject string
entries[].followerChange.windowDays required integer
entries[].platform required string
entries[].profile required string
entries[].reception required ActivityReception

Mirrors meshery/schemas v1beta3 `ActivityReception`. Measured reception with the date it was read, or collected false with a reason and one sentence. Never zeros for missing data. `comparison` is derived: the median of the same subject's other activities and this activity's ratio to it.

entries[].reception.asOf string (date) | null

Date the figures were read.

entries[].reception.collected required boolean
entries[].reception.comparison object | null
entries[].reception.measured required any
entries[].reception.message string
entries[].reception.metrics ActivityMetrics

Mirrors meshery/schemas v1beta3 `ActivityMetrics`. Per-activity engagement figures. The known keys are always present and are null when the source did not report them, never zero. A platform-specific figure not listed here is carried under its own key only when reported.

entries[].reception.reason string

Values: platform, notdelivered, nolink, notmatched, unavailable

entries[].reception.source string

Where the figures came from.

entries[].reception.subject string

Account or page the figures belong to.

generatedAt required string (date-time)
kind required string

post, repost or comment.

ok required any
platform required string
state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

title required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AnalyticsActivityResponse

Variant fields
NameTypeDescription
activityId required string
entries required array of ActivityResultEntry
entries[].delivery required ActivityDelivery

Mirrors meshery/schemas v1beta3 `ActivityDelivery`. Whether the activity went out for one profile and platform, from the content item's recorded outcome and the run log. Measured, never derived.

entries[].delivery.attemptedAt required string (date-time) | null
entries[].delivery.browserProfile required object | null
entries[].delivery.computer required string | null

Host name of the computer whose run delivered or attempted it.

entries[].delivery.deliveredAt required string (date-time) | null
entries[].delivery.durationSeconds required number | null
entries[].delivery.error required string | null
entries[].delivery.evidence required string

What the status rests on.

Values: recorded, run, none

entries[].delivery.fix required object | null
entries[].delivery.link required string | null

Link to the live item.

entries[].delivery.logPath required string | null

Log file of the matched run, on the computer that ran it.

entries[].delivery.runId required string | null
entries[].delivery.scheduledFor required string (date-time) | null
entries[].delivery.status required string

delivered, unconfirmed (clicked, outcome not read), failed (last attempt failed), scheduled, pending (due, not yet run), draft (not approved).

Values: delivered, unconfirmed, failed, scheduled, pending, draft

entries[].followerChange required ActivityFollowerChange

Mirrors meshery/schemas v1beta3 `ActivityFollowerChange`. Derived: followers at the reading before the activity and the first reading at least `windowDays` after it. Correlation only.

entries[].followerChange.after object | null
entries[].followerChange.before object | null
entries[].followerChange.change integer | null
entries[].followerChange.collected required boolean
entries[].followerChange.derived required any
entries[].followerChange.formula required string
entries[].followerChange.message string
entries[].followerChange.reason string

Values: platform, notdelivered, noreadings

entries[].followerChange.subject string
entries[].followerChange.windowDays required integer
entries[].platform required string
entries[].profile required string
entries[].reception required ActivityReception

Mirrors meshery/schemas v1beta3 `ActivityReception`. Measured reception with the date it was read, or collected false with a reason and one sentence. Never zeros for missing data. `comparison` is derived: the median of the same subject's other activities and this activity's ratio to it.

entries[].reception.asOf string (date) | null

Date the figures were read.

entries[].reception.collected required boolean
entries[].reception.comparison object | null
entries[].reception.measured required any
entries[].reception.message string
entries[].reception.metrics ActivityMetrics

Mirrors meshery/schemas v1beta3 `ActivityMetrics`. Per-activity engagement figures. The known keys are always present and are null when the source did not report them, never zero. A platform-specific figure not listed here is carried under its own key only when reported.

entries[].reception.reason string

Values: platform, notdelivered, nolink, notmatched, unavailable

entries[].reception.source string

Where the figures came from.

entries[].reception.subject string

Account or page the figures belong to.

generatedAt required string (date-time)
kind required string

post, repost or comment.

ok required any
platform required string
state required ContentActivityState

Where a content activity stands. `pending` is ready to post, `scheduled` waits for its Promote On time, `draft` is not approved, `partial` has posted for some profiles, `done` for all, `unread` was clicked with its outcome never read, `invalid` has an unparseable Promote On and `unsupported` names no known platform.

Values: pending, scheduled, draft, partial, done, unread, invalid, unsupported

title required string
POST /cli/analytics/history Print the follower history the collectors have already recorded, as one dated series read from the store: every LinkedIn page and every profile's X trend, whichever machine collected them. Tolerates the N/A cells the collectors write, and derives the 30-day net, growth rate and (LinkedIn pages only) unfollowed figures, each labelled derived in the output; X profiles carry a net change and no unfollowed count, because X measures only the total. A store that cannot be reached exits 3.

Print the follower history the collectors have already recorded, as one dated series read from the store: every LinkedIn page and every profile's X trend, whichever machine collected them. Tolerates the N/A cells the collectors write, and derives the 30-day net, growth rate and (LinkedIn pages only) unfollowed figures, each labelled derived in the output; X profiles carry a net change and no unfollowed count, because X measures only the total. A store that cannot be reached exits 3.

Request body required

The flags and arguments of `blowhorn analytics history`, one property per parameter.

Request body of Print the follower history the collectors have already recorded, as one dated series read from the store: every LinkedIn page and every profile's X trend, whichever machine collected them. Tolerates the N/A cells the collectors write, and derives the 30-day net, growth rate and (LinkedIn pages only) unfollowed figures, each labelled derived in the output; X profiles carry a net change and no unfollowed count, because X measures only the total. A store that cannot be reached exits 3. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn analytics history' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn analytics'. One of: bluesky, linkedin, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn analytics history' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

since string

How far back to read: a number with d, w, m or y (30d, 12m, 1y), or 'all' (default: 12m)

subject string

One page or profile name (all by default)

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints follower history read from the analytics CSVs: one series point per subject per date, a summary per subject with the 30-day derivations, and what each source file held. LinkedIn pages also carry `unfollowed_30d` and their reception: the latest impressions and reactions, the date they were read, and their derived 30-day change.

Response 200 of Print the follower history the collectors have already recorded, as one dated series read from the store: every LinkedIn page and every profile's X trend, whichever machine collected them. Tolerates the N/A cells the collectors write, and derives the 30-day net, growth rate and (LinkedIn pages only) unfollowed figures, each labelled derived in the output; X profiles carry a net change and no unfollowed count, because X measures only the total. A store that cannot be reached exits 3. fields
NameTypeDescription
baseline_days required integer
derived required object

Derived field name to how it is computed.

ok required any
series required array of object
series[].date required string
series[].followers required integer | null
series[].following required integer | null
series[].growth_rate required number | null
series[].impressions required integer | null
series[].kind required string

Values: page, profile

series[].net_30d required integer | null
series[].new_30d required integer | null
series[].platform required string
series[].reactions required integer | null
series[].subject required string
series[].unfollowed_30d integer | null

LinkedIn pages only.

since required string | null
since_label required string
sources required array of object
sources[].error required string | null
sources[].exists required boolean
sources[].kind required string

Values: page, profile

sources[].path required string | null
sources[].platform required string
sources[].profile string

The profile a per-profile source belongs to.

sources[].rows required integer
sources[].skipped required integer
subject required string

The subject asked for, or `all`.

subjects required array of object
subjects[].baseline_date required string | null

The reading the 30-day figures are measured against.

subjects[].first required string
subjects[].followers required integer | null
subjects[].following required integer | null
subjects[].growth_rate required number | null
subjects[].impressions integer | null

LinkedIn pages only. Impressions at the latest reading that has reception figures.

subjects[].impressions_change_30d integer | null

LinkedIn pages only. Derived; impressions minus the reading at least 30 days earlier.

subjects[].kind required string

Values: page, profile

subjects[].last required string
subjects[].net_30d required integer | null
subjects[].new_30d required integer | null
subjects[].platform required string
subjects[].points required integer
subjects[].reactions integer | null

LinkedIn pages only. Reactions at that reading.

subjects[].reactions_change_30d integer | null

LinkedIn pages only. Derived; reactions minus the reading at least 30 days earlier.

subjects[].reception_date string | null

LinkedIn pages only. The date the reception figures were read.

subjects[].subject required string
subjects[].unfollowed_30d integer | null

LinkedIn pages only.

subjects[].username required string | null
unreadable_rows required integer
until required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AnalyticsHistoryResponse

Variant fields
NameTypeDescription
baseline_days required integer
derived required object

Derived field name to how it is computed.

ok required any
series required array of object
series[].date required string
series[].followers required integer | null
series[].following required integer | null
series[].growth_rate required number | null
series[].impressions required integer | null
series[].kind required string

Values: page, profile

series[].net_30d required integer | null
series[].new_30d required integer | null
series[].platform required string
series[].reactions required integer | null
series[].subject required string
series[].unfollowed_30d integer | null

LinkedIn pages only.

since required string | null
since_label required string
sources required array of object
sources[].error required string | null
sources[].exists required boolean
sources[].kind required string

Values: page, profile

sources[].path required string | null
sources[].platform required string
sources[].profile string

The profile a per-profile source belongs to.

sources[].rows required integer
sources[].skipped required integer
subject required string

The subject asked for, or `all`.

subjects required array of object
subjects[].baseline_date required string | null

The reading the 30-day figures are measured against.

subjects[].first required string
subjects[].followers required integer | null
subjects[].following required integer | null
subjects[].growth_rate required number | null
subjects[].impressions integer | null

LinkedIn pages only. Impressions at the latest reading that has reception figures.

subjects[].impressions_change_30d integer | null

LinkedIn pages only. Derived; impressions minus the reading at least 30 days earlier.

subjects[].kind required string

Values: page, profile

subjects[].last required string
subjects[].net_30d required integer | null
subjects[].new_30d required integer | null
subjects[].platform required string
subjects[].points required integer
subjects[].reactions integer | null

LinkedIn pages only. Reactions at that reading.

subjects[].reactions_change_30d integer | null

LinkedIn pages only. Derived; reactions minus the reading at least 30 days earlier.

subjects[].reception_date string | null

LinkedIn pages only. The date the reception figures were read.

subjects[].subject required string
subjects[].unfollowed_30d integer | null

LinkedIn pages only.

subjects[].username required string | null
unreadable_rows required integer
until required string
POST /cli/analytics/import-exports Read the LinkedIn page content exports already on disk and record each activity's reception figures in the store: impressions, views, clicks, click-through rate, likes, comments, reposts, follows and engagement rate from each export's All posts sheet, dated by the export. Each activity is matched to its activity by its link; activities made outside Blowhorn are kept and marked unmatched. Prints activities read, matched and stored per file. Safe to run again: a reading lands once per activity and export date. --dry-run reads and matches without writing.

Read the LinkedIn page content exports already on disk and record each activity's reception figures in the store: impressions, views, clicks, click-through rate, likes, comments, reposts, follows and engagement rate from each export's All posts sheet, dated by the export. Each activity is matched to its activity by its link; activities made outside Blowhorn are kept and marked unmatched. Prints activities read, matched and stored per file. Safe to run again: a reading lands once per activity and export date. --dry-run reads and matches without writing.

Request body required

The flags and arguments of `blowhorn analytics import-exports`, one property per parameter.

Request body of Read the LinkedIn page content exports already on disk and record each activity's reception figures in the store: impressions, views, clicks, click-through rate, likes, comments, reposts, follows and engagement rate from each export's All posts sheet, dated by the export. Each activity is matched to its activity by its link; activities made outside Blowhorn are kept and marked unmatched. Prints activities read, matched and stored per file. Safe to run again: a reading lands once per activity and export date. --dry-run reads and matches without writing. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn analytics import-exports' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

paths array of string

Export files to read (default: every content export in the analytics folder)

platform string

Platform scope for 'blowhorn analytics'. One of: bluesky, linkedin, x, all ('all' means every platform listed here)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn analytics import-exports' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the per-file verification of an import of LinkedIn page content exports' All posts sheets: rows read, matched to a content item by link, unmatched (kept), skipped with a reason, and the error for a file that could not be read, plus totals. `stored` is 0 on a dry run.

Response 200 of Read the LinkedIn page content exports already on disk and record each activity's reception figures in the store: impressions, views, clicks, click-through rate, likes, comments, reposts, follows and engagement rate from each export's All posts sheet, dated by the export. Each activity is matched to its activity by its link; activities made outside Blowhorn are kept and marked unmatched. Prints activities read, matched and stored per file. Safe to run again: a reading lands once per activity and export date. --dry-run reads and matches without writing. fields
NameTypeDescription
dryRun required boolean
files required array of object
files[].activities required integer
files[].error required string | null
files[].exportDate required string (date) | null
files[].file required string
files[].matched required integer
files[].page required string | null
files[].sheet required string
files[].sheetPresent required boolean
files[].skipped required array of object
files[].skipped[].reason required string
files[].skipped[].row required integer
files[].unmatched required integer
ok required any
totals required object
totals.activities required integer
totals.errors required integer
totals.files required integer
totals.filesWithSheet required integer
totals.matched required integer
totals.readings required integer
totals.stored required integer
totals.unmatched required integer

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AnalyticsImportExportsResponse

Variant fields
NameTypeDescription
dryRun required boolean
files required array of object
files[].activities required integer
files[].error required string | null
files[].exportDate required string (date) | null
files[].file required string
files[].matched required integer
files[].page required string | null
files[].sheet required string
files[].sheetPresent required boolean
files[].skipped required array of object
files[].skipped[].reason required string
files[].skipped[].row required integer
files[].unmatched required integer
ok required any
totals required object
totals.activities required integer
totals.errors required integer
totals.files required integer
totals.filesWithSheet required integer
totals.matched required integer
totals.readings required integer
totals.stored required integer
totals.unmatched required integer
POST /cli/analytics/operations Aggregate the run ledger, read from the store, into operations per profile, per platform, per command, per day or per host: how many runs each action made, what they confirmed, skipped and failed, and which profiles and platforms were active. Reads the ledger only - a dry run is counted apart and never as an operation, and records exist from the first run after this command shipped, so a window older than that reports zero rather than guessing. Per host is the per-machine history now that several machines share one store. A store that cannot be reached exits 3.

Aggregate the run ledger, read from the store, into operations per profile, per platform, per command, per day or per host: how many runs each action made, what they confirmed, skipped and failed, and which profiles and platforms were active. Reads the ledger only - a dry run is counted apart and never as an operation, and records exist from the first run after this command shipped, so a window older than that reports zero rather than guessing. Per host is the per-machine history now that several machines share one store. A store that cannot be reached exits 3.

Request body required

The flags and arguments of `blowhorn analytics operations`, one property per parameter.

Request body of Aggregate the run ledger, read from the store, into operations per profile, per platform, per command, per day or per host: how many runs each action made, what they confirmed, skipped and failed, and which profiles and platforms were active. Reads the ledger only - a dry run is counted apart and never as an operation, and records exist from the first run after this command shipped, so a window older than that reports zero rather than guessing. Per host is the per-machine history now that several machines share one store. A store that cannot be reached exits 3. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

group_by string

Aggregate per profile, platform, command, day or host -- host is the machine that ran it (default: profile)

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

kind string

Which ledger records to aggregate: run (action runs, the default) or tick (scheduler passes)

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn analytics operations' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn analytics'. One of: bluesky, linkedin, x, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn analytics operations' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

since string

How far back to read: a number with d, w, m or y (30d, 12m, 1y), or 'all' (default: 30d)

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints the operations ledger aggregated for a window: totals, and one group per profile, platform, command, day or host with its record count, failed runs, summed counts and breakdowns. A dry run counts only in `dry_runs`.

Response 200 of Aggregate the run ledger, read from the store, into operations per profile, per platform, per command, per day or per host: how many runs each action made, what they confirmed, skipped and failed, and which profiles and platforms were active. Reads the ledger only - a dry run is counted apart and never as an operation, and records exist from the first run after this command shipped, so a window older than that reports zero rather than guessing. Per host is the per-machine history now that several machines share one store. A store that cannot be reached exits 3. fields
NameTypeDescription
dry_runs required integer
group_by required string

Values: profile, platform, command, day, host

groups required array of object
groups[].by_command required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

groups[].by_platform required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

groups[].by_profile required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

groups[].counts required RunCounts

Summed action counts of a set of ledger records. For a scheduler tick record the counts are activities, not actions.

groups[].counts.attempted required integer
groups[].counts.confirmed required integer
groups[].counts.failed required integer
groups[].counts.skipped required integer
groups[].counts.unconfirmed required integer

Actions clicked whose outcome was never read.

groups[].dry_runs required integer
groups[].failed_runs required integer
groups[].first_ts required string | null
groups[].key required string

The profile, platform, command, day or host this group is.

groups[].last_ts required string | null
groups[].records required integer
kind required string

Values: run, tick

ok required any
records required integer
runs_dir required string
since required string | null
since_label required string
totals required object
totals.by_command required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

totals.by_platform required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

totals.by_profile required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

totals.by_source required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

totals.commands required array of string
totals.counts required RunCounts

Summed action counts of a set of ledger records. For a scheduler tick record the counts are activities, not actions.

totals.counts.attempted required integer
totals.counts.confirmed required integer
totals.counts.failed required integer
totals.counts.skipped required integer
totals.counts.unconfirmed required integer

Actions clicked whose outcome was never read.

totals.dry_runs required integer
totals.failed_runs required integer
totals.first_ts required string | null
totals.last_ts required string | null
totals.platforms_active required integer
totals.profiles_active required integer
totals.records required integer
unreadable_lines required integer
until required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AnalyticsOperationsResponse

Variant fields
NameTypeDescription
dry_runs required integer
group_by required string

Values: profile, platform, command, day, host

groups required array of object
groups[].by_command required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

groups[].by_platform required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

groups[].by_profile required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

groups[].counts required RunCounts

Summed action counts of a set of ledger records. For a scheduler tick record the counts are activities, not actions.

groups[].counts.attempted required integer
groups[].counts.confirmed required integer
groups[].counts.failed required integer
groups[].counts.skipped required integer
groups[].counts.unconfirmed required integer

Actions clicked whose outcome was never read.

groups[].dry_runs required integer
groups[].failed_runs required integer
groups[].first_ts required string | null
groups[].key required string

The profile, platform, command, day or host this group is.

groups[].last_ts required string | null
groups[].records required integer
kind required string

Values: run, tick

ok required any
records required integer
runs_dir required string
since required string | null
since_label required string
totals required object
totals.by_command required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

totals.by_platform required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

totals.by_profile required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

totals.by_source required CountMap

Record counts keyed by data - a command, a platform, a profile or a source name.

totals.commands required array of string
totals.counts required RunCounts

Summed action counts of a set of ledger records. For a scheduler tick record the counts are activities, not actions.

totals.counts.attempted required integer
totals.counts.confirmed required integer
totals.counts.failed required integer
totals.counts.skipped required integer
totals.counts.unconfirmed required integer

Actions clicked whose outcome was never read.

totals.dry_runs required integer
totals.failed_runs required integer
totals.first_ts required string | null
totals.last_ts required string | null
totals.platforms_active required integer
totals.profiles_active required integer
totals.records required integer
unreadable_lines required integer
until required string
POST /cli/analytics/pages List, add, remove or seed the LinkedIn pages analytics collects from for the signed-in organization, read from and written to the store: each page's name, its kind (company or showcase) and its numeric page id, from which the followers and content analytics URLs are derived. Collection walks the enabled pages; an organization with none configured collects the pages in use before per-organization configuration, and 'seed' writes those pages into the store once so the organization can edit its own list from there. --dry-run shows an add, remove or seed without writing. A store that cannot be reached exits 3.

List, add, remove or seed the LinkedIn pages analytics collects from for the signed-in organization, read from and written to the store: each page's name, its kind (company or showcase) and its numeric page id, from which the followers and content analytics URLs are derived. Collection walks the enabled pages; an organization with none configured collects the pages in use before per-organization configuration, and 'seed' writes those pages into the store once so the organization can edit its own list from there. --dry-run shows an add, remove or seed without writing. A store that cannot be reached exits 3.

Request body required

The flags and arguments of `blowhorn analytics pages`, one property per parameter.

Request body of List, add, remove or seed the LinkedIn pages analytics collects from for the signed-in organization, read from and written to the store: each page's name, its kind (company or showcase) and its numeric page id, from which the followers and content analytics URLs are derived. Collection walks the enabled pages; an organization with none configured collects the pages in use before per-organization configuration, and 'seed' writes those pages into the store once so the organization can edit its own list from there. --dry-run shows an add, remove or seed without writing. A store that cannot be reached exits 3. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn analytics pages' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

pages_action string

What to do: list (the default), add, remove or seed

pages_disabled boolean

Add the page disabled: listed, but collection skips it until it is added again without this flag

Default: false

pages_id string

The page's numeric LinkedIn id, or its LinkedIn page URL (add)

pages_kind string

The page's LinkedIn kind: company or showcase (add; read from the URL when the page value is one)

pages_name string

The page's display name, as analytics history names it (add and remove)

platform string

Platform scope for 'blowhorn analytics'. One of: bluesky, linkedin, x, all ('all' means every platform listed here)

Default: "all"

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

Default: "all"

risk_profile string

Accepted by 'blowhorn analytics pages' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The signed-in organization's LinkedIn analytics pages (#741): for `list`, every configured page; for `add` and `seed`, the pages the write left; for `remove`, an empty list. Each page carries its name, its LinkedIn kind (company or showcase), its numeric page id and whether it is enabled, plus the followers and content analytics URLs derived from the kind and id.

Response 200 of List, add, remove or seed the LinkedIn pages analytics collects from for the signed-in organization, read from and written to the store: each page's name, its kind (company or showcase) and its numeric page id, from which the followers and content analytics URLs are derived. Collection walks the enabled pages; an organization with none configured collects the pages in use before per-organization configuration, and 'seed' writes those pages into the store once so the organization can edit its own list from there. --dry-run shows an add, remove or seed without writing. A store that cannot be reached exits 3. fields
NameTypeDescription
action required string

Values: list, add, remove, seed

dryRun required boolean
ok required any
pages required array of object
pages[].content_url required string
pages[].enabled required boolean
pages[].followers_url required string
pages[].name required string
pages[].page_id required string
pages[].page_kind required string

Values: company, showcase

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

AnalyticsPagesResponse

Variant fields
NameTypeDescription
action required string

Values: list, add, remove, seed

dryRun required boolean
ok required any
pages required array of object
pages[].content_url required string
pages[].enabled required boolean
pages[].followers_url required string
pages[].name required string
pages[].page_id required string
pages[].page_kind required string

Values: company, showcase

POST /cli/report Generate a consolidated report summary from the exports already sitting in the repository-root data/analytics/ directory. Reads local files only - run 'blowhorn analytics' first if you want fresh numbers.

Generate a consolidated report summary from the exports already sitting in the repository-root data/analytics/ directory. Reads local files only - run 'blowhorn analytics' first if you want fresh numbers.

Request body required

The flags and arguments of `blowhorn report`, one property per parameter.

Request body of Generate a consolidated report summary from the exports already sitting in the repository-root data/analytics/ directory. Reads local files only - run 'blowhorn analytics' first if you want fresh numbers. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

no_open boolean

Write data/analytics/analytics_report.html without opening a browser

Default: false

pace string

Accepted by 'blowhorn report' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope. 'blowhorn report' is not platform-specific, so the only accepted value is 'all' (the default)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn report' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Whether the HTML analytics report was regenerated and opened. With --json, stdout is exactly this one JSON document; the summary table and progress lines are written to stderr instead. The same shape is printed with ok false (and exit 1) when generation failed.

Response 200 of Generate a consolidated report summary from the exports already sitting in the repository-root data/analytics/ directory. Reads local files only - run 'blowhorn analytics' first if you want fresh numbers. fields
NameTypeDescription
dry_run required boolean
error required string | null
generated required boolean
ok required boolean
opened required boolean

Whether the report was opened in the default browser; false with --no-open.

path required string

Where the report is (or would be) written.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ReportResponse

Variant fields
NameTypeDescription
dry_run required boolean
error required string | null
generated required boolean
ok required boolean
opened required boolean

Whether the report was opened in the default browser; false with --no-open.

path required string

Where the report is (or would be) written.

Platforms

POST /cli/platforms/capabilities Show what each platform allows: text limits and counting, media, tagging, actions and published rates, with the provenance of every fact and the tier overrides a profile's account unlocks. A fact with no value is not on record and is never enforced. --platform reads one platform; --stale keeps only the platforms with a fact past its review date.

Show what each platform allows: text limits and counting, media, tagging, actions and published rates, with the provenance of every fact and the tier overrides a profile's account unlocks. A fact with no value is not on record and is never enforced. --platform reads one platform; --stale keeps only the platforms with a fact past its review date.

Request body required

The flags and arguments of `blowhorn platforms capabilities`, one property per parameter.

Request body of Show what each platform allows: text limits and counting, media, tagging, actions and published rates, with the provenance of every fact and the tier overrides a profile's account unlocks. A fact with no value is not on record and is never enforced. --platform reads one platform; --stale keeps only the platforms with a fact past its review date. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit machine-readable JSON output

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn platforms capabilities' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Platform scope for 'blowhorn platforms'. One of: bluesky, facebook, github, hn, instagram, linkedin, reddit, slack, tiktok, x, youtube, all ('all' means every platform listed here)

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn platforms capabilities' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

stale boolean

Keep only the platforms with a fact past their review date

Default: false

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What each platform allows, from the registry: the capability facts with their provenance, the tier overrides a profile's account unlocks, and how Blowhorn itself reads and paces the platform. A fact with no value is not on record and is never enforced.

Response 200 of Show what each platform allows: text limits and counting, media, tagging, actions and published rates, with the provenance of every fact and the tier overrides a profile's account unlocks. A fact with no value is not on record and is never enforced. --platform reads one platform; --stale keeps only the platforms with a fact past its review date. fields
NameTypeDescription
ok required any
platforms required object

The platforms read, keyed by platform id.

stale required array of object

The facts past their review date, scoped to the platforms included.

stale[].path required string

The dotted path of the fact.

stale[].platform required string

The platform id the fact belongs to.

stale[].reviewBy required string (date)

The date the fact was due to be re-read by.

stale[].tier required string | null

The tier the override belongs to, or null for a base fact.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

PlatformsCapabilitiesResponse

Variant fields
NameTypeDescription
ok required any
platforms required object

The platforms read, keyed by platform id.

stale required array of object

The facts past their review date, scoped to the platforms included.

stale[].path required string

The dotted path of the fact.

stale[].platform required string

The platform id the fact belongs to.

stale[].reviewBy required string (date)

The date the fact was due to be re-read by.

stale[].tier required string | null

The tier the override belongs to, or null for a base fact.

Meta

POST /cli/classify-url Say what a link is, by the platform rules the engine keeps: 'post' when it names an existing post or comment on a supported platform, with the verbs that platform takes ('respond', 'amplify'); 'page' for any other web link, with the platform whose site it is on; 'none' for anything that is not a web link. The desktop app's drop reads this to choose its sheet. It reads the link text only: it opens no browser, signs in to nothing and writes nothing.

Say what a link is, by the platform rules the engine keeps: 'post' when it names an existing post or comment on a supported platform, with the verbs that platform takes ('respond', 'amplify'); 'page' for any other web link, with the platform whose site it is on; 'none' for anything that is not a web link. The desktop app's drop reads this to choose its sheet. It reads the link text only: it opens no browser, signs in to nothing and writes nothing.

Request body required

The flags and arguments of `blowhorn classify-url`, one property per parameter.

Request body of Say what a link is, by the platform rules the engine keeps: 'post' when it names an existing post or comment on a supported platform, with the verbs that platform takes ('respond', 'amplify'); 'page' for any other web link, with the platform whose site it is on; 'none' for anything that is not a web link. The desktop app's drop reads this to choose its sheet. It reads the link text only: it opens no browser, signs in to nothing and writes nothing. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the classification as JSON with a stable shape, for a script or the desktop app

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn classify-url' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn classify-url': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn classify-url' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

url required string

The link to classify

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

Prints, for one link, what it is: `post` (an existing post or comment on a supported platform, with the verbs it takes), `page` (any other web link) or `none` (not a web link). Nothing is opened, signed in to or written; the classification reads the link text by the engine's own platform rules (`engine/blowhorn/url_classify.py`).

Response 200 of Say what a link is, by the platform rules the engine keeps: 'post' when it names an existing post or comment on a supported platform, with the verbs that platform takes ('respond', 'amplify'); 'page' for any other web link, with the platform whose site it is on; 'none' for anything that is not a web link. The desktop app's drop reads this to choose its sheet. It reads the link text only: it opens no browser, signs in to nothing and writes nothing. fields
NameTypeDescription
kind required string

post (engage with it), page (a new order that uses the page) or none (not a web link).

Values: post, page, none

ok required any
platform required string | null

The platform the link names, or null when it names none.

Values: linkedin, x, reddit, bluesky, slack, github, hn,

url required string

The link as it was given, trimmed.

verbs required array of string

The engage verbs a post takes, in the order the sheet offers them; empty for a page and for a platform with none.

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

ClassifyUrlResponse

Variant fields
NameTypeDescription
kind required string

post (engage with it), page (a new order that uses the page) or none (not a web link).

Values: post, page, none

ok required any
platform required string | null

The platform the link names, or null when it names none.

Values: linkedin, x, reddit, bluesky, slack, github, hn,

url required string

The link as it was given, trimmed.

verbs required array of string

The engage verbs a post takes, in the order the sheet offers them; empty for a page and for a platform with none.

POST /cli/setup Set up Blowhorn on this computer, safely and repeatably. Each step checks first and changes only what is missing or stale, so running it again is safe; every line says ok (already in place), changed (written by this run), planned, declined, todo (needs you) or failed. It checks that blowhorn is on PATH, lists your Chrome profiles from Chrome's Local State file, writes the Chrome extension's native-messaging host when it is missing or stale, adds the marked PATH block to your shell startup files (~/.zshrc, ~/.bashrc, ~/.config/fish/config.fish) when it is missing, so new terminals find blowhorn in ~/.local/bin, and checks your Blowhorn sign-in. Before it writes the native host it says, in plain language, which Chrome profiles the file reaches and asks; the PATH block is asked about the same way; --yes answers yes for a script, and --dry-run writes nothing. With --json, or with no terminal to ask in, nobody is asked: it writes nothing and reports planned. It never starts, opens or attaches to Chrome and installs no extension. It ends with what is left for you: sign in, install the extension from the Chrome Web Store, check it answers, and get the app from blowhorn.ai/download. --json prints the same as one object.

Set up Blowhorn on this computer, safely and repeatably. Each step checks first and changes only what is missing or stale, so running it again is safe; every line says ok (already in place), changed (written by this run), planned, declined, todo (needs you) or failed. It checks that blowhorn is on PATH, lists your Chrome profiles from Chrome's Local State file, writes the Chrome extension's native-messaging host when it is missing or stale, adds the marked PATH block to your shell startup files (~/.zshrc, ~/.bashrc, ~/.config/fish/config.fish) when it is missing, so new terminals find blowhorn in ~/.local/bin, and checks your Blowhorn sign-in. Before it writes the native host it says, in plain language, which Chrome profiles the file reaches and asks; the PATH block is asked about the same way; --yes answers yes for a script, and --dry-run writes nothing. With --json, or with no terminal to ask in, nobody is asked: it writes nothing and reports planned. It never starts, opens or attaches to Chrome and installs no extension. It ends with what is left for you: sign in, install the extension from the Chrome Web Store, check it answers, and get the app from blowhorn.ai/download. --json prints the same as one object.

Request body required

The flags and arguments of `blowhorn setup`, one property per parameter.

Request body of Set up Blowhorn on this computer, safely and repeatably. Each step checks first and changes only what is missing or stale, so running it again is safe; every line says ok (already in place), changed (written by this run), planned, declined, todo (needs you) or failed. It checks that blowhorn is on PATH, lists your Chrome profiles from Chrome's Local State file, writes the Chrome extension's native-messaging host when it is missing or stale, adds the marked PATH block to your shell startup files (~/.zshrc, ~/.bashrc, ~/.config/fish/config.fish) when it is missing, so new terminals find blowhorn in ~/.local/bin, and checks your Blowhorn sign-in. Before it writes the native host it says, in plain language, which Chrome profiles the file reaches and asks; the PATH block is asked about the same way; --yes answers yes for a script, and --dry-run writes nothing. With --json, or with no terminal to ask in, nobody is asked: it writes nothing and reports planned. It never starts, opens or attaches to Chrome and installs no extension. It ends with what is left for you: sign in, install the extension from the Chrome Web Store, check it answers, and get the app from blowhorn.ai/download. --json prints the same as one object. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Print the result as one JSON object: steps, profiles, changed, already_ok and next_steps.

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn setup' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn setup': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn setup' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

yes boolean

Agree to the writes setup asks about (the Chrome native host, the PATH block) without being asked, for a script or the desktop app.

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

What setup did or would do, step by step. It carries no ok key; the exit code is 1 when any step failed and the same object is still printed.

Response 200 of Set up Blowhorn on this computer, safely and repeatably. Each step checks first and changes only what is missing or stale, so running it again is safe; every line says ok (already in place), changed (written by this run), planned, declined, todo (needs you) or failed. It checks that blowhorn is on PATH, lists your Chrome profiles from Chrome's Local State file, writes the Chrome extension's native-messaging host when it is missing or stale, adds the marked PATH block to your shell startup files (~/.zshrc, ~/.bashrc, ~/.config/fish/config.fish) when it is missing, so new terminals find blowhorn in ~/.local/bin, and checks your Blowhorn sign-in. Before it writes the native host it says, in plain language, which Chrome profiles the file reaches and asks; the PATH block is asked about the same way; --yes answers yes for a script, and --dry-run writes nothing. With --json, or with no terminal to ask in, nobody is asked: it writes nothing and reports planned. It never starts, opens or attaches to Chrome and installs no extension. It ends with what is left for you: sign in, install the extension from the Chrome Web Store, check it answers, and get the app from blowhorn.ai/download. --json prints the same as one object. fields
NameTypeDescription
already_ok required array of string

Names of the steps whose state is ok.

changed required array of string

Names of the steps whose state is changed.

next_steps required array of string
profiles required array of string

Chrome profiles detected, labelled by name and directory. These are real people's names; never commit real output.

steps required array of object
steps[].detail required string
steps[].name required string
steps[].state required string

Values: ok, changed, planned, declined, todo, failed

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

SetupResponse

Variant fields
NameTypeDescription
already_ok required array of string

Names of the steps whose state is ok.

changed required array of string

Names of the steps whose state is changed.

next_steps required array of string
profiles required array of string

Chrome profiles detected, labelled by name and directory. These are real people's names; never commit real output.

steps required array of object
steps[].detail required string
steps[].name required string
steps[].state required string

Values: ok, changed, planned, declined, todo, failed

POST /cli/uninstall Remove Blowhorn from this machine: the background service (the ai.blowhorn.service LaunchAgent, booted out and its file removed), the Chrome native host (the com.blowhorn.chrome_extension manifest, its launcher under ~/Library/Application Support/Blowhorn, and the every-profile install entries 'chrome extension-install --all-profiles' wrote), the ~/.local/bin/blowhorn launcher tools/install.sh wrote (a launcher without the installer's marker is kept), the marked PATH block in ~/.zshrc, ~/.bashrc and ~/.config/fish/config.fish (only the block comes out; the rest of each file stays), /Applications/Blowhorn.app (the bundle first unregisters its own Login Items entries - open at login and the background service - through its --unregister-login-items; a bundle too old to have it is reported not unregistered, naming System Settings > General > Login Items as the step left to you), and the checkout's venv/ (--venv-dir, else BLOWHORN_VENV_DIR, else <checkout>/venv, removed only when it holds pyvenv.cfg), node_modules/, desktop/node_modules/ and data/logs/. Every item is reported as removed, not present, not checked, kept, not unregistered or failed, in that order; a failed or not-unregistered item is reported and the run goes on, exiting 1 at the end. The sign-in on this machine (the Keychain item, the account record, and the guided setup progress) is removed on every uninstall. Local data is kept and named -- the store connection file an older version wrote (no longer read), the desktop app's state, Blowhorn's own Chrome trees and the checkout's data/cache/ (each profile's runtime state) -- unless --purge is given, which removes it after you type 'purge' at the prompt (or pass --yes, which --json needs); your Blowhorn account is never touched. Nothing under a Chrome profile is read or written: nobody is signed out of Chrome, Google Chrome stays installed, and the extension stays on the Chrome Web Store. Refused by name, in a dry run too, while a scheduler tick, a service pass, an extension run, Blowhorn.app, a desktop app started from this checkout or, with --purge, a Chrome on ~/.blowhorn is running, and while a write-back spool this run removes (under the checkout's data/logs/, or the app's state under --purge) holds run records that have not reached the shared store (run 'blowhorn store --replay-spool' first, or pass --discard-unsynced-runs when the store will never be reachable again); a spool in any other log directory is left in place and named with its count. --dry-run lists every item and removes nothing. ./tools/uninstall.sh at the checkout root runs the same code with the system Python once the venv is gone. --from-trash is the run the app makes on itself after you drag it to the Trash, before the Trash is emptied: purge scope is forced, the bundle is looked for in ~/.Trash as well as /Applications and removing it takes it out of the Trash, and the Login Items entry of a bundle that is already gone is reported instead of failing the run. macOS only; Linux and any other platform are refused by name.

Remove Blowhorn from this machine: the background service (the ai.blowhorn.service LaunchAgent, booted out and its file removed), the Chrome native host (the com.blowhorn.chrome_extension manifest, its launcher under ~/Library/Application Support/Blowhorn, and the every-profile install entries 'chrome extension-install --all-profiles' wrote), the ~/.local/bin/blowhorn launcher tools/install.sh wrote (a launcher without the installer's marker is kept), the marked PATH block in ~/.zshrc, ~/.bashrc and ~/.config/fish/config.fish (only the block comes out; the rest of each file stays), /Applications/Blowhorn.app (the bundle first unregisters its own Login Items entries - open at login and the background service - through its --unregister-login-items; a bundle too old to have it is reported not unregistered, naming System Settings > General > Login Items as the step left to you), and the checkout's venv/ (--venv-dir, else BLOWHORN_VENV_DIR, else <checkout>/venv, removed only when it holds pyvenv.cfg), node_modules/, desktop/node_modules/ and data/logs/. Every item is reported as removed, not present, not checked, kept, not unregistered or failed, in that order; a failed or not-unregistered item is reported and the run goes on, exiting 1 at the end. The sign-in on this machine (the Keychain item, the account record, and the guided setup progress) is removed on every uninstall. Local data is kept and named -- the store connection file an older version wrote (no longer read), the desktop app's state, Blowhorn's own Chrome trees and the checkout's data/cache/ (each profile's runtime state) -- unless --purge is given, which removes it after you type 'purge' at the prompt (or pass --yes, which --json needs); your Blowhorn account is never touched. Nothing under a Chrome profile is read or written: nobody is signed out of Chrome, Google Chrome stays installed, and the extension stays on the Chrome Web Store. Refused by name, in a dry run too, while a scheduler tick, a service pass, an extension run, Blowhorn.app, a desktop app started from this checkout or, with --purge, a Chrome on ~/.blowhorn is running, and while a write-back spool this run removes (under the checkout's data/logs/, or the app's state under --purge) holds run records that have not reached the shared store (run 'blowhorn store --replay-spool' first, or pass --discard-unsynced-runs when the store will never be reachable again); a spool in any other log directory is left in place and named with its count. --dry-run lists every item and removes nothing. ./tools/uninstall.sh at the checkout root runs the same code with the system Python once the venv is gone. --from-trash is the run the app makes on itself after you drag it to the Trash, before the Trash is emptied: purge scope is forced, the bundle is looked for in ~/.Trash as well as /Applications and removing it takes it out of the Trash, and the Login Items entry of a bundle that is already gone is reported instead of failing the run. macOS only; Linux and any other platform are refused by name.

Request body required

The flags and arguments of `blowhorn uninstall`, one property per parameter.

Request body of Remove Blowhorn from this machine: the background service (the ai.blowhorn.service LaunchAgent, booted out and its file removed), the Chrome native host (the com.blowhorn.chrome_extension manifest, its launcher under ~/Library/Application Support/Blowhorn, and the every-profile install entries 'chrome extension-install --all-profiles' wrote), the ~/.local/bin/blowhorn launcher tools/install.sh wrote (a launcher without the installer's marker is kept), the marked PATH block in ~/.zshrc, ~/.bashrc and ~/.config/fish/config.fish (only the block comes out; the rest of each file stays), /Applications/Blowhorn.app (the bundle first unregisters its own Login Items entries - open at login and the background service - through its --unregister-login-items; a bundle too old to have it is reported not unregistered, naming System Settings > General > Login Items as the step left to you), and the checkout's venv/ (--venv-dir, else BLOWHORN_VENV_DIR, else <checkout>/venv, removed only when it holds pyvenv.cfg), node_modules/, desktop/node_modules/ and data/logs/. Every item is reported as removed, not present, not checked, kept, not unregistered or failed, in that order; a failed or not-unregistered item is reported and the run goes on, exiting 1 at the end. The sign-in on this machine (the Keychain item, the account record, and the guided setup progress) is removed on every uninstall. Local data is kept and named -- the store connection file an older version wrote (no longer read), the desktop app's state, Blowhorn's own Chrome trees and the checkout's data/cache/ (each profile's runtime state) -- unless --purge is given, which removes it after you type 'purge' at the prompt (or pass --yes, which --json needs); your Blowhorn account is never touched. Nothing under a Chrome profile is read or written: nobody is signed out of Chrome, Google Chrome stays installed, and the extension stays on the Chrome Web Store. Refused by name, in a dry run too, while a scheduler tick, a service pass, an extension run, Blowhorn.app, a desktop app started from this checkout or, with --purge, a Chrome on ~/.blowhorn is running, and while a write-back spool this run removes (under the checkout's data/logs/, or the app's state under --purge) holds run records that have not reached the shared store (run 'blowhorn store --replay-spool' first, or pass --discard-unsynced-runs when the store will never be reachable again); a spool in any other log directory is left in place and named with its count. --dry-run lists every item and removes nothing. ./tools/uninstall.sh at the checkout root runs the same code with the system Python once the venv is gone. --from-trash is the run the app makes on itself after you drag it to the Trash, before the Trash is emptied: purge scope is forced, the bundle is looked for in ~/.Trash as well as /Applications and removing it takes it out of the Trash, and the Login Items entry of a bundle that is already gone is reported instead of failing the run. macOS only; Linux and any other platform are refused by name. fields
NameTypeDescription
bin_dir string

Where tools/install.sh wrote the launchers (its --bin-dir; default ~/.local/bin)

chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

discard_unsynced_runs boolean

Give up the run records waiting in a write-back spool this run removes (runs/pending-store.ndjson under the checkout's data/logs/, or the app's state under --purge) instead of refusing, when the shared store will never be reachable again; otherwise run 'blowhorn store --replay-spool' first. Asks you to type discard after naming the count of records it deletes (or pass --yes, which --json needs)

Default: false

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

from_trash boolean

The run the app makes on itself after you drag Blowhorn.app to the Trash, before the Trash is emptied: purge scope is forced (everything goes, local data included), the bundle is looked for in ~/.Trash as well as /Applications, removing it takes it out of the Trash, and the Login Items entry of a bundle that is already gone is reported instead of failing the run. Asks you to type purge first (or pass --yes, which --json needs)

Default: false

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as one JSON object: ok, dry_run, purge, from_trash, refusals, every item with its result, and the kept paths

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn uninstall' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn uninstall': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

purge boolean

Also remove local data a plain uninstall keeps: ~/.config/blowhorn (an older version's store.yaml, no longer read), the rest of ~/Library/Application Support/Blowhorn, ~/.blowhorn, the app's per-user Library state, the checkout's data/cache/ (each profile's runtime state) and the pause file. The sign-in (Keychain item and account record) is removed on every uninstall, with or without this flag. Asks you to type purge first; your Blowhorn account is never touched

Default: false

risk_profile string

Accepted by 'blowhorn uninstall' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

venv_dir string

The virtualenv tools/install.sh created (its --venv-dir; default BLOWHORN_VENV_DIR, else <checkout>/venv). Removed only when it holds pyvenv.cfg; the filesystem root, your home directory, the checkout and a directory holding either are refused

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

yes boolean

Confirm --purge, --from-trash or --discard-unsynced-runs without the prompt, for a run with no terminal; refused without one of them

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The uninstall report - one object listing each artifact and what happened to it. The same shape is printed on a failed or refused run with ok false and error set; the early refusals (an option combination, an unsupported platform) print only ok false and error (see Problem).

Response 200 of Remove Blowhorn from this machine: the background service (the ai.blowhorn.service LaunchAgent, booted out and its file removed), the Chrome native host (the com.blowhorn.chrome_extension manifest, its launcher under ~/Library/Application Support/Blowhorn, and the every-profile install entries 'chrome extension-install --all-profiles' wrote), the ~/.local/bin/blowhorn launcher tools/install.sh wrote (a launcher without the installer's marker is kept), the marked PATH block in ~/.zshrc, ~/.bashrc and ~/.config/fish/config.fish (only the block comes out; the rest of each file stays), /Applications/Blowhorn.app (the bundle first unregisters its own Login Items entries - open at login and the background service - through its --unregister-login-items; a bundle too old to have it is reported not unregistered, naming System Settings > General > Login Items as the step left to you), and the checkout's venv/ (--venv-dir, else BLOWHORN_VENV_DIR, else <checkout>/venv, removed only when it holds pyvenv.cfg), node_modules/, desktop/node_modules/ and data/logs/. Every item is reported as removed, not present, not checked, kept, not unregistered or failed, in that order; a failed or not-unregistered item is reported and the run goes on, exiting 1 at the end. The sign-in on this machine (the Keychain item, the account record, and the guided setup progress) is removed on every uninstall. Local data is kept and named -- the store connection file an older version wrote (no longer read), the desktop app's state, Blowhorn's own Chrome trees and the checkout's data/cache/ (each profile's runtime state) -- unless --purge is given, which removes it after you type 'purge' at the prompt (or pass --yes, which --json needs); your Blowhorn account is never touched. Nothing under a Chrome profile is read or written: nobody is signed out of Chrome, Google Chrome stays installed, and the extension stays on the Chrome Web Store. Refused by name, in a dry run too, while a scheduler tick, a service pass, an extension run, Blowhorn.app, a desktop app started from this checkout or, with --purge, a Chrome on ~/.blowhorn is running, and while a write-back spool this run removes (under the checkout's data/logs/, or the app's state under --purge) holds run records that have not reached the shared store (run 'blowhorn store --replay-spool' first, or pass --discard-unsynced-runs when the store will never be reachable again); a spool in any other log directory is left in place and named with its count. --dry-run lists every item and removes nothing. ./tools/uninstall.sh at the checkout root runs the same code with the system Python once the venv is gone. --from-trash is the run the app makes on itself after you drag it to the Trash, before the Trash is emptied: purge scope is forced, the bundle is looked for in ~/.Trash as well as /Applications and removing it takes it out of the Trash, and the Login Items entry of a bundle that is already gone is reported instead of failing the run. macOS only; Linux and any other platform are refused by name. fields
NameTypeDescription
discard_unsynced_runs required boolean
dry_run required boolean
error required string | null
from_trash required boolean

True on the --from-trash run the trashed app makes on itself; purge is forced true there.

items required array of object
items[].action required string

Values: remove, keep, never, manual

items[].category required string

service, native host, chrome install, launcher, path block, desktop app, checkout, local data or shared store.

items[].detail required string | null
items[].kind required string

launchd, file, tree, policy entry, login items, path block or remote.

items[].label required string
items[].note required string | null
items[].path required string | null
items[].present required boolean
items[].result required string

removed, would remove, not present, not checked, kept, never touched, failed or not unregistered.

kept required array of string | null

Paths of local data left in place.

not_checked required array of string

Places that could not be read, each naming the file and what was therefore not looked for.

ok required boolean
platform required string
purge required boolean
refusals required array of string

Sentences naming what is in flight; when non-empty nothing runs.

store_touched required boolean

Always false; uninstall never changes the shared store.

unsynced_runs_given_up required array of object
unsynced_runs_given_up[].count required integer
unsynced_runs_given_up[].log_dir required string
unsynced_runs_given_up[].path required string
unsynced_runs_kept required array of object
unsynced_runs_kept[].count required integer
unsynced_runs_kept[].log_dir required string
unsynced_runs_kept[].path required string

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

UninstallResponse

Variant fields
NameTypeDescription
discard_unsynced_runs required boolean
dry_run required boolean
error required string | null
from_trash required boolean

True on the --from-trash run the trashed app makes on itself; purge is forced true there.

items required array of object
items[].action required string

Values: remove, keep, never, manual

items[].category required string

service, native host, chrome install, launcher, path block, desktop app, checkout, local data or shared store.

items[].detail required string | null
items[].kind required string

launchd, file, tree, policy entry, login items, path block or remote.

items[].label required string
items[].note required string | null
items[].path required string | null
items[].present required boolean
items[].result required string

removed, would remove, not present, not checked, kept, never touched, failed or not unregistered.

kept required array of string | null

Paths of local data left in place.

not_checked required array of string

Places that could not be read, each naming the file and what was therefore not looked for.

ok required boolean
platform required string
purge required boolean
refusals required array of string

Sentences naming what is in flight; when non-empty nothing runs.

store_touched required boolean

Always false; uninstall never changes the shared store.

unsynced_runs_given_up required array of object
unsynced_runs_given_up[].count required integer
unsynced_runs_given_up[].log_dir required string
unsynced_runs_given_up[].path required string
unsynced_runs_kept required array of object
unsynced_runs_kept[].count required integer
unsynced_runs_kept[].log_dir required string
unsynced_runs_kept[].path required string
POST /cli/upgrade Carry this Mac's local data from an earlier home into this one. It carries profiles, analytics history, every run-ledger line (duplicates included), the write-back spool, the pause and the settings you changed. The earlier home is left exactly as it was, nothing already in the new home is overwritten, and a file that differs is kept and named in the report. Browser session folders are never carried. The pause is carried first, so a paused Mac is paused in the new home even if a later part fails. Each completed carry is recorded in upgrades.json in the new home, so it runs once; a carry that fails is not recorded and the next run tries again, adding nothing twice. While a scheduled pass is still running from the earlier home the answer is busy and nothing is touched. The earlier home is found from --source, then from the engine record the app wrote before this launch, then from the previous checkout at ~/code/blowhorn. A folder that belongs to another macOS user is never carried, even when named with --source; the report says so and carries nothing. --dry-run -- including one that comes from your settings -- only plans: the answer is planned, nothing is written, and the upgrade is still due. The desktop app runs this itself at launch; you never need a terminal for it.

Carry this Mac's local data from an earlier home into this one. It carries profiles, analytics history, every run-ledger line (duplicates included), the write-back spool, the pause and the settings you changed. The earlier home is left exactly as it was, nothing already in the new home is overwritten, and a file that differs is kept and named in the report. Browser session folders are never carried. The pause is carried first, so a paused Mac is paused in the new home even if a later part fails. Each completed carry is recorded in upgrades.json in the new home, so it runs once; a carry that fails is not recorded and the next run tries again, adding nothing twice. While a scheduled pass is still running from the earlier home the answer is busy and nothing is touched. The earlier home is found from --source, then from the engine record the app wrote before this launch, then from the previous checkout at ~/code/blowhorn. A folder that belongs to another macOS user is never carried, even when named with --source; the report says so and carries nothing. --dry-run -- including one that comes from your settings -- only plans: the answer is planned, nothing is written, and the upgrade is still due. The desktop app runs this itself at launch; you never need a terminal for it.

Request body required

The flags and arguments of `blowhorn upgrade`, one property per parameter.

Request body of Carry this Mac's local data from an earlier home into this one. It carries profiles, analytics history, every run-ledger line (duplicates included), the write-back spool, the pause and the settings you changed. The earlier home is left exactly as it was, nothing already in the new home is overwritten, and a file that differs is kept and named in the report. Browser session folders are never carried. The pause is carried first, so a paused Mac is paused in the new home even if a later part fails. Each completed carry is recorded in upgrades.json in the new home, so it runs once; a carry that fails is not recorded and the next run tries again, adding nothing twice. While a scheduled pass is still running from the earlier home the answer is busy and nothing is touched. The earlier home is found from --source, then from the engine record the app wrote before this launch, then from the previous checkout at ~/code/blowhorn. A folder that belongs to another macOS user is never carried, even when named with --source; the report says so and carries nothing. --dry-run -- including one that comes from your settings -- only plans: the answer is planned, nothing is written, and the upgrade is still due. The desktop app runs this itself at launch; you never need a terminal for it. fields
NameTypeDescription
chrome_launch string

How this run reaches Google Chrome, for one run: 'extension' (the default) drives Chrome through the Blowhorn Chrome extension and starts no Playwright Chrome; 'persistent' launches Blowhorn's own Chrome from chrome.automation_data_dir with the mapped profile directory inside it; 'cdp' attaches to a running Chrome that is ready per 'blowhorn chrome status' (daily Chrome asks you to Allow once per connection; a dedicated attach-chrome started with 'blowhorn chrome attach-chrome start' does not); and 'auto' attaches when Chrome is ready and launches otherwise. This flag, then BLOWHORN_CHROME_LAUNCH, then BLOWHORN_BROWSER_DRIVER, then defaults.chrome.launch_mode in .blowhorn.yaml, selects the driver a run actually uses. Any other value is refused before anything runs. Every browser run needs the profile mapped with 'blowhorn chrome map' first, in any mode. A scheduler tick's jobs each resolve their own mode (this flag on the tick, BLOWHORN_CHROME_LAUNCH, the job's driver mode, BLOWHORN_BROWSER_DRIVER, defaults.chrome.launch_mode, then the extension)

config string

Path to global config file (default: ./.blowhorn.yaml)

dry_run boolean

Simulate execution without making changes

Default: false

exclude string

Profiles to exclude when using --profile all (comma delimited); alias: --e; use 'none' or an empty string to clear configured defaults

headed boolean

Keep the browser window visible and in front (overrides --headless) so a person can finish a sign-in or challenge

Default: false

headless boolean

Run unattended in a background browser window: never headless (Chrome would announce the run as HeadlessChrome to every site) and no waiting for a person at a sign-in or challenge. When the run attaches to your running Chrome (--chrome-launch cdp, or auto while 'blowhorn chrome status' says ready) the tab opens in its own visible Chrome window instead: a hidden tab is throttled, so the previous app is only re-activated once

Default: false

json_output boolean

Emit the report as one JSON object: ok, status (upgraded, planned, current, busy or failed), dry_run, target, source, a one-line summary, and one entry per step with its counts and any files kept

Default: false

log_dir string

Directory where daily rotated stdout/stderr logs are persisted while still streaming to the console

pace string

Accepted by 'blowhorn upgrade' because --pace is a global flag, but this command does no paced work -- it sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs

platform string

Accepted and ignored by 'blowhorn upgrade': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml

profile string

Profile context (name, comma-delimited names, or 'all' alias for all profiles)

risk_profile string

Accepted by 'blowhorn upgrade' because --risk-profile is a global flag, but this command does no gated work -- no Risk Profile hold, cap or pace applies to it -- so the value changes nothing about how it runs. A value after the command word wins over one before it; an unrecognized value is rejected before anything runs

source string

The earlier version's home to carry data from; without it the engine record from before this launch is read, then the previous checkout at ~/code/blowhorn

verbose boolean

Enable verbose output (includes COMMAND SUMMARY)

Default: false

Responses

200

What the command prints to stdout with --json when it exits 0.

The upgrade report: whether this Mac's local data was carried from an earlier version's home into the home this version uses, and what each step carried. The same shape is printed on a busy or failed run with ok false and error set (a busy or failed status is read from status, here and in details on the desktop runner's failure shape).

Response 200 of Carry this Mac's local data from an earlier home into this one. It carries profiles, analytics history, every run-ledger line (duplicates included), the write-back spool, the pause and the settings you changed. The earlier home is left exactly as it was, nothing already in the new home is overwritten, and a file that differs is kept and named in the report. Browser session folders are never carried. The pause is carried first, so a paused Mac is paused in the new home even if a later part fails. Each completed carry is recorded in upgrades.json in the new home, so it runs once; a carry that fails is not recorded and the next run tries again, adding nothing twice. While a scheduled pass is still running from the earlier home the answer is busy and nothing is touched. The earlier home is found from --source, then from the engine record the app wrote before this launch, then from the previous checkout at ~/code/blowhorn. A folder that belongs to another macOS user is never carried, even when named with --source; the report says so and carries nothing. --dry-run -- including one that comes from your settings -- only plans: the answer is planned, nothing is written, and the upgrade is still due. The desktop app runs this itself at launch; you never need a terminal for it. fields
NameTypeDescription
dry_run required boolean
error required string | null
ok required boolean
source required string | null

The earlier home data was carried from, or null when none was found.

status required string

upgraded (steps applied and recorded), planned (a dry run: nothing written, the upgrade is still due), current (nothing to carry), busy (a pass is running from the earlier home; nothing was touched), failed (a step failed or the record would not parse; nothing was recorded for the failed step).

Values: upgraded, planned, current, busy, failed

steps required array of object
steps[].counts object

What the step carried, by kind (pause, analytics_files, ledger_lines, spool_lines, settings).

steps[].id required string
steps[].kept array of string

Target files that already held different bytes -- kept, never overwritten, and named.

steps[].layout string

The earlier home's layout (root for analytics at its root, data for it under data/).

Values: root, data

steps[].notes array of string
steps[].reason string

Why a not-run step did not run.

steps[].source string
steps[].status required string

not-run when the earlier home was refused (it belongs to another macOS user); reason says why and nothing was carried.

Values: applied, planned, already-applied, not-run

summary required string

One line saying what happened, for a notification or a status row.

target required string

The home data was carried into (the home whose .blowhorn.yaml the run resolved).

default

What the command prints to stdout with --json when it exits non-zero: a Problem, or, for a command that reports per-item outcomes, its own shape with `ok: false`.

Problem

Variant fields
NameTypeDescription
code string

A stable machine-readable cause, where the command names one.

details any

Structured context for the cause, where the command has any.

error required string

The cause, as one sentence a person can act on.

ok required any

UpgradeResponse

Variant fields
NameTypeDescription
dry_run required boolean
error required string | null
ok required boolean
source required string | null

The earlier home data was carried from, or null when none was found.

status required string

upgraded (steps applied and recorded), planned (a dry run: nothing written, the upgrade is still due), current (nothing to carry), busy (a pass is running from the earlier home; nothing was touched), failed (a step failed or the record would not parse; nothing was recorded for the failed step).

Values: upgraded, planned, current, busy, failed

steps required array of object
steps[].counts object

What the step carried, by kind (pause, analytics_files, ledger_lines, spool_lines, settings).

steps[].id required string
steps[].kept array of string

Target files that already held different bytes -- kept, never overwritten, and named.

steps[].layout string

The earlier home's layout (root for analytics at its root, data for it under data/).

Values: root, data

steps[].notes array of string
steps[].reason string

Why a not-run step did not run.

steps[].source string
steps[].status required string

not-run when the earlier home was refused (it belongs to another macOS user); reason says why and nothing was carried.

Values: applied, planned, already-applied, not-run

summary required string

One line saying what happened, for a notification or a status row.

target required string

The home data was carried into (the home whose .blowhorn.yaml the run resolved).

Last modified November 5, 2026

Free tool

Am I a Bot?

See what sites can read about your Chrome profile.

Support

Stuck on something?

Email us and a person on the team will help.