# Research Group Requests (Natural Language)

This guide explains how the natural-language Research Group Request pipeline works
and what to expect from sync create, async create, and status polling.

Related guides:
[All Guides](./index.md) |
[Research Study Requests](./research_study_requests.md) |
[Recruitment Filter Expansion](./recruitment_filter_expansion.md) |
[Filters Reference](./filters.md)

## Endpoint Summary

- Create/resume: `POST /v1/research-group-requests`
- Status polling: `GET /v1/research-group-requests/{request_id}`

## Create Modes

`POST /v1/research-group-requests` supports two response modes:

- `response_mode=async`
  - returns `202`
  - queues background orchestration
  - includes request metadata plus `job_id` / `status_url`
- `response_mode=sync`
  - waits for the current request worker pass
  - may immediately return:
    - `201 completed` with embedded group payload
    - `400 needs_clarification`
    - `202 awaiting_review`
    - `422 refused`
    - `202 queued` when the request is still in progress

Only poll when the create call actually returned a non-terminal queued/running
request.

## High-Level Workflow

1) **Parse**: The request text is parsed into filters, a draft group name/description,
   and an optional screening question for criteria that cannot be filtered.
2) **Recruit**: Agents are recruited using the generated filters. The system may
   over-recruit to allow later screening.
3) **Eligibility screen**: If needed, the shared request execution layer asks
   an internal screening question to confirm hard-to-filter criteria. This is
   not the public `POST /v1/research-groups/interview` endpoint.
4) **Prune**: Only agents who pass the screening step remain in the group.
5) **Fit check**: An LLM evaluates whether the group matches the original request.
6) **Refine filters**: If the group is not a fit, filters are refined and the loop repeats.

The loop runs up to 5 times before pausing for review.

## Status Fields

Polling responses include:

- `status`: `queued`, `running`, `needs_clarification`, `awaiting_review`, `refused`, `completed`, `failed`
- `phase`: the current orchestration phase (for example `parse`, `recruit`, `interview`,
  `prune`, `fit_check`, `refine_filters`, `review`)
- `progress_message`: a short, user-facing explanation of what is happening now
- `tasks`: all high-level tasks completed so far
- `latest_tasks`: tasks executed in the most recent loop
- `loop`: current loop index (1-5)
- `recruited_agent_ids`: all agents who have been recruited during the request
- `recruited_agents`: recruited participant links (`{ id, url }`)
- `group.url`: organization-scoped group page (when a group exists)
- `specialist_host_mode`, `requested_specialist_host_mode`,
  `specialist_host_status`, `specialist_host_message`: compact Specialist host
  runtime metadata
- `specialist_prompt_step_status`, `specialist_prompt_step_reason_code`,
  `specialist_prompt_step_message`: prompt-step execution status and operator
  detail
- `specialist_prompt_decision_action`,
  `specialist_prompt_decision_message`: the prompt host's final advisory action
  when one exists (for example `continue` or `refuse`)
- `specialist_prompt_applied_change_count`,
  `specialist_prompt_applied_change_keys`: compact summary of which request
  fields the Specialist prompt step actually rewrote before deterministic
  compile/preflight execution

In production environments with the bound Specialist host, skill row, and
provider credentials available, these fields will usually show
`specialist_host_mode=prompt_assisted` and
`specialist_host_status=auto_prompt_assisted`. `deterministic_only` remains the
fallback for bare or unconfigured runtimes.

## Example Status Response

```json
{
  "request_id": "rgr_123",
  "status": "running",
  "phase": "recruit",
  "progress_message": "Filtering for Americans over 21.",
  "group": {
    "id": 622,
    "uuid": "5feff2c082fb4d4882d058482e881cbc",
    "name": "US 22+ Recent Drinkers",
    "description": "Adults in the USA aged 22+ who have consumed alcohol within the past 30 days.",
    "agent_count": 12,
    "url": "https://cat.fish.dog/organization/8b1a996a-0ce7-4e08-b8f4-264dbf2f8cf7/research_group/5feff2c082fb4d4882d058482e881cbc"
  },
  "last_filters": { "country": { "any": ["usa"] }, "age_min": 22 },
  "attempt_count": 2,
  "loop": 2,
  "tasks": ["parse_request", "recruit", "interview_screen", "prune", "fit_check"],
  "latest_tasks": ["recruit", "interview_screen", "prune", "fit_check"],
  "recruited_agent_ids": [578399, 578401],
  "recruited_agents": [
    {
      "id": 578399,
      "url": "https://cat.fish.dog/organization/8b1a996a-0ce7-4e08-b8f4-264dbf2f8cf7/agent/578399"
    },
    {
      "id": 578401,
      "url": "https://cat.fish.dog/organization/8b1a996a-0ce7-4e08-b8f4-264dbf2f8cf7/agent/578401"
    }
  ],
  "warnings": []
}
```

## Review and Clarification

- `needs_clarification`: Returned when required fields are missing or unsupported.
  The response includes `clarifying_questions` and `missing_info`.
- `awaiting_review`: Returned when the system cannot meet the criteria within the
  loop limit or attempts limit. The response includes `suggested_relaxations`.
- `refused`: Returned when the deterministic Specialist concludes the current
  audience/constraint combination is not executable. The response includes
  `refusal`, `refusal_reason`, and compact Specialist host/prompt-step fields
  that explain whether the refusal came from deterministic execution or a
  prompt-assisted host decision.

To resume, send the `request_id` with optional `additional_text` and `review_action`.

If the resumed request is run with `response_mode=sync`, it can again return a
terminal sync outcome immediately.

## Notes

- Countries are limited to USA, UK, Germany, and Canada.
- US states must use 2-letter codes (for example, CA, NY).
- Unsupported filters will trigger clarification requests.
- Legacy top-level `description_contains` is still accepted on raw filter
  payloads for compatibility, but new callers should prefer
  `filters.description.contains`. When the legacy shorthand is consumed on
  direct raw-filter routes or agent search/find routes, it is transparently
  coerced to the canonical structured text-filter shape and the response may
  include `compatibility_warnings` describing the migration target.
- Prefer `GET /v1/research-group-requests/{request_id}` over `GET /v1/jobs/{job_id}`
  when you already have a `request_id`.
