Send Replies (queued — returns job ids, does not wait)
li_send_replyAnswer ONE person with profileUrl + message, or up to 25 in a single call with replies. Either way it returns IMMEDIATELY with a job id per recipient instead of holding the call open. This is the tool for answering replies; li_send_message stays the blocking one for when you genuinely need the outcome inline.
Why it returns early: LinkedIn work runs one browser session at a time, so a perfectly healthy send can sit queued for many minutes before it even starts — that is normal, not a fault. A client that gives up waiting reports an error for a message that is about to be delivered, the caller sends again, and the person receives it twice.
A returned job id is NOT a failure and NOT a timeout: the message is queued and the worker will send it. NEVER call this again for the same recipient to "make sure".
⚠️ DO NOT VERIFY BY DEFAULT. Queued means it is going out; polling spends time on news that was never in doubt. Say it is queued and finish. Verify only when somebody ASKS whether it landed, or when something later suggests it did not: the surest sign is a person still in li_replies_to_answer an hour on, because a delivered reply leaves that queue by itself. Then it is ONE call — li_job_result with jobIds set to the ids this returned, plus waitMs if you genuinely need to wait inside that call. One call covers the whole batch however many accounts it spans; stillRunning: 0 is how you know they have all landed.
The array form queues ONE ORDINARY SEND PER PERSON rather than a single bulk run, which is why it cannot time out and why each reply is recoverable on its own. Each reply is delivered in its own browser session addressed to one profile URL, so no part of a batch can reach the wrong person. Every recipient is checked BEFORE anything is queued: if one is refused, nothing at all is queued, so you can drop that row and call again safely.
✅ A BATCH MAY SPAN ACCOUNTS. Leave the account unnamed and every reply is sent from the account whose conversation it actually is — so the queue from li_replies_to_answer can be answered exactly as it came back, in one call, however many accounts it covers. Each queued row reports the accountId that sent it, and the reply carries an accounts summary.
Naming an account with sessionLabel still pins the WHOLE call to it, and any row whose conversation belongs elsewhere is then refused — use that when you mean it. Either way a row that cannot be placed (untracked, or on two of your own accounts) is refused BY NAME and nothing at all is queued, so you can drop it and call again safely. Answering someone from an account they never heard from is worse than answering them late.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| message | No | Exact message text to send (supplied, never generated at send time) | |
| replies | No | Several replies in ONE call, max 25. Each becomes its own queued send with its own job id. Two rows naming the same person are REFUSED — that is two answers to one conversation, and it would arrive as two messages seconds apart. | |
| sessionId | No | Sending account id (or use sessionLabel) | |
| profileUrl | No | One recipient, by profile URL (https://www.linkedin.com/in/<slug>); the fsd-id URL from li_list_conversations also resolves. Use `replies` instead for several. | |
| confirmSend | Yes | Must be true to queue the send; false returns a dry-run preview | |
| bookingClose | No | These messages CLOSE the booking thread for people li_booking_due listed with nextAction "close". Same refusals, plus: only after the nudges are used. On queueing, the booking becomes closed, the verdict no_outcome and any remaining steps are cancelled. Applies to every reply in the call; never together with bookingNudge. | |
| bookingNudge | No | These messages are BOOKING NUDGES for people li_booking_due listed with nextAction "nudge". Each is REFUSED unless that person agreed (li_track_booking), their check date has arrived, they have nudges left, no nudge went out in the last 20 hours, they have not written since the link/agreement, they are not on the do-not-contact list and the account is not paused. The booking state moves in the same step as the queueing (nudge count +1, next check = send time + the account's close-after days), and outside the account's office hours the send is queued for when they open. With no account named, each person is sent from the account their agreed booking is on (li_booking_due's accountId), even when they are in campaigns on more than one of your accounts. Applies to every reply in the call. | |
| sessionLabel | No | Sending account label | |
| calendarChecked | No | REQUIRED (true) for anyone with our booking link out (bookingPending / bookingCheckRequired in li_replies_to_answer) or a call already booked: it says you searched the calendar for THIS person just before this call. This server reads no calendar, so a booking made minutes ago is invisible here until li_mark_booked records it. If they booked: send NOTHING (no "grab a slot", no "see you then") and record it with li_mark_booked. Without it such a row is refused and nothing is queued. Implied by bookingNudge / bookingClose (their routine sweeps the calendar first), but search again right before those too. Applies to every reply in the call. |