@go-labs-sg/rsvp
v0.10.3
Published
RSVP CLI for authorized event operations.
Readme
Registration CLI (rsvp)
JSON-first command-line access to Registration organizations, events, guests, check-in, payments, reporting, retention, and administrative operations.
Runtime and installation
The CLI requires Bun 1.4.0 or newer. npm distributes the package, while the installed command runs under Bun.
Check whether a compatible Bun version is already available:
bun --versionIf Bun is missing, install it for the current operating system:
macOS (Homebrew)
brew install oven-sh/bun/bunWindows (PowerShell)
powershell -c "irm bun.sh/install.ps1|iex"Linux
curl -fsSL https://bun.com/install | bashIf Bun is installed but older than 1.4.0, run bun upgrade. Open a new terminal if the installer changes PATH, then verify bun --version again. Installation agents should skip the platform installer when Bun 1.4.0 or newer is already available.
Install and verify the CLI:
bun add --global @go-labs-sg/rsvp
rsvp versionAuthentication
rsvp auth login
rsvp auth status
rsvp auth whoamirsvp auth login opens Registration in the system browser. Sign in through Registration's normal email one-time-password flow if needed, verify that the displayed device code matches the terminal, then click Authorize CLI. Existing browser sessions skip another email-code prompt but never skip explicit CLI approval.
Registration CLI access is limited to global administrators. The server rechecks the account's current global role and ban state when issuing, refreshing, and authenticating CLI credentials, so removing the admin role immediately blocks further CLI access.
The CLI polls automatically and stores only Registration-issued credentials through Bun.secrets. It does not store browser or email-OTP credentials and has no API-key or environment-variable fallback. Use rsvp auth logout to revoke the remote session and remove its local credential. For SSH or containers, pass --no-browser and open the printed URL on another device.
The default API is https://rsvp.getout.events. Override it with RSVP_API_URL or --api-url; non-local overrides must use HTTPS.
Global administrators can update another account's global role from the web user manager or with rsvp user role update --allow-state-change --target <user-id> --payload '{"userId":"<user-id>","role":"admin"}'. Administrators cannot remove their own global admin role.
Command safety
Argument-parsing errors use a safe command label and never echo option values,
including roster JSON passed through --payload. Surrounding shell history and
automation can still record arguments independently of the CLI's error output.
Use rsvp help for the command catalogue. Every response is a JSON envelope on stdout; errors and diagnostics go to stderr. rsvp manifest emits the machine-readable command reference for tooling.
For a read-only registration-journey diagnosis, inspect the event, draft, published versions, and event-owned resources together:
rsvp event get --organization-id org_123 --event-id evt_123
rsvp event flow draft get --organization-id org_123 --event-id evt_123
rsvp event flow versions list --organization-id org_123 --event-id evt_123
rsvp event flow resources get --organization-id org_123 --event-id evt_123Create an immutable menu version with an explicit mutation acknowledgement. The target is the event ID in the payload:
rsvp event flow menu version create \
--allow-state-change \
--target evt_123 \
--payload '{"organizationId":"org_123","eventId":"evt_123","menu":{"mode":"NEW","key":"dinner","name":"Dinner"},"choices":[{"key":"standard","label":"Standard meal","dietaryTags":[]}]}'To version an existing menu, use "menu":{"mode":"EXISTING","menuId":"..."}
in the payload and keep the event ID as --target.
Create a new immutable version of an existing consent document with the same
event target. Rich text is optional; when supplied, content is derived from
its visible text and stored as the legacy plain-text fallback.
rsvp event flow consent version create \
--allow-state-change \
--target evt_123 \
--payload '{"organizationId":"org_123","eventId":"evt_123","document":{"mode":"EXISTING","documentId":"consent_123"},"contentRichText":{"json":{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"I agree to the terms."}]}]},"html":"<p>I agree to the terms.</p>"}}'Creating a consent version does not change a registration draft or publish a
flow. Reference the returned version.id from the draft's CONSENT block in
a separate revision-checked draft save.
Reference the returned version.id from the draft's MENU_SELECTION blocks,
then save with event flow draft save, --allow-state-change, and the event ID
as --target. Include the latest draft revision as expectedRevision; nested
resource IDs such as a seating block's seatMapId are not approval targets for
this command. Saving the draft does not publish the registration flow.
Commands returning invitee, registration, roster, report, audit, export, payment, or user data require --ack-sensitive-data. Mutations require their exact effect gates and a --target matching the affected resource in the payload. Depending on the operation, gates include:
--allow-state-change--allow-email--allow-delete--allow-external-write--allow-financial-write
Example:
rsvp check-in by-reference \
--allow-state-change \
--target ABC123 \
--payload '{"organizationId":"org_123","eventId":"evt_123","referenceCode":"ABC123"}'High-impact payment and retention commands remain protected by their matching financial, deletion, external-write, and sensitive-data gates. The API always re-evaluates the authenticated user's current Registration role, organization membership, event assignment, and resource permissions.
Seating updates
event seating layout update moves tables, changes table geometry, and can save a floor plan. It requires --allow-state-change; its --target must match the payload's top-level eventId, not a nested table or seat-map ID:
rsvp event seating layout update \
--allow-state-change \
--target evt_123 \
--payload '{"organizationId":"org_123","eventId":"evt_123","tables":[{"tableId":"table_123","x":12,"y":8,"rotation":0}]}'event seating table update changes a table label or capacity. Because reducing capacity removes seats, it always requires both --allow-state-change and --allow-delete; its --target must match the payload's top-level tableId, including for label-only updates:
rsvp event seating table update \
--allow-state-change \
--allow-delete \
--target table_123 \
--payload '{"organizationId":"org_123","eventId":"evt_123","tableId":"table_123","label":"Table 1"}'Audience tiers
Audience tiers control which invitees can use a seat or table. List the tiers and their current invitee, table, and seat counts with:
rsvp event audience-tier list \
--organization-id org_123 \
--event-id evt_123Create a tier with the event as the approval target, then update a tier by its own ID:
rsvp event audience-tier create \
--allow-state-change \
--target evt_123 \
--payload '{"organizationId":"org_123","eventId":"evt_123","label":"VIP","description":"Executive guests"}'
rsvp event audience-tier update \
--allow-state-change \
--target tier_123 \
--payload '{"organizationId":"org_123","eventId":"evt_123","tierId":"tier_123","label":"VIP","active":true}'For tables and seats, audienceTierId: null means Everyone; a tier ID permits only invitees in that exact tier. For invitees, audienceTierId: null removes membership (Unassigned), so their party can use only Everyone seats. Invitee assignment applies to the whole party, so bulk assignment uses up to 500 invitee IDs and targets the event:
rsvp event invitee tier update \
--allow-state-change \
--target evt_123 \
--payload '{"organizationId":"org_123","eventId":"evt_123","inviteeIds":["invitee_123","invitee_456"],"audienceTierId":"tier_123"}'The existing people invitee list command accepts audienceTierId in its JSON payload: omit it for all people, use a tier ID to filter membership, or null for Unassigned. Listing people requires --ack-sensitive-data.
Set table tiers in bulk with up to 200 table IDs. Set seat tiers with up to 500 seat IDs. Seats inherit their table's tier when inheritFromTable is true; when it is false, include audienceTierId (including null for everyone). Use resetSeatOverrides: true on a table update when existing seat overrides should be cleared:
rsvp event seating table tier update \
--allow-state-change \
--target evt_123 \
--payload '{"organizationId":"org_123","eventId":"evt_123","tableIds":["table_123"],"audienceTierId":"tier_123","resetSeatOverrides":true}'
rsvp event seating seat tier update \
--allow-state-change \
--target evt_123 \
--payload '{"organizationId":"org_123","eventId":"evt_123","seatIds":["seat_123"],"inheritFromTable":false,"audienceTierId":null}'These commands update setup and assignment data only. They do not delete seats, cannot unseat an occupied seat, and should be run before the event is live; live seating modifications remain unavailable.
Reusable event configurations
template create accepts a complete JSON payload, so event-specific workflows remain data rather than product code. This package includes examples/amgen-dinner-dance-2026-template.json as a validated example of Staff ID confirmation, employment-based eligibility, conditional fields, guest limits, and categorized decline reasons.
Create the template after replacing its organization ID:
payload="$(jq --arg organizationId "org_123" '.organizationId = $organizationId' examples/amgen-dinner-dance-2026-template.json)"
rsvp template create \
--allow-state-change \
--target "Amgen Dinner & Dance 2026" \
--payload "$payload"Instantiate the returned template version through rsvp template instantiate, then use the existing staged roster import. The example expects roster columns email, name, staffId, and employmentType; only FTE and INTERN values are configured as eligible profiles. Rules for other events should be expressed in their own template payloads using the same generic contracts.
Managed organization provisioning
organization create requires an ownerEmail in its JSON payload and the
email, external-write, and state-change effect approvals. It creates a workspace
awaiting owner acceptance and sends a 48-hour first-owner invitation. The internal
provisioner does not receive customer organization membership. The response
reports invitation delivery failure so the invitation can be resent safely.
