@gravity-rail/cli
Command-line interface for Gravity Rail — manage workspaces, Agents, Chats, Workflows, and other resources from your terminal.
gr workflows list -w $WORKSPACE
gr chats list -w $WORKSPACE -o json | jq '.[].summary'
gr members create -w $WORKSPACE --data '{"first_name":"Jane","email":"jane@example.com"}' --allow-write
Installation
npm install -g @gravity-rail/cli
# or one-shot
npx @gravity-rail/cli --help
Requires Node.js 18+.
Authentication
OAuth (interactive)
gr login # opens browser for OAuth authorization
gr whoami # verify your identity
gr logout # clear cached credentials
gr login runs authorization code + PKCE (S256) against Gravity Rail's pinned
first-party OAuth client, discovered from the server's
/.well-known/oauth-authorization-server document. That client is public: it
has no client secret, because a CLI distributed to end users cannot keep one
(RFC 8252 §8.5). PKCE, not a shared secret, is what protects the exchange.
A self-hosted server old enough not to advertise a first-party client falls back
to RFC 7591 dynamic client registration, which does issue this installation its
own confidential client. Both kinds of profile refresh normally; nothing needs to
be re-authorized when a server gains the pinned client, though the next
gr login on that profile will switch to it.
Multi-account profiles
Like aws --profile / gh auth switch, the public CLI supports multiple named accounts:
gr auth login --name work # OAuth into a named profile (also sets it current)
gr auth login --name personal
gr auth list # list profiles for the current --env
gr auth switch --name personal # change the default profile
gr auth logout --name work # clear one profile
# Per-invocation selection (does not change the stored default)
gr whoami --profile work
gr tasks list -w $WORKSPACE --profile personal
export GRAVITY_RAIL_PROFILE=work
gr login / gr logout remain as shortcuts for the active profile (or --name).
Credential store (security)
| Item | Detail |
|---|---|
| Location | ~/.gr-cli/credentials-<env>.json (e.g. credentials-prod.json) |
| Directory mode | 0700 on ~/.gr-cli |
| File mode | 0600 on credential files |
| Contents | OAuth client id (+ secret only for a dynamically registered client) + tokens, and/or token-only dev profiles (+ optional workspace UUID) |
| Not stored | GRAVITY_RAIL_API_KEY (process environment only; always overrides profiles) |
| Name validation | Profiles allow name or workspace/member; env names are allowlisted |
| Logging | CLI code never writes token or secret values to stdout/stderr |
Security review notes: tokens never leave the local filesystem except on HTTPS calls to the Gravity Rail API. There is no cloud sync of CLI credentials. Rotate by gr auth logout --name <profile> (or deleting the store file) and logging in again. Multi-user machines should rely on OS home-directory isolation plus the 0600/0700 modes. Do not commit ~/.gr-cli into repositories or bake it into container images.
Profile resolution order for a command:
GRAVITY_RAIL_API_KEY(if set — skips the store entirely)--profile <name>(or--account)GRAVITY_RAIL_PROFILEcurrentProfilein the credential store (fromgr auth switch/ last login)- Profile name
default
API Key (non-interactive)
export GRAVITY_RAIL_API_KEY=your-api-key
gr tasks list -w $WORKSPACE
Useful for scripts, CI/CD, and automation. Create scoped keys from Account API Keys or via @gravity-rail/sdk. API keys are not written into the profile store.
Organizations that require SSO
Workspaces in an SSO-enforced organization need an org-scoped session in addition to your account login. When you run a gr command against such a workspace after gr login, the CLI opens your browser once to complete the organization's SSO, captures the resulting session over a local 127.0.0.1 callback, and caches it under your profile — subsequent commands reuse it silently until it expires. No token is ever placed in a URL.
For non-interactive use (CI, scripts, code-interpreter sandboxes, or any machine with no browser), org SSO cannot run; use an API key instead — API keys are exempt from the org SSO gate:
export GRAVITY_RAIL_API_KEY=your-api-key # set GRAVITY_RAIL_NO_BROWSER=1 to force the API-key hint
Search the Documentation
Search without signing in or selecting a Workspace:
gr docs search -q "outbound workflow"
gr docs search -q "webhook signing" -o json
Table output shows page titles and URLs. JSON output includes title, url, snippet, and score for each result.
Work with a Coding Agent
Give an agent the CLI's bootstrap instructions, then use each domain's --help to discover its commands:
gr get-started --format llm
Command Pattern
gr <domain> <action> [options]
gr <domain> <sub-resource> <action> [options]
gr tasks list -w $WORKSPACE # list tasks
gr tasks get -w $WORKSPACE --id 42 # get specific task
gr tasks create -w $WORKSPACE --data '{"name":"New"}' --allow-write # create
gr chats labels list -w $WORKSPACE # sub-resource
Every domain supports --help:
gr --help # all domains
gr tasks --help # task commands
gr chats labels --help # sub-resource commands
Write Safety
The CLI is read-only by default. Any command that creates, updates, or deletes data requires the --allow-write flag:
# Reads — no flag needed
gr members list -w $WORKSPACE
gr members list -w $WORKSPACE --search "Smith"
gr members list -w $WORKSPACE --page 2 --page-size 100
gr members list -w $WORKSPACE --all
gr workflows get -w $WORKSPACE --id 5
# Writes — must opt in
gr members create -w $WORKSPACE --data '{"first_name":"Test"}' --allow-write
gr tasks delete -w $WORKSPACE --id 42 --allow-write
This prevents accidental mutations when exploring production data or running under automation.
Output Formats
Output adapts to context: human-readable tables in interactive terminals, JSON when piped.
# Table (default in TTY)
gr tasks list -w $WORKSPACE
# JSON (default when piped, or explicit)
gr tasks list -w $WORKSPACE -o json
# JSONL (one object per line — great for streaming)
gr tasks list -w $WORKSPACE -o jsonl
# Pipe to jq
gr members list -w $WORKSPACE -o json | jq '.[].email'
--jq — filter without a pipe
--jq runs the filter against the JSON the CLI just produced, so the input is
valid by construction and there is no pipeline to get wrong. It implies
-o json.
gr tasks list -w $WORKSPACE --jq 'length'
gr workflows get -w $WORKSPACE --id 3 --jq '{id, name}'
Prefer it over | jq in scripts and agent tooling: a mistake in the filter is
reported as a filter error, rather than surfacing as a parse error about output
you cannot see. It needs the jq binary on PATH.
# Chain commands
gr chats list -w $WORKSPACE -o json | jq '.[0].id' | xargs -I{} gr chats get -w $WORKSPACE --id {}
Data Input
Pass data for create/update operations via inline JSON, file, or stdin:
# Inline JSON
gr tasks create -w $WORKSPACE --data '{"name":"My Task"}' --allow-write
# From file
gr workflows create -w $WORKSPACE --file workflow.json --allow-write
# From stdin (pipe)
echo '{"name":"Piped Task"}' | gr tasks create -w $WORKSPACE --allow-write
cat member.json | gr members update -w $WORKSPACE --id 42 --allow-write
Global Options
| Flag | Description |
|---|---|
--api-url <url> | Override API base URL |
--profile <name> | Named account profile (alias: --account) |
-w, --workspace <uuid> | Workspace UUID (required for most commands) |
-o, --output-format <fmt> | Output format: json, jsonl, table |
-v, --verbose | Verbose output to stderr |
--allow-write | Required for create/update/delete operations |
--id <id> | Entity ID (for get/update/delete) |
-d, --data <json> | Inline JSON payload |
-f, --file <path> | Read JSON payload from file |
--help | Show help |
--version | Show version |
Environment Variables
| Variable | Description |
|---|---|
GRAVITY_RAIL_API_KEY | API key for non-interactive authentication |
GRAVITY_RAIL_PROFILE | Default named credential profile |
GRAVITY_RAIL_API_URL | Override API base URL (default: https://api.gravityrail.com) |
GRAVITY_RAIL_FRONTEND_URL | Override frontend URL (default: https://app.gravityrail.com) |
Exit Codes
| Code | Meaning |
|---|---|
0 | Success |
1 | General error |
2 | Authentication required (run gr login or set GRAVITY_RAIL_API_KEY) |
3 | A human-in-the-loop (HITL) interrupt requires out-of-band action before the operation can continue |
4 | No assistant reply — a --message turn made tool calls but returned no final summary, or an inference error occurred with no final message (side effects may have been applied) |
UI prompts (present_choice_list / render_ui, and the connect_*_account
login prompts) do not pause the agent. The tool returns straight away and the
prompt arrives as a tool result, which the CLI draws in the terminal when stdin
is a TTY: OpenUI markup becomes an arrow-key picker (ChoiceButtons, with Space
to tick a multi-select) or field-by-field form prompts, and a connect prompt
prints the link to open. Your answer is then sent as an ordinary chat message,
so the agent picks it up on its next turn. Manager (gr manage) and Concierge
(gr concierge chat) share this path. Typing prose instead of using the
controls sends exactly what you typed. When stdin is not a TTY the prompt is
printed and left unanswered, and the turn ends normally.
Approvals, access grants and checkout still interrupt the run and resume it
in place; unsupported interrupt types exit 3 with the JSON contract below.
When a concierge command run with --message (non-interactive mode) hits a HITL
interrupt the CLI cannot resolve on its own, it exits 3 and writes a single
machine-readable JSON object to stdout:
{
"error": "hitl_interrupt",
"message": "A human-in-the-loop interrupt requires out-of-band action before the operation can continue.",
"resume": { "chatId": 42, "chatUuid": "…", "interruptId": "int-abc" },
"interrupt": { "type": "stripe_checkout", "interrupt_id": "int-abc", "url": "…" }
}
The interrupt object is intentionally limited to routing fields (type,
interrupt_id, and a redirect url when present); the raw server payload is
not forwarded, as it may contain sensitive data. Use resume.chatUuid /
resume.interruptId to continue the flow out-of-band (e.g. in the web app).
Commands
Core
| Domain | Description |
|---|---|
tasks | Workflow tasks — CRUD, archive, clone |
chats | Conversations — CRUD, labels, filters, messages, export, summaries |
members | Contacts and team — CRUD, labels, filters, roles, fields, import/export |
workspaces | Workspace management — CRUD, features, themes, invitations, export/import |
workflows | Conversational workflows — CRUD, notes, contributors, templates, analytics |
journeys | Journey definitions — CRUD, V2 authoring, drafts, clone |
agents | Autonomous AI members — CRUD, archive |
assistants | AI personas — CRUD with model and voice configuration |
assignments | Task-based conversations — CRUD, messages, tools |
Data & Content
| Domain | Description |
|---|---|
data-types | Schema-driven forms and records — CRUD, field indexing, computed fields |
files | Documents and folders — CRUD, sharing, semantic search, labels |
sites | Customer portals — CRUD, pages, menus, web crawling |
events | Automation rules — triggers, CEL conditions, delayed actions, webhooks |
calendars | Scheduling — calendars, events, event types, availability, Google sync |
qualifications | Skills evaluation — requirements, submissions, scoring |
queries | Unified Query Engine — fields, validate, preview, run, syntax reference |
Global (no workspace)
| Domain | Description |
|---|---|
concierge | Global Concierge — interactive chat, PSTN --call, SMS --sms |
account verify-phone | Verify your account phone (required before concierge outbound) |
Communication
| Domain | Description |
|---|---|
phone-numbers | Voice and SMS phone numbers — provisioning, calls, messages |
recordings | Call recordings — list chunks, get a presigned download URL, save the WAV |
inboxes | Email inboxes — threads, attachments, routing |
operator-groups | Live operator routing — groups, presence, strategies |
notification-rules | Alert configuration — event-based notifications |
Integrations
| Domain | Description |
|---|---|
discord-bots | Discord bot management — slash commands, threads, account linking |
slack-apps | Slack app management — installations, threads, accounts |
fhir-connections | FHIR healthcare connections — patients, practitioners, encounters |
patientsync | PatientSync PSTN-transfer connection — configure / status / delete (singleton per workspace) |
documo-fax | Documo (mFax) inbound fax — install / configure / setup / verify / status / faxes |
app-connections | Platform app integrations |
monday | Monday.com — boards, columns, webhooks |
Platform
| Domain | Description |
|---|---|
api-keys | API key management — workspace and global scopes |
subscriptions | Subscription management |
reports | Usage reports — AI tokens, voice minutes, SMS, storage |
ai-models | Available AI models (no workspace required) |
features | Feature registry (no workspace required) |
custom-toolkits | Custom tool definitions |
mcp-servers | MCP server connections |
milestones | Goal tracking |
supervisors | AI supervisor management |
access-grants | Temporary access delegation |
pronunciations | Voice pronunciation overrides |
apps | OAuth app management |
oauth | OAuth provider connections |
auth | Two-factor authentication, credentials, and password management |
org | Organization membership, invitations, and org domains |
Recipes
Export all chats as JSON
gr chats list -w $WORKSPACE -o json > chats.json
List chat messages with pagination
# Default returns up to 100 messages (API max)
gr chats messages list -w $WORKSPACE --id $CHAT_ID
# Paginate with --limit and --offset
gr chats messages list -w $WORKSPACE --id $CHAT_ID --limit 50 --offset 100
Filter chats by type
# Only manager (operator-side) chats, sorted most-recent-first.
gr chats list -w $WORKSPACE --chat-type manager
Assign and link a phone number to a workspace
# Assign a phone number from the org pool to a workspace…
gr org phone-numbers assign --org-uuid $ORG_UUID --phone-number-id $PN_ID --workspace-uuid $WORKSPACE --allow-write
# …then enable it (link inboxes, voice config) so it can place/receive calls.
gr phone-numbers link -w $WORKSPACE --id $PN_ID --allow-write
Manage pronunciations from the workspace context
# `pronunciations` resolves the org from -w; you don't need --org-uuid.
gr pronunciations list -w $WORKSPACE
gr pronunciations create -w $WORKSPACE --term "GravityRail" --respelling "GRA-vit-ee rail" --allow-write
Download a call recording
# Get a single short-lived presigned URL for the whole call (assembled once, cached).
gr recordings download-url -w $WORKSPACE --chat-id 42
# Or download the assembled WAV straight to a file.
gr recordings download -w $WORKSPACE --chat-id 42 -o call.wav
Register and verify an org domain
# Register the domain and get the ownership TXT record
gr org domains register --org-uuid $ORG_UUID --domain example.com --allow-write
# Verify ownership TXT
gr org domains verify-ownership --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write
# Email DNS: enable records, then verify SPF/DKIM/DMARC/MX/return-path
gr org domains email enable --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write
gr org domains email verify-mx --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write
# Site DNS: check the CNAME resolves, enable custom site routing, then verify
gr org domains site preflight --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID
gr org domains site enable --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write
gr org domains site verify-cname --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write
Host many sites under one wildcard
# Enable a verified domain as a parent: publish *.<domain> first, then enable
gr org domains site preflight --org-uuid $ORG_UUID --domain-uuid $PARENT_UUID --child-hosting
gr org domains site enable --org-uuid $ORG_UUID --domain-uuid $PARENT_UUID --child-hosting --allow-write
# Add hosts one at a time; each reports its own result, and the command exits
# non-zero if any failed. New hosts verify on their own within minutes.
gr org domains site children add --org-uuid $ORG_UUID --domain-uuid $PARENT_UUID \
--label clinic-a --label clinic-b --allow-write
gr org domains site children add --org-uuid $ORG_UUID --domain-uuid $PARENT_UUID \
--labels-file labels.txt --allow-write
gr org domains site children list --org-uuid $ORG_UUID --domain-uuid $PARENT_UUID
# Remove hosts: certificate, routing and registration. --confirm also removes
# hosts that still route sites.
gr org domains site children remove --org-uuid $ORG_UUID --domain-uuid $PARENT_UUID \
--label clinic-a --allow-write
Find members by label
gr members list -w $WORKSPACE -o json | jq '[.[] | select(.labels[]?.name == "VIP")]'
Bulk create from a file
# members.jsonl — one JSON object per line
cat members.jsonl | while read line; do
echo "$line" | gr members create -w $WORKSPACE --allow-write
done
Pipe workspace config between environments
gr workspaces get -w $SOURCE_WORKSPACE -o json | \
gr workspaces create --data "$(cat -)" --allow-write
Check configuration readiness
gr workspaces configuration-readiness -w $WORKSPACE -o json
The combined read fails closed in its configurationReady field when
configuration-health collectors fail, critical or warning findings remain,
post-import bindings are missing, skipped EventRules remain outstanding, or a
readiness scan is truncated. Informational configuration findings are reported
but do not block. This is a saved-configuration check, not an end-to-end launch
or patient-experience certification.
List all workflows with their task counts
gr workflows list -w $WORKSPACE -o json | jq '.[] | {name, task_count: (.tasks | length)}'
Check who's live as an operator
gr operator-groups list -w $WORKSPACE -o json | jq '.[].name'
Concierge setup over your phone (PSTN / SMS)
Outbound concierge modes ring or text your verified account phone only — no destination argument. Verify once, then initiate:
# One-time: OTP SMS + TCPA notice (writes Account.phone + phone_verified_at)
gr account verify-phone --allow-write
# Optional: pass E.164 inline instead of the prompt
gr account verify-phone --phone +15551234567 --allow-write
# Server originates call; CLI exits when Twilio returns callSid
gr concierge chat --call --allow-write
# Server sends opening SMS; CLI exits when messageSid is returned
gr concierge chat --sms --allow-write
--call and --sms are mutually exclusive. They cannot be combined with --voice or --realtime on the same gr concierge chat invocation. (gr concierge --call / --sms are equivalent shorthand.)
If the API returns 412 account_phone_unverified, run gr account verify-phone first.
Journey V2 authoring
The same commands are available through the internal yarn gr entry point.
All examples require workspace membership and journeys:write (which implies
journeys:read, also checked when cloning).
gr journeys create -w $WORKSPACE --data '{"name":"Onboarding V2","executionModel":"v2","status":"draft"}' --allow-write
gr journeys authoring -w $WORKSPACE --id 42 -o json
gr journeys v2 put -w $WORKSPACE --id 42 --file definition.json --allow-write
gr journeys v2 validate -w $WORKSPACE --id 42 -o json
gr journeys v2 clone -w $WORKSPACE --id 12 --allow-write
v2 put replaces the complete executable definition. Include expectedUpdatedAt
from the latest authoring read, ordered touches, and endings. Each touch can
carry eventRules, an incoming wait, and a branch with ordered routes.
Endings carry celCondition and/or filterId predicates and eventRules; they
need no incoming edge.
Omitting goals preserves them; sending an empty array removes them. A minimal
definition.json is:
{
"expectedUpdatedAt": "2026-09-26T00:00:00Z",
"touches": [{ "name": "Welcome", "eventRules": [] }],
"endings": []
}
A branch route can include memberPredicate, an unnamed Query IR condition
owned by the route. For example, this branch selects Members with an email:
{
"routes": [
{
"name": "Has email",
"memberPredicate": {
"kind": "compare",
"field": "member.email",
"op": "notEmpty"
}
},
{ "name": "Otherwise", "memberPredicate": null }
]
}
Use this object as a touch's branch in the complete definition. The owned
predicate is ANDed with any filterId and celCondition; it creates no saved
MemberFilter. Reading or changing it additionally requires members:read and
access to its fields. On v2 put, omission preserves an existing owned predicate
and explicit null clears it. v2 validate reports
decision_rule_predicate_invalid when a stored owned predicate cannot compile.
Existing steps and routes retain identity through their UUIDs. Omit a step or
route UUID to create it; use request-local key values for new rejoin targets.
EventRules require UUIDs: preserve existing ones when editing, and generate new
ones when copying actions into another Journey. Read responses are not replacement
payloads; construct the touches/endings structure explicitly.
To preserve an unfinished editor document without changing the executable Journey, use the separate draft commands:
gr journeys v2 draft put -w $WORKSPACE --id 42 --file draft.json --allow-write
gr journeys authoring -w $WORKSPACE --id 42 -o json
gr journeys v2 draft delete -w $WORKSPACE --id 42 --expected-updated-at '2026-09-26T00:00:00Z' --allow-write
draft.json contains expectedUpdatedAt and document with version, model,
and optional goalsDirty. Use the editor's document format. Read it back as
authoringDraft through journeys authoring. Discarding that document preserves
the executable definition. Neither draft command publishes a Journey.
Use journeys update --id 42 --data '{"status":"active"}' --allow-write to
publish, or status: "paused" to pause. Publishing runs server validation;
v2 validate reports stored-definition issues without publishing. A stale
expectedUpdatedAt is rejected: reload and reconcile rather than blindly retrying.
v2 clone creates a separate draft from a supported legacy definition and leaves
the source and enrollments unchanged; unsupported topologies are rejected.
Related
@gravity-rail/sdk— TypeScript SDK for programmatic access (500+ methods, full type safety)- Developer Guide — Full API reference and guides
License
MIT
Publish and consume reusable Workflows
Author in the owning workspace. workflows publish freezes the draft and queues
its shared release. Bundles are how a published Workflow reaches another
workspace: workflows catalog lists released Workflow and revision UUIDs to pin
in a Bundle draft.
# Publisher
gr workflows publish -w "$PUBLISHER" --workflow-id "$WORKFLOW" --allow-write
gr workflows catalog -w "$PUBLISHER" -o json
gr bundles create -w "$PUBLISHER" --name "Starter Kit" --allow-write
gr bundles draft set -w "$PUBLISHER" --bundle "$BUNDLE" --file items.json --allow-write
gr bundles publish -w "$PUBLISHER" --bundle "$BUNDLE" --allow-write
gr bundles grant -w "$PUBLISHER" --bundle "$BUNDLE" \
--to organization --uuid "$ORGANIZATION" --allow-write
# Consumer: installs the latest version, then opts into updates
gr bundles install -w "$CONSUMER" --bundle "$BUNDLE" --allow-write
gr bundles policy -w "$CONSUMER" --installation "$INSTALLATION" --set auto --allow-write
gr bundles readiness -w "$CONSUMER" --installation "$INSTALLATION"
# Make an independent editable copy of an installed Workflow
gr workflows clone -w "$CONSUMER" --id "$LOCAL_WORKFLOW_ID" \
--data '{"name":"My customized Workflow"}' --allow-write
Installation creates the consumer's local Workflow ID and UUID; use those for
consumer reads and copying. Installation and policy changes require
workflows:admin in the destination plus publisher access;
organization-admin status is not an additional requirement.
When a Bundle upgrade changes an installed Form schema, the acting administrator
also needs datatypes:write and Form write permission (Form admin permission for
field-policy changes, including new fields). Missing Form authority or an archived
Form leaves the held release active and reports that repair is required; it does
not revoke the Bundle subscription.
Bundle lifecycle
| Task | Command |
|---|---|
| Find published Workflow/revision UUIDs for composition | workflows catalog |
| Inspect publisher subscriber versions/policies | bundles subscribers --bundle <uuid> |
| Edit package metadata | bundles update --bundle <uuid> --name "Starter Kit" --description "Contents" |
| Create a package | bundles create --name "Starter Kit" |
| Inspect/edit the package draft | bundles draft get/set --bundle <uuid> |
| Publish an immutable version | bundles publish --bundle <uuid> --release-notes "What's new" |
| Inspect summary and latest content | bundles get --bundle <uuid> |
| Inspect history or one version | bundles versions/version --bundle <uuid> |
| Share within the publisher organization | bundles grant --bundle <uuid> --to organization --uuid <organization> |
| Install the latest version (initially pinned) | bundles install --bundle <uuid> |
| Follow new releases | bundles policy --installation <uuid> --set auto |
| Hold the installed release | bundles policy --installation <uuid> --set pinned |
| Stop updates and keep installed content | bundles unsubscribe --installation <uuid> |
| Preview an upgrade | bundles preview --installation <uuid> --to 2 |
| Apply an upgrade | bundles upgrade --installation <uuid> --to 2 |
| Inspect missing local dependencies | bundles readiness --installation <uuid> |
| Supply local dependencies | bundles bind --installation <uuid> --bindings bindings.json |
Commands require a workspace (-w or a workspace profile); mutations also require
--allow-write. Draft item files contain { "items": [...] }; Workflow items
pin workflowUuid and revisionUuid, and Form/Site items carry definitions.
Configuration is reusable software. Records and credentials stay local.
For safe editing, retain draftGeneration from bundles draft get -o json and
pass it as --expected-generation to draft set or publish. Omitting it reads
the current generation just before the write and only detects changes during
that request sequence. The server's refusal details are preserved.
Use bundles get --bundle <uuid> or bundles install --bundle <uuid> for direct access
to an authorized official solution, including one hidden from bundles list.
Private Bundles still require an existing grant or ownership.
bundles get -o json returns one { summary, version } object; version is null
before publication. Revocations also return JSON when requested. Full UUIDs in
tables can be copied directly into later commands.
Bundle grants use full source visibility; restricted Bundle disclosure remains unavailable while Form and Site definitions are copied at installation.