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

# Structured search DSL

> Send explicit structured search intent to Orbit instead of relying only on natural-language parsing.

Use `structuredIntent` when your product already knows the search filters it wants to apply. For `POST /v2/developer/search`, Orbit still requires a human-readable `query`; the structured intent is used as the search plan instead of asking Orbit to infer every clause from the query text. For Deep Search, the same structured intent envelope is one of the identity signals that can seed an async profile generation run.

Structured intent is supported on these developer API surfaces:

```http theme={"dark"}
POST /v2/developer/search
POST /v2/developer/search/sse
POST /v2/developer/deep-search
```

The same search request body works for global search and directory-scoped search. The API key must have `search:read`.

## Contract Invariants

| Surface     | Required fields                                                                                                 | Structured intent behavior                                                                                                                                                                                |
| ----------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Search      | `query`, `numUsers`                                                                                             | `structuredIntent` is optional. When present, `query` still must describe the same user intent in plain English.                                                                                          |
| Search SSE  | `query`, `numUsers`                                                                                             | Same request body as Search.                                                                                                                                                                              |
| Deep Search | At least one identity signal: `structuredIntent`, `email`, `phoneNumber`, `linkedinUrl`, `urls`, or `usernames` | `structuredIntent` is an optional identity seed for the run; `personalization` is rejected. Legacy top-level fields (`mode`, `partialProfile`, `dry_run`, `options`) are rejected with replacement hints. |

`structuredIntent.version` is required and must be `"v1"`. The DSL fields are flat under `structuredIntent`; do not wrap them in an extra `intent` object.

<Note>
  `entityClauses` are not part of the public DSL yet. Do not send entity-resolution internals such as `resolvedCompanyFilters`, `resolvedSchoolFilters`, `resolvedEntityFilters`, or `entityClauses`; the server rejects those fields.
</Note>

## Search shape

```json theme={"dark"}
{
  "query": "current OpenAI engineers in San Francisco with infrastructure experience",
  "numUsers": 10,
  "structuredIntent": {
    "version": "v1",
    "experiences": [
      {
        "organization": "OpenAI",
        "title": "engineer",
        "temporalScope": "current"
      }
    ],
    "geo": {
      "place": "San Francisco",
      "distance": "50km"
    },
    "semanticClauses": [
      {
        "text": "infrastructure"
      }
    ]
  }
}
```

`query` remains required even with structured intent. It is used for moderation, logging, summaries, match reasons, and search-quality training. Do not duplicate every structured clause into prose; the query should describe the same user intent in plain English.

## Deep Search Shape

Deep Search accepts the same `structuredIntent` object on `POST /v2/developer/deep-search` as one of its identity signals, alongside `email`, `phoneNumber`, `linkedinUrl`, `urls`, and `usernames`. At least one identity signal is required; `structuredIntent` can stand alone or be combined with the other signals.

```json theme={"dark"}
{
  "runShape": "partial_plus_full",
  "linkedinUrl": "https://www.linkedin.com/in/janedoe",
  "email": "jane.doe@example.com"
}
```

Structured intent can also seed a run on its own, or sharpen another seed with a name and matching signals:

```json theme={"dark"}
{
  "urls": ["https://github.com/janedoe"],
  "structuredIntent": {
    "version": "v1",
    "names": ["Jane Doe"],
    "experiences": [{ "organization": "Example Labs" }],
    "demographics": { "ageRange": { "min": 23, "max": 25 } }
  }
}
```

Deep Search maps `structuredIntent.names[0]` to the person name; experiences, schools, semantic clauses, location, and demographic fields are matching signals for the run.

Deep Search rejects unknown top-level fields. The legacy `mode`, `partialProfile`, `dry_run`, and `options` fields return `developer_deep_search_field_unsupported` with a replacement hint; run behavior is controlled by `runShape` instead. See [Deep Search](/api/search/deep-search) for the full request contract.

<Note>
  Deep Search does not have the synchronous search endpoint's API-key user network or location context, so use `POST /v2/developer/search` for strict `personalization.network` or `personalization.nearMe` behavior.
</Note>

## Structured Intent Fields

| Field                              | Type      | Required         | Description                                                     |
| ---------------------------------- | --------- | ---------------- | --------------------------------------------------------------- |
| `structuredIntent.version`         | string    | Yes              | Must be `"v1"`                                                  |
| `structuredIntent.names`           | string\[] | Deep Search only | Person names to match. `names[0]` is treated as the person name |
| `structuredIntent.experiences`     | object\[] | No               | Work history filters                                            |
| `structuredIntent.schools`         | object\[] | No               | Education filters                                               |
| `structuredIntent.semanticClauses` | object\[] | No               | Text concepts to match against profile evidence                 |
| `structuredIntent.geo`             | object    | No               | Current or historical location filter                           |
| `structuredIntent.demographics`    | object    | No               | Age, birth year, or gender filters                              |
| `structuredIntent.personalization` | object    | Search only      | Search relative to the API key user's network or location       |

## Experience clauses

```json theme={"dark"}
{
  "experiences": [
    {
      "organization": "OpenAI",
      "title": "engineer",
      "titleAnyOf": ["engineer", "researcher"],
      "year": 2024,
      "startYear": 2020,
      "startYearLte": 2021,
      "endYear": 2024,
      "temporalScope": "current"
    }
  ]
}
```

| Field           | Type      | Description                                         |
| --------------- | --------- | --------------------------------------------------- |
| `organization`  | string    | Company or organization name                        |
| `title`         | string    | Role title                                          |
| `titleAnyOf`    | string\[] | Alternative role titles                             |
| `year`          | number    | A year that should overlap the experience           |
| `startYear`     | number    | Exact start year                                    |
| `startYearLte`  | number    | Start year must be less than or equal to this value |
| `endYear`       | number    | Exact end year                                      |
| `temporalScope` | string    | `"current"`, `"historical"`, or `"both"`            |

## School clauses

```json theme={"dark"}
{
  "schools": [
    {
      "school": "Stanford",
      "schoolAnyOf": ["Stanford University"],
      "graduationYear": 2014,
      "startYear": 2010,
      "endYear": 2014,
      "temporalScope": "historical"
    }
  ]
}
```

| Field            | Type      | Description                              |
| ---------------- | --------- | ---------------------------------------- |
| `school`         | string    | School name                              |
| `schoolAnyOf`    | string\[] | Alternative school names                 |
| `graduationYear` | number    | Graduation year                          |
| `startYear`      | number    | Education start year                     |
| `endYear`        | number    | Education end year                       |
| `temporalScope`  | string    | `"current"`, `"historical"`, or `"both"` |
| `relation`       | string    | Optional advanced relation label         |

## Semantic clauses

```json theme={"dark"}
{
  "semanticClauses": [
    {
      "text": "machine learning infrastructure"
    }
  ]
}
```

Use `text` for the main profile evidence concept. Add `anyOf` only when aliases or sibling phrases should satisfy that same concept:

```json theme={"dark"}
{
  "semanticClauses": [
    {
      "text": "machine learning infrastructure",
      "anyOf": ["distributed training", "model serving"]
    }
  ]
}
```

When both are set, Search treats them as one semantic group: `text` is the canonical phrase, and `anyOf` adds alternatives for the same clause. Deep Search treats both the `text` phrase and each `anyOf` phrase as matching signals for the run.

## Location and demographics

```json theme={"dark"}
{
  "geo": {
    "place": "San Francisco",
    "distance": "50km",
    "isHistorical": false
  },
  "demographics": {
    "ageRange": { "min": 30, "max": 45 },
    "birthYearRange": { "min": 1980, "max": 1995 },
    "gender": "female"
  }
}
```

`ageRange` and `birthYearRange` require both `min` and `max`, and `min` must be less than or equal to `max`.

## Personalization

```json theme={"dark"}
{
  "personalization": {
    "network": { "scope": "first_degree" },
    "nearMe": { "distance": "25km" }
  }
}
```

`network.scope` currently supports `"first_degree"`. `nearMe` uses the API key user's location context when available.

## Streaming

`POST /v2/developer/search/sse` accepts the same body as synchronous Search:

```bash theme={"dark"}
curl -N -X POST "https://api.orbitsearch.com/v2/developer/search/sse" \
  -H "Authorization: Bearer sk_orb_REDACTED" \
  -H "Idempotency-Key: request-uuid-or-client-retry-key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "current OpenAI engineers with infrastructure experience",
    "numUsers": 10,
    "includeMatchReason": true,
    "structuredIntent": {
      "version": "v1",
      "experiences": [{ "organization": "OpenAI", "title": "engineer", "temporalScope": "current" }],
      "semanticClauses": [{ "text": "infrastructure" }]
    }
  }'
```

The first `initial` event contains the same user payload returned by synchronous Search. If `includeMatchReason` is `true`, additional `update` events can add match reasons for top results.
