Match booking attendees you read from the user's calendar (Google Calendar, Calendly, Cal.com, or booking-confirmation emails) to prospects — deterministic, read-only, no LLM. Pass what the event shows: `name`, `email` if any, the calendar's `eventId`, `startsAt`, and ALSO `createdAt` (when the event was created/booked) and the raw `description` whenever the event has them — the timing and time-zone signals are read from those two.
The pool: people with a booking in play (link sent, agreed, cancelled), everyone in an active campaign, anyone messaged in the last 60 days, and anyone our booking link went to in the last 30 days — only on accounts you can see. Names are normalised (case, accents, punctuation, titles like Dr/PhD, middle names and initials, "Last, First", a small nickname table: Bob=Robert, Bill=William…); a prospect's name is cleaned the same way as the name prefilled into the booking link.
Rules, in order: (1) exact full name among people sent the booking link in the last 30 days → match (`full_name_recent_link`); two such namesakes → the one who booked within 15 minutes of their own LinkedIn message, else ambiguous. (2) email's company domain equals the prospect's `companyDomain` AND first or last name matches → strong; full name matches exactly → strong; first and last each within one typo → WEAK; exactly one strong → match. Personal email domains (gmail, outlook, hotmail, yahoo, icloud, proton…) never count as a company domain. (3) Scoring (`score`): first_name +2 (or first_name_typo +1), last_name_in_email +3 (prospect's surname in the email's local part), recent_link +2, booked_after_message +3 (event created 0–15 min after their inbound LinkedIn message), timezone_fits +1 (a zone in the description fits theirs). A conflicting surname excludes a prospect. Match only with a first-name signal, a surname (on the booking or in the email), score ≥ 7 and a lead ≥ 3.
Per candidate: `match` (safe to li_mark_booked), `ambiguous` (NEVER mark it — list the candidates in `others`, best first, for a human to pick), or `none`. Each carries the rule, the chosen prospect's `signals`/`score`, a one-sentence `reason`, and the other candidates.