owlette docs
clireference

site

a site is the top-level scope every operator command nests under (roosts, machines, audit log, quota, deploys, processes — all site-scoped). the site record itself stays read-only — create / update / delete live in the dashboard — but membership is managed here, because it is the part operators script.

verbs:

site set-role and owlette user promote are not the same thing. set-role changes a role on one site. user promote changes a global role, which applies to every site that person belongs to. If you want someone to administer one site, set-role is the command you want.


site list

lists every site the active token has access to, server-resolved from the caller's identity. emits a plain ascii table by default and a { "sites": [...] } envelope under --json (legacy unwrapped shape — kept stable for scripts that pipe into jq).

owlette site list [--json]

no verb-specific flags. inherits --json, --profile, and --api-url from the global set.

examples

# human-readable table — id, name, plan, timezone, createdAt
owlette site list
# pull just the ids for use in another command
owlette --json site list | jq -r '.sites[].id'
# count how many sites you can see
owlette --json site list | jq '.sites | length'

backing: GET /api/sites.


site get

prints the full detail record for one site (id, name, plan, timezone, owner, createdAt). under --json the cli emits the raw detail object directly — there is no { "site": {...} } wrapper.

owlette site get <siteId> [--json]

no verb-specific flags. the positional <siteId> is required. inherits --json, --profile, and --api-url from the global set.

examples

# human-readable detail block
owlette site get site-1
# pull a single field
owlette --json site get site-1 | jq -r '.timezone'
# scripted: only proceed if the site is on the pro plan
plan=$(owlette --json site get site-1 | jq -r '.plan')
[ "$plan" = "pro" ] || { echo "skipping — not on pro plan"; exit 0; }

backing: GET /api/sites/{siteId}.


site members

lists everyone with access to a site, with the role they hold on that site. emits an ascii table by default (uid, email, role, globalRole) and { "members": [...] } under --json.

owlette site members <siteId> [--json]

role is the per-site standing: owner, superadmin, admin or member. globalRole is that person's account-wide role — the two differ on purpose, and seeing both side by side is usually what you came for.

a member's other site memberships are deliberately not returned: listing them would hand a site admin the site ids of every other organisation that person belongs to.

examples

owlette site members site-1
# just the admins
owlette --json site members site-1 | jq -r '.members[] | select(.role=="admin") | .email'

backing: GET /api/sites/{siteId}/members.


site add-member

adds someone to a site. identify them by exactly one of --uid or --email — supplying both, or neither, fails locally without spending a request.

owlette site add-member <siteId> (--uid <uid> | --email <email>) [--role member|admin] [--idempotency-key <key>]

--role defaults to member. requesting admin only takes effect if that person's global role is already admin or superadmin; otherwise the membership is still added and the cli prints a WARNING ... NOT honored line. adding someone to a site never promotes them.

re-adding an existing member is a no-op and returns success. the site's owner cannot be re-added at a lesser role — that returns 409 target_is_owner.

examples

# the usual case: you know a colleague's address, not their uid
owlette site add-member site-1 --email alice@example.com
# add as a site admin (only honored if their global role already allows it)
owlette site add-member site-1 --uid u_123 --role admin

backing: POST /api/sites/{siteId}/members.


site set-role

changes a member's role on this site only. their global role is untouched.

owlette site set-role <siteId> <uid> <admin|member> [--idempotency-key <key>]

owner is not a settable role. ownership moves through site transfer-ownership, and the cli rejects set-role ... owner locally with a pointer to it. the site owner also cannot be demoted through this command — that returns 409 cannot_change_owner_role.

examples

owlette site set-role site-1 u_123 admin
# demote back to a read-only operator on this site
owlette site set-role site-1 u_123 member

backing: PATCH /api/sites/{siteId}/members/{uid}.


site remove-member

removes someone from a site. idempotent: removing a non-member succeeds and reports was not a member.

owlette site remove-member <siteId> <uid> [--talon-successor <uid>]

talons that person authored survive the removal — they belong to the site, not the author — but a talon with a hoot output re-resolves its author's site access on every run, so it starts failing silently once the author is gone. the cli prints a WARNING ... talon(s) ... left in place line when that happens. pass --talon-successor <uid> to reassign them first; reassignment is never implicit.

the site owner cannot be removed (409 cannot_remove_owner) — transfer ownership first.

examples

owlette site remove-member site-1 u_123
# hand their automations to someone else on the way out
owlette site remove-member site-1 u_123 --talon-successor u_456

backing: DELETE /api/sites/{siteId}/members/{uid}.


site transfer-ownership

hands a site to another user, atomically.

owlette site transfer-ownership <siteId> <successorUid> [--idempotency-key <key>]

only the current owner or a superadmin may transfer; a site admin is refused with 403 not_owner — administering a site and owning it are different things. the successor does not need to be a member already; they are made one by the same transaction. the outgoing owner is demoted to admin and keeps their membership, because handing a site over is not the same as leaving it.

transferring to the current owner returns 409 successor_already_owner rather than silently succeeding.

examples

owlette site transfer-ownership site-1 u_456
# leaving the company: transfer, then remove
owlette site transfer-ownership site-1 u_456
owlette site remove-member site-1 u_departing --talon-successor u_456

backing: POST /api/sites/{siteId}/transfer-ownership.


exit codes

  • 0 — success (table printed or detail emitted)
  • 1 — network failure or non-2xx api response (e.g. 404 if <siteId> doesn't exist or you lack access)
  • 2 — no token configured for the active profile (run owlette auth login first)

notes

  • scope: user — lists sites the caller's token has access to; no --site flag because the noun is the site
  • tier: [ready]. the site record is read-only here — create, update and delete stay in the dashboard — but membership is fully managed from the cli
  • roles: per-site (owner / admin / member) are distinct from the global account role. site set-role changes the former; owlette user promote changes the latter and affects every site that person belongs to
  • json envelope: list emits { "sites": [...] } (legacy unwrapped shape); get emits the raw detail object. new commands wrap in { ok, data } — these two predate that convention and stay stable for compatibility
  • related: overview (auth + config precedence), roost, quota, and every other operator noun (all consume --site <siteId> against the ids returned here)

On this page