## TL;DR

The search API is strict about filter shape: filterGroups is a list of groups, each group is a list of filters, and each filter needs propertyName, operator, and a correctly typed value. Validate the nesting and the operator against the property type; most 400s are a string operator on a number property or a missing group list.

## Error

```text
{
  "status": "error",
  "message": "Invalid input JSON on line 1, column 1: filterGroups is invalid",
  "correlationId": "aaaa-bbbb-cccc"
}
```

## Steps

1. Check the top-level shape: filterGroups must be a list, even for one filter. Expected: a single filter still sits inside two lists.
2. Check each filter has propertyName (internal name), operator, and value. Expected: no missing keys.
3. Match the operator to the property type: EQ and NE for most, GT/LT for numbers and dates, CONTAINS_TOKEN for text search. Expected: type-appropriate operators only.
4. Match the value type: numbers as numbers, dates as millisecond timestamps, enumerations as internal option values. Expected: no quoted numbers or label strings.
5. Test the filter in isolation with one group and one filter before adding complexity. Expected: the minimal query returns 200, then you expand.

## When to use

- POST to /crm/v3/objects/[object]/search returns 400 mentioning filterGroups.
- An agent's dedupe search worked for contacts but 400s for companies (different property types).
- After adding a second filter group the query breaks.

## When not to use

- Search 429s (rate limits, different fix).
- "property does not exist" (wrong name, different fix).
- Empty results with a 200 (the filter is valid but matches nothing).

## Tool compatibility

- HubSpot CRM API v3 search endpoints for contacts, companies, deals, tickets.
- Private apps with crm.objects.[object].read scopes.

## Variant phrasings

### Invalid input JSON: filterGroups

The nesting is wrong; wrap filters in groups, groups in the list.

### operator not supported for property type

The operator does not fit the property; pick a type-appropriate one.

## Why it happens

The search DSL looks like JSON but behaves like a typed query language. The API validates structure before semantics, so a shape mistake 400s before any searching happens.

## Edge cases

- Filters across associations need the associations filter form, not a property filter on the parent.
- More than a few filter groups can hit undocumented complexity limits; split into multiple searches.
- Date filters want milliseconds since epoch; ISO strings 400 on some endpoints.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_v483UactIAA2JA5VZa1lEg
