---
title: "simulateDirect"
method: POST
path: "/v1/erp/updates/direct_simulation"
tags: ["erp"]
---

# simulateDirect

`POST /v1/erp/updates/direct_simulation`

Dry run for direct-mode payloads: validates a `DirectPayload` against a `direct: true`
use case configuration and previews the internal entity and meter reading updates the
pipeline would apply — without persisting anything.

Unlike the ingest endpoint, validation does not stop at the first problem: ALL issues
across all operations are collected into `errors`, each carrying the index of the
operation it refers to. When the payload is valid, the response additionally contains
the translated update previews plus non-blocking `warnings` (for example unique-id
fields that are not defined in the target entity schema, or a failed server-side
unique-id type derivation).

Use this endpoint while designing a direct integration, before sending events to
`POST /v3/erp/updates/events`. See documentation at /docs/DIRECT_MODE.md.

## Request body

- DirectSimulationRequest — Request for a direct-mode dry run: the `direct: true` use case configuration to test against, and the payload to validate and translate.
  - `event_configuration` InboundIntegrationEventConfiguration, required — Configuration for inbound use cases (ERP to epilot)
    - `direct` boolean — Enables direct mode for this use case. When `true`, every event routed to this use case must carry a `DirectPayload` - pre-mapped entity and meter reading operations - instead of raw ERP data, and the mapping engine is skipped entirely. Everything else in the inbound pipeline (deduplication, ordering, create-vs-update resolution, relation resolution, monitoring) behaves exactly as in mapped mode. In direct mode, `entities` doubles as an optional allowlist: when non-empty, each operation's `entity_slug` must match an entry's `entity_schema`, and when that entry declares `unique_ids`, the operation's unique-id keys must be exactly that set (or just `_id`). Entries need only `entity_schema` and `unique_ids` - `fields` is not required and is ignored. `meter_readings` is ignored in direct mode. Flag changes take up to 5 minutes to propagate (configuration cache).
    - `entities` IntegrationEntity[] — Array of entity configurations for this event
      - `entity_schema` string, required — Target entity schema (e.g., 'contact', 'contract')
      - `unique_ids` string[], required — Array of attribute names that uniquely identify this entity. The _type hint for repeatable fields (e.g., email, phone) should be specified on the corresponding field definition in the fields array.
      - `jsonataExpression` string — Optional JSONata expression to pre-process the event data before field mapping
      - `enabled` union — Controls whether this entity mapping should be processed. Can be a boolean or a JSONata expression (string) that evaluates to a boolean.
        - boolean
        - string
      - `mode` 'upsert' | 'delete' | 'purge' | 'upsert-prune-scope-purge' | 'upsert-prune-scope-delete' — Operation mode for entity mapping: - 'upsert': Create or update the entity (default) - 'delete': Soft delete the entity (marks as deleted) - 'purge': Hard delete the entity (permanent removal) - 'upsert-prune-scope-purge': Upsert entities from array, then purge entities in scope that weren't upserted - 'upsert-prune-scope-delete': Upsert entities from array, then soft delete entities in scope that weren't upserted
      - `scope` PruneScopeConfig — Scope configuration for upsert-prune-scope modes. Defines how to find entities that should be pruned if not in the upsert payload. The scope is resolved against the original event payload (not individual array items).
        - `scope_mode` 'relations' | 'query', required — Scope mode for finding entities to prune: - 'relations': Find scope by looking at all entities related to a specific entity (both direct and reverse relations) - 'query': Find scope entities directly via query parameters
        - `schema` string — For 'relations' mode: The schema of the entity to find (e.g., 'billing_account'). Not used for 'query' mode.
        - `unique_ids` RelationUniqueIdField[] — For 'relations' mode: How to identify the scope entity from the payload. Not used for 'query' mode.
          - `attribute` string, required — Target attribute name in the related entity
          - `_type` 'email' | 'phone' — Type hint for repeatable fields that require special search handling. These fields are stored as arrays of objects (e.g., email: [{ email: "value" }]).
          - `field` string — Source field name from the event data
          - `jsonataExpression` string — JSONata expression to compute the value
          - `constant` unknown
        - `query` RelationUniqueIdField[] — For 'query' mode: Direct query parameters to find scope entities. Not used for 'relations' or 'reverse-relations' modes.
          - `attribute` string, required — Target attribute name in the related entity
          - `_type` 'email' | 'phone' — Type hint for repeatable fields that require special search handling. These fields are stored as arrays of objects (e.g., email: [{ email: "value" }]).
          - `field` string — Source field name from the event data
          - `jsonataExpression` string — JSONata expression to compute the value
          - `constant` unknown
      - `fields` IntegrationEntityField[] — Field mapping definitions. Required for mapped-mode entity configurations; may be omitted for allowlist entries of a `direct: true` use case, where no mapping runs.
        - `attribute` string, required — Target attribute name
        - `field` string — Source field name or JSONPath expression (if starts with $)
        - `jsonataExpression` string — JSONata expression for transformation
        - `constant` unknown
        - `_type` 'email' | 'phone' — Type hint for repeatable fields that require special search handling. These fields are stored as arrays of objects (e.g., email: [{ email: "value" }]).
        - `enabled` union — Controls whether this field mapping should be processed. Can be a boolean or a JSONata expression (string) that evaluates to a boolean. Defaults to true if omitted.
          - boolean
          - string
        - `relations` RelationConfig
          - `operation` '_set' | '_append' | '_append_all', required — Relation operation: - '_set': Replace all existing relations with the specified items - '_append': Add new unique items to existing relations (deduplicates by entity_id) - '_append_all': Add all items to existing relations (no deduplication, allows duplicates)
          - `items` RelationItemConfig[] — Array of relation item configurations
            - `entity_schema` string, required — Related entity schema
            - `_tags` string[] — Optional tags for this relation
            - `unique_ids` RelationUniqueIdField[], required — Unique identifier mappings for the related entity
              - …
          - `jsonataExpression` string — JSONata expression that returns relation items array (alternative to 'items')
        - `relation_refs` RelationRefsConfig — Configuration for relation references ($relation_ref). Relation references link to a specific item within a repeatable attribute on a related entity. Common use case: referencing a specific address within a contact's address list.
          - `operation` '_set' | '_append' | '_append_all', required — Relation reference operation: - '_set': Replace all existing relation_refs with the specified items - '_append': Add new unique items to existing relation_refs (deduplicates by entity_id + _id) - '_append_all': Add all items to existing relation_refs (no deduplication, allows duplicates)
          - `items` RelationRefItemConfig[] — Array of relation reference item configurations
            - `entity_schema` string, required — Schema of the related entity (e.g., "contact")
            - `unique_ids` RelationUniqueIdField[], required — Unique identifier mappings for the related entity
              - …
            - `path` string, required — Attribute path on the related entity (e.g., "address")
            - `value` RelationRefValueConfig, required — Configuration for the value to set on the related entity's attribute
              - …
          - `jsonataExpression` string — JSONata expression that returns relation_ref items array (alternative to 'items')
        - `file_proxy_url` union — Auto-constructs a file proxy download URL. orgId and integrationId are injected from context. Exactly one of use_case_id or use_case_slug must be provided. Using use_case_slug is recommended as it is portable across environments.
          - object
            - `use_case_slug` string, required — Recommended. Slug of the file_proxy use case. Maps to useCaseSlug query parameter. Portable across environments.
            - `params` FileProxyUrlParams — Custom query parameters. Keys become URL param names, values resolved from payload.
          - object
            - `use_case_id` string, required — Legacy. UUID of the file_proxy use case. Maps to useCaseId query parameter. Prefer use_case_slug for portable configuration.
            - `params` FileProxyUrlParams — Custom query parameters. Keys become URL param names, values resolved from payload.
        - `portal_ref` union — Resolves to a property of one of the calling organization's epilot portal configurations at runtime, replacing hard-coded environment-specific portal UUIDs in inbound mappings. Matched portals are sorted ascending by `(_created_at, portal_id)`; portals without `_created_at` sort first (treated as oldest). When `select: "single"` matches more than one portal, the resolver still returns the oldest match and emits a `PORTAL_REF_AMBIGUOUS` warning.
          - object
            - `filter` PortalRefFilter — Filter applied to the org's portal configurations before selection. All filters default to "match any" except `enabled` (default `true`) and `is_dummy` (default `false`). Set `enabled` or `is_dummy` to `null` to opt out of the default.
              - …
            - `select` 'single' | 'all' — `single` returns one literal value (the oldest matching portal); `all` returns an array of literal values (0, 1, or many).
            - `return` 'portal_id' | 'origin' | 'domain' | 'name' — Which portal field to emit.
          - object
            - `filter` PortalRefFilter — Filter applied to the org's portal configurations before selection. All filters default to "match any" except `enabled` (default `true`) and `is_dummy` (default `false`). Set `enabled` or `is_dummy` to `null` to opt out of the default.
              - …
            - `select` 'single' | 'all'
            - `return` 'jsonata', required — Evaluate the sibling `jsonataExpression` against the matched portal(s). With `select: "single"` the input is the matched PortalConfig object; with `select: "all"` it is the full filtered+sorted array.
            - `jsonataExpression` string, required — JSONata expression evaluated against the matched portal(s).
        - `env_var_ref` union — Resolves to an org-scoped environment variable from the epilot environments-api service at runtime, replacing hard-coded environment-specific values (URLs, prefixes, identifiers) in inbound mappings. Secrets (`SecretString` values) are never exposed; the runtime treats both "missing key" and "secret-typed key" as identical `undefined` outcomes (no info disclosure via error code). For secret-resolving contexts (e.g. authorization headers in managed-call or file-proxy step configurations), use the templated `{{ env.<key> }}` syntax instead — that mechanism does decrypt secrets.
          - object
            - `key` string, required — Environment variable key. Must match the environments-api key contract (lowercase, digits, `_`, `.`, `-`; max 128 chars; starts with a lowercase letter or digit). Supports dot-namespaced keys like `erp_api.base_url`.
            - `default` string — Literal string returned when the key is missing (or is a secret). When provided, the `ENV_VAR_REF_NOT_FOUND` warning is suppressed because the author signalled the absence is expected.
            - `return` 'value' — `value` returns the resolved env-var string as-is.
          - object
            - `key` string, required
            - `default` string
            - `return` 'jsonata', required — Evaluate the sibling `jsonataExpression` against the resolved env-var value (the input `$` is the string value).
            - `jsonataExpression` string, required — JSONata expression evaluated against the resolved env-var value.
    - `meter_readings` IntegrationMeterReading[] — Array of meter reading configurations for this event
      - `jsonataExpression` string — Optional JSONata expression to extract meter reading items from the event data. If not provided, the entire payload is used as the reading data. Useful when you need to extract an array of readings from a nested structure (e.g., "$.readings").
      - `reading_matching` 'external_id' | 'strict-date' — Strategy for matching incoming readings against existing readings. - 'external_id': Match readings by external_id attribute (default behavior) - 'strict-date': Match by meter_id + counter_id + direction + date (German timezone). Useful when readings originate from ECP and are echoed back by the ERP with truncated timestamps.
      - `mode` 'upsert' | 'delete' | 'upsert-prune-scope' — Operation mode for meter reading mapping: - 'upsert': Create or update meter readings (default) - 'delete': Delete the meter reading - 'upsert-prune-scope': Upsert readings from array, then delete all other readings for the same meter+counter that weren't upserted
      - `scope` MeterReadingPruneScopeConfig — Scope configuration for meter reading upsert-prune-scope mode. The scope is all readings for the same meter + counter.
        - `source` string — Optional source filter. When set, only readings with this source are eligible for pruning (e.g., 'ERP' to only prune ERP-synced readings).
      - `meter` MeterUniqueIdsConfig, required
        - `unique_ids` RelationUniqueIdField[], required — Array of unique identifier field mappings
          - `attribute` string, required — Target attribute name in the related entity
          - `_type` 'email' | 'phone' — Type hint for repeatable fields that require special search handling. These fields are stored as arrays of objects (e.g., email: [{ email: "value" }]).
          - `field` string — Source field name from the event data
          - `jsonataExpression` string — JSONata expression to compute the value
          - `constant` unknown
      - `meter_counter` MeterUniqueIdsConfig
        - `unique_ids` RelationUniqueIdField[], required — Array of unique identifier field mappings
          - `attribute` string, required — Target attribute name in the related entity
          - `_type` 'email' | 'phone' — Type hint for repeatable fields that require special search handling. These fields are stored as arrays of objects (e.g., email: [{ email: "value" }]).
          - `field` string — Source field name from the event data
          - `jsonataExpression` string — JSONata expression to compute the value
          - `constant` unknown
      - `fields` IntegrationEntityField[], required — Field mapping definitions for meter reading attributes
        - `attribute` string, required — Target attribute name
        - `field` string — Source field name or JSONPath expression (if starts with $)
        - `jsonataExpression` string — JSONata expression for transformation
        - `constant` unknown
        - `_type` 'email' | 'phone' — Type hint for repeatable fields that require special search handling. These fields are stored as arrays of objects (e.g., email: [{ email: "value" }]).
        - `enabled` union — Controls whether this field mapping should be processed. Can be a boolean or a JSONata expression (string) that evaluates to a boolean. Defaults to true if omitted.
          - boolean
          - string
        - `relations` RelationConfig
          - `operation` '_set' | '_append' | '_append_all', required — Relation operation: - '_set': Replace all existing relations with the specified items - '_append': Add new unique items to existing relations (deduplicates by entity_id) - '_append_all': Add all items to existing relations (no deduplication, allows duplicates)
          - `items` RelationItemConfig[] — Array of relation item configurations
            - `entity_schema` string, required — Related entity schema
            - `_tags` string[] — Optional tags for this relation
            - `unique_ids` RelationUniqueIdField[], required — Unique identifier mappings for the related entity
              - …
          - `jsonataExpression` string — JSONata expression that returns relation items array (alternative to 'items')
        - `relation_refs` RelationRefsConfig — Configuration for relation references ($relation_ref). Relation references link to a specific item within a repeatable attribute on a related entity. Common use case: referencing a specific address within a contact's address list.
          - `operation` '_set' | '_append' | '_append_all', required — Relation reference operation: - '_set': Replace all existing relation_refs with the specified items - '_append': Add new unique items to existing relation_refs (deduplicates by entity_id + _id) - '_append_all': Add all items to existing relation_refs (no deduplication, allows duplicates)
          - `items` RelationRefItemConfig[] — Array of relation reference item configurations
            - `entity_schema` string, required — Schema of the related entity (e.g., "contact")
            - `unique_ids` RelationUniqueIdField[], required — Unique identifier mappings for the related entity
              - …
            - `path` string, required — Attribute path on the related entity (e.g., "address")
            - `value` RelationRefValueConfig, required — Configuration for the value to set on the related entity's attribute
              - …
          - `jsonataExpression` string — JSONata expression that returns relation_ref items array (alternative to 'items')
        - `file_proxy_url` union — Auto-constructs a file proxy download URL. orgId and integrationId are injected from context. Exactly one of use_case_id or use_case_slug must be provided. Using use_case_slug is recommended as it is portable across environments.
          - object
            - `use_case_slug` string, required — Recommended. Slug of the file_proxy use case. Maps to useCaseSlug query parameter. Portable across environments.
            - `params` FileProxyUrlParams — Custom query parameters. Keys become URL param names, values resolved from payload.
          - object
            - `use_case_id` string, required — Legacy. UUID of the file_proxy use case. Maps to useCaseId query parameter. Prefer use_case_slug for portable configuration.
            - `params` FileProxyUrlParams — Custom query parameters. Keys become URL param names, values resolved from payload.
        - `portal_ref` union — Resolves to a property of one of the calling organization's epilot portal configurations at runtime, replacing hard-coded environment-specific portal UUIDs in inbound mappings. Matched portals are sorted ascending by `(_created_at, portal_id)`; portals without `_created_at` sort first (treated as oldest). When `select: "single"` matches more than one portal, the resolver still returns the oldest match and emits a `PORTAL_REF_AMBIGUOUS` warning.
          - object
            - `filter` PortalRefFilter — Filter applied to the org's portal configurations before selection. All filters default to "match any" except `enabled` (default `true`) and `is_dummy` (default `false`). Set `enabled` or `is_dummy` to `null` to opt out of the default.
              - …
            - `select` 'single' | 'all' — `single` returns one literal value (the oldest matching portal); `all` returns an array of literal values (0, 1, or many).
            - `return` 'portal_id' | 'origin' | 'domain' | 'name' — Which portal field to emit.
          - object
            - `filter` PortalRefFilter — Filter applied to the org's portal configurations before selection. All filters default to "match any" except `enabled` (default `true`) and `is_dummy` (default `false`). Set `enabled` or `is_dummy` to `null` to opt out of the default.
              - …
            - `select` 'single' | 'all'
            - `return` 'jsonata', required — Evaluate the sibling `jsonataExpression` against the matched portal(s). With `select: "single"` the input is the matched PortalConfig object; with `select: "all"` it is the full filtered+sorted array.
            - `jsonataExpression` string, required — JSONata expression evaluated against the matched portal(s).
        - `env_var_ref` union — Resolves to an org-scoped environment variable from the epilot environments-api service at runtime, replacing hard-coded environment-specific values (URLs, prefixes, identifiers) in inbound mappings. Secrets (`SecretString` values) are never exposed; the runtime treats both "missing key" and "secret-typed key" as identical `undefined` outcomes (no info disclosure via error code). For secret-resolving contexts (e.g. authorization headers in managed-call or file-proxy step configurations), use the templated `{{ env.<key> }}` syntax instead — that mechanism does decrypt secrets.
          - object
            - `key` string, required — Environment variable key. Must match the environments-api key contract (lowercase, digits, `_`, `.`, `-`; max 128 chars; starts with a lowercase letter or digit). Supports dot-namespaced keys like `erp_api.base_url`.
            - `default` string — Literal string returned when the key is missing (or is a secret). When provided, the `ENV_VAR_REF_NOT_FOUND` warning is suppressed because the author signalled the absence is expected.
            - `return` 'value' — `value` returns the resolved env-var string as-is.
          - object
            - `key` string, required
            - `default` string
            - `return` 'jsonata', required — Evaluate the sibling `jsonataExpression` against the resolved env-var value (the input `$` is the string value).
            - `jsonataExpression` string, required — JSONata expression evaluated against the resolved env-var value.
  - `payload` union, required — The direct payload to validate - a `DirectPayload` object, or its JSON string form.
    - string — The direct payload as a JSON string (as it would be sent in the event's `payload`).
    - DirectPayload — The payload of an ERP event routed to a `direct: true` use case: a versioned envelope of pre-mapped operations that skip the mapping engine. Sent as the `payload` of an `ErpEventV3` - either as a JSON object or as a JSON string (`format` must be `json`; XML is rejected for direct use cases). The schema is strict: unknown keys on the envelope or on any operation are rejected with an error naming the offending path. Attribute values, by contrast, pass through to the entity verbatim (except `$relation` / `$relation_ref` envelopes, which are validated and resolved). One event lands as one queue message, so the whole event (including this payload) must stay within the 256 KiB message budget. Identical consecutive payloads within 5 minutes can be deduplicated by the queue - use the event's `deduplication_id` deliberately when re-sending identical data.
      - `version` string, required — Version of the direct payload contract. Currently only `"1"` is supported; an unsupported version is rejected with `DIRECT_VERSION_UNSUPPORTED`. Deliberately not an enum: the constraint is enforced by the handler so the dry-run endpoint returns a structured `valid: false` verdict instead of a schema-level 400.
      - `operations` union[], required — The operations to apply, in order. At most 100 operations per event (enforced by the handler, with a structured error naming the limit). Each item is either an entity operation (`type` omitted or `"entity"`) or a meter reading operation (`type: "meter_reading"`).
        - union
          - DirectEntityOperation — A single pre-mapped entity operation: upserts, soft-deletes, or purges one entity, identified by its unique ids. Produces the same internal update as a mapped-mode entity configuration, so create-vs-update resolution, relation resolution, ordering, and monitoring behave identically.
            - `type` 'entity' — Operation discriminator. Optional - an operation without `type` is an entity operation.
            - `entity_slug` string, required — Target entity schema slug (e.g. `contact`, `contract`).
            - `unique_ids` object, required — Attribute values that uniquely identify this entity, as `{ attribute: value }`. Values may be strings or numbers (numbers are coerced to strings); values that are empty after trimming are rejected. Use `_id` as the sole key to reference an entity directly by its epilot id. Unlike mapped mode, the unique-id ATTRIBUTE NAMES are given directly - there is no field-to-attribute mapping step.
            - `unique_id_types` object — Optional per-field type hints for repeatable unique-id fields (`email` / `phone`), needed so lookups search the repeatable storage format correctly. When omitted, the server derives the types from the entity schema; explicit values always win over derivation.
            - `mode` 'upsert' | 'delete' | 'purge' — Operation mode: `upsert` creates or updates the entity (default), `delete` soft deletes it, `purge` removes it permanently. For `delete` / `purge`, `attributes` may be omitted - only `unique_ids` are used to locate the entity.
            - `attributes` object — Attribute values to write, keyed by attribute name. Required when `mode` is `upsert` (an empty object is allowed); optional for `delete` / `purge`. Values are written verbatim - the server does not validate them against the entity schema (attributes not defined in the schema are stored but not indexed; the simulation endpoint surfaces warnings for unique-id fields missing from the schema). Two envelope forms receive special treatment: `$relation` values (`DirectRelationValue`) and `$relation_ref` values (`DirectRelationRefValue`) are validated and resolved to entity relations.
          - DirectMeterReadingOperation — A single pre-mapped meter reading operation: upserts or deletes one reading on a meter (and optionally a specific counter), identified by their unique ids. Produces the same internal update as a mapped-mode meter reading configuration.
            - `type` 'meter_reading', required — Operation discriminator. Required for meter reading operations.
            - `meter` object, required — Reference to the meter this reading belongs to.
              - …
            - `counter` object — Optional reference to the meter counter (register) the reading belongs to. Required when `reading_matching` is `strict-date`.
              - …
            - `mode` 'upsert' | 'delete' — Operation mode: `upsert` creates or updates the reading (default), `delete` removes it.
            - `reading_matching` 'external_id' | 'strict-date' — Strategy for matching this reading against existing readings. `external_id` (the default) matches by the reading's `external_id`; `strict-date` matches by meter + counter + direction + date and REQUIRES `counter`. Useful when readings originate in the portal and are echoed back by the ERP with truncated timestamps.
            - `attributes` object, required — The reading data. Additional keys (e.g. `direction`, `reason`, `read_by`, `status`, `metadata`) pass through to the metering service verbatim.
              - …

## Response `200`

Simulation executed - `valid` reports the verdict, `errors` lists every violation

- DirectSimulationResponse — Result of a direct-mode dry run. `valid: false` responses list EVERY violation found across all operations in `errors`. `valid: true` responses preview the internal updates the pipeline would apply, plus non-blocking `warnings`.
  - `valid` boolean, required — Whether the payload would be accepted by the ingest endpoint.
  - `errors` DirectSimulationError[], required — All validation errors found. Empty when `valid` is `true`.
    - `code` 'DIRECT_PAYLOAD_INVALID' | 'DIRECT_VERSION_UNSUPPORTED' | 'DIRECT_ENTITY_NOT_ALLOWED', required — Error class, matching the monitoring codes the ingest pipeline emits: `DIRECT_PAYLOAD_INVALID` (schema violation, JSON parse failure, or XML format), `DIRECT_VERSION_UNSUPPORTED` (unknown payload `version`), `DIRECT_ENTITY_NOT_ALLOWED` (entity or unique-id keys not permitted by the use case's allowlist).
    - `message` string, required — Human-readable description, including the path of the offending element.
    - `operation_index` integer — Zero-based index of the operation the error refers to, when attributable to one.
  - `warnings` MappingSimulationWarning[] — Non-blocking findings - e.g. unique-id fields not defined in the target entity schema (lookups would never match), or a failed server-side unique-id type derivation.
    - `entity_schema` string, required — The entity schema slug where the issue was found
    - `field` string, required — The field name that caused the warning
    - `message` string, required — Description of the validation issue
  - `entity_updates` object[] — The translated internal entity updates, exactly as the pipeline would process them. Only present when `valid` is `true`.
    - `entity_slug` string, required — The entity type slug
    - `unique_identifiers` object, required — Unique identifier mappings for this entity
    - `attributes` object, required — Mapped attribute values
    - `pricing` object — Present when the entity mapping has a `pricing` block. Echoes the pricing configuration and the data extracted for it, so mapping authors can see what would be sent to the pricing service.
      - `config` object, required — The pricing configuration from the entity mapping
      - `data` object[], required — The pricing input data extracted from the payload
    - `mode` 'upsert' | 'delete' | 'purge', required — Effective operation mode applied to this entity at runtime. `upsert-prune-scope-purge` / `upsert-prune-scope-delete` configurations report `upsert` here, because the individual entities in the payload are upserted — the destructive part of those modes is reported separately in `prune_scope_updates`. For `delete` / `purge`, `attributes` are still mapped and returned but ignored at runtime: only `unique_identifiers` are used to locate the entity to remove.
    - `unique_identifiers_metadata` object — Per-field metadata for repeatable unique-id fields - explicit `unique_id_types` merged with server-side schema derivation. Absent when no field resolved to a repeatable type.
  - `meter_reading_updates` object[] — The translated internal meter reading updates. Only present when `valid` is `true`.
    - `meter` object, required
      - `$entity_unique_ids` object, required — Unique identifiers for the meter
    - `meter_counter` object
      - `$entity_unique_ids` object — Unique identifiers for the meter counter
    - `attributes` object, required — Meter reading attributes. Required: external_id, timestamp, source, value. `timestamp` must be ISO 8601 — either `YYYY-MM-DD` or `YYYY-MM-DDTHH:mm:ss` (with optional fractional seconds and optional `Z` / `±HH:mm` timezone offset); non-ISO formats (e.g. `DD.MM.YYYY` or epoch numbers) are rejected and must be converted upstream via a `jsonataExpression` (e.g. `$fromMillis(...)`). Date-only values are normalized to midnight UTC and offset-less date-times are anchored to UTC before being forwarded to the metering API. `source` must be one of: ECP, ERP, 360, journey-submission. `reason` (optional) must be one of: regular, irregular, last, first, meter_change, contract_change, meter_adjustment (or empty/null).
    - `mode` 'upsert' | 'delete', required — Effective operation mode applied to this reading at runtime. `upsert-prune-scope` configurations report `upsert` here — their destructive part is reported separately in `meter_readings_prune_scope_updates`.
    - `_config` object — Per-reading pipeline configuration carried by the operation.
      - `reading_matching` 'external_id' | 'strict-date'

## Other responses

- `400` — Bad request
- `401` — Unauthorized request
- `422` — Unprocessable entity - simulation could not be executed
- `500` — Internal Server Error

---

[API](https://skmtc.dev/epilot/apis/integration-toolkit-api.md) · [All operations](https://skmtc.dev/epilot/apis/integration-toolkit-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/epilot/integration-toolkit-api/revisions/a765dc127359/schema)
