# Schemas and extractors

A schema names the fields to pull out of a message and how. No model is involved: each field has an ordered list of deterministic extractors, and the first that yields a value wins.

```json
{
  "name": "warranty-claim-v1",
  "fields": {
    "serial": { "type": "string", "required": true,
      "extract": [
        { "kind": "labeled_value", "labels": ["Serial", "Serial number", "S/N"], "source": "all_text", "value": "[A-Z0-9-]{6,}" },
        { "kind": "regex", "pattern": "\\bS/?N[:\\s]+(?<value>[A-Z0-9-]{6,})", "source": "body" }
      ] },
    "purchased": { "type": "date", "extract": [ { "kind": "labeled_value", "labels": ["Purchased", "Purchase date"] } ] },
    "customer_email": { "type": "email", "required": true, "extract": [ { "kind": "mailbox", "source": "reply_to", "part": "address" }, { "kind": "mailbox", "source": "from", "part": "address" } ] },
    "return_address": { "type": "string", "requires_human": true, "extract": [ { "kind": "labeled_value", "labels": ["Return to", "Ship back to"] } ] }
  }
}
```

## Field types

`string`, `number`, `integer`, `boolean`, `date` (normalized to YYYY-MM-DD), `datetime`, `email`, `string[]`. Values are coerced to the type after extraction; a value that cannot be coerced is kept as text with confidence 0.5 and a validation issue.

## Field options

| Option | Effect |
|---|---|
| `required` | Missing value is a validation issue; policy can refuse to auto-accept. |
| `requires_human` | A value here always creates a task. Use for anything a person must confirm: addresses of record, contract terms, bank details. |
| `enum`, `pattern`, `min`, `max` | Validation. |
| `description`, `examples` | Documentation for the next agent. |

## Extractors

The kinds and their options are listed in [Extractors](/docs/extractors). Sources: `subject`, `body`, `from`, `from_name`, `to`, `reply_to`, `attachments` (each allowed attachment's text, one at a time), `all_text` (subject, then body, then attachments).

Every extracted value records where it came from: the source, the document id for attachments, and character offsets into that text. `get_document_text` returns the text those offsets refer to.

## Iterating

`dry_run_schema` runs a schema against stored messages without saving. Pass `expected` (message id to field values) and each result reports pass or fail per field. Keep those expectations; they are the bucket's test set. When a correction is made on a live capture (`correct_field`), the old and new values are recorded and can be turned into new expectations.

## Starters

Copy from [Starters](/docs/starters) or reference one as `"schema": "starter:support-request-v1"`.
