Guide
Guest Handshake Flow
How a Lead sends a commitment to someone without an account, and how that Guest accepts or refuses via a public link.
01 · Overview
Overview
Not every counterparty has a Shrubbery login. When the Lead sends a Shrubbery to an email that does not match a user, the system mints a Guest Token, emails a public link, and waits. The Guest opens /handshake/[token], sees the task and deadline in neutral copy, and accepts or refuses — no account required. Consent still gates CFS: nothing counts until the Guest accepts.
02 · Actors
Actors
- Lead — creates and sends the Shrubbery; remains
assigner. Initiates by addressing an external email. - Guest — recipient with no session; credential is the signed token only.
- Knight — not involved until the Guest later claims the row by signing up with the same email (then they become the assignee user).
03 · Flow
Flow
- Lead sends a Draft (or pending row) to an email via the send-to-assignee path. If no user matches the email, the system takes the guest path instead of failing.
- System sets
Pending_Handshake, leavesassigneenull, storesguest_assignee_email, appends a send audit event, mints a Guest Token (7-day TTL), and fires the guest invite email (Inngest). - Guest opens
/handshake/{token}. Invalid, expired, or already-used tokens show a status card — not the accept UI. - Guest accepts —
Pending_Handshake→Active; token marked used; auditacceptedwithactor_kind = guest. Or Guest refuses — →Refused; same token and actor rules. - Optional later: Guest creates an account with the same email. A signup trigger claims matching rows (
assigneeset,guest_assignee_emailcleared) and attaches past guest audit events to the new user id.
04 · Edge cases
Edge cases
- Known user email — if the address resolves to an existing user, the in-app Handshake path runs instead (assignee set; no guest token).
- Double submit — status and token
used_atupdates are guarded so a second click cannot re-resolve the row. - Already resolved — if the shrubbery is no longer
Pending_Handshake, the page treats the link as already responded. - No counter-offer, no sentiment — Guests cannot propose alternative terms or record acceptance sentiment on this surface.
- Source fields on accept — guest accept clears
raw_textandsource_excerpton the row (privacy for external counterparties). - Resend — an expired link cannot be revived by the Guest; the Lead must send again (new token).
05 · Related
Related concepts
Last updated: 24 July 2026