--- name: book-agreed-calls description: Make sure LinkedIn prospects who agreed to a call actually book it. Records who agreed, matches calendar bookings to prospects, notices cancelled or moved calls, and sends one nudge then one polite close through PoliteReach. Use when the user asks who agreed to a call, who booked, whether someone booked, to follow up on a booking link, or to put booking follow-up on a schedule. --- # Get agreed calls booked PoliteReach stores the booking state, matches names, works out who is due and refuses anything over the limits. It reads no calendar and writes no text: you read the user's calendar through the user's own calendar connector and write every word. ## First: what must be connected 1. **The PoliteReach connector** (`li_*` tools). Missing: use the `get-started` skill. 2. **A calendar connector in the same app**, for part B. Any of: - Google Calendar. - Microsoft 365 / Outlook calendar. - A Calendly or Cal.com connector that lists bookings. It needs its list-events and get-event (or search-events) tools, allowed to run. Check by loading them before part B. Missing: tell the user "To see who booked, connect your calendar (Google Calendar or Microsoft 365 / Outlook) in this app's connector settings, then ask again." Do part A meanwhile. Skip B and C: without the calendar you cannot rule out that someone already booked, and a nudge to them is wrong. Never guess. 3. **The booking link** on each account, for nudges: `booking.urls` in `li_list_accounts` (empty = no link). Missing: ask for it and set it with `li_set_booking_settings` on a yes. PoliteReach never connects to the calendar. You pass it only each external attendee's name, email, event id, times and description. ## A. Who agreed but has not booked (no calendar needed) 1. Start with the agreements already tracked; these need no inbox scan. `li_bookings` with `status: "agreed"` is the full waiting list; `li_booking_due` lists only those whose check date has arrived. A row that says agreed but has a `calendarEventId` / `meetingStart`: trust the meeting and flag the mismatch. 2. Read the replies waiting on the user (`li_replies_to_answer` with `includeThread: true`) and their conversations from the last 30 days (`li_list_conversations`, `li_conversation_history`). 3. List everyone who agreed to a call, with their own words about when. 4. Before tracking anyone, search the calendar for them (part B tools): someone who already booked gets `li_mark_booked`, never `li_track_booking`, and no message. For each one the user approves: `li_track_booking` with `profileUrls`, `agreed: true`, their words as `agreedDayText`, and `agreedDate` (YYYY-MM-DD, their timezone) if they named a day. 5. Anyone not yet sent the booking link: draft a short reply with the plain link for the user's approval. PoliteReach adds the recipient's name to the link by itself. ## B. Match the calendar (needs the calendar connector) No calendar tools loaded: say so with the message above and stop after part A. Never guess whether someone booked. 1. **Find bookings.** On the calendar the bookings land on, list events from 7 days ago to 30 days ahead. Keep booking-tool events (cal.com, Calendly, or the user's booking link in the organiser, description, location or title). Ignore meetings with only the user's own team. 2. **Match.** One `li_match_contacts` call with every external attendee: `name`, `email`, `eventId`, `startsAt`, `createdAt`, and the full `description`. - `match`: `li_mark_booked` with `calendarEventId` and `meetingStart`. A booked call is `meeting_booked`, never `won`. - `ambiguous`: mark nobody; list the candidates for the user. - `none`: ignore, unless the attendee gave only one name; then list it for the user. 3. **Cancelled or moved.** `li_bookings` from 7 days ago onward, compared with the calendar. - Gone or cancelled: `li_mark_booking_cancelled`. - New start time: `li_mark_booked` with the same event id and the new time. ## C. Nudge or close (only after part B ran) 1. `li_booking_due` (all accounts at once). Read `truncated` and `excluded`; never contact anyone excluded. 2. Per row, from the messages it carries (`thread`): - `bookingLinkSentAt` is null: the link never went out. Send it with one line, not a reminder about it. - `nudge`: one line pointing back to what they said about the call, then the plain booking link on its own line. Never propose a time, never pitch. - `close`: one line, no link, e.g. "Should I close this off, or still want a slot this week?" in the tone of the thread. - Max two short lines, no exclamation marks, no emojis, no "just checking in". 3. If the thread shows they already booked, mark booked instead. If they changed their mind, skip and tell the user. 4. Right before sending, search the calendar once more for each person (full name, then first name + company): people book minutes after they are listed. Booked → `li_mark_booked`, send nothing. 5. Show the drafts. On a yes (or under a scheduled task's send mode): `li_send_reply` with `confirmSend: true`, `calendarChecked: true` and `replies`, `bookingNudge: true` for nudges, a separate call with `bookingClose: true` for closes. A refusal names its reason: relay it, never work around it, never retry that person. ## Settings Booking links and limits are per account: `li_set_booking_settings` (`bookingUrls`, `maxNudges`, `closeAfterDays`, `firstNudgeHours`, `cancelRecheckHours`, `prefillName`). Current values are in `li_list_accounts` (`booking`). ## Put it on a schedule Follow `references/scheduled-task.md`.