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

# Engine configuration

> Edit an engine's draft configuration safely, validate it, and read the contract your integration builds against.

Every engine has one draft configuration: a JSON document whose shape depends on the engine type. You edit the draft as often as you like. A run submitted through the API executes a released version, which is an immutable copy of the draft taken when you publish, unless the run pins `draft` from a sandbox key. This split lets you change an engine without changing what production callers get until you decide to. Three settings are the exception; see [Settings read from the saved configuration](#settings-read-from-the-saved-configuration). Publishing is covered on [Versions and releases](/engines/versions).

## How it works

1. Read the draft and the type's default with `GET /api/v1/engines/{engine_slug}/config`.
2. Check a candidate with `POST /api/v1/engines/{engine_slug}/config/validate`. Nothing is saved.
3. Save it with `PUT /api/v1/engines/{engine_slug}/config`. The whole draft is replaced, and the saved configuration is archived under its hash.
4. Publish a version when you want traffic to use it.

```text theme={null}
PUT /config ──▶ draft (hash a41c9e07d2b85f36) ──publish──▶ version 1.1 ──▶ unpinned and 1.x runs
                 └─ every saved draft archived by hash     (immutable)
```

Every saved configuration gets a 16-character hash. A run is stamped with the hash of the configuration it executes (`engine_config_version_hash`), and the worker loads that archived configuration, so a later save or publish does not change a run that already exists.

## Draft and released configuration

|            | Draft                                                                                                                                                                                                           | Released version                                       |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| Changed by | `PUT /config`, the portal configuration editor, an approved improvement proposal                                                                                                                                | Never. A new publish creates a new version.            |
| Used by    | Runs that pin `engine_version: "draft"` from a sandbox key, and runs started from the portal's Playground and Run pages (engines that require an exact version pin refuse those with `engine_version_required`) | Runs submitted through the API that do not pin `draft` |
| Shown on   | `GET /config`, the portal Contract page                                                                                                                                                                         | `GET /versions` (hashes and metadata)                  |

A save does not change what an API run executes unless the run pins `draft`: a run that omits `engine_version` uses the latest released version. See [Versions and releases](/engines/versions).

## Settings read from the saved configuration

Execution settings are versioned: a save changes only the draft, and a run executes the configuration of the version it resolved. Three settings are policies about what your systems receive or how fast they may call, and Nexio reads them from the engine's current saved configuration instead of from the version:

| Setting                      | When Nexio reads it                                             | Effect of a save                                                                                                                                                                                                                                                                          |
| ---------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `expose_warnings`            | Each time a run is read with `GET /api/v1/runs/{run_id}`        | Takes effect on the next read, for every run of the engine, including runs that finished before the save.                                                                                                                                                                                 |
| `notify_on_supersede`        | When Nexio records a correction, in the per-run delivery setup  | Governs corrections recorded after the save. In the per-correction delivery setup Nexio reads the setting from the version the run executed instead, so a save or publish changes only runs of later versions that carry it. See [run.superseded](/events/webhook-events#run-superseded). |
| `quotas.requests_per_minute` | On requests to the engine's routes, cached for up to 30 seconds | Takes effect within 30 seconds of the save, without a publish.                                                                                                                                                                                                                            |

## Read the configuration

<CodeGroup>
  ```bash curl theme={null}
  curl -s https://api.usenexio.com/api/v1/engines/vendor-review/config \
    -H "Authorization: Bearer $NEXIO_API_KEY"
  ```

  ```python Python theme={null}
  import os

  import requests

  resp = requests.get(
      "https://api.usenexio.com/api/v1/engines/vendor-review/config",
      headers={"Authorization": "Bearer " + os.environ["NEXIO_API_KEY"]},
      timeout=30,
  )
  body = resp.json()
  print(body["engine_type"], body["config_updated_at"], sorted(body["preset"].keys()))
  ```

  ```typescript TypeScript theme={null}
  const resp = await fetch("https://api.usenexio.com/api/v1/engines/vendor-review/config", {
    headers: { Authorization: `Bearer ${process.env.NEXIO_API_KEY}` },
  });
  const body = await resp.json();
  console.log(body.engine_type, body.config_updated_at, Object.keys(body.preset).sort());
  ```
</CodeGroup>

| Response field                           | Meaning                                                                                                                                       |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `config`                                 | The current draft.                                                                                                                            |
| `config_updated_at`, `config_updated_by` | When and by whom the draft was last saved. Creating an engine sets both.                                                                      |
| `engine_type`                            | The engine's type.                                                                                                                            |
| `preset`                                 | The type's default configuration. Start from it: it carries every required key, including `egress_manifest_version` where the type needs one. |
| `request_schema`                         | The typed request contract derived from the draft, as a tree of nodes.                                                                        |

## Validate before you save

`POST /config/validate` runs the same configuration checks as a save and saves nothing. A well-formed request with a `config` object returns `200` whether the config is valid or not; read `valid`. Malformed JSON, a body over 256 KiB or an absent `config` answers `400 invalid_request`, and an unknown slug answers `404 engine_not_found`.

The example below is the `entity_analysis` preset adapted for a supplier review, with one mistake: the dimension key `security` appears twice.

<CodeGroup>
  ```bash curl theme={null}
  curl -s -X POST https://api.usenexio.com/api/v1/engines/vendor-review/config/validate \
    -H "Authorization: Bearer $NEXIO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "config": {
        "schema_version": 1,
        "egress_manifest_version": "ee5f5d33e35b99102c9a8f9ae29ced43b3b7dfcbc59d34d8600383d267b7069f",
        "pack_key": "harbor_vendor_review",
        "pack_version": 1,
        "privacy_policy": { "mode": "default_deny" },
        "domain_key": "vendor",
        "response_type": "VENDOR_REVIEW",
        "analysis_instructions": "Review the supplier profile against each enabled dimension. Report every gap with a severity (HIGH, MEDIUM, LOW), a category, a title, a description and a recommendation.",
        "analysis_dimensions": [
          { "key": "security", "label": "Security", "enabled": true },
          { "key": "financial", "label": "Financial health", "enabled": true },
          { "key": "security", "label": "Security posture", "enabled": true }
        ],
        "profile_extraction_rules": [],
        "summary_counter_rules": [],
        "extra_result_sections": [],
        "deterministic_checks": [],
        "overlay_categories": [],
        "requirement_rules": [],
        "deterministic_gap_rules": [],
        "knowledge_overlay": []
      }
    }'
  ```

  ```python Python theme={null}
  import os

  import requests

  config = {
      "schema_version": 1,
      "egress_manifest_version": "ee5f5d33e35b99102c9a8f9ae29ced43b3b7dfcbc59d34d8600383d267b7069f",
      "pack_key": "harbor_vendor_review",
      "pack_version": 1,
      "privacy_policy": {"mode": "default_deny"},
      "domain_key": "vendor",
      "response_type": "VENDOR_REVIEW",
      "analysis_instructions": (
          "Review the supplier profile against each enabled dimension. Report every gap with a severity "
          "(HIGH, MEDIUM, LOW), a category, a title, a description and a recommendation."
      ),
      "analysis_dimensions": [
          {"key": "security", "label": "Security", "enabled": True},
          {"key": "financial", "label": "Financial health", "enabled": True},
          {"key": "security", "label": "Security posture", "enabled": True},
      ],
      "profile_extraction_rules": [],
      "summary_counter_rules": [],
      "extra_result_sections": [],
      "deterministic_checks": [],
      "overlay_categories": [],
      "requirement_rules": [],
      "deterministic_gap_rules": [],
      "knowledge_overlay": [],
  }

  resp = requests.post(
      "https://api.usenexio.com/api/v1/engines/vendor-review/config/validate",
      headers={"Authorization": "Bearer " + os.environ["NEXIO_API_KEY"]},
      json={"config": config},
      timeout=30,
  )
  result = resp.json()
  for issue in result["errors"]:
      print(issue["path"], issue["message"])
  ```

  ```typescript TypeScript theme={null}
  const config = {
    schema_version: 1,
    egress_manifest_version: "ee5f5d33e35b99102c9a8f9ae29ced43b3b7dfcbc59d34d8600383d267b7069f",
    pack_key: "harbor_vendor_review",
    pack_version: 1,
    privacy_policy: { mode: "default_deny" },
    domain_key: "vendor",
    response_type: "VENDOR_REVIEW",
    analysis_instructions:
      "Review the supplier profile against each enabled dimension. Report every gap with a severity (HIGH, MEDIUM, LOW), a category, a title, a description and a recommendation.",
    analysis_dimensions: [
      { key: "security", label: "Security", enabled: true },
      { key: "financial", label: "Financial health", enabled: true },
      { key: "security", label: "Security posture", enabled: true },
    ],
    profile_extraction_rules: [],
    summary_counter_rules: [],
    extra_result_sections: [],
    deterministic_checks: [],
    overlay_categories: [],
    requirement_rules: [],
    deterministic_gap_rules: [],
    knowledge_overlay: [],
  };

  const resp = await fetch(
    "https://api.usenexio.com/api/v1/engines/vendor-review/config/validate",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.NEXIO_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ config }),
    },
  );
  const result = await resp.json();
  for (const issue of result.errors) console.log(issue.path, issue.message);
  ```
</CodeGroup>

The response names the repeated key:

```json theme={null}
{
  "valid": false,
  "errors": [
    {
      "path": "analysis_dimensions.2.key",
      "message": "Duplicate analysis dimension key \"security\""
    }
  ]
}
```

Each issue has a `path`, a `message`, and, for policy issues, a `code` (for example `provider_not_approved`, `egress_manifest_version_required`, `egress_manifest_version_mismatch`). Fix the value at `path` and validate again. A valid configuration returns `{"valid": true, "errors": []}`.

## Save the draft

`PUT /api/v1/engines/{engine_slug}/config` with `{"config": {...}}` replaces the draft. The body must be the complete configuration; there is no partial update. A full example is in [Declared-contract engines](/engines/guides/contract-mode).

| Status | Code                      | Meaning                                                                 |
| ------ | ------------------------- | ----------------------------------------------------------------------- |
| 200    |                           | Saved. The body has the same shape as `GET /config`.                    |
| 400    | `invalid_request`         | `config` is missing, the body is not JSON, or the body is over 256 KiB. |
| 400    | `validation_error`        | The configuration failed validation. `details` is the list of issues.   |
| 403    | `engine_archived`         | Archived engines cannot be edited. Reactivate first.                    |
| 403    | `insufficient_capability` | A scoped key was used. Saves need the organization key.                 |
| 404    | `engine_not_found`        | No engine with that slug.                                               |

## Keys shared across types

These keys appear on more than one engine type. The Types column lists where each is accepted; a type that does not list a key refuses it when you save. Each type adds its own keys; the `preset` shows them.

| Key                          | Types                                        | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ---------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `egress_manifest_version`    | `comparison`, `entity_analysis`, `diligence` | Pins the list of approved external processors and the fields sent to them. Required when the configuration calls an external processor (a `model`, an enabled enrichment source on `entity_analysis`, an enabled grounding provider on `diligence`). When present it must equal the platform's current value. Copy it from `preset`.                                                                                                                                                                                |
| `model`                      | `comparison`, `entity_analysis`, `diligence` | The model the engine calls, by the ID of an entry in the platform model list. Nexio maintains the list. For `comparison` and `entity_analysis` you choose from it in the portal on the engine's General configuration page. A declared-contract `entity_analysis` configuration (one that declares `output_contract`) takes no model. Publishing checks the chosen model's provider against the platform's processor list. `matching` names its models under `models` instead, with the default `platform-default`. |
| `request_bounds`             | `comparison`, `entity_analysis`, `diligence` | Replaces the type's request bounds field by field, up to the platform ceiling. It can raise a bound as well as lower it. See [Request bounds](#request-bounds).                                                                                                                                                                                                                                                                                                                                                     |
| `resource_limits`            | `comparison`, `entity_analysis`, `diligence` | `max_provider_calls` (0, or 1 to 100) and `max_output_tokens` (0, or 1 to 1,000,000). Validated when you save. Not enforced when a run executes, whatever the value.                                                                                                                                                                                                                                                                                                                                                |
| `expose_warnings`            | every type except `matching`                 | When `true`, runs expose recorded warnings as `warnings` on `GET /api/v1/runs/{run_id}`. Read from the saved configuration on each read; see [Settings read from the saved configuration](#settings-read-from-the-saved-configuration).                                                                                                                                                                                                                                                                             |
| `notify_on_supersede`        | every type except `matching`                 | When `true`, the engine's runs send `run.superseded` if a corrected result is attached. Read when the correction is recorded in the per-run delivery setup, and from the version the run executed in the per-correction setup; see [Settings read from the saved configuration](#settings-read-from-the-saved-configuration) and [Defensibility](/engines/defensibility#supersession).                                                                                                                              |
| `quotas.requests_per_minute` | every type except `matching`                 | A per-engine request rate limit that replaces the organization default for routes on this engine. Read from the saved configuration; see [Settings read from the saved configuration](#settings-read-from-the-saved-configuration).                                                                                                                                                                                                                                                                                 |
| `privacy_policy`             | every type except `diligence`                | How fields with no privacy classification are treated before external calls: `default_allow` treats them as public, `default_deny` as sensitive. Fields with their own classification follow it.                                                                                                                                                                                                                                                                                                                    |
| `enrichment_sources`         | `entity_analysis`                            | External lookups, registered by Nexio, that the engine calls before analysis. Unknown and duplicate kinds are refused when you save. The registered kinds are a fixed set.                                                                                                                                                                                                                                                                                                                                          |

## Request bounds

Every run submission is measured against request bounds before a run is created. The measurement covers the submission serialized as `{"input": ..., "offerings": ...}` with object keys sorted. A configuration can set `request_bounds` on `comparison`, `entity_analysis` and `diligence` engines.

| Bound                                          | Default | `triage` | Highest value a configuration can set |
| ---------------------------------------------- | ------- | -------- | ------------------------------------- |
| `max_string_length` (characters in one string) | 16,384  | 16,384   | 65,536                                |
| `max_array_items`                              | 100     | 10,000   | 1,000                                 |
| `max_object_fields`                            | 100     | 100      | 1,000                                 |
| `max_object_depth`                             | 12      | 12       | 32                                    |
| `max_canonical_bytes` (whole submission)       | 256 KiB | 16 MiB   | 1 MiB                                 |
| HTTP body                                      | 1 MiB   | 16 MiB   | not configurable                      |

Two engines Nexio operates have larger bounds of their own.

A configuration's `request_bounds` replaces the type's profile field by field, up to the ceiling. `0` keeps the profile value. The bounds that apply are the ones in the configuration of the version the run resolves to.

`details.path` locates the value, starting at `$` for the whole submission. A violation returns `request_bound_exceeded` with status `413` for `max_canonical_bytes` and the HTTP body, and `400` for the other bounds. No run is created.

```json theme={null}
{
  "code": "request_bound_exceeded",
  "message": "The submission exceeds max_array_items: measured 140, limit 100. No run was submitted; reduce the submission or ask an operator to review this bound.",
  "details": {
    "bound": "max_array_items",
    "measured": 140,
    "actual": 140,
    "limit": 100,
    "path": "$.offerings"
  }
}
```

## The contract page

Each `comparison`, `entity_analysis`, `matching` and `triage` engine has a Contract page in the portal (Engines, then your engine, then Contract). It renders the request and response contract for the draft configuration, with three example requests (minimal, realistic and full). Two actions build integrations from it:

* **Copy for agents** copies integration instructions as one Markdown document, ready to paste into a coding agent's context.
* **OpenAPI**, then **Download openapi.json**, saves `<slug>-openapi.json`, an OpenAPI 3.1 file for this engine's `POST /api/v1/engines/{engine_slug}/runs` and `GET /api/v1/runs/{run_id}`.

The Contract page shows the draft. Unpinned runs execute the latest released version. If you have saved changes you have not published, the page describes the next version, not the one serving traffic.

## Credentials

Reading configuration accepts the organization key or a scoped key with `engines:read` bound to the engine. Validate and save accept only the organization key; a scoped key gets `403 insufficient_capability`. See [Authentication and access](/authentication).

<CardGroup cols={2}>
  <Card title="Versions and releases" href="/engines/versions">
    Publish the draft and pin versions.
  </Card>

  <Card title="Declared-contract engines" href="/engines/guides/contract-mode">
    Declare an engine's input fields and output envelope.
  </Card>

  <Card title="Update engine config" href="/api-reference/engines/update-engine-config">
    Endpoint reference.
  </Card>

  <Card title="Validate engine config" href="/api-reference/engines/validate-engine-config">
    Endpoint reference.
  </Card>
</CardGroup>
