# Custom fields

> Metadata templates: define your own fields on documents and lines, validate them, print them, and get them back in webhooks.

Source: /docs/guides/custom-fields

Custom fields let you carry information Alya Books does not model itself (purchase order numbers, cost codes, project phases, sales representatives) on invoices, quotes, order confirmations, proformas and incoming invoices, at document ("head") level and per line.

## Concepts

- A **field** has a `key` (lowercase letters, digits, underscores; used in the API), a `label` (and optional Arabic label), a `type` (`text`, `number`, `date`, `boolean`, `select` with options), a `level` (`head` or `line`), and flags `required` and `showOnPdf`.
- A **template** groups fields for one `documentKind` (`invoice`, `credit_note`, `debit_note`, `proforma`, `quote`, `order_confirmation`, `incoming_invoice`). Exactly one template per kind can be **active**.
- Values travel as `metadata: { key: value }` on the document and on each line, are validated against the active template when created through the API or the editor, and are returned on every read and in webhook payloads.

## Define fields and a template

```bash
curl -X POST "$BASE/metadata/fields" -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{ "label": "PO number", "type": "text", "level": "head", "required": true, "showOnPdf": true }'
# → { "id": "f1…", "key": "po_number", … }

curl -X POST "$BASE/metadata/fields" -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{ "label": "Cost code", "type": "select", "level": "line", "options": [{ "value": "OPS", "label": "Operations" }, { "value": "RND", "label": "R&D" }] }'
# → { "id": "f2…", "key": "cost_code", … }

curl -X POST "$BASE/metadata/templates" -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Standard", "documentKind": "invoice", "fieldIds": ["f1…", "f2…"], "isActive": true }'
```

## Use them

```json
{
  "invoices": [{
    "buyer": { "id": "…" },
    "metadata": { "po_number": "PO-4711" },
    "items": [{ "description": "Consulting", "quantity": 10, "unitPrice": 500, "metadata": { "cost_code": "OPS" } }]
  }]
}
```

Validation errors come back as `E_VALIDATION` with `field: "metadata"` (or `items[i].metadata`) and `extra.fields[]` naming the offending keys. Values are coerced: numbers from strings, `yes`/`no` to booleans, `YYYYMMDD` to `YYYY-MM-DD`. Unknown keys are kept as-is, so you can attach data that is not part of the template.

Head-level values on issued invoices can still be changed with `PATCH /invoices/{id}` (`mergeMetadata` defaults to `true`), because they are not part of the reported document. Fields flagged `showOnPdf` print under "Additional information" on the PDF.

## Routes

| Route | Purpose |
| --- | --- |
| `GET/POST /metadata/fields`, `GET/PATCH/DELETE /metadata/fields/{id}` | Field definitions |
| `GET/POST /metadata/templates`, `GET/PATCH/DELETE /metadata/templates/{id}` | Templates with their fields |
| `POST /metadata/templates/{id}/activate` | Make a template the active one for its kind |

Scopes: `metadata:read`, `metadata:write`. Business users manage the same objects under Settings → Documents → Custom fields.
