@go-labs-sg/rsvp 0.17.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +506 -39
- package/dist/index.js +5900 -188
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,6 +85,48 @@ The installed command uses the same portable Bun launcher as the other Go Labs C
|
|
|
85
85
|
|
|
86
86
|
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.
|
|
87
87
|
|
|
88
|
+
## Personal seating drafts
|
|
89
|
+
|
|
90
|
+
`event seating draft get`, `save`, `preview`, and `publish` use the same server-owned
|
|
91
|
+
personal draft as Assign Guests. Resume it on another authenticated device; drafts
|
|
92
|
+
are isolated by event and signed-in owner. Do not pass an owner ID. Stored changes
|
|
93
|
+
contain only `registrationAttendeeId`, `fromSeatId` (the first live source, nullable)
|
|
94
|
+
and `toSeatId` (nullable to unseat). Stage does not book seats, acquire holds or send
|
|
95
|
+
email. Preview reports fresh conflicts and grant-scoped guest details.
|
|
96
|
+
|
|
97
|
+
Guest reads are live by default. Add `--include-draft` (or JSON `includeDraft: true`)
|
|
98
|
+
to `event seating guests list` or `count` to apply only your saved draft before
|
|
99
|
+
seating filters, facets and pagination. `--include-draft=false` keeps live results.
|
|
100
|
+
Draft-mode cursors bind the saved version; restart pagination after a draft save.
|
|
101
|
+
The Assign guests page always reads this personal-draft view.
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
rsvp event seating draft get --organization-id org_123 --event-id evt_123 --ack-sensitive-data
|
|
105
|
+
rsvp event seating draft save --allow-state-change --target evt_123 \
|
|
106
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","expectedVersion":0,"changes":[{"registrationAttendeeId":"att_123","fromSeatId":null,"toSeatId":"seat_123"}]}'
|
|
107
|
+
rsvp event seating draft preview --organization-id org_123 --event-id evt_123 \
|
|
108
|
+
--expected-version 1 --ack-sensitive-data
|
|
109
|
+
rsvp event seating draft publish --organization-id org_123 --event-id evt_123 \
|
|
110
|
+
--expected-version 1 --request-id 123e4567-e89b-42d3-a456-426614174000 \
|
|
111
|
+
--reason 'Seat the confirmed guest' --allow-state-change --target evt_123
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Save and Publish require the latest `expectedVersion`. A successful Publish applies
|
|
115
|
+
all changed guests atomically and clears only that version of your draft. Resolve
|
|
116
|
+
preview issues before publishing; conflicts leave all bookings and the draft intact.
|
|
117
|
+
For an unknown outcome, retry the same UUID `requestId`, version, reason and email
|
|
118
|
+
choice: the durable receipt returns the previous counts without sending again or
|
|
119
|
+
clearing a newer draft. A different intent needs a new request ID. Email defaults
|
|
120
|
+
off. To opt in, use `--allow-email` and a full JSON payload including organizationId,
|
|
121
|
+
eventId, expectedVersion, requestId, reason and `sendSeatChangeEmail: true`. Do not
|
|
122
|
+
combine `--payload` with command-local version/reason/request flags;
|
|
123
|
+
the server additionally requires notice permission and enabled recipient policy.
|
|
124
|
+
Preview counts recipient registrations, not guests. On realtime-enabled servers,
|
|
125
|
+
free new targets require validated per-registration holds at Publish, supplied as
|
|
126
|
+
`seatHolds: [{registrationId, holds: [{seatMapId, clientId, holdToken}]}]` in the
|
|
127
|
+
payload. Acquire these through the existing organizer hold-token flow; Stage never
|
|
128
|
+
holds seats. Swaps and cycles of changed live sources do not require free-seat holds.
|
|
129
|
+
|
|
88
130
|
## Command safety
|
|
89
131
|
|
|
90
132
|
Argument-parsing errors use a safe command label and never echo option values,
|
|
@@ -125,6 +167,37 @@ key to clear the image. The public event guide reuses both assets. Campaign
|
|
|
125
167
|
HTML can link to the guide with `{{eventDetailsUrl}}`; `{{eventUrl}}` still
|
|
126
168
|
points to the registration page.
|
|
127
169
|
|
|
170
|
+
The optional event footer is one image shown at the bottom of the registration
|
|
171
|
+
and confirmation pages; events without one show no footer. Use
|
|
172
|
+
`event footer upload create` for the upload URL and key, upload the image
|
|
173
|
+
(PNG, JPG, WebP, or GIF up to 10 MB; SVG is not accepted), then save it with
|
|
174
|
+
`event footer update`. `variant` defaults to `DESKTOP`; send `MOBILE` to set a
|
|
175
|
+
separate image for screens under 640px. When only one variant is set it is used
|
|
176
|
+
on every screen size. A null `footerImageKey` clears that variant.
|
|
177
|
+
|
|
178
|
+
```sh
|
|
179
|
+
rsvp event footer update \
|
|
180
|
+
--allow-state-change \
|
|
181
|
+
--target evt_123 \
|
|
182
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","footerImageKey":"rsvp/events/evt_123/footers/<uuid>.png","variant":"MOBILE","version":7}'
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Instead of one image, the footer can show separate logos scrolling across the
|
|
186
|
+
page. Upload each logo with `event footer upload create`, then save the mode
|
|
187
|
+
and the ordered list with `event footer logos update`. Each logo needs a
|
|
188
|
+
`name` (also its alt text); `heading` (for example "Supported by"), a smaller
|
|
189
|
+
`label` (for example "Principal partners") and an `https://` `url` are optional.
|
|
190
|
+
Adjacent logos with the same heading appear together under it. Up to 30 logos.
|
|
191
|
+
`footerMode` `IMAGE` switches back to the single image; both modes keep their
|
|
192
|
+
uploads.
|
|
193
|
+
|
|
194
|
+
```sh
|
|
195
|
+
rsvp event footer logos update \
|
|
196
|
+
--allow-state-change \
|
|
197
|
+
--target evt_123 \
|
|
198
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","footerMode":"LOGOS","logos":[{"imageKey":"rsvp/events/evt_123/footers/<uuid>.png","name":"FARM","heading":"Produced by","url":"https://www.farm.sg"}],"version":8}'
|
|
199
|
+
```
|
|
200
|
+
|
|
128
201
|
For a read-only registration-journey diagnosis, inspect the event, draft,
|
|
129
202
|
published versions, and event-owned resources together:
|
|
130
203
|
|
|
@@ -135,6 +208,76 @@ rsvp event flow versions list --organization-id org_123 --event-id evt_123
|
|
|
135
208
|
rsvp event flow resources get --organization-id org_123 --event-id evt_123
|
|
136
209
|
```
|
|
137
210
|
|
|
211
|
+
### Registration-flow admission policy
|
|
212
|
+
|
|
213
|
+
`event flow draft get` returns normalized legacy invitation forms as
|
|
214
|
+
`authentication.mode: "EMAIL_OTP"` with `definition.admissionPolicy: "ROSTER_ONLY"`.
|
|
215
|
+
Carry that independent policy through `event flow draft save`, `event flow publish`,
|
|
216
|
+
and `event flow version activate`; none of those operations silently opens signup.
|
|
217
|
+
The participant-safe `event flow published get` admission manifest also reports
|
|
218
|
+
`admissionPolicy` (`OPEN` for modern definitions without an explicit policy).
|
|
219
|
+
|
|
220
|
+
Save the complete returned definition with its current `expectedRevision`.
|
|
221
|
+
Omitting `admissionPolicy` preserves the stored draft/publication policy for older
|
|
222
|
+
clients. To intentionally broaden admission, explicitly set it to `"OPEN"` in the
|
|
223
|
+
modern email-auth definition, save, then publish; this remains subject to event
|
|
224
|
+
permissions, audience restrictions, and email-domain policy for new attendees.
|
|
225
|
+
Existing eligible roster attendees keep their existing eligibility rules.
|
|
226
|
+
|
|
227
|
+
```sh
|
|
228
|
+
rsvp event flow draft get --payload '{"organizationId":"org_123","eventId":"evt_123","flowId":"flow_123"}'
|
|
229
|
+
# Save the complete JSON definition and expectedRevision in flow-draft.json.
|
|
230
|
+
# Keep admissionPolicy: "ROSTER_ONLY" unless the organiser approves open signup.
|
|
231
|
+
rsvp event flow draft save --allow-state-change --target evt_123 --payload "$(cat flow-draft.json)"
|
|
232
|
+
rsvp event flow publish --allow-state-change --target evt_123 --payload '{"organizationId":"org_123","eventId":"evt_123","flowId":"flow_123","expectedDraftRevision":2}'
|
|
233
|
+
rsvp event flow version activate --allow-state-change --target evt_123 --payload '{"organizationId":"org_123","eventId":"evt_123","flowId":"flow_123","flowVersionId":"version_123"}'
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Replace illustrative IDs and revisions with current readback values. Deploy API
|
|
237
|
+
policy enforcement before clients; an old API does not understand this field.
|
|
238
|
+
No authentication, consent flags, command names, or credential protocol changes.
|
|
239
|
+
|
|
240
|
+
### Registration-flow date-limit messages
|
|
241
|
+
|
|
242
|
+
`DATE` and `DATETIME` custom fields accept optional
|
|
243
|
+
`field.validation.minDateMessage` and `maxDateMessage`: trimmed, non-empty plain
|
|
244
|
+
text of at most 500 characters. Each replaces only its corresponding out-of-range
|
|
245
|
+
explanation. Inclusive `minDate`/`maxDate` bounds, malformed-date errors and
|
|
246
|
+
required-field errors are unchanged; omitted messages keep the existing defaults.
|
|
247
|
+
|
|
248
|
+
An illustrative DOB validation object is:
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"maxDate": "2008-01-01",
|
|
253
|
+
"maxDateMessage": "You must be at least 18 to register for this event."
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Age wording is organizer-authored, not a dynamic age rule. Choose the cutoff for
|
|
258
|
+
the event; do not reuse the illustrative date blindly.
|
|
259
|
+
|
|
260
|
+
Read the full current definition/revision using `event flow draft get`, edit the
|
|
261
|
+
intended field, then pass the complete definition to `event flow preview` and
|
|
262
|
+
`event flow draft save --allow-state-change --target <eventId> --payload <JSON>`.
|
|
263
|
+
Preview/save payloads include `organizationId`, `eventId`, optional `flowId`, and
|
|
264
|
+
`definition`; save also requires `expectedRevision`. Publish separately using
|
|
265
|
+
`event flow publish --allow-state-change --target <eventId> --payload <JSON>` with
|
|
266
|
+
`expectedDraftRevision`. Read back the saved definition with `event flow draft get`
|
|
267
|
+
and verify its compiled participant projection using the authenticated organizer
|
|
268
|
+
`event flow preview` command. Use `event flow versions list` to check immutable
|
|
269
|
+
version/publication identity; `event flow published get` provides public admission
|
|
270
|
+
and publication metadata in normal CLI execution, not the full participant fields.
|
|
271
|
+
It has no participant-session forwarding; the API returns `flow.participant` as
|
|
272
|
+
`null` without matching participant access. Verify the actual published date limits
|
|
273
|
+
and messages through the event's registration flow with a valid participant
|
|
274
|
+
session.
|
|
275
|
+
|
|
276
|
+
Existing commands forward the definition unchanged to the authenticated shared
|
|
277
|
+
API; no new flags or CLI-only validation are needed. Deploy matching API/web
|
|
278
|
+
support before saving these keys via the CLI; older APIs strip unrecognized
|
|
279
|
+
message keys. No event or published version is updated automatically.
|
|
280
|
+
|
|
138
281
|
### Registration-flow image upload lifecycle
|
|
139
282
|
|
|
140
283
|
Registration-flow images are **named, event-local assets**. Create one with an
|
|
@@ -175,6 +318,180 @@ protocol. Reference the finalized asset from the appropriate form choice, then
|
|
|
175
318
|
save and publish the flow separately when the complete draft is approved.
|
|
176
319
|
|
|
177
320
|
|
|
321
|
+
## Stripe payments and external refunds
|
|
322
|
+
|
|
323
|
+
`payment checkout create` uses the same server-enforced finite reservation policy
|
|
324
|
+
as the web app. New Checkout sessions exclude known slow-settling methods while
|
|
325
|
+
retaining dynamic immediate methods such as cards and PayNow. Existing sessions
|
|
326
|
+
and late-success safeguards are unchanged.
|
|
327
|
+
|
|
328
|
+
`payment event list` reports completed refund totals including separately
|
|
329
|
+
recorded, verified refunds issued outside RSVP. Pending/failed provider refunds
|
|
330
|
+
do not count, and an external refund never consumes an unrelated app refund
|
|
331
|
+
request. `payment refund` still requires the existing financial/external-write
|
|
332
|
+
gates and original account binding. `payment exception list` remains the place
|
|
333
|
+
to inspect unresolved payment exceptions. Existing command inputs and gates are
|
|
334
|
+
unchanged. `payment refund recover` checks an existing local or external refund
|
|
335
|
+
adjustment through its stored provider reference; it never creates a new refund:
|
|
336
|
+
|
|
337
|
+
```sh
|
|
338
|
+
rsvp payment refund recover \
|
|
339
|
+
--ack-sensitive-data --allow-external-write --allow-financial-write \
|
|
340
|
+
--allow-state-change --target adjustment_123 \
|
|
341
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","paymentAttemptId":"attempt_123","adjustmentId":"adjustment_123"}'
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
The organization/event/attempt and adjustment must belong together, and the
|
|
345
|
+
caller must retain payment-adjustment permission through the provider lookup;
|
|
346
|
+
current actor authority and event lifecycle are rechecked transactionally before
|
|
347
|
+
any financial or audit writes. Repeated checks reconcile the
|
|
348
|
+
same adjustment safely; a missing provider reference requires separate operator
|
|
349
|
+
reconciliation, not a new key or refund request.
|
|
350
|
+
|
|
351
|
+
### Tax configuration and breakdowns
|
|
352
|
+
|
|
353
|
+
`payment configure` accepts optional `tax` JSON. Use `{"mode":"NONE"}` for
|
|
354
|
+
no tax. `INCLUSIVE` extracts tax from the configured price; `EXCLUSIVE` adds
|
|
355
|
+
tax to it. Both taxable modes require a trimmed `label` (1–50 characters), an
|
|
356
|
+
integer `rateBasisPoints` from 0 to 10000, and explicit `donationTaxable`.
|
|
357
|
+
**100 basis points = 1%.** The example below is illustrative, not a recommended
|
|
358
|
+
rate or a decision about whether donations are taxable.
|
|
359
|
+
|
|
360
|
+
Read the current policy and `configurationVersion` before configuring, and
|
|
361
|
+
replace the IDs, version, amounts and tax settings for the intended event:
|
|
362
|
+
|
|
363
|
+
```sh
|
|
364
|
+
rsvp payment event get --allow-financial-write \
|
|
365
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123"}'
|
|
366
|
+
|
|
367
|
+
rsvp payment configure \
|
|
368
|
+
--allow-external-write --allow-financial-write --allow-state-change \
|
|
369
|
+
--target evt_123 \
|
|
370
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","eventVersion":7,"enabled":true,"currency":"SGD","amountMinor":1000,"pricingMode":"PER_REGISTRATION","donationMaximumMinor":500,"checkoutExpiryMinutes":30,"reservationPolicy":"HOLD_UNTIL_EXPIRY","tax":{"mode":"EXCLUSIVE","rateBasisPoints":900,"label":"Tax","donationTaxable":false}}'
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
This is a complete configuration write, not a tax-only patch: preserve the
|
|
374
|
+
intended payment policy and credential selection. Add `flowId` for a named
|
|
375
|
+
form override; `useEventDefaults: true` makes that form inherit the event policy,
|
|
376
|
+
including tax. Omitted tax remains compatible with legacy no-tax configuration.
|
|
377
|
+
Read `payment event get` back with the same scope to verify the saved policy.
|
|
378
|
+
|
|
379
|
+
`payment mine get` exposes the attempt's saved tax quote and subtotal/tax/final
|
|
380
|
+
total. `payment event list` exposes each attempt's original `quote` plus
|
|
381
|
+
`refundBreakdown` (cumulative refunded subtotal, tax and total). Pending refunds
|
|
382
|
+
are not confirmed refunded amounts. These are payment breakdowns, not statutory
|
|
383
|
+
tax invoices. Organizers choose applicable settings; configuration does not
|
|
384
|
+
establish legal approval or activate an event. Deploy matching API/web/CLI
|
|
385
|
+
support before using these fields.
|
|
386
|
+
|
|
387
|
+
### Pending Stripe connection preparation
|
|
388
|
+
|
|
389
|
+
Organization managers can persist separate named Sandbox and Live connections
|
|
390
|
+
without either secret. Generate one webhook UUID for each request, keep it for
|
|
391
|
+
retries, and replace all illustrative IDs below:
|
|
392
|
+
|
|
393
|
+
```sh
|
|
394
|
+
rsvp stripe credential prepare \
|
|
395
|
+
--allow-external-write --allow-state-change --target org_123 \
|
|
396
|
+
--payload '{"organizationId":"org_123","name":"Client Sandbox","livemode":false,"webhookSourceId":"9dd0ba39-b55e-4e65-aa3e-d08242c45b8f"}'
|
|
397
|
+
|
|
398
|
+
rsvp stripe credential preparation list \
|
|
399
|
+
--payload '{"organizationId":"org_123"}'
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
The response includes the saved preparation ID, webhook path, environment,
|
|
403
|
+
endpoint API version and required events. Confirm the deployment/hosting origin
|
|
404
|
+
before constructing the full client URL. Pending records survive reload but are
|
|
405
|
+
not usable payment credentials or defaults and cannot verify webhooks. Pending
|
|
406
|
+
names are also protected from legacy credential creation and rename operations;
|
|
407
|
+
unreserved names remain supported.
|
|
408
|
+
|
|
409
|
+
Prefer **Complete setup** in the encrypted integrations UI for secret entry.
|
|
410
|
+
For approved private automation, `stripe credential complete` calls the same
|
|
411
|
+
API with `organizationId`, the unchanged `name`, saved `preparationId` and
|
|
412
|
+
`webhookSourceId`, plus `secretKey` and `webhookSecret`. It requires
|
|
413
|
+
`--allow-external-write --allow-state-change --target <organizationId>`.
|
|
414
|
+
Both restricted `rk_` and secret `sk_` server keys are supported. Never place
|
|
415
|
+
secrets in chat, payment payloads, committed files, or shared shell history.
|
|
416
|
+
The complete command returns only public credential metadata.
|
|
417
|
+
|
|
418
|
+
Then `stripe credential test` takes `organizationId` and `credentialId` with
|
|
419
|
+
`--allow-external-write --allow-state-change --target <credentialId>`.
|
|
420
|
+
Its bounded account/Balance/Checkout/PaymentIntent/Refund reads verify the
|
|
421
|
+
prepared mode and read access, not write permission or webhook delivery. Only a
|
|
422
|
+
tested usable connection can be selected for payments; perform a separate sandbox
|
|
423
|
+
payment and webhook acceptance check before launch. These commands use the
|
|
424
|
+
normal authenticated API and never bypass organization-management permission.
|
|
425
|
+
|
|
426
|
+
Deploy with the schema/client and webhook prerequisites in
|
|
427
|
+
[the application guide](../../../apps/rsvp/README.md#refunds). Local generated
|
|
428
|
+
clients can be refreshed with `bun db:generate`; that is not a database push or
|
|
429
|
+
proof of deployed schema state.
|
|
430
|
+
|
|
431
|
+
## Lucky Draw check-in cutoff
|
|
432
|
+
|
|
433
|
+
The event manager can save an optional cutoff in **Check-in → Settings → Lucky
|
|
434
|
+
draw eligibility**, in the event's configured timezone. The API stores an absolute
|
|
435
|
+
ISO timestamp with an explicit offset (`Z` or `±HH:MM`), not a browser-local time.
|
|
436
|
+
There is no fixed cutoff or 8 PM default. Unconfigured events return
|
|
437
|
+
`luckyDrawEligibility.cutoffAt: null`; malformed stored values also return `null`
|
|
438
|
+
rather than being interpreted in the browser's timezone.
|
|
439
|
+
|
|
440
|
+
Read the event first:
|
|
441
|
+
|
|
442
|
+
```sh
|
|
443
|
+
rsvp event get --organization-id org_123 --event-id evt_123
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Copy its current `configurationVersion` into the setup payload's `version`.
|
|
447
|
+
Replace all example IDs, version `7`, and the illustrative timestamp below with
|
|
448
|
+
values for the intended event; the date, time, and offset are not defaults:
|
|
449
|
+
|
|
450
|
+
```sh
|
|
451
|
+
rsvp event setup step update \
|
|
452
|
+
--allow-state-change \
|
|
453
|
+
--target evt_123 \
|
|
454
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","version":7,"section":"LUCKY_DRAW","cutoffAt":"2030-06-15T13:30:00+02:00"}'
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
To clear the cutoff, read the event again and use its fresh version with
|
|
458
|
+
`cutoffAt: null` (not a blank string):
|
|
459
|
+
|
|
460
|
+
```sh
|
|
461
|
+
rsvp event setup step update \
|
|
462
|
+
--allow-state-change \
|
|
463
|
+
--target evt_123 \
|
|
464
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","version":7,"section":"LUCKY_DRAW","cutoffAt":null}'
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Both writes require `event.manage`, the explicit state-change acknowledgement,
|
|
468
|
+
and a `--target` matching the payload's `eventId`. A stale version returns
|
|
469
|
+
`CONFLICT`: fetch the event, review the latest settings, then retry with its
|
|
470
|
+
current version rather than replaying an old payload. Read the event back after
|
|
471
|
+
a successful write to verify the cutoff and configuration version.
|
|
472
|
+
|
|
473
|
+
This configuration step only stores the cutoff and preserves unrelated event
|
|
474
|
+
metadata. It does not choose eligible people, apply category/exclusion rules,
|
|
475
|
+
import a draw roster, or reconcile prizes/vouchers. Deploy the updated API before
|
|
476
|
+
saving the new setup section; no database schema migration is required.
|
|
477
|
+
|
|
478
|
+
### Registration card copy
|
|
479
|
+
|
|
480
|
+
`section: "PUBLIC_PAGE"` replaces the event's saved landing-page card copy.
|
|
481
|
+
Omit a text key to use the default wording; set a `show…` flag to `false` to
|
|
482
|
+
hide that notice. Read the event first and use its current version:
|
|
483
|
+
|
|
484
|
+
```sh
|
|
485
|
+
rsvp event setup step update \
|
|
486
|
+
--allow-state-change \
|
|
487
|
+
--target evt_123 \
|
|
488
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","version":7,"section":"PUBLIC_PAGE","publicPage":{"registrationTitle":"NELBD Quest Registration","deadlineLabel":"Register by","responseUpdateNotice":"You may return to update your registration details before the timing stated above.","accessNotice":"Continue to complete the registration for both team members.","registrationActionLabel":"Register now"}}'
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Limits: title 120, description 500, deadline label 60, response-update notice
|
|
492
|
+
300, access notice 500 and button label 100 characters. It requires
|
|
493
|
+
`event.manage`; no database migration is required.
|
|
494
|
+
|
|
178
495
|
## Per-form confirmation presentation
|
|
179
496
|
|
|
180
497
|
In the form builder's **Flow settings → Confirmation and check-in pass**, set
|
|
@@ -218,21 +535,37 @@ No database schema migration or attendee backfill is required.
|
|
|
218
535
|
|
|
219
536
|
## Administrator registration for an invitee
|
|
220
537
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
538
|
+
Organizers can register someone on their behalf at any time, including after
|
|
539
|
+
registration has closed. This is the same action as the **Register invitee**
|
|
540
|
+
page under People. Look the person up first. Pass `inviteeId` for someone on
|
|
541
|
+
the invitee list, or `person` with their details. The response includes the
|
|
542
|
+
published flow definition, the invitee, `isExistingInvitee`, any existing
|
|
543
|
+
registration, `archivedRegistrationId`, `canAddInvitee` and
|
|
544
|
+
`canOverrideCapacity`:
|
|
224
545
|
|
|
225
546
|
```sh
|
|
226
547
|
rsvp event flow admin registration context get \
|
|
227
548
|
--ack-sensitive-data \
|
|
228
549
|
--payload '{"organizationId":"org_123","eventId":"evt_123","inviteeId":"invitee_123"}'
|
|
550
|
+
|
|
551
|
+
rsvp event flow admin registration context get \
|
|
552
|
+
--ack-sensitive-data \
|
|
553
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","person":{"email":"ava@example.com","employeeId":"E1042","audienceTierId":null}}'
|
|
229
554
|
```
|
|
230
555
|
|
|
556
|
+
A `person` lookup matches an invitee with the same email (on the invitee or
|
|
557
|
+
their account) or the same employee ID. If the email and employee ID belong to
|
|
558
|
+
two different invitees, the lookup is refused. When nothing matches,
|
|
559
|
+
`isExistingInvitee` is `false`, and the context previews the form and seats
|
|
560
|
+
that a new invitee in the given audience tier would get. If
|
|
561
|
+
`archivedRegistrationId` is set, restore that registration instead (`people
|
|
562
|
+
registration restore`), which keeps the existing QR code.
|
|
563
|
+
|
|
231
564
|
Use the returned `flowVersionId` and `expectedRegistrationVersion` in the
|
|
232
|
-
submission. Set `expectedOutcomeId` to the outcome selected by the answers
|
|
233
|
-
the
|
|
234
|
-
|
|
235
|
-
|
|
565
|
+
submission. Set `expectedOutcomeId` to the outcome selected by the answers, and
|
|
566
|
+
key the answers by each field's `outputKey`. A new UUID `idempotencyKey`
|
|
567
|
+
identifies the submission. The audit reason records why an organizer submitted
|
|
568
|
+
on the person's behalf. Give exactly one of `inviteeId` or `newInvitee`:
|
|
236
569
|
|
|
237
570
|
```sh
|
|
238
571
|
rsvp event flow admin registration submit \
|
|
@@ -240,14 +573,42 @@ rsvp event flow admin registration submit \
|
|
|
240
573
|
--allow-state-change \
|
|
241
574
|
--target invitee_123 \
|
|
242
575
|
--payload '{"organizationId":"org_123","eventId":"evt_123","inviteeId":"invitee_123","flowVersionId":"flow_version_123","answers":{"attendance":"ATTENDING"},"expectedOutcomeId":"attending","expectedRegistrationVersion":0,"idempotencyKey":"9dd0ba39-b55e-4e65-aa3e-d08242c45b8f","auditReason":"Recorded response by phone"}'
|
|
576
|
+
|
|
577
|
+
rsvp event flow admin registration submit \
|
|
578
|
+
--ack-sensitive-data \
|
|
579
|
+
--allow-state-change \
|
|
580
|
+
--target ava@example.com \
|
|
581
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","newInvitee":{"displayName":"Ava Tan","email":"ava@example.com","employeeId":"E1042","audienceTierId":null},"flowVersionId":"flow_version_123","answers":{"attendance":"ATTENDING"},"expectedOutcomeId":"attending","expectedRegistrationVersion":0,"idempotencyKey":"3f0c5a4e-2a43-4d8b-9a59-6d0e8f5b8a11","auditReason":"Late addition approved by the client","sendConfirmation":true}'
|
|
243
582
|
```
|
|
244
583
|
|
|
584
|
+
With `newInvitee`, the target is the email. Matching uses the same identity
|
|
585
|
+
rules as roster import and admission: email case and Gmail dot/`+alias`
|
|
586
|
+
spellings are ignored, and so is employee ID case. If an invitee already matches
|
|
587
|
+
the details, that invitee is registered. Otherwise a new invitee is added to the
|
|
588
|
+
list and registered in the same transaction. Adding someone new needs the
|
|
589
|
+
`roster.manage` permission. Invitees who declined can be registered; anyone
|
|
590
|
+
with an active registration is changed through `people registration update`
|
|
591
|
+
instead, and archived registrations go through restore.
|
|
592
|
+
|
|
593
|
+
Optional fields:
|
|
594
|
+
|
|
595
|
+
- `sendConfirmation` (default `true`): set it to `false` to skip the
|
|
596
|
+
participant confirmation email with the QR code.
|
|
597
|
+
The result's `confirmationQueued` says whether the email was actually queued:
|
|
598
|
+
it is `false` when the form's email settings send no confirmation for this
|
|
599
|
+
registration, even if `sendConfirmation` is `true`.
|
|
600
|
+
- `overCapacityReason` (10–500 characters): registers the person even when the
|
|
601
|
+
event is full. This needs the `capacity.override` permission (event and
|
|
602
|
+
organization admins). The reason is recorded in the activity log. Without it,
|
|
603
|
+
a full event returns a conflict with `eventAtCapacity: true` in the error
|
|
604
|
+
data. Menu (dish) capacity is still enforced.
|
|
605
|
+
- `consentOverrideReason`: required when the flow has a consent step. It
|
|
606
|
+
records the organizer's reason in the audit trail; it does not record
|
|
607
|
+
attendee consent or a signature.
|
|
608
|
+
|
|
245
609
|
Replace the example answer key and value with those required by the returned
|
|
246
|
-
definition.
|
|
247
|
-
|
|
248
|
-
administrator's reason in the audit trail; it does not record attendee consent
|
|
249
|
-
or a signature. A stale registration version or flow version is rejected; fetch
|
|
250
|
-
the context again before retrying with a new idempotency key.
|
|
610
|
+
definition. A stale registration version or flow version is rejected; fetch the
|
|
611
|
+
context again before retrying with a new idempotency key.
|
|
251
612
|
|
|
252
613
|
Create an immutable menu version with an explicit mutation acknowledgement. The
|
|
253
614
|
target is the event ID in the payload:
|
|
@@ -302,40 +663,24 @@ rsvp check-in by-reference \
|
|
|
302
663
|
|
|
303
664
|
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.
|
|
304
665
|
|
|
305
|
-
##
|
|
306
|
-
|
|
307
|
-
`export request` accepts `format: "XLSX"` (the default when omitted) or `"CSV"`.
|
|
308
|
-
Use `filters.view` to name the population rather than relying on a dataset label:
|
|
666
|
+
## Registration master list export
|
|
309
667
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
The client XLSX has **Attending**, **Not Attending**, **Master**, and **Metadata**
|
|
315
|
-
sheets; its CSV contains Master data only. Guest staff/department fields
|
|
316
|
-
describe the host, while menu and dietary answers remain person-scoped.
|
|
317
|
-
- SEATING: `CURRENT` (active bookings; default) or `HISTORY` (active and released).
|
|
318
|
-
- ATTENDANCE: `CURRENT` (expected people; default) or `CHECKED_IN` (expected people
|
|
319
|
-
with a check-in that has not been undone), or `ALL_INVITEES` (roster invitees,
|
|
320
|
-
including nonrespondents/declines, plus current guests). Registered, submitted,
|
|
321
|
-
attending, and active check-in states remain separate. None defines draw
|
|
322
|
-
eligibility.
|
|
323
|
-
|
|
324
|
-
Form, registration-status and submission-date filters apply only to RSVP.
|
|
325
|
-
Other datasets do not accept `view`. These combinations are validated by the API.
|
|
668
|
+
`export request` queues the single **Registration master list** Excel workbook
|
|
669
|
+
(one **Master List** sheet; question columns follow the event's form). The
|
|
670
|
+
payload is `organizationId`, `eventId` and an audit `reason`; the old `dataset`,
|
|
671
|
+
`format` and `filters` fields are ignored. See the RSVP app README for columns.
|
|
326
672
|
|
|
327
673
|
```sh
|
|
328
674
|
rsvp export request \
|
|
329
675
|
--ack-sensitive-data \
|
|
330
676
|
--allow-state-change \
|
|
331
677
|
--target evt_123 \
|
|
332
|
-
--payload '{"organizationId":"org_123","eventId":"evt_123","
|
|
678
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","reason":"Organizer master list"}'
|
|
333
679
|
```
|
|
334
680
|
|
|
335
681
|
Use `export list` to check the requester-scoped job, then `export download` for
|
|
336
|
-
its current authorized download URL.
|
|
337
|
-
|
|
338
|
-
set `dataset` to `ATTENDANCE` and `filters.view` to `ALL_INVITEES`.
|
|
682
|
+
its current authorized download URL. `event registrations export` and
|
|
683
|
+
`event check-ins export` were removed; use `export request` instead.
|
|
339
684
|
|
|
340
685
|
Operational check-in accepts `ATTENDING` and legacy `SUBMITTED`; roster/detail,
|
|
341
686
|
manual/QR/reference entry, `report overview`, and `event people overview` use that same
|
|
@@ -343,9 +688,8 @@ population. Non-operational statuses remain excluded, and permissions, payment
|
|
|
343
688
|
gates, duplicate protection, and undo behavior are unchanged. The web panel's
|
|
344
689
|
realtime connection and manual refresh controls do not add a CLI subscription.
|
|
345
690
|
|
|
346
|
-
Deploy matching API and export-worker versions
|
|
347
|
-
|
|
348
|
-
payload to the shared typed API, so no new command or client-only rules are needed.
|
|
691
|
+
Deploy matching API and export-worker versions together; an old worker would build the retired
|
|
692
|
+
registration-records report for master-list jobs.
|
|
349
693
|
|
|
350
694
|
## Seating updates
|
|
351
695
|
|
|
@@ -374,15 +718,38 @@ Deploy the matching API and worker before using these commands from an updated
|
|
|
374
718
|
CLI. They retain browser authentication, event permissions and sensitive-field
|
|
375
719
|
grants; the CLI does not implement separate seating rules.
|
|
376
720
|
|
|
721
|
+
`event seating swap` refuses invalid swaps using first/second selected-attendee
|
|
722
|
+
labels, never attendee names. This applies even when the caller can view identity
|
|
723
|
+
fields: reseating permission alone is not identity access. Notice failures retain
|
|
724
|
+
their API error code but do not forward attendee names or nested private details;
|
|
725
|
+
the entire swap rolls back if its requested notices cannot be queued. A notice
|
|
726
|
+
precondition refusal can be retried without the optional notice. Input/output
|
|
727
|
+
schemas, acknowledgement gates and seating/payment rules are unchanged.
|
|
728
|
+
|
|
377
729
|
```sh
|
|
378
730
|
rsvp event seating live get --ack-sensitive-data \
|
|
379
731
|
--payload '{"organizationId":"org_123","eventId":"evt_123"}'
|
|
732
|
+
rsvp event seating guests list --ack-sensitive-data \
|
|
733
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","seatMapId":"map_123","seating":"UNSEATED","sort":"GROUP","limit":25}'
|
|
734
|
+
rsvp event seating guests count --ack-sensitive-data \
|
|
735
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","seatMapId":"map_123","seating":"UNSEATED"}'
|
|
380
736
|
rsvp event seating occupants list --ack-sensitive-data \
|
|
381
737
|
--payload '{"organizationId":"org_123","eventId":"evt_123","seatMapId":"map_123","categoryValue":"finance","limit":25,"cursor":0}'
|
|
382
738
|
rsvp people registration edit get --ack-sensitive-data \
|
|
383
739
|
--payload '{"organizationId":"org_123","eventId":"evt_123","registrationId":"reg_123"}'
|
|
384
740
|
```
|
|
385
741
|
|
|
742
|
+
`event seating category colors get` returns only the configured category
|
|
743
|
+
colour of each booked seat (for example job function) and the legend for one
|
|
744
|
+
seat map, which is what the live map paints without opening the occupant
|
|
745
|
+
workspace. It needs `seating.view` and registration view access, and respects
|
|
746
|
+
sensitive-field access to the configured field:
|
|
747
|
+
|
|
748
|
+
```sh
|
|
749
|
+
rsvp event seating category colors get --ack-sensitive-data \
|
|
750
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","seatMapId":"map_123"}'
|
|
751
|
+
```
|
|
752
|
+
|
|
386
753
|
Occupants supports optional `tableId`, `department`, `categoryValue` and `query`
|
|
387
754
|
filters. `categoryValue` is the canonical (trimmed, case-normalized) value
|
|
388
755
|
returned by the occupant response's category legend; it is not an arbitrary
|
|
@@ -438,12 +805,88 @@ notice is queued in the move's database transaction, so the move and notice
|
|
|
438
805
|
request commit together or not at all; this additionally requires
|
|
439
806
|
`--allow-email`. It is rejected alongside identity, contact or response edits.
|
|
440
807
|
|
|
808
|
+
To swap two seated people, use `event seating swap`. Give each attendee's
|
|
809
|
+
`registrationAttendeeId` with the `seatId` they hold now, plus a `reason`. Both
|
|
810
|
+
seats change in one transaction, so neither is free for anyone else in between.
|
|
811
|
+
If either person has moved since you looked, the swap is refused; list the
|
|
812
|
+
occupants again and retry. Each person must be attending or submitted with no
|
|
813
|
+
payment pending, and each seat must be allowed for the other person's audience
|
|
814
|
+
tier, form and published seat map. `--target` is either attendee ID:
|
|
815
|
+
|
|
816
|
+
```sh
|
|
817
|
+
rsvp event seating swap \
|
|
818
|
+
--ack-sensitive-data \
|
|
819
|
+
--allow-state-change \
|
|
820
|
+
--target attendee_a \
|
|
821
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","attendees":[{"registrationAttendeeId":"attendee_a","seatId":"seat_1"},{"registrationAttendeeId":"attendee_b","seatId":"seat_2"}],"reason":"Client asked to seat them with their team"}'
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
Add `"seatChangeNotice":{"idempotencyKey":"<uuid>"}` and `--allow-email` to
|
|
825
|
+
queue a seat-change notice for each registration involved. Use
|
|
826
|
+
`event seating occupants list` to find attendee and seat IDs. Swaps need the
|
|
827
|
+
`seating.reseat` permission; notices also need `confirmation.resend`.
|
|
828
|
+
|
|
441
829
|
```sh
|
|
442
830
|
rsvp people registration update \
|
|
443
831
|
--ack-sensitive-data --allow-state-change --allow-email --target reg_123 \
|
|
444
832
|
--payload '{"organizationId":"org_123","eventId":"evt_123","registrationId":"reg_123","expectedVersion":4,"reason":"Moved to finance table","seats":[{"registrationAttendeeId":"att_1","seatId":"seat_9"},{"registrationAttendeeId":"att_2","seatId":"seat_2"}],"seatChangeNotice":{"idempotencyKey":"4d68a320-013d-43c7-bfcc-d1126827d8c5"}}'
|
|
445
833
|
```
|
|
446
834
|
|
|
835
|
+
### Unarchive for a fresh response versus attending Restore
|
|
836
|
+
|
|
837
|
+
`people registration unarchive` forwards the permissioned, audited
|
|
838
|
+
`event.bulkManageRegistrations` RESTORE operation. Supply 1–100 unique
|
|
839
|
+
`registrations`, each with its current `expectedVersion` (positive integer), and
|
|
840
|
+
an audit `reason` (3–500 characters after trimming). `action` defaults to
|
|
841
|
+
`RESTORE` when omitted; any other action is rejected. The approval target is
|
|
842
|
+
exactly the payload's **event ID**, not an individual registration ID.
|
|
843
|
+
|
|
844
|
+
```sh
|
|
845
|
+
rsvp people registration unarchive \
|
|
846
|
+
--allow-state-change --target evt_123 \
|
|
847
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","reason":"Allow a fresh response","registrations":[{"id":"reg_123","expectedVersion":6}]}'
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
Unarchive clears the archive marker and lets an eligible cancelled nonattending
|
|
851
|
+
invitee return to `IMPORTED` for a fresh response. The server checks current
|
|
852
|
+
permissions, revisions, eligibility and settled financial state; the CLI does
|
|
853
|
+
not interpret cancelled history or make eligibility decisions. Historical
|
|
854
|
+
registration/payment records remain history, and the old cancelled
|
|
855
|
+
registration and QR code are **not reinstated**. Seats, capacity, session
|
|
856
|
+
selections and waitlist entries are not recovered. A fresh submission uses the
|
|
857
|
+
normal flow and current checks, with new payment and credentials where required.
|
|
858
|
+
Unarchive cannot shortcut attending recovery. The result's
|
|
859
|
+
`readmittedInviteeCount` reports invitees reopened for a fresh response;
|
|
860
|
+
`changedCount` and `releasedAttendeeCount` retain their existing meanings.
|
|
861
|
+
|
|
862
|
+
`people registration restore` instead reinstates a cancelled or archived
|
|
863
|
+
**attending registration**, with its existing identity/payment/QR subject to the
|
|
864
|
+
server's restoration checks. It is not Unarchive and does not start a fresh response.
|
|
865
|
+
Read it first with `people registration edit get` and `"mode":"RESTORE"` to see
|
|
866
|
+
eligible seats and each attendee's previous seat. Supply `expectedVersion`, an
|
|
867
|
+
audit `reason`, and a seat for every attendee who had one; the server claims
|
|
868
|
+
capacity, books the seats and rejects full events, re-registered invitees and
|
|
869
|
+
unresolved or refunded payments. A full event is rejected unless
|
|
870
|
+
`overCapacityReason` (10–500 characters) is given, which needs the
|
|
871
|
+
`capacity.override` permission (event and org admins); the override and reason
|
|
872
|
+
are recorded in the activity log. Menu (dish) capacity is still enforced and
|
|
873
|
+
cannot be overridden. While an event is over capacity, declines and smaller
|
|
874
|
+
parties, payments confirmed while their hold is still live, and unexpired
|
|
875
|
+
waitlist offers (up to the offered party size) still go through. A payment
|
|
876
|
+
confirmed after its hold lapsed gets the normal capacity check and goes to
|
|
877
|
+
review if the place was reused. The current limit can be saved unchanged; any
|
|
878
|
+
new limit must be at least the confirmed count. The existing QR code works
|
|
879
|
+
again. Add
|
|
880
|
+
`confirmation` (with `--allow-email`) to queue the participant confirmation.
|
|
881
|
+
Session selections cancelled by archiving are not restored; the response
|
|
882
|
+
reports `droppedSessionSelections`.
|
|
883
|
+
|
|
884
|
+
```sh
|
|
885
|
+
rsvp people registration restore \
|
|
886
|
+
--ack-sensitive-data --allow-state-change --allow-email --target reg_123 \
|
|
887
|
+
--payload '{"organizationId":"org_123","eventId":"evt_123","registrationId":"reg_123","expectedVersion":6,"reason":"Archived by mistake","seats":[{"registrationAttendeeId":"att_1","seatId":"seat_9"}],"confirmation":{"idempotencyKey":"4d68a320-013d-43c7-bfcc-d1126827d8c5"}}'
|
|
888
|
+
```
|
|
889
|
+
|
|
447
890
|
A separate manual notice for an already committed revision remains available:
|
|
448
891
|
|
|
449
892
|
```sh
|
|
@@ -556,3 +999,27 @@ email, external-write, and state-change effect approvals. It creates a workspace
|
|
|
556
999
|
awaiting owner acceptance and sends a 48-hour first-owner invitation. The internal
|
|
557
1000
|
provisioner does not receive customer organization membership. The response
|
|
558
1001
|
reports invitation delivery failure so the invitation can be resent safely.
|
|
1002
|
+
|
|
1003
|
+
### Assign guests rail reads
|
|
1004
|
+
|
|
1005
|
+
`event seating guests list` includes unarchived, confirmed attendees (legacy
|
|
1006
|
+
`ATTENDING` and current `SUBMITTED`), including guests with no active seat. An
|
|
1007
|
+
active booking on any layout counts as seated. Groups are registration parties.
|
|
1008
|
+
Both reads require `seating.view` and `rsvp.view`; names/search/name sorting and
|
|
1009
|
+
dietary filters/labels additionally require their sensitive-data grants.
|
|
1010
|
+
|
|
1011
|
+
Shared filters: `seating` (`UNSEATED`, `SEATED`, `ALL`), `query` (max 120),
|
|
1012
|
+
`audienceTierId`, `registrationId`, `dietaryTag`, `guestKind` (`ALL`, `HOST`,
|
|
1013
|
+
`GUEST`). List adds `limit` (1–100; default 25), `sort` (`GROUP`, `NAME`), opaque
|
|
1014
|
+
`cursor`, and `direction` (`forward`, `backward`). Backward requires a cursor.
|
|
1015
|
+
The response is `{items,pageInfo}`; use its `nextCursor`/`previousCursor`, not a
|
|
1016
|
+
row ID. Cursors are bound to filters, ordering, scope and sensitive grants; a
|
|
1017
|
+
changed context requires restarting the list. Deleted cursor anchor rows do not
|
|
1018
|
+
break paging. Offset `page`/`perPage` and unknown keys are rejected.
|
|
1019
|
+
|
|
1020
|
+
`event seating guests count` rejects paging/sort keys. It returns the matching
|
|
1021
|
+
count, seating-tab counts (ignore their own seating filter), tier counts (ignore
|
|
1022
|
+
their own tier filter), dietary choices (ignore their own dietary filter), and
|
|
1023
|
+
permission flags. Facets expose at most 100 choices with truncation flags; tier
|
|
1024
|
+
counts stream compact registration aggregates in bounded reads, without a hard
|
|
1025
|
+
registration-count ceiling. Rail page rows are never loaded event-wide.
|