---
title: "Research-plan tool selector"
method: POST
path: "/api/locus-plan-research"
tags: ["planning", "research", "workflow", "tool-selection", "reports", "x402", "category:reports"]
---

# Research-plan tool selector

`POST /api/locus-plan-research`

## Request

- Base URL: `https://api.locus.report`
- URL: `https://api.locus.report/api/locus-plan-research`
- Auth: one of:
  - API key in header `PAYMENT-SIGNATURE`
  - API key in header `Authorization`

LLM-assisted research plan for one goal. Selects and ranks inside the closed Locus catalog; the server joins rail, price, scope, and availability. For choosing the next record call. A deterministic charge-free fallback returns on no key, model failure, or too few valid selections. npx @velinussage/locus-agent-skill add.

## Request body

- object: Flat JSON body: { goal, place?, maxSuggestions? }. The model selects and orders only from the closed Locus catalog; price, rail, scope, and availability are server-joined.
  - `goal` string: Natural-language research goal. The model selects and ranks only from the closed Locus catalog; it does not author coverage, cost, or availability claims. When omitted, the server synthesizes a goal from intent + place/address.
  - `intent` string: Short research intent (e.g. 'homebuyer', 'renter due diligence', 'small business siting', 'community organizer'). Used to synthesize a goal when `goal` is omitted.
  - `address` string: Optional street address. Used for deterministic coverage-profile availability joins and to supply the place when `place` is omitted.
  - `place` string: Optional ZIP, city, county, or street address used only for deterministic coverage-profile availability joins.
  - `maxSuggestions` integer: Maximum number of selected tools to return. Defaults to 10 and caps at 15.

## Response `200`

Successful paid artifact, or a free pre-charge diagnostic when the request is outside current coverage.

- object: Closed-catalog research plan: validated model-selected tools plus deterministic fallback. charged:false when OpenRouter is unkeyed, the model fails, or fewer than three validated suggestions survive.
  - `kind` 'plan_research', required
  - `goal` string, required
  - `place` string
  - `charged` boolean, required
  - `suggestions` object[], required
    - `tool` string, required
    - `why` string, required
    - `title` string, required
    - `rail` 'free' | 'paid', required
    - `priceUsdc` string
    - `scope` string, required
    - `availableForPlace` union
      - boolean
      - 'varies'
  - `deterministicFallback` object, required
  - `diagnostics` object, required
    - `modelUsed` string
    - `modelAttempted` boolean, required
    - `modelSucceeded` boolean, required
    - `charged` boolean, required
    - `reason` string
    - `hallucinatedDropped` integer, required
    - `validatedSuggestions` integer, required
    - `deterministicFallbackOnly` boolean
  - `caveats` string[], required

## Other responses

- `400`: Invalid request body.
- `402`: Payment Required. For x402 v2, repeat the identical request with a valid PAYMENT-SIGNATURE header (legacy X-PAYMENT is accepted for compatibility), or use Authorization: Payment for MPP Tempo when advertised in x-payment-info.offers.

---

[API](https://skmtc.dev/locus/apis/locus-api.md) · [All operations](https://skmtc.dev/locus/apis/locus-api/llms.txt) · [OpenAPI document](https://skmtc.dev/locus/apis/locus-api/revisions/ba7786b74adb?raw)
