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

# Records and tasks

> What to put in each field, what each one buys you, and the three mistakes that quietly cost you selection quality.

A selection request is two halves: the **records** you could send, and the
**task** they have to serve. Everything else is a budget.

## Records

```json theme={null}
{
  "id": "r-812",
  "kind": "tool_result",
  "title": "get_invoice(4471)",
  "source": "billing-api",
  "step_id": "step-3",
  "text": "{\"invoice_id\": 4471, \"total_cents\": 128400, \"status\": \"open\"}",
  "importance": 0.0,
  "keywords": [],
  "tags": [],
  "attributes": {}
}
```

`text` is the only required field. Everything else is a signal, and each one
earns its place:

| Field                            | What it buys                                                                                                                                                                                                         |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                             | **Supply one.** It comes back as `selected[].record_id`, and it is how you map a selection onto the objects you already hold. Without it you get a server-generated id that matches nothing on your side.            |
| `kind`                           | The record's role. Roles carry different selection priors — labelling honestly is worth more than labelling everything `external_data`.                                                                              |
| `title`                          | A short human label. Returned on the selection, and **counted by the meter**.                                                                                                                                        |
| `source`                         | Where it came from. Returned on the selection.                                                                                                                                                                       |
| `step_id`                        | Which step of your workflow produced it. Feeds recency and drift signals.                                                                                                                                            |
| `importance`                     | Your own relevance prior. A **hint, not an override** — a high value does not pin a record in.                                                                                                                       |
| `keywords`, `tags`, `attributes` | Structural hints — the engine folds them into the text it infers entities, keywords and tags from, so short well-chosen values help. **Not metered**, and bounded by the published size cap — see the warning below. |

<Warning>
  **Do not move content into `attributes` to avoid the meter.**

  These three fields are bounded by the published size cap. Values beyond it
  are outside the contract and may be rejected — a payload built around
  stuffing them is not an allowance the contract grants.

  It also does not do what you would want in the meantime. These fields *are*
  read — the engine folds them into the text it infers entities, keywords and tags
  from — but they are indexing hints, not content: nothing parked there can be
  selected, excerpted or returned. Keep them short and structured, and put the
  content in `text`.
</Warning>

### Record kinds

| Kind            | Use it for                                                                              |
| --------------- | --------------------------------------------------------------------------------------- |
| `user_input`    | What the human said.                                                                    |
| `llm_response`  | What the model said.                                                                    |
| `tool_call`     | A tool invocation, with its arguments.                                                  |
| `tool_result`   | What a tool returned. Usually the largest and most compressible thing in an agent loop. |
| `external_data` | Retrieved documents, knowledge-base passages, anything fetched. The default.            |
| `error`         | A failure the loop should stay aware of.                                                |
| `artifact`      | Something produced and referred back to — a file, a generated table.                    |
| `tool_schema`   | Tool and function schemas.                                                              |

A `kind` outside this list is currently rejected with a `500` rather than a
`400` ([known rough edge](/errors#two-rough-edges-worth-knowing)).

<Note>
  **`tool_schema` today behaves like any other record.** A protected-kind
  behaviour exists for it — never dropped, never lossily excerpted — but it is not
  enabled on the operating points serving today. Label your schemas correctly
  anyway: protection is enabled per operating point, and mislabelled records will
  not benefit when it is.
</Note>

### Records are sent every time

There is no upload step and no index. The records ride in on each request and
are gone when it returns. That is what makes the service stateless.

It is also what the cost model will follow: metering is not live yet, and when
it arrives it counts what you send, every time you send it. So if the same 40
documents accompany every call in a loop, that is 40 documents metered on every
call. Worth designing around now — pre-filter with your retriever, or keep a
working set. It is not worth working around by splitting one call into several,
which costs more rather than less.

The same records also count against your
[per-request ceiling](/limits#per-request-ceilings) today, which is live.

## Tasks

```json theme={null}
{
  "prompt": "Can this invoice still be refunded?",
  "tool_name": "answer_customer",
  "required_record_ids": ["r-812"],
  "recent_prompts": ["Summarise the account.", "Pull the latest invoice."]
}
```

| Field                                | What it buys                                                                                                                                             |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prompt`                             | What the model is about to be asked. The primary relevance signal. **Metered.**                                                                          |
| `tool_name`                          | The tool about to be called, when the selection is serving a tool call rather than an answer.                                                            |
| `required_record_ids`                | Intended as the hard pin. **Does not pin today** — see below.                                                                                            |
| `parent_record_ids`                  | Records this task descends from, for lineage. Matched the same way as `required_record_ids`, and inert for the same reason.                              |
| `recent_prompts`                     | Prior-step prompts, oldest to newest, intended as drift signals. **Not wired into selection yet — but already counted against your ceiling.** See below. |
| `keywords`, `tags`, `step_id`        | Structural hints, as on records.                                                                                                                         |
| `output_mode`, `output_token_budget` | What the downstream answer is expected to look like and how long it may be.                                                                              |

### Nothing in the request pins a record today

`required_record_ids` is the field designed to do it — a constraint the engine
satisfies, as against `importance`, which is only a prior it weighs.

<Warning>
  **It does not pin today.** Ids you send are matched against the engine's own
  internal record identity rather than against your `records[].id`, so they
  currently match nothing. `importance` was never the pin either: turning it up to
  `1.0` is a strong hint and not a guarantee.

  So **if a record must reach the model, do not send it to be selected.** Keep it
  out of `records[]` and concatenate it around `rendered_context` yourself:

  ```python theme={null}
  prompt = f"{system_prompt}\n\n{result['rendered_context']}\n\n{user_turn}"
  ```

  That is the right pattern for a system prompt, a schema the model must call
  against, and the current user turn — and it stays right after the field is
  wired, because those things do not benefit from being selected anyway.
</Warning>

Keep sending `required_record_ids` for the records where a pin is a preference
rather than a requirement. It costs nothing and starts working without a change
on your side.

### `recent_prompts` is not wired yet — send few, or none

The field exists so the engine can judge whether the conversation has drifted,
which feeds the decision to engage or stand down.

<Warning>
  **It is not used for that today.** The deployed service accepts the field and
  does not pass it into selection — but it *does* count it against your
  [per-request ceiling](/limits#per-request-ceilings), and the meter will count it
  too. Right now a long history costs you allowance and buys nothing.

  Send a handful if you want to be ready for when it is wired. Do not send the
  whole transcript.
</Warning>

## Three mistakes that cost you quality

<AccordionGroup>
  <Accordion title="Labelling everything external_data">
    It is the default, so it is what you get if you never think about `kind`. But
    a tool result, a schema and a retrieved page are treated differently, and
    flattening them into one role discards a signal that costs you nothing to
    supply.
  </Accordion>

  <Accordion title="Omitting record ids">
    Without `id`, `selected[].record_id` is a server-generated value you cannot
    join back to anything, so you can render `rendered_context` but you cannot
    reason about *which* of your objects survived. Supply ids from the first call.
  </Accordion>

  <Accordion title="Sending one record that concatenates everything">
    Selection works at record granularity. One 200 KB record is one keep-or-drop
    decision, so there is nothing to select between — you get pass-through or
    nothing. Split along the boundaries that already exist in your system: one
    tool result, one document, one turn.
  </Accordion>
</AccordionGroup>

## What comes back

`selected[]` carries, per record: `record_id`, `kind`, `title`, `source`,
`score`, `reason`, `excerpt`, `excerpt_format` and `selected_tokens`.

* **`excerpt` is built from your record's own content.** Nothing is paraphrased
  or summarised into new prose, and no model writes replacement text.

  It is **not guaranteed to be a byte-identical span of your input**, and the
  difference is worth knowing before you build on it:

  | Situation                                                  | What you get                                                                                                                                                                                                                          |
  | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | The record fits the excerpt budget                         | Your text                                                                                                                                                                                                                             |
  | It does not fit, at the widest budget                      | A leading span of your text, with a `...[truncated]` marker appended                                                                                                                                                                  |
  | It does not fit, at a tighter budget                       | A **selection of lines** from the record — whitespace-normalised, ordered by relevance rather than by position, joined with single spaces, and sometimes preceded by a short generated `Entities: key=value` line summarising matches |
  | You sent `render_format: "hybrid"` and the excerpt is JSON | It may be re-encoded into a compact tabular form. `excerpt_format` tells you (`plain`, `toon`)                                                                                                                                        |

  **If you need byte-identical spans** — because you are resolving citations
  back to offsets, or re-parsing an excerpt as JSON — use `selected[].record_id`
  to look the record up in your own store and use *your* copy. That is what the
  ids are for.
* **`score` is comparable within one response**, and is not calibrated across
  requests or across operating points. Rank with it; do not threshold on an
  absolute value.
* **`reason` is open-ended.** Show it in a debugging view, do not branch on it.

Set `return_per_record: false` if you only want `rendered_context` and the token
arithmetic — the response body gets materially smaller.
