CLI and API keys

Full handbook: mint scoped API keys, install the speakerops CLI, authenticate, run every inventory command, read exit codes, and automate safely.

What you will learn on this page

This page is the complete operator and agent guide for SpeakerOps automation. You will mint scoped API keys in the admin UI, install and run the speakerops CLI, set environment variables, understand exit codes, and copy working examples for every inventory command family. You do not need to open a separate repository document to perform these tasks — examples and scope tables are included here.

The CLI maps one-to-one to domain commands on the Worker. Scopes are enforced server-side. The CLI cannot grant itself extra power. Prefer least-privilege keys and --json output for agents.

  • Mint and revoke API keys (secret shown once)
  • Install CLI from the monorepo package @speakerops/cli
  • Authenticate with SPEAKEROPS_API_KEY and SPEAKEROPS_API_URL
  • Run events, reports, design, schedule, files, comms, keys, and agent ops verbs
  • Interpret exit codes 0–4 and default-deny scopes

Mint an API key in the admin UI

Sign in as an admin. Open Admin → Settings → API keys (route /admin/settings/api-keys). Create a key with a human name and only the scopes the agent needs. The full secret is shown once at creation; the list view shows prefix only. Store the secret in a password manager or secrets channel — never in git.

Revocation is immediate for subsequent requests. Rotate keys by creating a new key, updating the agent env, then revoking the old id.

  1. Open /admin/settings/api-keys as an admin session.
  2. Click create and enter a name such as readiness-reporter.
  3. Select least-privilege scopes (for example events:read and reports:read).
  4. Submit create and copy the full secret immediately.
  5. Confirm the list shows prefix and scopes without the full secret.
  6. To disable a compromised key, revoke it from the same screen.
Agent roleExample scopes
Reportingevents:read, reports:read
Designdesign:read, design:write, events:read
Scheduleschedule:read, schedule:write, events:read
Comms draft onlycomms:draft (not comms:send)
Submissions readevents:read, submissions:read
Full send / key adminopt-in comms:send, keys:admin (default-deny)
Decisions automationopt-in decisions:write (default-deny)

Install the CLI

The CLI ships as package @speakerops/cli in the monorepo (packages/cli). Build it after pnpm install. Run via pnpm exec speakerops or node packages/cli/dist/main.js. There is no interactive TUI — every verb is a non-interactive command suitable for agents and CI.

  1. Clone the monorepo and run pnpm install from the root.
  2. Build the CLI package: pnpm --filter @speakerops/cli build
  3. Verify help output: pnpm exec speakerops --help
  4. Optionally alias speakerops to the built binary in your shell profile for local ops.
# From monorepo root
pnpm install
pnpm --filter @speakerops/cli build
pnpm exec speakerops --help
pnpm exec speakerops --version
# equivalent:
# node packages/cli/dist/main.js --help

Authenticate every invocation

The CLI sends Authorization: Bearer <secret> to the API base URL. Provide the secret through the SPEAKEROPS_API_KEY environment variable or the --api-key flag. Set SPEAKEROPS_API_URL when not using the local default http://127.0.0.1:8787. Optional SPEAKEROPS_CORRELATION_ID pins a correlation id; otherwise the CLI generates a cli_… id for audit trails.

export SPEAKEROPS_API_URL="https://www.speakerops.org"   # or local http://127.0.0.1:8787
export SPEAKEROPS_API_KEY="spk_…"   # from admin create; never commit

speakerops events list --json
Name or flagPurpose
SPEAKEROPS_API_KEYBearer secret spk_… (required for authenticated commands)
SPEAKEROPS_API_URLAPI base URL (default http://127.0.0.1:8787)
SPEAKEROPS_CORRELATION_IDOptional fixed correlation id for audit
--api-keyOverride SPEAKEROPS_API_KEY for one run
--api-urlOverride SPEAKEROPS_API_URL for one run
--json / -jStable JSON on stdout for agents
--help / -hCommand help
--version / -VCLI version

Exit codes

Agents must branch on exit codes. Do not retry authorization failures without a human expanding scopes. Do not retry schedule conflicts without changing room or time.

CodeMeaningAgent action
0SuccessParse stdout JSON and continue
1Validation / not found / missing client configFix args or data; do not blind retry
2Authz (401/403 missing scope or bad key)Stop; request scope grant or new key
3Conflict (409), e.g. schedule double-bookSurface conflicts[]; change placement
4Network / 5xxRetry with backoff; alert if persistent

Events and readiness commands

List events and fetch readiness reports. These are the safest first checks after minting a read-only key.

# CLI01 — list events (scope: events:read)
speakerops events list --json

# events get
speakerops events get --event <eventId> --json

# CLI02 — readiness (scope: reports:read)
speakerops reports readiness --event <eventId> --json
speakerops reports readiness --event <eventId> --overdue-only --json

Design kit commands

Read draft and published tokens, merge brand into draft, and publish. Freeform CSS is not accepted. Contrast failures return validation errors (exit 1).

# CLI03 — get design (design:read or design:write)
speakerops design get --event <eventId> --json

# CLI04 — set draft brand/radius (design:write)
speakerops design set --event <eventId> --brand '#4F46E5' --radius soft

# CLI05 — publish draft (design:write)
speakerops design publish --event <eventId> --json

Schedule commands

Place and list schedule placements. Hard conflicts exit 3. Scope schedule:write is required for mutations; schedule:read for list.

# CLI06 — place session (schedule:write)
speakerops schedule place \
  --event <eventId> \
  --session <sessionId> \
  --room <roomId> \
  --start 2026-09-12T09:00:00.000Z \
  --end 2026-09-12T09:45:00.000Z \
  --json

# List / unschedule
speakerops schedule list --event <eventId> --json
speakerops schedule unschedule --event <eventId> --placement <placementId> --json

# CLI07 — prove deny: reports-only key attempting place → exit 2

Submissions, decisions, speakers, rooms, tracks

Agent ops verbs cover the program pipeline without raw SQL. Staff frustration with private developer-only databases is solved by scoped domain commands — the same path as the admin UI — not by bypassing the system of record.

# Submissions (submissions:read)
speakerops submissions list --event <eventId> --json
speakerops submissions list --event <eventId> --status submitted --json
speakerops submissions get --id <submissionId> --json

# Decisions (decisions:write — default-deny)
speakerops decisions record --submission <id> --decision accept --reason 'PC' --json
# decisions: accept | reject | waitlist

# Speakers / rooms / tracks (read scopes)
speakerops speakers list --event <eventId> --json
speakerops speakers list --event <eventId> --q 'Ada' --json
speakerops rooms list --event <eventId> --json
speakerops tracks list --event <eventId> --json

Files upload

Upload flows presign, PUT bytes, and complete through domain commands. Scope files:write is required. Bind headshots or slides to a participation when the command supports --participation.

# CLI08
speakerops files upload --event <eventId> --file ./logo.png --purpose logo --json
speakerops files upload --event <eventId> --file ./headshot.jpg --participation <participationId> --json

Comms draft and send

Preview before send. Draft scope cannot send. Send enqueues only; provider HTTP stays off the request path.

# CLI09 — preview (comms:draft)
speakerops comms draft --template <templateId> --preview --json
speakerops comms templates --event <eventId> --json

# CLI10 — send (comms:send default-deny)
speakerops comms send --preview-id <previewId> --json
# optional: --idempotency-key <key>

Key administration from the CLI

Creating and revoking keys requires keys:admin (default-deny). The create response returns the secret once; list never includes it.

# CLI11 — create (keys:admin)
speakerops keys create --name 'agent-read' --scopes events:read,reports:read --json

speakerops keys list --json
speakerops keys revoke --id <keyId> --json

OpenAPI discovery

OpenAPI is a public discovery document for humans and agents. It lists paths for events, schedule, readiness, design, keys, and related resources.

# CLI12
speakerops openapi --json
# or: curl -s "$SPEAKEROPS_API_URL/openapi.json"

Optional Airtable status

When one-way Airtable projection is configured, agents can read status with airtable:read. Product core works when Airtable is unset (paused). Airtable is never a second system of record for writes from SpeakerOps program commands.

speakerops airtable status --event <eventId> --json

Agent rules of the road

These rules keep automation safe and debuggable.

  • Prefer --json on every automation command for stable stdout.
  • Treat exit 2 as wrong key or missing scope — do not loop without human grant.
  • Treat exit 3 as schedule conflict — show conflicts[] to an operator.
  • Never log full API key secrets or magic-link URLs.
  • Mutations write audit_events with correlationId (and key id when Bearer).
  • Do not use raw SQL against D1 for staff ops; use domain commands.
  • Keep high-risk scopes off default keys used by broad agent fleets.

End-to-end agent smoke sequence

After minting a key that includes events:read, reports:read, design:read, and schedule:read, run this sequence against demo or local to prove connectivity. Grant mutation scopes only for concrete write workflows you mean to run.

export SPEAKEROPS_API_URL="http://127.0.0.1:8787"
export SPEAKEROPS_API_KEY="spk_…"

speakerops events list --json
speakerops reports readiness --event <eventId> --json
speakerops design get --event <eventId> --json
speakerops schedule list --event <eventId> --json
speakerops openapi --json >/dev/null && echo openapi_ok

HTTP mapping reference

Each CLI verb calls the same Worker routes the admin SPA uses. Scopes are checked by Worker middleware. OpenAPI documents the HTTP shapes for client generation.

CLIHTTP sketchScope
events listGET /api/eventsevents:read
reports readinessGET /api/events/:eventId/readinessreports:read
design get/set/publishGET/PUT/POST design routesdesign:read / design:write
schedule placePOST …/schedule/placeschedule:write
files uploadpresign → upload → completefiles:write
comms draft/sendPOST preview / sendcomms:draft / comms:send
keys create/list/revoke/api/keyskeys:admin
openapiGET /openapi.jsonpublic

Security model for keys

Plaintext secrets are returned only on create. The Worker stores a SHA-256 hash. Bearer lookup hashes the presented secret. Revoked or expired keys get 401. Wrong scopes get 403 with a structured error envelope that can include required scopes. Audit logs never store the full secret.

UI session cookies and API keys are different credentials. Do not paste browser cookies into agent configs. Do not reuse a god-mode key across untrusted automation.