> ## 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.

# AI

> Group, classify, read and summarise items with one call — and attach an agent step to a watch.

Nephia's AI passes are on the API, not only in the dashboard. `POST /v1/analyses`
runs one of four passes over items you name; a watch created with an `ai` step
runs one of them on every event it catches.

## The four kinds

| `kind`      | What it does                                          | Needs                   |
| ----------- | ----------------------------------------------------- | ----------------------- |
| `group`     | Clusters the items by your instruction.               | `instruction`           |
| `classify`  | Sorts each item into buckets you define.              | `buckets`               |
| `agent`     | Applies your instruction and schema to **each** item. | `instruction`, `schema` |
| `summarise` | Answers one question about the whole set.             | `question`              |

`group`, `classify` and `summarise` are the passes the dashboard runs on a
Query's results. `agent` is the one you write yourself.

## Credits

| Kind                             | Rate                  |
| -------------------------------- | --------------------- |
| `group`, `classify`, `summarise` | 1 credit per 50 items |
| `agent`                          | 1 credit per 25 items |

The agent's batch is half the size for the same credit because its output is
generated text for every field you asked for, not one verdict per item.

`meta.credits_used` on the response is what was actually charged, and it is not
always what the estimate quoted: **a batch the provider could not answer is not
billed**. Price a call first with `GET /v1/analyses/estimate?item_count=…`,
which is free.

## Items

Send them inline:

```bash theme={null}
curl -X POST https://api.nephia.cc/v1/analyses \
  -H "Authorization: Bearer $NEPHIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "summarise",
    "question": "What are people complaining about?",
    "items": [
      { "id": "1", "source": "reddit", "title": "Latency spikes since Tuesday", "body": "…" },
      { "id": "2", "source": "reddit", "title": "Anyone else seeing 502s?", "body": "…" }
    ]
  }'
```

Or point at a watch you own and let Nephia read its recent events:

```json theme={null}
{
  "kind": "group",
  "instruction": "Group these by the product model they refer to.",
  "watch_id": "0b0c…",
  "since": "2026-08-17T00:00:00.000Z",
  "limit": 200
}
```

Up to 200 items per call, from one source or the other — never both.

## The agent step

An agent step is **one instruction and one flat schema of your own fields**:

```json theme={null}
{
  "kind": "agent",
  "instruction": "Decide whether this person is looking to buy, and how ready they are.",
  "schema": {
    "intent": { "type": "enum", "values": ["buying", "researching", "none"] },
    "readiness": { "type": "number", "description": "0 to 1." },
    "reason": { "type": "string", "maxLength": 160 }
  },
  "items": [ "…" ]
}
```

You get one object per item, with your keys:

```json theme={null}
{
  "meta": { "kind": "agent", "item_count": 2, "batches": 1, "credits_used": 1, "model": "…" },
  "data": {
    "outputs": [
      { "item_id": "1", "output": { "intent": "buying", "readiness": 0.8, "reason": "Asks where to buy it today." } },
      { "item_id": "2", "output": { "intent": "none", "readiness": 0, "reason": null } }
    ]
  }
}
```

### The schema subset

Deliberately narrow, so every schema can be enforced rather than merely
requested:

* **1 to 12 fields**, flat. No nesting, no arrays.
* Each field is `string`, `number`, `boolean` or `enum`.
* `values` (up to 12) belongs to `enum`; `maxLength` (up to 400) to `string`.
* `description` is read by the model — write it as a sentence someone else could
  apply by hand.
* Field names start with a letter and use letters, digits and underscores. They
  are yours: they come back as the keys of `output`, and become `agent:<name>`
  rule fields in the dashboard.

**Every field can answer `null`.** That is a real answer — "this item does not
support one" — not an error. A model forced to always produce a value produces
confident nonsense, which is worse than a gap you can see.

## On a watch

Pass `ai` when creating a watch and every event it emits carries the reading in
its `ai` block. Delivery waits for it, up to 120 seconds, and the event is
delivered either way — see
[Webhooks → Agent step on a watch](/webhooks#agent-step).

```json theme={null}
{
  "query": "nike dunk",
  "refresh_interval_seconds": 300,
  "webhook_url": "https://example.com/hooks/nephia",
  "ai": {
    "instruction": "Judge how likely this listing is a counterfeit.",
    "schema": {
      "risk": { "type": "enum", "values": ["low", "medium", "high"] },
      "signals": { "type": "string", "maxLength": 200 }
    }
  }
}
```

The step's credits are charged on top of the watch's tick rate, at the agent
rate above — one credit per 25 events read.

## Sentiment and intent

A search with **analysis** turned on has every item it catches read on two more
dimensions, on the same pass and at the same rate — one charge, one call, two
answers:

* **sentiment** — `positive`, `neutral`, `negative`, `question` or `mixed`
* **intent** — `purchase_intent`, `comparison`, `question`, `complaint`,
  `praise` or `other`

They ride the same batch as your `ai` step but stay out of `output`: the keys
you defined are the keys you get back. The reading is delivered on the event's
own [`analysis` key](/webhooks#analysis) and comes back from
[`GET /v1/{source}/watches/{id}/events`](/webhooks#replay-events).

`null` on either means nothing has read that item yet. It never means "we
looked and found nothing" — that is what `neutral` and `other` are for.

## Failures

An AI pass is an enhancement, never a dependency. Nothing here can lose a watch
event or stop a poll.

* A batch the provider could not answer is **not charged**, and `meta.batches`
  says how many actually ran.
* If no batch answered, the call returns `503` and charges nothing.
* On a watch, a step that times out or fails still delivers the event, with
  `ai.output: null` and `ai.status` saying which.
* With no provider configured, `POST /v1/analyses` returns `503` and watches
  deliver immediately with `ai: null`.
