---
title: "Search agent runs"
method: POST
path: "/api/agent-runs/search"
tags: ["agent_runs"]
---

# Search agent runs

`POST /api/agent-runs/search`

## Request

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

The "why did the AI SDR do that" ledger and the Analyzer's log. filter.outcome lists replies by what became of them, approved_by_user_sid is_null splits the written into sent itself and approved, q finds one by its words or the lead's; include reply, trigger, lead, scenario, then (what the lead did next). conversation_sid with kind classify: a thread's classification history; plan_sid a plan's trail; state failed the failure review; state pending the queue. page_size 0 returns counts alone.

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

## Request body

- SearchAgentRunsRequest: Request body of `search_agent_runs`.
  - `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.
    - `q` string: Words of the reply or of the lead's message it answered, up to 255 chars (the Analyzer's search).
    - `sid` object: Run sid(s), ub_rn_ prefix; one run in full is get_agent_run.
      - `eq` string: Agent run sid (ub_rn_…).
      - `in` string[]
    - `agent_sid` object: The AI SDR the turns ran as (ub_ag_…, block 10, 2026-10-30): one agent's ledger; { is_null: true } = the rows from before the agents were many.
      - `eq` string
      - `in` string[]
      - `is_null` boolean
    - `conversation_sid` object: Thread(s), ub_cv_ prefix: the whole run trail of one conversation.
      - `eq` string
      - `in` string[]
    - `kind` object: { eq: "classify" } is the classification history of a thread.
      - `eq` 'classify' | 'plan' | 'execute_step' | 'dialog_reply'
      - `in` string[]
    - `state` object: { eq: "failed" } is the failure review; { eq: "pending" } is the queue.
      - `eq` 'pending' | 'running' | 'done' | 'failed' | 'canceled'
      - `in` string[]
    - `scenario_key` object: Platform scenario key; { is_null: true } = runs that matched none (including every non-classify run).
      - `eq` string
      - `in` string[]
      - `is_null` boolean
    - `plan_sid` object: One plan's execution trail, ub_pl_ prefix: the run that built it and every step run under it.
      - `eq` string
      - `is_null` boolean
    - `outcome` object: The Analyzer's log: { is_null: false } is every row of it, { in: ["written", "edited", "unsure"] } the replies that went, { eq: "waiting" } the drafts, { eq: "test" } the rewrites.
      - `eq` 'written' | 'edited' | 'unsure' | 'waiting' | 'handed' | 'test'
      - `in` string[]
      - `is_null` boolean
    - `answer_sid` object: The runs whose pack carried a Knowledge answer (ub_an_…, 2026-10-26): an answer's "All in the Analyzer".
      - `eq` string
    - `example_sid` object: The runs that read a kept dialogue of the Golden set before their reply (ub_eg_…, 2026-10-27, block AC5): the dialogue's "the replies it was read before".
      - `eq` string
    - `approved_by_user_sid` object: The reply's approver (2026-10-27, block AC8): with outcome written, { is_null: true } is a reply that went out on its own (Sent itself), { is_null: false } one a person approved as written (Approved); { eq } one person's approvals.
      - `eq` string
      - `is_null` boolean
    - `created_at` object: ISO 8601 range; combine bounds in one object.
      - `gte` string
      - `lte` string
      - `gt` string
      - `lt` string
  - `include` string[]: reply (the message the run stands for: its text, the agent's original when a person changed it, state, who approved it), trigger (the lead's message it answered), lead (name, company, person), scenario (what it was read as), hints (what it lacked).
  - `sort` object: One column and its direction; omit for the default order.
    - `field` 'created_at' | 'execute_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.

- SearchAgentRunsResponse
  - `success` true, required
  - `operation` 'search', required
  - `items` object[], required
    - `item` object, required
      - `sid` string, required: Agent run sid (ub_rn_…).
      - `team_sid` string, required: Owning team (ts_tm_…, external - gs.service.id).
      - `agent_sid` string, nullable, required: The AI SDR the turn ran as (ub_ag_…), frozen at the dispatch (block 10, 2026-10-30); null on a row from before.
      - `conversation_sid` string, required: The thread this work ran on (ub_cv_…).
      - `kind` 'classify' | 'plan' | 'execute_step' | 'dialog_reply', required: classify = one debounced classification of an inbound burst; plan = a strategize turn that builds or replaces the thread plan; execute_step = one due plan step; dialog_reply = one goal-led live-dialog reply.
      - `state` 'pending' | 'running' | 'done' | 'failed' | 'canceled', required: pending = armed, waiting for execute_at or a queue slot; running = claimed; done / failed / canceled are terminal. A turn that reports a failure after its message or its plan was written is done, not tried again: the work stands. canceled = the work was no longer wanted (the lead wrote again, the plan was canceled or replaced, or a person rewrote the step the run was working for). failed means the retries were exhausted and the escalation policy fired.
      - `execute_at` string, nullable, required: ISO 8601 UTC the run is due at (the debounce or timer clock); null = run now. Sort ascending over pending rows to see what fires next.
      - `trigger_message_sid` string, nullable, required: The newest inbound message that armed a classify or dialog_reply (ub_ms_…). It is the freshness anchor: a newer inbound cancels the run at claim time.
      - `trigger_signal` 'awaiting_reply' | 'opened_not_replied' | 'clicked_not_replied' | 'multi_opened' | 'accepted_not_replied' | 'note_read_not_replied' | 'reacted_not_replied' | 'invited_us' | 'followed_us' | 'lead_messaged_first' | 'unanswered_inbound' | 'draft_pending' | 'escalated_open' | 'task_overdue' | 'went_quiet' | 'reactivated' | 'invite_pending' | 'connection_removed' | 'blocked_us' | 'deleted_message' | 'bounced' | 'bounced_hard' | 'complained' | 'booking_link_sent' | 'objective_reached' | 'viewed_our_profile' | 'visited_our_site' | 'visited_not_replied' | 'visited_key_page' | 'multi_visited' | 'submitted_form' | 'company_visited_our_site' | 'no_people' | 'thin_coverage' | 'nobody_reached' | 'no_engagement' | 'no_reply' | 'exhausted' | 'dormant' | 'dm_missing', nullable, required: The signal that armed a plan run when no reply did (accepted_not_replied, reacted_not_replied or clicked_not_replied): the thread gained the name and the agent owes it a move, strategized under the signal_followup behavior. Null on every other run; a classify landing on the thread re-arms the run and clears it.
      - `scenario_key` string, nullable, required: The platform scenario the classify matched, max 64 chars (the vocabulary is owned by conversations). null on non-classify runs.
      - `confidence` number, nullable, required: Classification confidence, 0..1. Below the platform floor it trips the low_confidence escalation.
      - `extracted` object, nullable, required: Entities the classify pulled out of the reply; null when nothing was extracted.
        - `return_date` string: Out of office: the ISO 8601 date the prospect is back.
        - `resume_date` string: Not now: the ISO 8601 date the prospect said to come back.
        - `proposed_time` string: Meeting or reschedule ask: the time the prospect proposed, in their own words ("next week", "Thursday 15:00 CET"), never converted to a timestamp.
        - `referred_contact` object: Referral or wrong person: the person the prospect pointed at. A LinkedIn link is never stored, only the identity it resolves to.
          - `name` string: Their name, as the reply gave it.
          - `role` string: Their role or title, as the reply gave it.
          - `email` string: Their email address, as the reply gave it.
          - `ln_nickname` string: LinkedIn vanity slug, the name after linkedin.com/in/, lowercased.
          - `ln_id` string: LinkedIn profile URN (ACoAA…), when the reply linked the profile by it.
          - `sn_id` string: Sales Navigator URN (ACwAA…), from a Sales Navigator link.
          - `recruiter_id` string: Recruiter URN (AEMAA…), from a Recruiter link.
          - `ln_member_id` string: The member id a URN decodes to, digits only: one person across the three URNs.
        - `objection_topic` string: Objection: what the prospect objected to, in a few words of their own.
        - `objection_kind` 'price' | 'timing' | 'has_solution' | 'other': Objection family (AgentRunObjectionKindEnum), when one clearly fits. Logged for analysis; it is not a scenario of its own.
      - `reason` string, nullable, required: The model's own explanation of the decision, max 512 chars. This is what the thread timeline pill renders, and what to quote when a user disputes a classification.
      - `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.
      - `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 driving a plan or execute run; null on classify runs and on custom-scenario-driven work.
      - `behavior_version` integer, nullable, required: Version of that module at run time: the audit stamp that makes a past decision reproducible.
      - `playbook_source` 'platform' | 'edited' | 'custom', nullable, required: Whose playbook the run planned under (2026-10-12): platform (the seed as it ships), edited (the team's version of it) or custom (a scenario of the team's own with steps of its own). Stamped on a classify run that planned a mechanical playbook by rule and on a plan run whose pack resolved one; null on every other run.
      - `playbook_version` integer, nullable, required: That playbook's version: the seed's, or 1 on a custom scenario's own steps. With behavior_version, the audit says which version of whose playbook planned.
      - `plan_sid` string, nullable, required: The plan created (kind plan) or executed (kind execute_step), ub_pl_…; null otherwise.
      - `step_id` integer, nullable, required: Which step of plan_sid this run executed, or the use_playbook step a Playbook turn rides; null on a run outside a plan.
      - `dialog_turn` 'reply' | 'stall' | 'followup', nullable, required: What a Playbook dialog turn is for (2026-10-19, ConversationDialogTurnEnum): reply, the reply to the lead's message; stall, the offer the stall rule forces after days of silence; followup, a follow-up after the offer with a new angle (or the one nudge after a yes). Null on every other run.
      - `guidance` string, nullable, required: The user's "try again, but..." steering on a regenerate, max 1024 chars. It rides the turn's context pack.
      - `regenerates_message_sid` string, nullable, required: The draft the user discarded (ub_ms_…); its body joins the context pack as the previous draft.
      - `llm_log_ref` string, nullable, required: The runner's transcript of the turn (the SDK's stream, on the runner's host), max 255 chars. It holds no context pack: what a reader needs of the turn is record.
      - `error_code` string, nullable, required: Terminal failure code once the retries are exhausted, max 64 chars; null otherwise. Group failures by it before reading any transcript.
      - `cost_usd` number, nullable, required: What the turn cost on the model in US dollars, as the runner reported it (2026-10-19): the agent's meter. A thread's spend is the sum over its runs, read in credits (25 a dollar) against the agent's credits_cap_per_conversation. Null on a run that never reported and on rows from before.
      - `example_sids` string[], nullable, required: The golden set rows the turn's pack carried (2026-10-20, ub_eg_…, at most five, golden first): what the reply was written from. Null on a turn that carried none.
      - `words_caught` object[], nullable, required: The Words the send door refused this run's messages for (2026-10-20), each with how often; the turn wrote the sentence again. Null when none.
        - `never` string, required
        - `times` integer, required
      - `knowledge_miss` string, nullable, required: A classify run only: the lead's question the Knowledge pick found nothing for above the floor, up to 500 chars. Null otherwise.
      - `outcome` 'written' | 'edited' | 'unsure' | 'waiting' | 'handed' | 'test', nullable, required: What became of the reply the run stands for (2026-10-21, the Analyzer's log): written, edited (a person changed its words before it went), unsure (it went as written and the lead's question found nothing in Knowledge), waiting (a draft waits for an OK), handed (a hand-off took the thread to a person instead), test (a rewrite, never sent). Null: no row of the log.
      - `approved_by_user_sid` string, nullable, required: The person who approved the reply the run stands for, as its message carries it (2026-10-27, block AC8), stamped with the outcome. Null on a reply that went out on its own (the Analyzer's Sent itself) and on a run with no reply.
      - `test_of_run_sid` string, nullable, required: A test run's original (ub_rn_…): the run whose reply rewrite_agent_reply wrote again. Null on every live run.
      - `test_sid` string, nullable, required: The test from a real reply this run is a turn of (ub_at_…, start_agent_test on /mcp/unibox/agent-memory, 2026-10-28): its classify of the lead's message or the reply written after it. Null on every live run and on a rewrite's test run.
      - `record` object, nullable, required: What the turn read, decided, wrote and was checked by (2026-10-21), every part optional: read (a classify's ranking, signals, questions, model numbers, hand-off), gather (the pack's inputs, used or only read), decision, write (model, tokens, prompt parts, first draft), checks, tries, spans and mix (what the reply's words came from), made_by (the plan run that wrote a planned send), text (a test run's reply), edit_says, files (the Knowledge files that went out with the reply, block AC3). Null on older runs.
        - `read` object
        - `gather` object
        - `decision` object
        - `write` object
        - `checks` object[]
        - `tries` integer
        - `spans` object[]
        - `mix` object
        - `made_by` string, nullable
        - `text` string, nullable
        - `edit_says` object, nullable
        - `files` object[]: The Knowledge files that went out with the reply (block AC3, 2026-10-26): the answer each came with.
          - `answer_sid` string, required
          - `question` string, required
          - `name` string, required
      - `hint_sids` string[], nullable, required: The Analyzer's hints the run opened or counted on its completion (2026-10-26, ub_hn_…, at most ten; search_agent_hints on /mcp/unibox/agent-memory): a classify's missing answers, a writing turn's missing dialogue. Null on a run that left none.
      - `created_at` string, required: ISO 8601 UTC.
      - `updated_at` string, required: ISO 8601 UTC.
    - `included` object, required
      - `hints` object[]: include hints: the hints the run hit, and the ones the classify that read its trigger hit; absent where both left none.
        - `sid` string, required: The hint (ub_hn_…).
        - `kind` string, required: missing_answer (a question nothing in Knowledge answers) or no_example (no close dialogue in the Golden set).
        - `subject` string, required: What was missing, as the lead put it.
        - `goes_to` string, required: Where the fix lands: knowledge or golden_set.
        - `scenario_key` string, nullable, required: The scenario of the reply that opened it.
        - `status` string, required: open, resolved or dismissed.
        - `count` integer, required: The real replies that hit it.
      - `reply` object: include reply: the message the run stands for.
        - `sid` string, required: The message (ub_ms_…), a split reply's first part.
        - `channel` string, required: linkedin or email.
        - `body_text` string, required: Its whole text as it went or waits, a split reply's parts joined.
        - `draft_body_text` string, nullable, required: The agent's own words when a person changed them before it went; null when nobody did.
        - `state` string, required: The message state: pending_approval, queued, sent, delivered, failed or canceled.
        - `parts` integer, required: How many messages it went as.
        - `sent_at` string, nullable, required: ISO 8601 UTC, when it went; null while it waits.
        - `created_at` string, nullable, required: ISO 8601 UTC, when it was written.
        - `person_edited_at` string, nullable, required: ISO 8601 UTC, when a person changed it; null when nobody did.
        - `approved_by_user_sid` string, nullable, required: The user who approved it; null when none did.
      - `trigger` object: include trigger: the lead's message it answered (the run's trigger, else the one of the classify that read it).
        - `sid` string, required: The lead's message (ub_ms_…).
        - `body_text` string, required: Its text.
        - `sent_at` string, nullable, required: ISO 8601 UTC.
      - `lead` object: include lead: the thread's lead.
        - `name` string, nullable, required: The lead as the thread names them.
        - `company` string, nullable, required: Their company, from the linked playtables person; null when unlinked or unknown.
        - `person_sid` string, nullable, required: The playtables person (cr_pp_…); null when the thread is not linked.
      - `scenario` object: include scenario: what the lead's message was read as.
        - `run_sid` string, required: The classify run (ub_rn_…).
        - `scenario_key` string, nullable, required: What the lead's message was read as.
        - `confidence` number, nullable, required: 0..1.
      - `then` object, nullable: include then (2026-10-27, block AC8): what the lead did after the reply, the thread's outcome read from the reply's send (a run with no reply, a hand-off's classify, from its own time): booked, their_link, confirmed_time, gave_contact, handed, replied, back_on or no_reply, with when. The Analyzer's "The lead then" column. Null where the thread is gone.
        - `kind` string, required: booked, signed_up, registered (the Playbook's end), their_link, confirmed_time, gave_contact (the lead's own evidence), handed (a person took the thread), replied (the lead wrote again), back_on (the lead named a date and the plan waits until it: at is the date), no_reply.
        - `at` string, nullable, required: ISO 8601 UTC of what happened; null on no_reply.
  - `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: Runs matching the filter (full filter scope, not the page).
    - `groups` object, required
      - `kind` union, required: Runs by kind; an absent key means zero.
        - object
          - `classify` integer
          - `plan` integer
          - `execute_step` integer
          - `dialog_reply` integer
        - unknown[]
          - unknown
      - `state` union, required: Runs by state; an absent key means zero. pending is queue depth, failed is the health signal.
        - object
          - `pending` integer
          - `running` integer
          - `done` integer
          - `failed` integer
          - `canceled` integer
        - unknown[]
          - unknown
      - `outcome` union, required: Runs by outcome (the Analyzer's log, 2026-10-21); an absent key means zero, and a run with none is in no group.
        - object
          - `written` integer
          - `edited` integer
          - `unsure` integer
          - `waiting` integer
          - `handed` integer
          - `test` integer
        - unknown[]
          - unknown
      - `written` union, required: The written replies within the filter split by the door's approver (2026-10-27, block AC8): itself, nobody approved it (it went out on its own); approved, a person approved it as written. The Analyzer's Sent itself and Approved.
        - object
          - `itself` integer
          - `approved` integer
        - 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)
