---
title: "Search agent plans"
method: POST
path: "/api/agent-plans/search"
tags: ["agent_plans"]
---

# Search agent plans

`POST /api/agent-plans/search`

## Request

- Base URL: `https://beta.getsales.io/unibox/v1`
- URL: `https://beta.getsales.io/unibox/v1/api/agent-plans/search`
- Auth: HTTP bearer and API key in header `Team-SID`

Cross-thread view of what the AI SDR intends to do: filter state eq proposed for everything waiting on approval, active for what is running, or conversation_sid for one thread's plan history; page_size 0 returns counts alone. A skipped or failed step says why in its cause; a pending step whose message cannot leave yet says what it waits for. One lead is better read on its thread (get_conversation carries the active plan); why a plan looks the way it does is a run fact: search_agent_runs.

Contract:
- MCP tool `search_agent_plans`, registry package `mcp.unibox/agent_plans`, mount `unibox.agent`.
- Operation `search`, response envelope `search`.
- Flags: read only.

## Request body

- SearchAgentPlansRequest: Request body of `search_agent_plans`.
  - `filter` object: Field filters, ANDed: {field: {eq | ne | in | nin | gte | lte | gt | lt | is_null}}; q inside it is free text. Omit for every row.
    - `sid` object: Plan sid(s), ub_pl_ prefix. Also the point-lookup path, since there is no get tool.
      - `eq` string: Agent plan sid (ub_pl_…).
      - `in` string[]
    - `agent_sid` object: The AI SDR the plans were made by (ub_ag_…, block 10, 2026-10-30).
      - `eq` string
      - `in` string[]
      - `is_null` boolean
    - `conversation_sid` object: Thread(s), ub_cv_ prefix: one thread's whole plan history, newest first.
      - `eq` string
      - `in` string[]
    - `state` object: { eq: "proposed" } is the approval queue; { eq: "active" } is what the agent is running now.
      - `eq` 'proposed' | 'active' | 'completed' | 'superseded' | 'canceled'
      - `in` string[]
    - `behavior` object: Per-behavior review; { is_null: true } = plans a custom scenario drove.
      - `eq` 'answer_interested' | 'meeting_request_book' | 'answer_info_request' | 'referral_capture' | 'not_now_defer' | 'ooo_delay_ping' | 'auto_reply_skip' | 'wrong_person_referral' | 'reschedule_meeting' | 'cancel_meeting' | 'return_to_orchestration' | 'close_not_interested' | 'handle_objection' | 'stoplist_do_not_contact' | 'vendor_pitch_skip' | 'acknowledge_low_signal' | 'followup_no_reply' | 'signal_followup' | 'followup_after_person'
      - `in` string[]
      - `is_null` boolean
    - `created_at` object: ISO 8601 range; combine bounds in one object.
      - `gte` string
      - `lte` string
      - `gt` string
      - `lt` string
    - `deleted_at` object: Default scope is { is_null: true }; nothing writes deleted_at today.
      - `is_null` boolean
  - `sort` object: One column and its direction; omit for the default order.
    - `field` 'created_at' | 'updated_at', required: The column to order by.
    - `direction` 'asc' | 'desc': asc or desc; default desc.
  - `page_size` integer: 0..500, default 50; 0 = counts only, 1 = the first row.
  - `cursor` string, nullable: pagination.next_cursor of the previous page.

## Response `200`

`search` success envelope.

- SearchAgentPlansResponse
  - `success` true, required
  - `operation` 'search', required
  - `items` object[], required
    - `item` object, required
      - `sid` string, required: Agent plan sid (ub_pl_…).
      - `team_sid` string, required: Owning team (ts_tm_…, external - gs.service.id).
      - `agent_sid` string, nullable, required: The AI SDR the plan was made by (ub_ag_…, block 10, 2026-10-30); null on a row from before.
      - `conversation_sid` string, required: The thread this plan belongs to (ub_cv_…). At most one plan per thread is live (proposed or active).
      - `state` 'proposed' | 'active' | 'completed' | 'superseded' | 'canceled', required: proposed = waiting for one-click approval, no step runs; active = executing or waiting on a timer; completed / superseded / canceled are terminal.
      - `goal` string, required: Human-readable intent of the plan, 1..255 chars, e.g. "Re-engage after OOO on Aug 5".
      - `steps` object[], required: Ordered steps, 1..8. They run in order; a failed one stops the plan.
        - `id` integer, required: Plan-local step id, stable across edits. Assigned by the server, counting from 1.
        - `kind` 'send_message' | 'wait_until' | 'wait_for_reply' | 'create_task' | 'move_stage' | 'find_people' | 'react' | 'like_post' | 'reach_objective' | 'resume_orchestration' | 'stoplist_person' | 'use_playbook', required: What the step does.
        - `params` union, required: The step payload, frozen at planning time. Per kind: send_message { intent, text, subject }, the message's direction and, since 2026-10-05, its text exactly as it goes out (an email's subject when it needs one): approving the plan approves the text and the step sends it as written, while a step with no text has its message written when it comes due; wait_until { until, reason }; wait_for_reply { timeout_days, until }, until stamped by the server when the wait begins; a message step also carries the playbook's three product parameters since 2026-10-12, purpose (reply, follow_up, nudge, closing, holding), link (none, when_natural, always: the text carries the effective booking link, the campaign's or the table's AI SDR setup over the team's) and length (short, normal, full_picture), and the booking link is a send_message with link always, the booking-link kind retired and its stored plans rewritten; create_task { title, due_in_days }; move_stage { target_stage_key }, one of soft_interest, strong_interest, meeting_requested, meeting_scheduled, closed_won, wrong_decision_maker, not_now, unresponsive, not_interested, disqualified; find_people { prompt, per_company_limit, company_sid, company_name, estimated_credits }, where the last three are stamped by the server (the company the search runs on and the credits it is expected to spend; a step the CRM could not price is refused, never stored); react { emoji, message_sid }, the sender profile's reaction on the LinkedIn message of theirs the step names (their newest when the writer named none): the agent plans it on the lead's first reply and when the stage move takes them forward in the pipeline, and on a later reply only when the lead made an effort in it (the heart on a warm reply, a thumbs-up on any other; never on an automatic reply or a do-not-contact); like_post {}, the sender profile's Like on the lead's own latest LinkedIn post, picked when the step runs (no plain repost, at most 90 days old, never one the account liked already); reach_objective {}, the mark of the live dialog's objective, run once the plan is approved; resume_orchestration { reason }; stoplist_person {}; use_playbook { from, outcome }, the stage the Playbook starts the dialog at (engage, discover, bridge, offer, after_yes; 2026-10-19) and, once the step is done, how the Playbook's lead ended (said_yes, booked, signed_up, registered, stalled_out, handed, left, cap).
          - object
          - unknown[]
            - unknown
        - `state` 'pending' | 'awaiting_approval' | 'waiting' | 'done' | 'skipped' | 'failed', required: Where this step stands.
        - `executed_at` string, nullable, required: ISO 8601 UTC, stamped when the step reached a terminal state; null before that.
        - `cause` string, nullable, required: Why the step was skipped or failed, or what a pending step waits for, as a reason token (snake_case, 64 chars at most, never a sentence: say it in your own words); null on a step in any other state, and on one that ended before 2026-10-02. A pending step carries one only while the turn that writes its message is held because the message could not leave now: sender_unavailable (no sender profile carries the account, or the account is signed out, disconnected, stopped or paused; nothing is lost, the step runs by itself once the account can send, so point the user at the sender profile; after seven days of waiting the step fails under the same token), subscription_required (the workspace is on the billing hold). A skipped step: not_linked (the thread is linked to no person: move_stage, find_people, resume_orchestration); already_beyond, stage_unknown, person_gone, playtables_refused, target_stage_key_unknown (move_stage: the person already stands further along the pipeline than the stage and the agent never moves a person back, the team has no stage under the key, the person is gone from the CRM, the CRM refused, the step names no outcome stage); no_company, no_company_anchor, person_not_found, prompt_missing (find_people); sender_unavailable, message_not_reactable, linkedin_refused, gtm_unavailable, gtm_bridge_unavailable, gtm_busy, step_job_failed (react: the account cannot act, the message takes no reaction, LinkedIn said no, gtm-api did not answer or stayed busy, the job that runs the reaction died; a reaction never fails, whatever keeps it from landing is a skip and the plan goes on); no_recent_post, already_liked, not_on_linkedin, no_linkedin_profile, post_not_likeable, gtm_scope_missing, gtm_access_lost, and, as for a reaction, sender_unavailable, linkedin_refused, gtm_unavailable, gtm_busy, step_job_failed (like_post: the lead shows the account no recent post of their own (a plain repost or a post older than 90 days does not count), the account liked their latest post already, the thread runs on no LinkedIn account, the lead has no profile the account can open, LinkedIn takes no Like on the post, the workspace connection lacks the engagements permission or no longer shows the account; a Like never fails either, it is skipped and the plan goes on); no_booking_link (send_booking_link: the team lost its link after planning); no_dialog, objective_already_reached (reach_objective); no_step_transport (stoplist_person: the block itself was applied when the reply was read). A failed step carries the error code of the run that died on it: playtables_unreachable (the CRM did not answer through three tries), no_message_sent (the turn wrote no message), turn_timeout, budget_exceeded, runner_error; or the reason the send door refused the step's message with: stoplisted, agent_disabled_on_thread (refused for good, the turn is not tried again), send_refused (the door named no reason, usually the thread has no account on the channel), sender_unavailable or subscription_required (the step waited for its sender for seven days, or the account dropped on each of three tries). unknown stands for a cause that was not a token. The list is open: read a token you do not know as "it could not run".
      - `created_by_run_sid` string, required: The strategize run that produced this plan (ub_rn_…). Its reason, confidence and LLM log ref answer "why this plan".
      - `behavior` 'answer_interested' | 'meeting_request_book' | 'answer_info_request' | 'referral_capture' | 'not_now_defer' | 'ooo_delay_ping' | 'auto_reply_skip' | 'wrong_person_referral' | 'reschedule_meeting' | 'cancel_meeting' | 'return_to_orchestration' | 'close_not_interested' | 'handle_objection' | 'stoplist_do_not_contact' | 'vendor_pitch_skip' | 'acknowledge_low_signal' | 'followup_no_reply' | 'signal_followup' | 'followup_after_person', nullable, required: The built-in competence module that planned; null when a matched custom scenario drove the plan on its own.
      - `behavior_version` integer, nullable, required: Version of that module at planning time (the audit stamp, rendered as "answer_interested v3"); null when behavior is null.
      - `custom_scenario_sid` string, nullable, required: The custom scenario that matched (ub_sc_…), which takes precedence over the built-in behavior; null when none did. The sid survives the scenario being deleted.
      - `approved_by_user_sid` string, nullable, required: User that approved a proposed plan; null while unapproved or when the plan was born active under autopilot.
      - `created_at` string, required: ISO 8601 UTC.
      - `updated_at` string, required: ISO 8601 UTC.
      - `deleted_at` string, nullable, required: ISO 8601 UTC. Always null in practice: plans have no delete path, the terminal states cover removal.
    - `included` object, required
  - `pagination` object, required
    - `next_cursor` string, nullable, required
    - `has_more` boolean, required
    - `total_count` integer, nullable, required
  - `applied_filters` object, required
  - `includes` string[], required
  - `meta` object, required
    - `trace_id` string, required: UUID v7; same 128-bit value as the X-Trace-Id header.
    - `span_id` string, required: 16 hex chars, root span of this request.
    - `timestamp` string, required: ISO 8601 UTC (Y-m-dTH:i:sZ), response time.
    - `duration_ms` integer, required: Server-side wall clock.
    - `team_sid` string, nullable: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends.
    - `actor_type` string, nullable: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends.
  - `counts` object
    - `total_count` integer, required: Plans matching the filter (full filter scope, not the page).
    - `groups` object, required
      - `state` union, required: Plans by state; an absent key means zero. proposed is the approval-queue badge.
        - object
          - `proposed` integer
          - `active` integer
          - `completed` integer
          - `superseded` integer
          - `canceled` integer
        - unknown[]
          - unknown
      - `behavior` union, required: Plans by behavior key, plus __null for the custom-scenario-driven ones; an absent key means zero.
        - object
        - unknown[]
          - unknown

## Other responses

- `4XX`: MCP error envelope. `error.code` is one of validation_failed, nothing_to_update, not_found, relation_not_found, invalid_transition, limit_exceeded, payment_required, duplicate_rejected, conflict, delete_blocked, unauthorized, forbidden, rate_limited, not_implemented.
- `5XX`: MCP error envelope with `error.code` internal_error or service_unavailable.

---

[API](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-unibox.md) - [All operations](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-unibox/llms.txt) - [OpenAPI document](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-unibox/revisions/8f1bc317897f?raw)
