Manage Outreach
Start and steer outreach plans and objectives — the consolidated outreach writer.
Objective reads (setup, audience, forecast, sourcing plan and runs) are on the read-only inspect_objective tool.
Actions:
- objective_pilot_refresh: refresh an expired pilot's undecided assessment. id = objective UUID; no payload. Does not restart work.
- objective_pilot_decide: record continue, change, wait, or stop. id = objective UUID; payload = {action_id, expected_version, expected_planning_version, decision, reason, next_check_at, idempotency_key}. Use null next_check_at only for stop; other choices require a future check within 90 days. Continue/change create owned preparation; wait schedules reassessment. No choice extends the pilot, applies setup, launches, or sends.
- objective_pilot_prepared: link a newly saved setup review to preparation. id = objective UUID; payload = {action_id, expected_version, setup_action_id, idempotency_key}. This completes preparation without applying setup.
- start: enroll a person in outreach. id = PERSON UUID (scalar starts one plan; an id ARRAY routes to the native bulk-start endpoint). payload optional: {max_touches?, allowed_channels?} — applied to every started plan. Enrollment always schedules the first action immediately; a start_immediately key is ignored (forced true). For researched/discovered prospects, call manage_person action=save_discovered first with a strong current-company binding and start only when its response has company_bound=true and the quality gates passed. Never use start to bypass an unresolved or name-only company save.
- plan_override: record a user override on a plan (id = plan UUID; payload = the override fields).
- channels: legacy plan-local policy action. It returns 409 objective_policy_owned; update the objective policy or use plan_override for one touch.
- trigger_touch: queue the plan's next touch now (id = plan UUID; no payload). Refused with a structured 409 when the plan's pending approved email cannot send now: email_daily_cap_reached (the conversation's owning mailbox — or, for a first email, the sender pool — is at its daily cap; the error carries the capacity numbers, when capacity resets, and when the plan retries automatically) or thread_mailbox_unavailable (the owning mailbox is disconnected, resting, or excluded — reconnect it; another mailbox is never substituted into an existing conversation). A success here means the touch is genuinely queued, not that it may silently re-defer.
- pause / resume: pause or resume a plan (id = plan UUID). Resume re-arms the cold sequence: approved cold messages release and the next step queues. A plan that a human reply paused refuses with 409 plan_paused_by_reply, carrying the latest reply, the pending cold messages, and the last step fired. Never resume to release a reply: replies send without the plan. Pass payload {continue_sequence: true} only when the operator decided to keep pitching this person; otherwise answer with a reply draft or stop the plan. A successful resume reports what it released.
- update: legacy plan-local policy action. It returns 409 objective_policy_owned; update the objective policy instead.
- objective_create: create one draft objective (no id; payload carries name, goal_statement, target, cohort, and policy_envelope). It does not activate or send.
- objective_setup_propose: persist SetupProposalRequest for review; no configuration writes.
- objective_agent_control: start an objective Delegation (prepare_business_plan, pursue_meetings or pursue_content) or control one. Only an Owner of the objective starts, steers, resumes or takes over; any Owner or a workspace admin pauses or cancels. Read objective_agent_options first. Controls require only command, request_id, delegation_id and expected_revision (from objective_agent_runs); omit planning versions and guidance except on steer. A Delegation whose grantor lost access is paused: take it over (command takeover, optional sender_config_id) or cancel it. Kinds run in parallel; plan work waits for other work's paid steps. pursue_meetings (users call it Outreach) moves replies or meetings and needs sender_config_id on start: a mailbox the grantor sends from, a shared one, or one held by Sender delegation. It prepares replies and reviewed opportunity emails, follows exact reviews, delivery, replies and due work in the current period (fixed, or this week or month of an ongoing objective), and starts the next period's Job by itself. stop_period ends this period's Outreach work on an ongoing objective and keeps the grant. Content ownership requires a current fixed period and accepted human_handoff approach without dependencies. On start, content_brief names approach_key, author_user_id, follow_up_owner_user_id and publication_notes; no mailbox. It prepares one post, waits for exact publication approval, monitors captured responses, and prepares only human-assigned response work. A brief is immutable. Publication and engagement are not business outcomes. Steering permits another preparation attempt. Existing reviews remain reviewable. Reuse an exact request_id after uncertainty. Never applies a plan or sends contact.
- objective_setup_prepare: explicitly invoke the Architect and persist its setup review. mode=business_plan revises only business intent/plan. Active objectives accept plan and parent changes while preserving independent execution authority. A business plan may also propose up to five contributing_objectives (name, goal_statement, metric people_with_replies|people_with_meetings, goal, cadence weekly|monthly or end_at, reason, expected_contribution, timing, overlap_exclusions, suggested_kind outreach|content); approval creates them as drafts owned by this objective's Owners, linked as contributing. Nothing launches or starts agent work. objective_create accepts owner_user_ids (default: you) and business.original_wording, scope, constraints, measurement_prerequisite and an optional plan or parent contribution. An unresolved measure creates owned follow-up work and never fake progress.
- objective_business_complete: record an owned task completion with action_id, expected_version and completion_note. The task owner, any objective Owner or an admin can attest completion; this never asserts the business result.
- objective_opportunity_send: send one exact, human-approved email for a reviewed reactivation/deal-recovery work Action. Inspect objective_business first; payload carries action_id, expected_version, sender_config_id, to_email, subject and body. Repeat the identical payload only to recover or safely retry its canonical delivery. Never turn an unclear receipt into a new send.
- objective_setup_apply: apply the saved action_id and expected_version. Saves only configuration atomically, never sourcing, enrollment, launch, or sending.
- objective_setup_reject: reject the saved action_id and expected_version without applying it.
- objective_update: edit a native draft/paused objective (id = objective UUID; payload requires expected_planning_version and carries any of name, goal_statement, target, cohort, or policy_envelope). Required rates are the one active-objective edit: {<metric>: {min_rate: 0-1, min_rate_basis: '<arithmetic>'}} for contacted_to_reply | contacted_to_meeting | meeting_to_won, merged into target.required_rates (a metric set to null clears it). It is the viability loop's first seed basis (VRU-846); never an industry average. Without applicable observed conversion, a reviewed calibration pilot uses target.pilot = {max_contacts: 1-100, duration_days: 1-28, assumed_conversion_rate: >0 and <=1, assumption_basis, measurement_plan}. Preserve the saved target fields. A pilot also needs an executable sequence saved through objective_pin; target.pilot.claim_id is unsupported. With business_hours_only in policy_envelope.forbidden_moves, optional policy_envelope.sending_hours = {start_hour: 6, end_hour: 22} sets a same-day window in the workspace timezone, including daylight saving. Hours must be integers (start 0-23, end 1-24), with start < end. Opening is inclusive and closing exclusive; omitted hours keep 09:00-18:00.
- objective_research_brief: generate commercial research questions for review (id = objective UUID; payload = {expected_planning_version, guidance?}). Uses a budgeted model; never saves, researches prospects, assigns, or sends. Review/edit the proposal before objective_update saves cohort.research_brief; preserve other cohort fields and the proposal's expected_planning_version. Your own agent can instead draft the same typed brief using objective and company context, with brief_context_fingerprint from inspect_objective action=objective_audience_build.
- objective_account: select or deselect an existing CRM account (id = objective UUID; payload = {company_id, selected: bool}). Accounts may have zero contacts. Selection is planning context, not enrollment or a sourcing restriction. Selected accounts appear in inspect_objective action=objective_audience. Never enroll every employee automatically.
- objective_members: assign or unassign people (id = objective UUID; payload = {action: assign|unassign, person_ids: [...]}, max 100).
- objective_pin: set one claim-backed hard constraint (id = objective UUID; payload = {pin_key, pin_value, statement?}) on a draft/paused objective. Creates and links the backing Claim automatically. For an outreach sequence, use pin_key="sequence", pin_value=[{step: 1, delay_days: 0, channels: [{channel: "linkedin_connection"}], instructions: "..."}, ...], and statement=the sequence rationale. Steps need unique positive step numbers and non-empty channels; delay_days and instructions are optional. A standalone Claim, verdict, or tone/drafting brief does not save a sequence. For a bare connection request, separately pin connection_request_no_note=true. Repeating a live pin_key returns the existing pin; it does not replace it.
- objective_unpin: retire one pin and its Claim (id = objective UUID; payload = {pin_key, reason}).
- objective_retire: end an objective for good (id = objective UUID; no payload); its runnable plans stop under objective_retired. Works the same for every objective.
- objective_clone: a new draft with the objective's inputs and live pins (id = objective UUID; optional payload {name}); members are not copied. Works the same for every objective.
- objective_pause: pause an objective (id = objective UUID; no payload). Its runnable plans park under the objective's own stop reason; relaunch through forecast and approval restores exactly those. Works the same for every objective.
- objective_observe: propose a measurement review for an observe objective (id = objective UUID; payload = source_bindings and client_request_id). No sender, research, enrollment, or outreach.
- objective_observe_approve: approve only after the user reviews and confirms the observation definition and sources (payload = action_id and expected_version). This freezes measurement, not execution permissions.
- objective_observe_policy: set or revoke the caller's STANDING approval of observe-only runs (id = objective UUID; payload = expected_planning_version and definition {metric {id, definition_version}, source_bindings (sorted, distinct), cohort_rule {subject, admission, eligibility_rule, entry_event?}}, or definition null to revoke). Only after the user confirms it: the caller (an owner or operator) becomes the named approver, and the observation autopilot then activates matching observe-only runs as that person's approval with no further review. A run outside the definition, or an approver who loses the role, waits for a person. It never grants execution. An objective_update carries this section unchanged.
- objective_cohort_revise: record an explicit, evidenced admission or correction on an approved run (payload = run_action_id and revision). Corrections preserve the previous report and need the user's approval.
- objective_launch: propose the review-class launch Action over the exact current fingerprint (id = objective UUID; no payload). It does not activate or send. Requires a ready inspect_objective action=objective_forecast. For pilot_sequence_claim_required, use objective_pin with pin_key="sequence", then refresh the forecast. Separate claims/verdicts cannot clear this blocker; changing the target metric or pilot duration cannot save the missing sequence. A hand-off-only objective (every plan approach is a human hand-off, no outreach metric) has launch_mode "handoff": its launch names its approaches, needs no target or people, sends nothing, and enrolls no one. Launch it before delegating a content Job.
- objective_approve: accept that launch Action after a fresh fingerprint check (id = objective UUID; payload = {action_id}). A changed forecast abandons the action and returns 409. Approval activates the objective, enrolls eligible members, and schedules execution under its policy. and enqueues the executor, which enrolls the forecast's planned members through the research and supply gates; sends then follow the normal draft-and-approve path.
- objective_source: propose a review Action for that exact sourcing plan (id = objective UUID; no payload needed: the calibration pilot, target.pilot, sizes a pilot run, and {pilot_size} must equal its max_contacts if sent). Returns existing live sourcing work at once, or status=satisfied when the pilot audience is already full. Otherwise it returns status=preparing with job_id: a background job checks CRM imports (best company matches first) and attaches fits, then sizes additional sourcing. Poll inspect_objective objective_audience_build: preparing_job shows progress, and latest_run shows the proposal when it is saved. If existing members then cover the audience, the job completes with result.status=satisfied and no Action. No new discovery starts until approval. A pilot is one bounded sourcing run, not an outreach launch or a forecast of replies. Existing launch gates apply.
- objective_source_approve: approve the sourcing Action after a fresh fingerprint check (id = objective UUID; payload = {action_id}). Approval enqueues one bounded cohort build. Only people who pass the existing objective fit checks join the objective.
- objective_sourcing_stop: stop further work with action_id and expected_version; saved results remain.
- objective_sourcing_resume: explicitly approve recovery of saved work (id = objective UUID; payload = {job_id, reviewed_inputs}). reviewed_inputs come from inspect_objective action=objective_sourcing_recovery. Changed inputs renew fit on saved candidates without discovery or enrichment; the rubric model may run. Unknown provider outcomes require reconciliation and cannot be overridden with resume.
- objective_sourcing_reconcile: OPERATOR ONLY. Settle a saved run whose recovery read says reconcilable=true (id = objective UUID; payload = {job_id, note}; note = what you checked with the provider, 3-500 characters). Unconfirmed outcomes settle at their reserved amount; the interrupted step is cut and re-runs only after objective_sourcing_resume. A run the user stopped never blocks a new objective_source proposal.
- objective_apply_template: render a touch's template over the objective's UNAUTHORED (needs_draft) rows for that touch — deterministic substitution of the operator's own template prose ({{first_name}}, {{last_name}}, {{company}}, {{title}}), never an LLM (id = objective UUID; payload = {step, cursor?}). BATCHED: processes up to 500 rows per call and returns {rendered, skipped (first 50), skipped_total, skipped_counts, total_unauthored, has_more, next_cursor}. While has_more is true you MUST call again with payload {step, cursor: <next_cursor>} — repeating without the cursor restarts from the beginning and skipped rows block the tail. Unrenderable rows are skipped with the reason and stay unauthored (missing variable value, channel outside the step's chain, email/InMail template without a subject). Set the template first via objective_update: a policy_envelope sequence step gains {"template": {"subject"?, "body"}}.
- objective_touch_asset: attach or remove one reusable asset for one touch channel (id = objective UUID; payload = {step, channel, asset_id} referencing a library entry uploaded through the objective assets route; or {step, channel, remove: true}). No inline upload or Drive-source fields are accepted. Assets are supported on email, linkedin_message, and linkedin_inmail only.
- objective_delegate: let one member manage every current and future objective for a sender (no id; payload = {sender_user_id, delegate_user_id}). The sender, owner, or operator can grant access.
- objective_revoke_delegation: revoke that grant immediately (id = delegation UUID). The sender, owner, or operator can revoke access.
- recommendation_decision: accept, reject, or defer one diagnosis or viability recommendation Action (id = the Action UUID from fetch type='actions_inbox'; payload = {decision: accept|reject|defer, reason, evidence_ids? (up to 20 evidence UUIDs from this workspace, such as the Action's justifications from fetch type='action'), expected_version? (the version you read), idempotency_key? (reuse it on a retry)}). accept records the user's approval (state approved) under the approval policy; reject ends it (rejected); defer keeps a proposed recommendation open and changes no state. Each decision appends one recommendation_decision event with the reason and evidence. Record only what the user decided: an agent never accepts on its own. A recommendation that names an objective needs write access to it; a workspace-wide one needs an owner or operator. Attention portfolio rows are refused here (decide them with attention_decision); a decided or expired recommendation returns 409 decision_refused.
- first_impact_setup_accept: accept a prepared First Impact measurement (id = the first_impact_setup Action UUID from fetch type='actions_inbox'; payload = {expected_version: the Action version you read}). Only after the user confirms it: one transaction creates the offer's observe objective with the prepared target and records the caller (an owner or operator) as the standing approver of its observe-only runs, so VM7, CR6 and the time to verified impact can be read. Nothing is sent and no objective is activated. A proposal whose cohort moved returns 409; reload it from the inbox.
- experiment_design: design a controlled experiment for one hypothesis (id = a live recommendation Action UUID whose intervention target names an objective, from fetch type='actions_inbox'; payload optional: {primary_metric?, guardrail_metrics?, minimum_detectable_effect?, mde_kind? (absolute | relative), alpha? (0.001-0.2), power? (0.5-0.99), horizon_days? (14-180), stop_rule? (fixed_horizon | fixed_horizon_harm_stop)}). What the user declares wins; the design agent drafts the rest; code computes the sample per arm from the objective's baseline and refuses an undeclared effect, alpha or power, a metric the registry does not know or cannot read per arm, and a design the objective's volume cannot power inside the horizon (the refusal names the shortfall and a learning task). It writes an experiment Claim, its Evidence and a review-class start Action; it starts nothing. One live experiment per objective.
- experiment_start: start a proposed experiment after the user reviewed and approved its design (id = the experiment Action UUID; payload = {expected_version}). The start approves it and assigns every live member of the objective to an arm (a fixed function of the member), in one transaction; it re-checks the volume there and refuses (409 insufficient_volume, each arm's shortfall) when the members the objective still sends to can no longer reach the sample. Record only what the user decided: an agent never starts an experiment on its own.
- experiment_stop: record the user's decision to decline a proposed experiment or stop a started one (id = the experiment Action UUID; payload = {expected_version, reason?}). A proposal becomes rejected, a started experiment abandoned; its evaluation reads stopped, never a verdict. After its one analysis the same call closes the experiment instead (executed): the analysis stays its reading.
- attention_decision: record the user's decision on one attention portfolio row (id = the actions_inbox row id attention:<uuid> as it is, or a recommendation id from fetch type='attention_portfolio'; payload = {decision: dismissed | not_yet | confirmed | undone | opened | prepared, idempotency_key (8-200 characters; reuse it on a retry), detail? {platform?: linkedin | x, reply_ref?, minutes?}}). dismissed ends the row (its Action is rejected); not_yet parks it; confirmed reports that the user did the work (a manual post or task; a review queue row completes in its own queue instead); undone reverses the last decision; opened and prepared record progress. Each decision is one append-only event; nothing sends or posts. Needs an interactive (OAuth) session: a personal access token gets 403. Record only what the user decided.
Bulk: start arrays hit the native bulk endpoint; pause/resume/ plan_override/channels/trigger_touch arrays loop with per-item status — partial failure is reported per item, never silent (max 100). Payloads are validated by the underlying route's own schema.
Request
- Base URL: https://api.vruum.ai
- URL: https://api.vruum.ai/api/facade/manage_outreach
- Auth: HTTPBearer (a scheme the document does not define)
Headers
Optional. Send a unique key (a UUID) to make this call safe to retry: an exact retry of a SUCCESSFUL call replays the original response instead of re-running the action, so a dropped connection cannot double-send or double-spend. A deterministic 4xx refusal releases the key — the same key may retry once the refusing condition clears; only an ambiguous failure (>=500 or timeout) pins the key to a 409 conflict. Reusing a key with a DIFFERENT request body is rejected with 409. Keys are scoped to your company and expire after 24 hours.
Request body
Response
Successful Response
Changes
No recorded changes to this operation across all 1 revision of this API.