ShrubberyDocs
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

  1. 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.
  2. System sets Pending_Handshake, leaves assignee null, stores guest_assignee_email, appends a send audit event, mints a Guest Token (7-day TTL), and fires the guest invite email (Inngest).
  3. Guest opens /handshake/{token}. Invalid, expired, or already-used tokens show a status card — not the accept UI.
  4. Guest accepts — Pending_HandshakeActive; token marked used; audit accepted with actor_kind = guest. Or Guest refuses — → Refused; same token and actor rules.
  5. Optional later: Guest creates an account with the same email. A signup trigger claims matching rows (assignee set, guest_assignee_email cleared) 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_at updates 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_text and source_excerpt on 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

Last updated: 24 July 2026