Skip to content
Recued
Menu
← Back to recipes

Propose follow-up reply conversion options

by recued-core v1

Neutral detection recipe for the outbound follow-up loop. It scans one response_needs_owner data.shared watch row, fetches the reply body, extracts possible conversion facets for meeting, task, insight note, commitment, and linked CRM deal next-step updates, writes compatible proposal rows for the matching owner-invoked action recipes, records a per-reply notification marker, and sends one notification that the reply has conversion options. It does not choose an action and never creates calendar events, tasks, notes, commitments, or CRM updates. User intent is expressed later by running one or more manual action recipes.

Complete workflow

Outbound Follow-Up Loop 13 pinned recipes

End-to-end outbound follow-up workflow pack for one-person companies. It turns Email / Outbox into a repeatable loop: surface outbound threads that may need closure, draft concise follow-ups, send selected drafts through the mail-send approval boundary, and carry loop state through data.shared with links back to the source mail.

You are viewing one recipe in this workflow. Installing it opens the complete pack so its other recipes and background automations arrive together.

Neutral detection recipe for the outbound follow-up loop. It scans one response_needs_owner data.shared watch row, fetches the reply body, extracts possible conversion facets for meeting, task, insight note, commitment, and linked CRM deal next-step updates, writes compatible proposal rows for the matching owner-invoked action recipes, records a per-reply notification marker, and sends one notification that the reply has conversion options. It does not choose an action and never creates calendar events, tasks, notes, commitments, or CRM updates. User intent is expressed later by running one or more manual action recipes.

emailmailfollowupconversion
by recued-core pinned v1

Read-only outbound follow-up queue for a one-person company. It scans a sent/outbound mail folder, classifies recently sent threads that may need closure, and shows the owner what to follow up on before running the draft/send workflows.

emailmailoutboxfollowup
by recued-core pinned v1

Read-only Email/Outbox workflow. Scans a sent/outbound mail folder, classifies likely follow-up candidates, and drafts a small batch of plain-text replies without sending. Use this as the preview lane before running batch-approve-followups.

emailmailfollowupoutbox
by recued-core pinned v1

Action workflow over Email/Outbox. Scans likely silent outbound threads, drafts plain-text follow-ups, then sends the selected batch through the mail-send kernel ingredient. Because mail-send is the irreversible send-class boundary, attended runs route the send step through the existing approval gate. The send is the originating context, so after sending it seeds durable data.shared watch state (status watching_customer_response, loop_kind outbound_followup) keyed by thread — the same key the pressure producer uses, so it supersedes any followup_due row — and outbound-follow-up-response-watcher adopts that linkage to close the loop when the contact replies.

emailmailfollowupoutbox
by recued-core pinned v1
Outbound follow-up pressure producer
Background automation

Silent auto-run (hourly) producer for the outbound follow-up loop. It scans a sent/outbound mail folder, classifies outbound threads that may need closure, and writes durable data.shared watch state (status followup_due, keyed by sanitized thread id) into the outbound-follow-up-response-watcher namespace. It records watched_contact (the primary recipient) so outbound-follow-up-response-watcher can confirm a reply actually came from the contact rather than from the owner's own sent mail, and it skips threads already advanced by that watcher or by send-support-reply-from-mail. unified-work-queue and weekly-owner-review read this same state to surface follow-up discipline without registering a new enrichment topic.

emailmailoutboxfollowup
by recued-core pinned v1
Watch outbound follow-up replies
Background automation

Reactive reply-side of the outbound follow-up loop. On every inbound mail it matches the message by thread against durable data.shared watch state seeded by TWO producers — outbound-follow-up-pressure-producer (status followup_due, watched_contact = the primary recipient) and send-support-reply-from-mail (status watching_customer_response, watched_contact = the customer). It confirms the reply's sender is that watched contact — recovering the contact from the linked source mail (via the stored record_id) when a producer denormalized no address, and failing closed (skipping) when it cannot establish the counterparty — then classifies the reply, advances the shared state to responded_closed or response_needs_owner, and links the reply mail to the watched source mail. Those state transitions are what unified-work-queue and weekly-owner-review read to surface or retire the follow-up. It never sends mail or mutates an external system.

emailmailfollowupreply
by recued-core pinned v1
Show all 13 recipes

Reactive outbound mirror of outbound-follow-up-response-watcher. It fires on the same data.mail created stream but processes the OWNER's outbound sends rather than inbound replies: when the owner sends on a thread already tracked in data.shared watch state, it confirms the sender is the owner (derived from the linked source mail's From via the stored record_id) and refreshes the row to watching_customer_response with a fresh last_outbound_at and sent_message_id, moving the ball back to the contact. Refresh-only: it never creates rows (the pressure producer and the send recipes own creation) and never reopens responded_closed threads, and it fails closed (skips) when it cannot confirm the owner or find a matching row. This captures owner replies sent from a normal mail client, which the send recipes cannot record.

emailmailfollowupoutbound
by recued-core pinned v1

Owner-invoked create half of the meeting-request conversion edge. It reads one awaiting_owner_approval proposal written by outbound-follow-up-meeting-request-calendar (optionally targeted by proposal_thread_key), creates the calendar event from the proposal's already-extracted schedule, links the event back to the reply and source mail, closes the follow-up loop on the source watch row, and marks the proposal scheduled. This recipe is manual-only on purpose: the calendar create (and, on some providers, the attendee invite) is an outward-facing action, and running it is the owner's explicit approval. It takes no calendar action when there is no pending proposal or the proposal lacks a concrete start and end time.

emailmailcalendarmeeting
by recued-core pinned v1

Owner-invoked create half of the task conversion edge. It reads one awaiting_owner_approval proposal written by outbound-follow-up-task-from-reply, optionally targeted by proposal_thread_key, creates a Recued task from the proposal, links the task back to the reply and source mail, closes the source follow-up watch row, and marks the proposal created. This recipe is manual-only on purpose: running it is the owner's approval to turn the proposed reply action into durable work.

emailmailtaskfollowup
by recued-core pinned v1

Owner-invoked create half of the customer-insight note conversion edge. It reads one awaiting_owner_approval proposal written by outbound-follow-up-insight-note-from-reply, optionally targeted by proposal_thread_key, creates a Recued note from the proposal, links the note back to the reply and source mail, and records the note id on the source follow-up watch row WITHOUT closing the loop — an insight note is complementary memory capture, so the thread stays open (its status is preserved) for the actual task/meeting/reply. It marks the proposal created so it is not re-proposed. This recipe is manual-only on purpose: running it is the owner's approval to turn the proposed customer signal into durable relationship memory.

emailmailnotecustomer-insight
by recued-core pinned v1

Owner-invoked create half of the commitment conversion edge. It reads one awaiting_owner_approval proposal written by outbound-follow-up-commitment-from-reply, optionally targeted by proposal_thread_key, creates a Recued commitment from the proposal, links the commitment back to the reply and source mail, and records the commitment id on the source follow-up watch row WITHOUT closing the loop. A commitment is complementary accountability capture, so the thread keeps its current status for the actual task/meeting/reply. It marks the proposal created so it is not re-proposed. This recipe is manual-only on purpose: running it is the owner's approval to turn the proposed promise into durable work.

emailmailcommitmentfollowup
by recued-core pinned v1

Owner-invoked action half of the CRM deal update conversion edge for the outbound follow-up loop. It reads one awaiting_owner_approval proposal written by outbound-follow-up-reply-conversion-options, verifies the proposal is still tied to the same source watch row and CRM connection, updates the canonical CRM deal next_step, links the deal back to the reply/source mail, and records the CRM deal linkage on the data.shared watch row without closing the loop. This recipe is manual-only on purpose: running it is the owner's approval to update the CRM deal.

emailmailfollowupcrm
by recued-core pinned v1

Housekeeping GC for the outbound follow-up loop. data.shared writes do not honor ttl (the durable store rejects it), so the per-thread conversion proposals written by the calendar/task/insight-note/commitment/CRM deal update detectors would otherwise persist forever after they are approved. This daily auto_run lists the outbound-follow-up namespace and deletes only proposal rows in a terminal status (scheduled / task_created / note_created / commitment_created / crm_deal_updated). It is safe because re-proposal is already blocked by the conversion id recorded on the source watch row for one-shot conversions (calendar_source_id / task_id / insight_note_id / commitment_id), while CRM deal-update duplicates for the same reply are blocked by the per-reply notification marker plus pending proposal rows. It never touches the watch rows themselves (they stay for re-open on a later reply), and it leaves awaiting_owner_approval proposals alone (they carry no timestamp, so a stale-unactioned proposal cannot be aged out here — record a created_at on proposals if that is wanted later).

followuphousekeepinggcclosed-loop
by recued-core pinned v1
View pack details, dependencies, and permissions →
How it works 161 steps

Inspect the data fetches, transforms, gates, and output this recipe runs.

Data fetch
shared_followups ?
existing_notification_markers ?
existing_meeting_proposals ?
existing_task_proposals ?
existing_insight_proposals ?
existing_commitment_proposals ?
existing_crm_deal_update_proposals ?
Process (154 steps)
existing_meeting_proposals_family filter
Filter by condition
existing_task_proposals_family filter
Filter by condition
existing_insight_proposals_family filter
Filter by condition
existing_commitment_proposals_family filter
Filter by condition
existing_crm_deal_update_proposals_family filter
Filter by condition
existing_notification_markers_family filter
Filter by condition
response_needed_rows filter
Filter by condition
conversion_candidate_rows filter
Filter by condition
notified_response_record_ids map
Extract from each item
rows_without_notification_marker filter
Filter by condition
candidate_rows slice
Take a subset of
pending_count count
Count items in rows without notification marker
has_candidate compare
Check if candidate rows has data
candidate_key default
Apply default
thread_id default
Apply default
thread_key default
Apply default
meeting_proposal_key default
Apply default
task_proposal_key default
Apply default
insight_proposal_key default
Apply default
commitment_proposal_key default
Apply default
crm_deal_update_proposal_key default
Apply default
response_record_id default
Apply default
response_key_colon replace
Replace text in response record id
response_key_slash replace
Replace text in response key colon
response_key_dot replace
Replace text in response key slash
response_key_at replace
Replace text in response key dot
response_key_space replace
Replace text in response key at
response_key_lt replace
Replace text in response key space
response_key_gt replace
Replace text in response key lt
response_key_plus replace
Replace text in response key gt
response_key_equals replace
Replace text in response key plus
response_key_question replace
Replace text in response key equals
notification_marker_key default
Apply default
response_mail_slug default
Apply default
response_subject default
Apply default
response_from default
Apply default
attendee_email default
Apply default
prior_mail_record_id default
Apply default
response_category default
Apply default
response_reasoning default
Apply default
owner_action default
Apply default
crm_connection default
Apply default
crm_deal_id default
Apply default
crm_deal_name default
Apply default
has_response_record compare
Check if response record id has data
has_response_mail_slug compare
Check if response mail slug has data
skip_body_read any
Check if any of the conditions are true
body_read ?
skip: step.skip_body_read equal true
response_body default
Apply default
body_present compare
Check if response body has data
now_iso date_add
Add 0 minutes to
skip_extract any
Check if any of the conditions are true
protect pii-protect
Apply pii-protect
extract_options ?
skip: step.skip_extract equal true
restore_options pii-restore
Apply pii-restore
category_is_meeting compare
Check if response category equals meeting_requested
category_is_positive compare
Check if response category equals positive_reply
category_is_objection compare
Check if response category equals objection_or_blocker
category_is_needs_owner compare
Check if response category equals needs_owner_reply
category_is_task_or_commitment any
Check if any of the conditions are true
category_is_insight any
Check if any of the conditions are true
meeting_title default
Apply default
meeting_time_zone default
Apply default
meeting_location default
Apply default
meeting_confidence default
Apply default
meeting_start_at date_parse
Parse restore options restored meeting start at iso as a date
meeting_end_at_from_reply date_parse
Parse restore options restored meeting end at iso as a date
has_meeting_start_at compare
Check if meeting start at has data
has_meeting_end_at_from_reply compare
Check if meeting end at from reply has data
fallback_meeting_end_iso date_add
Add setting default duration minutes minutes to
skip: step.has_meeting_start_at not_equal true
fallback_meeting_end_at date_parse
Parse fallback meeting end iso as a date
meeting_end_at ternary
Apply ternary
has_meeting_end_at compare
Check if meeting end at has data
has_attendee compare
Check if attendee email has data
meeting_already_converted compare
Check if candidate rows 0 value calendar source id has data
meeting_existing_for_thread filter
Filter by condition
meeting_already_proposed compare
Check if meeting existing for thread has data
meeting_below_confidence compare
Check if meeting confidence is less than setting meeting confidence threshold
skip_meeting_propose any
Check if any of the conditions are true
task_title_raw default
Apply default
task_title trim
Trim whitespace from task title raw
task_notes default
Apply default
task_due_at date_parse
Parse restore options restored task due at iso as a date
raw_task_priority default
Apply default
task_priority_is_low compare
Check if raw task priority equals low
task_priority_is_high compare
Check if raw task priority equals high
task_priority_high_or_medium ternary
Apply ternary
task_priority ternary
Apply ternary
task_confidence default
Apply default
task_missing_title compare
Check if task title is empty
task_already_created compare
Check if candidate rows 0 value task id has data
task_existing_for_thread filter
Filter by condition
task_already_proposed compare
Check if task existing for thread has data
task_below_confidence compare
Check if task confidence is less than setting task confidence threshold
skip_task_propose any
Check if any of the conditions are true
note_title_raw default
Apply default
note_title trim
Trim whitespace from note title raw
note_body_raw default
Apply default
note_body trim
Trim whitespace from note body raw
raw_insight_type default
Apply default
is_positive_signal compare
Check if raw insight type equals positive_signal
is_objection compare
Check if raw insight type equals objection
is_blocker compare
Check if raw insight type equals blocker
is_commercial_context compare
Check if raw insight type equals commercial_context
objection_or_default ternary
Apply ternary
blocker_or_objection ternary
Apply ternary
commercial_or_blocker ternary
Apply ternary
insight_type ternary
Apply ternary
note_confidence default
Apply default
note_missing_title compare
Check if note title is empty
note_missing_body compare
Check if note body is empty
note_already_created compare
Check if candidate rows 0 value insight note id has data
insight_existing_for_thread filter
Filter by condition
insight_already_proposed compare
Check if insight existing for thread has data
note_below_confidence compare
Check if note confidence is less than setting insight confidence threshold
skip_insight_propose any
Check if any of the conditions are true
commitment_statement_raw default
Apply default
commitment_statement trim
Trim whitespace from commitment statement raw
raw_commitment_direction default
Apply default
commitment_direction_is_outbound compare
Check if raw commitment direction equals outbound
commitment_direction_is_internal compare
Check if raw commitment direction equals internal
commitment_internal_or_inbound ternary
Apply ternary
commitment_direction ternary
Apply ternary
commitment_counterparty_email_raw default
Apply default
commitment_counterparty_email trim
Trim whitespace from commitment counterparty email raw
commitment_promised_for_at date_parse
Parse restore options restored commitment promised for at iso as a date
commitment_confidence default
Apply default
commitment_missing_statement compare
Check if commitment statement is empty
commitment_already_created compare
Check if candidate rows 0 value commitment id has data
commitment_existing_for_thread filter
Filter by condition
commitment_already_proposed compare
Check if commitment existing for thread has data
commitment_below_confidence compare
Check if commitment confidence is less than setting commitment confidence threshold
skip_commitment_propose any
Check if any of the conditions are true
crm_deal_next_step_raw default
Apply default
crm_deal_next_step trim
Trim whitespace from crm deal next step raw
crm_deal_update_summary default
Apply default
crm_deal_confidence default
Apply default
has_crm_connection compare
Check if crm connection has data
has_crm_deal_id compare
Check if crm deal id has data
crm_deal_next_step_missing compare
Check if crm deal next step is empty
crm_deal_update_existing_for_thread filter
Filter by condition
crm_deal_update_already_proposed compare
Check if crm deal update existing for thread has data
crm_deal_below_confidence compare
Check if crm deal confidence is less than setting crm deal update confidence threshold
skip_crm_deal_update_propose any
Check if any of the conditions are true
write_meeting_proposal ?
skip: step.skip_meeting_propose equal true
write_task_proposal ?
skip: step.skip_task_propose equal true
write_insight_proposal ?
skip: step.skip_insight_propose equal true
write_commitment_proposal ?
skip: step.skip_commitment_propose equal true
write_crm_deal_update_proposal ?
skip: step.skip_crm_deal_update_propose equal true
has_conversion_option any
Check if any of the conditions are true
write_notification_marker ?
skip: step.has_conversion_option not_equal true
skip_notify any
Check if any of the conditions are true
notify ?
skip: step.skip_notify equal true
card to_summary
Format results as a summary card
Settings 10 configurable

Configurable at install. Defaults shown — change them anytime in Recued.

channels setting = in_app
calendar id setting = primary
calendar slug setting = primary
default time zone setting = UTC
default duration minutes setting = 30
task confidence threshold setting = 0.6
insight confidence threshold setting = 0.6
meeting confidence threshold setting = 0.65
commitment confidence threshold setting = 0.65
crm deal update confidence threshold setting = 0.65

Trust & control

What installing this recipe would let it do. Recued grants these permissions at install — review them there before approving.

Where it runs

Device + server Runs on your device (browser) or your server.

Permissions it requires

Send notifications
Declared by the recipe — Recued grants these at install, where you review them before approving.

Data it touches

Warehousedata.shared

About

Tags

emailmailfollowupconversioncrmproposeoptionsclosed-loopwork-queueone-person-companystate:outbound-follow-up-response-watcherpack:outbound-follow-up-responseai_surface

Details

161 steps 10 configurable settings recipe_id: outbound-follow-up-reply-conversion-options