> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oneshotagent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Local Businesses

> Discover main-street businesses by category and location, and resolve a business name + address to its website domain, phone, and operating status.

`research/company` and `enrich/company` are backed by a B2B database: strong for a 40-person HVAC company, thin for the single-location taqueria. These two endpoints cover the long tail — independent restaurants, contractors, dental practices, auto shops, owner-operator trucking companies.

Both are async. You POST a request, get back a `request_id`, and poll `GET /v1/requests/{request_id}` until the job finishes. Both return the same `LocalResult` shape.

<Note>
  All paid endpoints on this page accept optional `memo` (≤ 1000 chars) and
  `decisionContext` (object) body fields. Stored on the receipt for debugging
  and audit — see [Audit Trail](/sdk/audit-context).
</Note>

## Local Resolve

`POST /v1/tools/local/resolve`

**Given a business name and a street address, what is this place's domain?**

Every public registry (a state business licence, NPPES, FMCSA) gives you a legal name and an address. `enrich/email` needs `company_domain`. This is the join.

<ParamField body="name" type="string" required>
  Business name as it appears on the record, e.g. `"TAQUERIA LOS AMIGOS"`
</ParamField>

<ParamField body="address" type="string" optional>
  Street address, e.g. `"2334 Mission St"`
</ParamField>

<ParamField body="city" type="string" optional>
  City
</ParamField>

<ParamField body="region" type="string" optional>
  State / province. Not a locating field on its own.
</ParamField>

<ParamField body="postal_code" type="string" optional>
  Postal code
</ParamField>

<ParamField body="phone" type="string" optional>
  Phone number. The strongest single match signal after the name.
</ParamField>

`name` plus at least one of `address`, `city`, `postal_code`, or `phone` is required.

```bash theme={null}
curl -X POST https://win.oneshotagent.com/v1/tools/local/resolve \
  -H "Content-Type: application/json" \
  -H "X-Payment-Proof: <signature>" \
  -d '{
    "name": "Franklin Barbecue",
    "address": "900 E 11th St",
    "city": "Austin",
    "region": "TX"
  }'
```

### Response Structure

```json theme={null}
{
  "status": "completed",
  "result": {
    "found": true,
    "confidence": 0.97,
    "result": {
      "domain": "franklinbbq.com",
      "operating_status": "open",
      "confidence": 0.97,
      "phone": "(512) 653-1187",
      "category": "Barbecue restaurant",
      "is_chain": null,
      "name": "Franklin Barbecue",
      "address": "900 E 11th St, Austin, TX 78702",
      "socials": {},
      "review_count": 8412,
      "rating": 4.7,
      "id": "loc_3f1c9a2b7d8e4f60",
      "website": "https://franklinbbq.com/",
      "latitude": 30.2701,
      "longitude": -97.7313
    },
    "candidates_considered": 4
  }
}
```

<Info>
  A miss is a **completed** job with `found: false`, `result: null`, and the best `confidence` seen — the same contract as `enrich/email`. It is never an error. When a candidate existed but did not clear the threshold, it is returned as `closest_match` so you can inspect it.
</Info>

`confidence` is 0–1, a weighted mean over the signals you supplied (name, phone, address, postal code, city). `found` requires confidence ≥ 0.65 and a name similarity ≥ 0.5.

## Local Search

`POST /v1/tools/local/search`

**Which businesses of this kind operate in this place?**

Discovery by category × geography. Priced flat per search, not per result, so you can size a territory without metering every row.

<ParamField body="category" type="string[]" optional>
  Business categories, e.g. `["hvac contractor", "dental practice"]` (max 50). `category` or `keywords` is required.
</ParamField>

<ParamField body="keywords" type="string[]" optional>
  Free-text keywords, an alternative to `category` (max 50)
</ParamField>

<ParamField body="location" type="string[]" required>
  Cities, neighborhoods, or `"City, ST"` strings, e.g. `["Austin, TX", "Round Rock, TX"]` (max 50)
</ParamField>

<ParamField body="min_rating" type="number" optional>
  Minimum star rating, 0–5
</ParamField>

<ParamField body="min_review_count" type="number" optional>
  Minimum review count
</ParamField>

<ParamField body="is_chain" type="boolean" optional>
  `true` keeps only detected chains; `false` drops them. Rows where no chain was detected have `is_chain: null` and survive `false`.
</ParamField>

<ParamField body="operating_status" type="'open' | 'any'" default="open" optional>
  `open` drops businesses detected as closed
</ParamField>

<ParamField body="has_domain" type="boolean" optional>
  `true` returns only rows with a resolvable website domain — the ones `enrich/email` can act on
</ParamField>

<ParamField body="limit" type="number" default="100" optional>
  Max results, 1–500. See `truncated` below.
</ParamField>

```bash theme={null}
curl -X POST https://win.oneshotagent.com/v1/tools/local/search \
  -H "Content-Type: application/json" \
  -H "X-Payment-Proof: <signature>" \
  -d '{
    "category": ["hvac contractor"],
    "location": ["Austin, TX"],
    "has_domain": true,
    "is_chain": false,
    "limit": 50
  }'
```

### Response Structure

```json theme={null}
{
  "status": "completed",
  "result": {
    "results": [
      {
        "domain": "example-hvac.com",
        "operating_status": "open",
        "phone": "(512) 555-0142",
        "category": "HVAC contractor",
        "is_chain": null,
        "name": "Example Heating & Air",
        "address": "4100 Example Ln, Austin, TX 78744",
        "socials": {},
        "review_count": 212,
        "rating": 4.8,
        "id": "loc_a91b2c3d4e5f6a70",
        "website": "https://example-hvac.com/",
        "latitude": 30.19,
        "longitude": -97.75
      }
    ],
    "total_found": 50,
    "truncated": false,
    "vendor_calls": 5,
    "filters": { "category": ["hvac contractor"], "location": ["Austin, TX"], "has_domain": true, "is_chain": false, "limit": 50 }
  }
}
```

<Info>
  Each search is capped at a fixed number of upstream calls to keep the flat price honest. When the cap stops the search before `limit` is reached, `truncated` is `true`. In practice a search yields up to \~60 rows (three pages of 20); split large territories across searches.
</Info>

## The LocalResult shape

Fields are ordered by how much they matter to a caller: the join key first, then whether the place is real and operating, then contactability.

| Field                              | Type               | Notes                                                                                                                                                     |
| ---------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `domain`                           | string \| null     | Bare website domain. `null` when the business only has a social or aggregator page (see `socials`). Pass to `enrich/email` as `company_domain`.           |
| `operating_status`                 | `open` \| `closed` | Best-effort closure detection.                                                                                                                            |
| `confidence`                       | number             | Resolve results only.                                                                                                                                     |
| `phone`                            | string \| null     |                                                                                                                                                           |
| `category`                         | string \| null     | Free-text category from the data source.                                                                                                                  |
| `is_chain`                         | `true` \| null     | `true` when the business is a known national brand or the same name appears at 3+ addresses in the result set. `null` means not detected — never `false`. |
| `name`, `address`                  | string             |                                                                                                                                                           |
| `socials`                          | object             | Platform → URL, e.g. `{ "facebook": "https://facebook.com/…" }`.                                                                                          |
| `review_count`, `rating`           | number \| null     |                                                                                                                                                           |
| `id`                               | string             | Stable across runs and data sources (a hash of normalized name + address). Dedupe on this.                                                                |
| `website`, `latitude`, `longitude` |                    | Raw website URL and coordinates when available.                                                                                                           |

## What a registry still answers

A local tool tells you whether a business is real, operating, and reachable. It does not carry a licence issue date, an NPI taxonomy, an FMCSA authority type, or a NAICS code. Registries say *who just became a buyer and what they're licensed to do*; these endpoints say *whether they're real and how to reach them*. Use both.

## SDK

```typescript theme={null}
const territory = await agent.localSearch({
  category: ['hvac contractor'],
  location: ['Austin, TX'],
  has_domain: true,
  is_chain: false,
  limit: 50,
});

const match = await agent.localResolve({
  name: 'TAQUERIA LOS AMIGOS',
  address: '2334 Mission St',
  city: 'San Francisco',
  region: 'CA',
});
if (match.found) {
  const { email } = await agent.findEmail({ full_name: 'Owner Name', company_domain: match.result!.domain! });
}
```

See [Pricing](/pricing) for costs.
