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

# Find people with your problem

> Build a query from a pain phrase instead of a brand name, then read only the mentions that need you.

Most monitoring starts from a name you already own. That works once people are
saying it. Before then — a product nobody has heard of, a name you picked last
week — the feed is empty and the tool looks broken.

This guide builds the other kind of query: one that starts from **what people
say when they have the problem you solve**, and reads back only the mentions
worth answering.

Nothing here is a separate product. It is `POST /v1/queries` with different
terms, and `GET /v1/mentions` with two filters.

## 1. Search for the sentence, not the name

A brand query looks for one token. A discovery query looks for the phrases
people type when they are stuck:

```json theme={null}
{
  "name": "Brand monitoring pain",
  "globalCriteria": {
    "market": "global",
    "terms": [
      "how do I track brand mentions",
      "alternative to Brand24",
      "tired of manually searching reddit"
    ],
    "match": "contains"
  }
}
```

`terms` are OR'd into one upstream call, so several phrasings of the same
complaint cost one poll, not three. `match: "contains"` rather than `"word"`:
these are phrases, and people do not type them the way you wrote them.

<Note>
  Write the phrases in the words your customers use, not the words you use.
  "Brand monitoring" is your category; "I keep missing when people mention us"
  is their problem.
</Note>

## 2. Poll the places people describe problems in prose

Five sources carry that kind of sentence. Enable them together:

```json theme={null}
{
  "sources": [
    { "source": "reddit", "enabled": true },
    { "source": "hackernews", "enabled": true },
    { "source": "stackoverflow", "enabled": true },
    { "source": "lemmy", "enabled": true },
    { "source": "x", "enabled": true }
  ]
}
```

Each enabled source is one watch slot and one charge per poll — see
[Credits](/credits).

## 3. Switch the readings on

A phrase query catches everyone who used the phrase, and most of them are not
prospects: someone answering, someone quoting, someone complaining about a
different tool. Two readings sort them, and they are what step 4 filters on.

```json theme={null}
{
  "sentimentEnabled": true,
  "aiEnabled": true
}
```

`sentimentEnabled` reads every mention as `positive`, `neutral`, `negative`,
`question` or `mixed`. `aiEnabled` also writes the **intent** — what the item is
trying to do: `purchase_intent`, `comparison`, `question`, `complaint`, `praise`
or `other`.

Both cost credits per classified item. Without them, step 4 has nothing to
filter on.

## 4. Read back only what needs you

`GET /v1/mentions` filters on both readings. Each is repeatable, and repeated
values are OR'd:

```bash theme={null}
curl "https://api.nephia.cc/v1/mentions?intent=complaint&intent=question&sentiment=negative" \
  -H "Authorization: Bearer $NEPHIA_API_KEY"
```

That reads: *someone with a problem or a question, who is unhappy about it.*

| Ask                                         | Query string                                          |
| ------------------------------------------- | ----------------------------------------------------- |
| People describing a pain                    | `?intent=complaint`                                   |
| People asking how to do it                  | `?intent=question`                                    |
| People weighing tools                       | `?intent=comparison&intent=purchase_intent`           |
| The unhappy ones, whatever they are doing   | `?sentiment=negative`                                 |
| What nothing has read yet                   | `?sentiment=unread`                                   |
| Only the two forums that matter to you      | `?source=reddit&source=hackernews`                    |
| Only what people actually reacted to        | `?engagement_min=10`                                  |
| A hundred likes on X, fifty score on Reddit | `?engagement=x:likes>=100&engagement=reddit:score>50` |

**Repeated values of one key are OR'd; different keys are AND'd.** So
`?source=reddit&source=hackernews&sentiment=negative&intent=complaint` reads:
*on Reddit or Hacker News, negative, and a complaint.* `source`, `sentiment` and
`intent` are all repeatable.

`unread` is the wire spelling of "not yet classified" — a real filter value, not
an absence. It is how you find what your readings have not caught up with.

<Warning>
  Filtering on `sentiment`, `intent`, `engagement_min` or `engagement` reads the
  polled stream only. Items kept from a one-off Explore run carry no readings and
  no counters, so they cannot satisfy those filters and are left out rather than
  shown misleadingly.
</Warning>

## Cutting the mentions nobody reacted to

`engagement_min` keeps mentions with at least that many **interactions** — likes,
replies, reposts, comments or score, whichever the Source reports. It is the
noise gate for a busy Source: on a typical month of X mentions, roughly seven in
ten have no interactions at all, so `?engagement_min=1` alone removes most of
what you would otherwise page through.

Three things it does not do, each of which will otherwise surprise you:

* **It never counts views.** A view is not an interaction, and on the Sources
  that report one it is an order of magnitude bigger than every other counter —
  including it would make this a filter on reach.
* **It leaves out mentions with no counters at all**, rather than treating them
  as zero. RSS items and AI answers report no audience, and mentions recorded
  before 4 September 2026 predate the field. None of them were measured, so none
  can satisfy a threshold. `?engagement_min=0` is therefore *narrower* than no
  filter: it asks for everything that was counted.
* **The numbers are captured when we collect a mention, and never refreshed.**
  A post caught two minutes after it went up is measured with the counters it
  had then. So a threshold reads against your most recent mentions, which are
  the ones with the least time to have earned anything.

A number that means one thing on one Source means another elsewhere: five points
on Hacker News is a story that landed, five likes on X is nothing. That is what
`engagement` below is for.

## Asking a different question of each Source

`engagement_min` thresholds one number for everything, and the sum it thresholds
is lossy: a post with a hundred replies and no likes clears `engagement_min=100`
exactly as a post with a hundred likes does, and those are not the same
mentions. *It was liked* and *it was argued with* is the distinction a sum
erases.

`engagement` is a **rule**, and it is repeatable:

```
<source|*>:<metric><operator><number>
```

```bash theme={null}
curl -G https://api.nephia.cc/v1/mentions \
  -H "Authorization: Bearer $NEPHIA_API_KEY" \
  --data-urlencode "engagement=x:likes>=100" \
  --data-urlencode "engagement=reddit:score>50"
```

* **Metrics** are `likes`, `replies`, `reposts`, `comments`, `score` and
  `views`, plus `total` for the same interaction sum `engagement_min` reads.
  `engagement_min=10` is exactly `engagement=*:total>=10`.
* **Operators** are `>=`, `>`, `=`, `<` and `<=`. The number is whole and may be
  negative — Reddit and Lemmy net downvotes out, so `?engagement=reddit:score<0`
  is a real question: the mentions that were argued with.
* **`*`** stands for any Source no named rule already covers. Pair it with
  `total` — `?engagement=*:total>=25` is "at least 25 interactions, wherever it
  was said", which is `engagement_min` with an operator of your choosing. A
  named counter under `*` is legal but rarely what you mean: `*:likes>=100`
  drops every Source that does not count likes, by the rule below.

Two rules about the scope, and both are the kind you discover by noticing rows
missing:

* **A Source no rule names passes.** `?engagement=x:likes>=100` narrows X and
  leaves Hacker News exactly as it was. Without that, every rule would also be a
  Source filter in disguise — and `?source=` already exists for that. A named
  rule overrides `*` for its own Source, and several rules on one Source are
  ANDed.
* **A metric that was never counted satisfies nothing — `<` included.** This is
  the `engagement_min` rule above, one level down and sharper: YouTube reports
  no likes at all, so `?engagement=youtube:likes<10` returns *none* of your
  YouTube mentions rather than all of them. Only a metric the Source actually
  reports can be filtered on.

Send `engagement` or `engagement_min`, never both: they are two spellings of one
filter, and a request carrying both is a `400`.

Every mention comes back with both readings on it, so you can sort further
without a second call:

```json theme={null}
{
  "id": "…",
  "source": "reddit",
  "title": "Tired of manually searching for our name every morning",
  "sentiment": "negative",
  "intent": "complaint",
  "url": "https://reddit.com/…",
  "occurredAt": "2026-09-01T08:12:00.000Z"
}
```

## 5. Qualify each one with your own question

The readings say what an item *is*. To ask whether it is **your** problem, give
the query an agent step — one instruction and a schema, applied to every item it
catches:

```json theme={null}
{
  "aiStep": {
    "instruction": "Decide whether the person behind this item is describing the problem your product solves, and how badly it is hurting them. Judge only from what the item itself says.",
    "schema": {
      "problem_fit": { "type": "boolean" },
      "urgency": { "type": "enum", "values": ["low", "med", "high"] },
      "current_workaround": { "type": "string", "maxLength": 120 }
    }
  }
}
```

The answers ride on each mention under `agent`, spelled with your own field
names:

```json theme={null}
{
  "agent": {
    "problem_fit": true,
    "urgency": "high",
    "current_workaround": "a saved search and a calendar reminder"
  }
}
```

`current_workaround` is the one with commercial value beyond triage: what
someone names as their current answer is what your reply has to be better than.

See [AI](/ai) for the schema's field types and what an agent pass costs.

## 6. Get told instead of polling

Do not poll `/v1/mentions` on a timer. A query delivers what it catches to a
[webhook](/webhooks), and every mention arrives with its readings already on it
— so the filter that reads well in step 4 becomes a condition in your own
handler.

## What this does not do

It finds people **describing a problem**. It does not find customers, and it
does not tell you they want to buy — a `problem_fit` of `true` is a reading of
one post, not a qualified lead. Treat the output as a list of conversations
worth joining, and expect to discard some of it.
